* feat!(frontend): Drop react-scripts

* chore(frontend): Add missing dependency on escape-html

* refactor(frontend): Use ~ to prefix paths from src/ root

* chore(frontend): Add & configure jest & ts-jest

* chore(frontend): Fix bugs & tests revealed by updated jest, skipping EntityDetails

* chore(frontend): Add ESLint as explicit dev dependency; satisfy linter

* chore(frontend): Clean up dependencies, using caret ^ range for all

* chore(frontend): Switch from yarn to npm & configure as a workspace

Prettier & ESLint dependencies are now defined in the root package.json

Changing package management means that many path & minor updates are
simultaneously applied via the caret ^ ranges.

The local/pontoon-frontend Docker image now uses node:16 as a base
and /app/frontend rather than /frontend for its contents & workdir,
in order to accommodate the changed topography.

* chore: Collect devDependencies in root package.json

* chore(tag-admin): Move Babel config to repo root

* feat(frontend): Adjust public/ for being served as Django static files

* chore(frontend): Add build with Rollup

* feat(pontoon): Drop frontend proxy, server static files normally, add pipeline for frontend

* chore(server): Add dependencies via pontoon/package.json; clean up Dockerfile

* chore: Add build:prod targets for frontend & tag-admin

frontend image droapped from Dockerfile as unused

styled-components updated to v5 to avoid a broken import in v4.

* chore: Add watch targets for workspaces & root, using concurrently

* chore: Code review & CI-identified fixes

* chore: Refresh lockfiles, dropping frontend/yaml.lock

* chore: Add /bin/watch.sh to watch all builds; refactor `make run`

* ci: Fix frontend TS test CLI args due to yarn -> npm change

* chore: Use `npm start` rather than `npm run watch`

* chore(pontoon): Mark django-pipeline JS dependencies as devDependencies

* refactor: Rename frontend/ as translate/
This commit is contained in:
Eemeli Aro 2022-03-02 09:10:42 -06:00 коммит произвёл GitHub
Родитель 8d2714d5db
Коммит 9fc9430cc7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
525 изменённых файлов: 10207 добавлений и 19280 удалений

Просмотреть файл

@ -8,7 +8,7 @@ flags:
frontend:
paths:
- frontend/
- translate/
carryforward: true
non-frontend-js:

Просмотреть файл

@ -11,4 +11,5 @@ exclude =
setup.py,
node_modules,
bin,
frontend
tag-admin,
translate

2
.github/actions/check-tsc/action.yml поставляемый
Просмотреть файл

@ -4,7 +4,7 @@ author: 'Pontoon'
inputs:
run:
required: true
description: 'yarn tsc command'
description: 'tsc command'
default: 'default value if applicable'
working-directory:
required: false

4
.github/actions/check-tsc/index.js поставляемый
Просмотреть файл

@ -5,8 +5,8 @@ const { exec } = require('child_process');
async function run() {
console.log('::group::tsc');
let errors = '0';
const run = process.env['INPUT_RUN'] || 'yarn types --pretty ';
const cwd = process.env['INPUT_WORKING-DIRECTORY'] || 'frontend';
const run = process.env['INPUT_RUN'] || 'npm run types --pretty ';
const cwd = process.env['INPUT_WORKING-DIRECTORY'] || 'translate';
let stdout, stderr;
try {
({ stdout, stderr } = await asyncExec(run, {

45
.github/workflows/frontend.yml поставляемый
Просмотреть файл

@ -5,13 +5,13 @@ on:
branches:
- master
paths:
- frontend/**
- translate/**
- .github/workflows/frontend.yml
pull_request:
branches:
- master
paths:
- frontend/**
- translate/**
- .github/workflows/frontend.yml
workflow_dispatch:
@ -22,17 +22,15 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
with: { node-version: '16' }
- name: Install dependencies
run: yarn
working-directory: frontend
run: npm ci
- name: Check TypeScript
run: yarn types --pretty
working-directory: frontend
run: npm run types -- --pretty
working-directory: translate
- name: build
run: yarn build
working-directory: frontend
run: npm run build
working-directory: translate
ts-strict:
name: Stricter TypeScript
@ -42,29 +40,27 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
with: { node-version: '16' }
- name: Install dependencies
run: yarn
working-directory: frontend
run: npm ci
- id: noImplicitAny
name: noImplicitAny
uses: ./.github/actions/check-tsc
with:
run: yarn types --pretty --noImplicitAny
working-directory: frontend
run: npm run types -- --pretty --noImplicitAny
working-directory: translate
- id: strictNullChecks
name: strictNullChecks
uses: ./.github/actions/check-tsc
with:
run: yarn types --pretty --strictNullChecks
working-directory: frontend
run: npm run types -- --pretty --strictNullChecks
working-directory: translate
- id: strict
name: strict
uses: ./.github/actions/check-tsc
with:
run: yarn types --pretty --strict
working-directory: frontend
run: npm run types -- --pretty --strict
working-directory: translate
- name: Summary
run: |
echo "noImplicitAny: ${{ steps.noImplicitAny.outputs.errors }}"
@ -79,12 +75,11 @@ jobs:
with:
node-version: '14'
- name: Install globals
run: npm install --global codecov
run: npm install --global codecov npm@8
- name: Install dependencies
run: npm install
working-directory: frontend
run: npm ci
- name: Test
run: yarn test --coverage
working-directory: frontend
run: npm test --coverage
working-directory: translate
- name: codecov.io
run: codecov -F frontend

3
.github/workflows/js-lint.yml поставляемый
Просмотреть файл

@ -37,9 +37,6 @@ jobs:
with: { node-version: '16' }
- name: Install dependencies
run: npm ci
- name: Install frontend
run: yarn
working-directory: frontend
- name: eslint
run: npm run eslint

1
.github/workflows/tag-admin.yml поставляемый
Просмотреть файл

@ -32,6 +32,7 @@ jobs:
run: npm ci
- name: Test
run: npm test
working-directory: tag-admin
- name: codecov.io
run: codecov -F non-frontend-js
- name: Build

10
.gitignore поставляемый
Просмотреть файл

@ -28,16 +28,6 @@ venv
coverage/
# Docker
.frontend-build
.server-build
docker/config/server.env
docker/config/server.env-e
# Obsolete Docker
.docker-build
docker/config/webapp.env
docker/config/webapp.env-e
# Webpack
/assets/
webpack-stats.json

Просмотреть файл

@ -1,7 +1,7 @@
# minified code
*.min.js
*.min.css
frontend/build
translate/dist
tag-admin/dist
coverage/

Просмотреть файл

@ -172,7 +172,7 @@ Additionally, there are linting rules that are defined in our
In the rare case when you cannot fix an eslint error, use ``// eslint-disable`` to make the linter
ignore that error. Note that in most cases, it is better to fix the issues than ignore them.
For more specifics about the ```frontend`` folder, look at the README.md file there.
For more specifics about the ```translate`` folder, look at the README.md file there.
Git conventions
@ -309,7 +309,7 @@ To run the entire test suite, do:
$ make test
To run only the ``frontend`` tests:
To run only the ``translate`` tests:
.. code-block:: shell
@ -395,24 +395,3 @@ steps, as they don't affect your setup if nothing has changed:
# Run database migrations.
python manage.py migrate
Building front-end resources
============================
We use Rollup to build our JavaScript files for some pages
(currently only the tag admin UI).
While `make build` will build those files for you,
you might want to rebuild them while programming on the front.
To build the files just once, run:
.. code-block:: shell
$ npm run build
If you want to have those files be built automatically when you make changes,
you can run:
.. code-block:: shell
$ npm run build-w

Просмотреть файл

@ -4,19 +4,18 @@ DOCKER := $(shell which docker)
# *IMPORTANT*
# Don't use this instance in a production setting. More info at:
# https://docs.djangoproject.com/en/dev/ref/django-admin/#runserver
FRONTEND_URL ?= http://frontend:3000
SITE_URL ?= http://localhost:8000
USER_ID?=1000
GROUP_ID?=1000
.PHONY: build build-frontend build-server server-env setup run clean shell ci test test-frontend test-server jest pytest format lint types eslint prettier check-prettier flake8 pyupgrade check-pyupgrade black check-black dropdb dumpdb loaddb build-tagadmin build-tagadmin-w sync-projects requirements
.PHONY: build build-translate build-tagadmin build-server server-env setup run clean shell ci test test-translate test-tagadmin test-server jest pytest format lint types eslint prettier check-prettier flake8 pyupgrade check-pyupgrade black check-black dropdb dumpdb loaddb sync-projects requirements
help:
@echo "Welcome to Pontoon!\n"
@echo "The list of commands for local development:\n"
@echo " build Builds the docker images for the docker-compose setup"
@echo " build-frontend Builds just the frontend image"
@echo " build-translate Builds just the translate frontend component"
@echo " build-tagadmin Builds just the tag-admin frontend component"
@echo " build-server Builds just the Django server image"
@echo " server-env Regenerates the env variable file used by server"
@ -24,17 +23,17 @@ help:
@echo " run Runs the whole stack, served on http://localhost:8000/"
@echo " clean Forces a rebuild of docker containers"
@echo " shell Opens a Bash shell in a server docker container"
@echo " ci Test and lint both frontend and server"
@echo " test Runs both frontend and server test suites"
@echo " test-frontend Runs the translate frontend test suite (Jest)"
@echo " ci Test and lint all code"
@echo " test Runs all test suites"
@echo " test-translate Runs the translate frontend test suite (Jest)"
@echo " test-tagadmin Runs the tag-admin test suite (Jest)"
@echo " test-server Runs the server test suite (Pytest)"
@echo " format Runs formatters for both the frontend and Python code"
@echo " lint Runs linters for both the frontend and Python code"
@echo " format Runs all formatters"
@echo " lint Runs all linters"
@echo " types Runs the tsc compiler to check TypeScript on all frontend code"
@echo " eslint Runs a code linter on the JavaScript code"
@echo " prettier Runs the prettier formatter on the frontend code"
@echo " check-prettier Runs a check for format issues with the prettier formatter"
@echo " prettier Runs the Prettier formatter"
@echo " check-prettier Runs a check for format issues with the Prettier formatter"
@echo " flake8 Runs the flake8 style guides on all Python code"
@echo " pyupgrade Upgrades all Python code to newer syntax of Python"
@echo " check-pyupgrade Runs a check for outdated syntax of Python with the pyupgrade formatter"
@ -46,8 +45,8 @@ help:
@echo " sync-projects Runs the synchronization task on all projects"
@echo " requirements Compiles all requirements files with pip-compile\n"
.frontend-build:
make build-frontend
translate/dist:
make build-translate
tag-admin/dist:
make build-tagadmin
.server-build:
@ -55,42 +54,43 @@ tag-admin/dist:
node_modules:
npm install
build: build-frontend build-tagadmin build-server
build: build-translate build-tagadmin build-server
build-frontend: server-env
"${DC}" build frontend
touch .frontend-build
build-translate: node_modules
npm run build -w translate
build-tagadmin: node_modules
npm run build -w tag-admin
build-server: server-env
build-server: server-env translate/dist tag-admin/dist
"${DC}" build --build-arg USER_ID=$(USER_ID) --build-arg GROUP_ID=$(GROUP_ID) server
touch .server-build
server-env:
cp ./docker/config/server.env.template ./docker/config/server.env
sed -i -e 's/#FRONTEND_URL#/$(subst /,\/,${FRONTEND_URL})/g;s/#SITE_URL#/$(subst /,\/,${SITE_URL})/g' ./docker/config/server.env
sed -e 's/#SITE_URL#/$(subst /,\/,${SITE_URL})/g' \
./docker/config/server.env.template > ./docker/config/server.env
setup: .server-build
"${DC}" run server //app/docker/server_setup.sh
run: .frontend-build tag-admin/dist .server-build
"${DC}" up
run: translate/dist tag-admin/dist .server-build
"${DC}" up --detach
bash -c 'set -m; bash ./bin/watch.sh'
"${DC}" stop
clean:
rm -rf .docker-build .frontend-build tag-admin/dist .server-build
rm -rf translate/dist tag-admin/dist .server-build
shell:
"${DC}" run --rm server //bin/bash
ci: test lint
test: test-server test-frontend test-tagadmin
test: test-server test-translate test-tagadmin
test-frontend: jest
test-translate: jest
jest:
"${DC}" run --rm -w //frontend frontend yarn test
npm test -w translate
test-tagadmin:
npm test -w tag-admin
@ -104,19 +104,16 @@ format: prettier pyupgrade black
lint: types eslint check-prettier flake8 check-pyupgrade check-black
types:
"${DC}" run --rm -w //frontend frontend yarn types
npm run types -w translate
eslint:
"${DC}" run --rm frontend npm run lint
"${DC}" run --rm server npm run eslint
npm run eslint
prettier:
"${DC}" run --rm frontend npm run prettier
"${DC}" run --rm server npm run prettier
npm run prettier
check-prettier:
"${DC}" run --rm frontend npm run check-prettier
"${DC}" run --rm server npm run check-prettier
npm run check-prettier
flake8:
"${DC}" run --rm server flake8 pontoon/

Просмотреть файл

Просмотреть файл

@ -8,15 +8,11 @@ fi
# Compile static assets
export PATH=/app/.heroku/node/bin:$PATH
npm install --global npm@8 yarn
npm install --global npm@8
npm ci
pushd frontend && yarn && popd
echo "Building tag-admin..."
npm run build
echo "Building frontend..."
pushd frontend && yarn run build && popd
echo "Building translate & tag-admin..."
npm run build:prod
echo "Collecting static files..."
./manage.py migrate --noinput

14
bin/watch.sh Executable file
Просмотреть файл

@ -0,0 +1,14 @@
#!/bin/bash
# Traps & exits on SIGINT, so calling from make as
# bash -c 'set -m; bash ./bin/watch.sh'
# will not cause the parent process to exit.
# Source: https://stackoverflow.com/a/65935205
sigint_handler() { exit 0; }
trap sigint_handler INT
npx concurrently -n translate,tagadmin,server,pg -c cyan,magenta,green,red \
'npm start -w translate' \
'npm start -w tag-admin' \
'docker-compose logs --tail=0 --follow --no-log-prefix server' \
'docker-compose logs --tail=0 --follow --no-log-prefix postgresql' \

Просмотреть файл

@ -3,18 +3,6 @@
# Note: Requires docker-compose 1.10+.
version: "2.3"
services:
frontend:
build:
context: .
dockerfile: ./docker/Dockerfile
target: frontend
image: local/pontoon-frontend
ports:
- "3000:3000"
volumes:
- ./frontend/src:/frontend/src
- ./frontend/public:/frontend/public
server:
build:
context: .
@ -29,8 +17,9 @@ services:
- "8000:8000"
volumes:
- ./pontoon:/app/pontoon
- ./tag-admin:/app/tag-admin
- ./requirements:/app/requirements
- ./tag-admin:/app/tag-admin
- ./translate:/app/translate
- ~/.ssh:/home/pontoon/.ssh
# Database

Просмотреть файл

@ -1,12 +1,3 @@
# Frontend build
FROM node:14-bullseye AS frontend
WORKDIR /frontend
COPY frontend/package.json frontend/yarn.lock ./
RUN yarn install
COPY frontend/ .
RUN yarn build
CMD ["yarn", "start"]
FROM python:3.9-bullseye AS server
ARG USER_ID=1000
@ -20,19 +11,12 @@ ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONPATH /app
# JavaScript applications paths
ENV YUGLIFY_BINARY /app/node_modules/.bin/yuglify
ENV TERSER_BINARY /app/node_modules/.bin/terser
# Install required software.
RUN apt-get update \
# Enable downloading packages over https
&& apt-get install -y apt-transport-https \
# Get source for node.js
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
# Get source for yarn
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo 'deb https://dl.yarnpkg.com/debian/ stable main' > /etc/apt/sources.list.d/yarn.list \
&& curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \
# Install software
&& apt-get update \
&& apt-get install -y --no-install-recommends \
@ -41,8 +25,6 @@ RUN apt-get update \
nodejs \
postgresql-client \
postgresql-server-dev-13 \
yarn \
&& npm install --global npm@8 \
# Clean up what can be cleaned up.
&& apt-get autoremove -y
@ -58,24 +40,15 @@ RUN groupadd -r --gid=${GROUP_ID} pontoon && useradd --uid=${USER_ID} --no-log-i
RUN chown -R pontoon:pontoon /app
USER pontoon
# Install node requirements
COPY package.json .
COPY package-lock.json .
COPY --chown=pontoon:pontoon tag-admin/package.json ./tag-admin/
RUN npm ci
# Install the server's Node.js requirements
ENV YUGLIFY_BINARY /app/node_modules/.bin/yuglify
ENV TERSER_BINARY /app/node_modules/.bin/terser
COPY pontoon/package.json .
RUN npm install
COPY ./docker/config/server.env .env
COPY --chown=pontoon:pontoon . /app/
# Create the folder for front-end assets
RUN mkdir -p assets
# Build JS files
RUN npm run build
COPY --from=frontend /frontend/ ./frontend/
# Run collectstatic in container which puts files in the default place for
# static files.
RUN python manage.py collectstatic --noinput
RUN python manage.py collectstatic
CMD ["/app/docker/server_run.sh"]

Просмотреть файл

@ -4,7 +4,6 @@ DJANGO_DEBUG=True
DATABASE_URL=postgres://pontoon:asdf@postgresql/pontoon
ENABLE_INSIGHTS_TAB=True
SESSION_COOKIE_SECURE=False
FRONTEND_URL=#FRONTEND_URL#
SITE_URL=#SITE_URL#
FXA_CLIENT_ID=727f0251c388a993
FXA_SECRET_KEY=e43fd751ca5687d28288098e3e9b1294792ed9954008388e39b1cdaac0a1ebd6

Просмотреть файл

@ -21,8 +21,9 @@ fi
echo ">>> Setting up the db for Django" >> /app/server_run.log
python manage.py migrate >> /app/server_run.log
echo ">>> Starting frontend build process in the background" >> /app/server_run.log
cd frontend && yarn start &
echo ">>> Starting translate & tag-admin builds in the background" >> /app/server_run.log
npm start -w translate &
npm start -w tag-admin &
# syncing projects if env SYNC_INTERVAL is set, if it is set and you need to "work" on the bash, kill the process syncprojects.sh
echo ">>> starting continuos syncing projects" >> /app/server_run.log

Просмотреть файл

@ -139,7 +139,7 @@ documentation resources:
- If you want to work on the front-end, it is important that you read
the `Front-End
Documentation <https://github.com/mozilla/pontoon/tree/master/frontend>`_.
Documentation <https://github.com/mozilla/pontoon/tree/master/translate>`_.
- Most of the documentation around installing and developing can be
found in `Pontoon's
Documentation <https://mozilla-pontoon.readthedocs.io/en/latest/>`_.

Просмотреть файл

@ -41,8 +41,8 @@ Quickstart
$ make build
That will build the containers required for development:
frontend and server.
That will install Pontoon's JS dependencies,
build the frontend packages, and build the server container.
.. Note::
@ -78,16 +78,6 @@ Quickstart
The app should now be available at http://localhost:8000 or the custom SITE_URL.
.. Warning::
After you execute ``make run``, the console output might trick you into
accessing Pontoon in the browser at http://localhost:3000/. Note that
the URL only refers to the frontend app, which is not meant to be used
standalone.
To access Pontoon app, you should use http://localhost:8000 or the
custom SITE_URL.
And with that, you're ready to start :doc:`contributing`!

Просмотреть файл

@ -1,102 +0,0 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@fluent/bundle": "0.16.1",
"@fluent/langneg": "0.5.1",
"@fluent/react": "0.13.1",
"@fluent/syntax": "0.17.0",
"@reduxjs/toolkit": "^1.6.1",
"connected-react-router": "6.8.0",
"date-and-time": "0.14.2",
"diff-match-patch": "1.0.4",
"highcharts": "9.0.0",
"highcharts-react-official": "2.2.2",
"html-react-parser": "0.13.0",
"javascript-time-ago": "2.3.9",
"jest-each": "24.5.0",
"linkify-it": "2.1.0",
"lodash.clonedeep": "4.5.0",
"lodash.escaperegexp": "4.1.2",
"lodash.flattendeep": "4.4.0",
"lodash.isempty": "4.4.0",
"lodash.isequal": "4.5.0",
"nprogress": "0.2.0",
"prettier": "2.2.0",
"react": "16.13.1",
"react-content-marker": "2.0.0",
"react-dom": "16.13.1",
"react-infinite-scroll-hook": "^4.0.1",
"react-infinite-scroller": "1.2.4",
"react-linkify": "0.2.2",
"react-redux": "7.1.3",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
"react-scripts": "4.0.3",
"react-tabs": "3.0.0",
"react-time-ago": "^7.1.3",
"reactour": "1.15.1",
"redux": "^4.1.1",
"redux-logger": "3.0.6",
"redux-thunk": "2.3.0",
"reselect": "4.0.0",
"slate": "0.63.0",
"slate-react": "0.58.4",
"styled-components": "4.3.2",
"tlds": "1.218.0"
},
"devDependencies": {
"@types/date-and-time": "^0.13.0",
"@types/diff-match-patch": "^1.0.32",
"@types/enzyme": "^3.10.8",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/escape-html": "^1.0.1",
"@types/history": "^4.7.8",
"@types/javascript-time-ago": "^2.0.3",
"@types/jest": "^26.0.23",
"@types/linkify-it": "^3.0.1",
"@types/lodash.clonedeep": "^4.5.6",
"@types/lodash.escaperegexp": "^4.1.6",
"@types/lodash.flattendeep": "^4.4.6",
"@types/lodash.isempty": "^4.4.6",
"@types/lodash.isequal": "^4.5.5",
"@types/node": "^15.3.0",
"@types/nprogress": "^0.2.0",
"@types/react": "^17.0.5",
"@types/react-dom": "^17.0.5",
"@types/react-infinite-scroller": "^1.2.1",
"@types/react-redux": "^7.1.18",
"@types/react-router": "^5.1.14",
"@types/react-router-dom": "^5.1.7",
"@types/reactour": "^1.18.1",
"@types/redux": "^3.6.0",
"@types/redux-logger": "^3.0.9",
"@types/sinon": "^10.0.1",
"@types/styled-components": "^5.1.9",
"@types/tlds": "^1.207.0",
"chokidar-cli": "2.1.0",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.6",
"sinon": "9.2.4",
"typescript": "^4.2.4",
"utility-types": "^3.10.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"lint": "eslint 'src/**/*.{js,ts,tsx}'",
"prettier": "prettier --write src/",
"check-prettier": "prettier --check src/",
"test": "react-scripts test --modulePaths=src",
"types": "tsc",
"eject": "react-scripts eject"
},
"prettier": {
"endOfLine": "lf",
"trailingComma": "all",
"tabWidth": 4,
"jsxSingleQuote": true,
"singleQuote": true
}
}

Просмотреть файл

@ -1,58 +0,0 @@
<!DOCTYPE html>
<html{% if locale %} lang="{{ locale }}"{% endif %}>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="description" content="Mozillas Localization Platform">
<meta name="author" content="Mozilla">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Pontoon</title>
<!-- Inlining CSS mitigates/prevents fouc. -->
<style>
body {
background: #3f4752 !important;
}
</style>
<link href="/static/css/fontawesome-all.css" rel="stylesheet" type="text/css" />
<link href="/static/css/boilerplate.css" rel="stylesheet" type="text/css" />
<link href="/static/css/fonts.css" rel="stylesheet" type="text/css" />
{% include "tracker.html" %}
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div
id="root"
data-csrf-token="{{ csrf_token }}"
data-notifications="{{ notifications | to_json }}"
></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

1
frontend/src/react-app-env.d.ts поставляемый
Просмотреть файл

@ -1 +0,0 @@
/// <reference types="react-scripts" />

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -6,5 +6,5 @@ locales = [
]
[[paths]]
reference = "frontend/public/static/locale/en-US/*.ftl"
l10n = "frontend/public/static/locale/{locale}/*.ftl"
reference = "translate/public/static/locale/en-US/*.ftl"
l10n = "translate/public/static/locale/{locale}/*.ftl"

14861
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -16,16 +16,18 @@
"author": "Mozilla",
"license": "BSD",
"scripts": {
"test": "npm test -w tag-admin",
"build": "npm run build -w tag-admin",
"build-w": "npm run build-w -w tag-admin",
"test": "npm test --workspaces --if-present",
"build": "npm run build --workspaces --if-present",
"build:prod": "npm run build:prod --workspaces --if-present",
"heroku-postbuild": "echo Build is taken care of in ./bin/post_compile",
"prettier": "prettier --write '**/frontend/**/*.{js,ts,tsx,css}' '**/pontoon/**/*.{js,css}' '**/tag-admin/**/*.{js,css}'",
"check-prettier": "prettier --check '**/frontend/**/*.{js,ts,tsx,css}' '**/pontoon/**/*.{js,css}' '**/tag-admin/**/*.{js,css}'",
"eslint": "eslint 'frontend/**/*.{js,ts,tsx}' 'pontoon/**/*.js' 'tag-admin/**/*.js'"
"prettier": "prettier --write '**/translate/**/*.{js,ts,tsx,css}' '**/pontoon/**/*.{js,css}' '**/tag-admin/**/*.{js,css}'",
"check-prettier": "prettier --check '**/translate/**/*.{js,ts,tsx,css}' '**/pontoon/**/*.{js,css}' '**/tag-admin/**/*.{js,css}'",
"eslint": "eslint 'translate/**/*.{js,ts,tsx}' 'pontoon/**/*.js' 'tag-admin/**/*.js'"
},
"workspaces": [
"tag-admin"
"translate",
"tag-admin",
"pontoon"
],
"devDependencies": {
"@babel/core": "^7.13.10",
@ -34,11 +36,58 @@
"@babel/preset-env": "^7.13.10",
"@babel/preset-react": "^7.12.13",
"@babel/runtime": "^7.13.10",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-replace": "^3.1.0",
"@rollup/plugin-typescript": "^8.3.0",
"@types/date-and-time": "^0.13.0",
"@types/diff-match-patch": "^1.0.32",
"@types/enzyme": "^3.10.8",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/escape-html": "^1.0.1",
"@types/history": "^4.7.8",
"@types/javascript-time-ago": "^2.0.3",
"@types/jest": "^27.4.0",
"@types/linkify-it": "^3.0.1",
"@types/lodash.clonedeep": "^4.5.6",
"@types/lodash.escaperegexp": "^4.1.6",
"@types/lodash.flattendeep": "^4.4.6",
"@types/lodash.isempty": "^4.4.6",
"@types/lodash.isequal": "^4.5.5",
"@types/node": "^15.3.0",
"@types/nprogress": "^0.2.0",
"@types/react": "^17.0.5",
"@types/react-dom": "^17.0.5",
"@types/react-infinite-scroller": "^1.2.1",
"@types/react-redux": "^7.1.18",
"@types/react-router": "^5.1.14",
"@types/react-router-dom": "^5.1.7",
"@types/reactour": "^1.18.1",
"@types/redux-logger": "^3.0.9",
"@types/shortid": "^0.0.29",
"@types/sinon": "^10.0.1",
"@types/styled-components": "^5.1.9",
"@typescript-eslint/eslint-plugin": "^5.11.0",
"@typescript-eslint/parser": "^5.11.0",
"concurrently": "^7.0.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"eslint": "^8.9.0",
"eslint-plugin-react": "^7.22.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^27.5.1",
"jest-each": "^27.5.1",
"jest-watch-typeahead": "^1.0.0",
"prettier": "^2.5.1",
"terser": "^5.10.0",
"yuglify": "^2.0.0"
"rollup": "^2.67.2",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-terser": "^7.0.2",
"sinon": "^9.2.4",
"ts-jest": "^27.1.3",
"typescript": "^4.2.4",
"utility-types": "^3.10.0"
},
"prettier": {
"endOfLine": "lf",

Просмотреть файл

@ -143,7 +143,7 @@ var Pontoon = (function (my) {
/* Main code */
$(function () {
/*
* If Google Analytics is enabled, frontend will send additional about Ajax calls.
* If Google Analytics is enabled, the translate frontend will send additional about Ajax calls.
*
* To send an event to GA, We pass following informations:
* event category - hardcoded 'ajax' string.

Просмотреть файл

@ -20,7 +20,7 @@ class BatchActionsForm(forms.Form):
def decode_field(self, param_name):
"""
The frontend sends quoted form fields to avoid issues with e.g. non breakable spaces.
The translate frontend sends quoted form fields to avoid issues with e.g. non breakable spaces.
Related bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1438575
"""

8
pontoon/package.json Normal file
Просмотреть файл

@ -0,0 +1,8 @@
{
"name": "server",
"private": true,
"devDependencies": {
"terser": "^5.10.0",
"yuglify": "^2.0.0"
}
}

Просмотреть файл

@ -64,8 +64,8 @@ if not DEV and not DEBUG:
DATABASES["default"]["OPTIONS"] = {}
DATABASES["default"]["OPTIONS"]["sslmode"] = "require"
FRONTEND_DIR = os.path.join(ROOT, "frontend")
TAGADMIN_DIR = os.path.join(ROOT, "tag-admin")
TRANSLATE_DIR = os.path.join(ROOT, "translate")
# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
@ -81,7 +81,6 @@ SESSION_COOKIE_SECURE = os.environ.get("SESSION_COOKIE_SECURE", "True") != "Fals
APP_URL_KEY = "APP_URL"
FRONTEND_URL = os.environ.get("FRONTEND_URL", "http://localhost:3000")
SITE_URL = os.environ.get("SITE_URL", "http://localhost:8000")
# Custom LD_LIBRARY_PATH environment variable for SVN
@ -154,7 +153,6 @@ INSTALLED_APPS = (
"django.contrib.contenttypes",
"django.contrib.messages",
"django.contrib.sessions",
"whitenoise.runserver_nostatic",
"django.contrib.staticfiles",
# Django sites app is required by django-allauth
"django.contrib.sites",
@ -208,7 +206,7 @@ TEMPLATES = [
"BACKEND": "django_jinja.backend.Jinja2",
"NAME": "jinja2",
"APP_DIRS": True,
"DIRS": [os.path.join(FRONTEND_DIR, "build")],
"DIRS": [os.path.join(TRANSLATE_DIR, "public")],
"OPTIONS": {
"match_extension": "",
"match_regex": re.compile(
@ -279,6 +277,10 @@ PIPELINE_CSS = {
),
"output_filename": "css/base.min.css",
},
"translate": {
"source_filenames": ("translate.css",),
"output_filename": "css/translate.min.css",
},
"admin": {
"source_filenames": (
"css/table.css",
@ -416,6 +418,10 @@ PIPELINE_JS = {
),
"output_filename": "js/base.min.js",
},
"translate": {
"source_filenames": ("translate.js",),
"output_filename": "js/translate.min.js",
},
"admin": {
"source_filenames": ("js/table.js",),
"output_filename": "js/admin.min.js",
@ -572,8 +578,8 @@ STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
)
STATICFILES_DIRS = [
path("assets"),
os.path.join(FRONTEND_DIR, "build", "static"),
os.path.join(TRANSLATE_DIR, "dist"),
os.path.join(TRANSLATE_DIR, "public"),
os.path.join(TAGADMIN_DIR, "dist"),
]

Просмотреть файл

@ -1,7 +1,4 @@
# Translate
This app is responsible for rendering the Translate page. Because that page is a
front-end single page app (with React), served differently in our production environment
and in development, we have a few views to handle all those cases.
Note that the front-end code can be found in the `/frontend` folder in the Pontoon repo.
This app is responsible for rendering the Translate page,
which is mostly defined as a React component in the `/translate` folder.

Просмотреть файл

@ -1,5 +1,4 @@
from django.conf import settings
from django.urls import path, re_path
from django.urls import path
from django.views.generic import RedirectView
from . import views
@ -26,25 +25,3 @@ urlpatterns = [
name="pontoon.translate",
),
]
# For local development, we want to serve front-end static files from the
# webserver that create-react-app is running. This way we benefit from
# hot reloading and all the niceties of React dev tools.
# To do that, we must overwrite the staticfiles serve view, and we also
# want to catch websocket connexions.
#
# Note that because we override the static files serving, you will need to
# run your local django server with the `--nostatic` option. That's
# automatically done when running with `make run`.
if settings.DEV:
urlpatterns += [
path(
"static/<path:path>",
views.static_serve_dev,
),
re_path(
r"^sockjs-node/.*$",
views.translate,
),
]

Просмотреть файл

@ -1,15 +1,10 @@
import requests
from django import http
from django.conf import settings
from django.contrib import messages
from django.contrib.staticfiles.views import serve
from django.http import Http404
from django.shortcuts import (
get_object_or_404,
render,
)
from django.template import engines
from django.views.decorators.csrf import (
csrf_exempt,
ensure_csrf_cookie,
@ -21,70 +16,14 @@ from pontoon.base.models import (
)
def static_serve_dev(request, path):
"""Proxy missing static files to the frontend server.
This view replaces django's static files serve view. When a file is
missing from django's paths, then we make a proxy request to the
frontend server to see if it's a front-end file.
Note that to enable this view, you need to run your django with the
nostatic option, like this::
$ ./manage.py runserver --nostatic
"""
try:
# First try to load the file with django's regular serve view.
return serve(request, path, insecure=True)
except Http404:
# If the file couldn't be found in django's static files, then we
# try to proxy it to the frontend server.
return catchall_dev(request)
@csrf_exempt
def catchall_dev(request, context=None):
"""Proxy HTTP requests to the frontend dev server in development.
The implementation is very basic e.g. it doesn't handle HTTP headers.
"""
# URL to the development frontend server, used to redirect front-end requests.
upstream_url = settings.FRONTEND_URL + request.path
# Redirect websocket requests directly to the frontend server.
if request.META.get("HTTP_UPGRADE", "").lower() == "websocket":
return http.HttpResponseRedirect(upstream_url)
method = request.META["REQUEST_METHOD"].lower()
response = getattr(requests, method)(upstream_url, stream=True)
content_type = response.headers.get("Content-Type")
if content_type == "text/html; charset=UTF-8":
return http.HttpResponse(
content=(
engines["jinja2"]
.from_string(response.text)
.render(request=request, context=context)
),
status=response.status_code,
reason=response.reason,
)
return http.StreamingHttpResponse(
streaming_content=response.iter_content(2 ** 12),
content_type=content_type,
status=response.status_code,
reason=response.reason,
)
return render(request, "translate.html", context=context, using="jinja2")
# For production, we've added the front-end static files to our list of
# static folders. We can thus simply return a template view of index.html.
@ensure_csrf_cookie
def catchall_prod(request, context=None):
return render(request, "index.html", context=context, using="jinja2")
return render(request, "translate.html", context=context, using="jinja2")
def get_preferred_locale(request):

Просмотреть файл

@ -8,5 +8,8 @@ module.exports = {
'\\.(css|less)$': 'identity-obj-proxy',
},
setupFiles: ['./src/setupTests.js'],
transform: {
'\\.[jt]sx?$': ['babel-jest', { configFile: '../babel.config.json' }],
},
collectCoverage: true,
};

Просмотреть файл

@ -1,29 +1,16 @@
{
"name": "tag-admin",
"private": true,
"author": "Mozilla",
"license": "BSD",
"scripts": {
"test": "jest",
"start": "rollup -c --watch",
"build": "rollup -c",
"build-w": "rollup -cw"
"build:prod": "rollup -c --environment BUILD:production",
"test": "jest"
},
"dependencies": {
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-table": "^6.11.5"
},
"devDependencies": {
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-replace": "^3.1.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"identity-obj-proxy": "^3.0.0",
"jest": "^27.5.1",
"rollup": "^2.67.2",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-terser": "^7.0.2"
}
}

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-env node */
import path from 'path';
import babel from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
@ -6,20 +8,27 @@ import replace from '@rollup/plugin-replace';
import css from 'rollup-plugin-css-only';
/** @type {import('rollup').RollupOptions} */
export default {
input: { tag_admin: 'src/index.js' },
output: { dir: 'dist/' },
const config = {
input: 'src/index.js',
output: { file: 'dist/tag_admin.js' },
treeshake: 'recommended',
plugins: [
replace({
preventAssignment: true,
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.NODE_ENV': JSON.stringify(
process.env.BUILD ?? 'development',
),
}),
resolve(),
babel({
babelHelpers: 'runtime',
configFile: path.resolve('.babelrc'),
configFile: path.resolve('../babel.config.json'),
}),
commonjs(),
css({ output: 'tag_admin.css' }),
],
};
export default config;

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -17,5 +17,3 @@
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

Просмотреть файл

@ -10,8 +10,8 @@
</tr>
<tr>
<td>Package manager</td>
<td>Yarn</td>
<td>https://yarnpkg.com/</td>
<td>npm</td>
<td>https://docs.npmjs.com/cli/v8</td>
</tr>
<tr>
<td>Views</td>
@ -69,12 +69,25 @@ Here are the files commonly found in a module:
Of course, more can be added if needed. For example, modules with a high number of action types might want to have an `actionTypes.js` file to separate them from actions.
### Imports
To import code from further away than the parent directory,
use paths starting with `~` to refer to the root of the `src/` directory.
For example:
```js
import { SearchBox } from '~/modules/search';
```
In general, imports ought to be ordered first from the most general to the most specific, and then alphabetically.
## Running and deploying
### Production
The only required step for the front-end is to build static files with `yarn build`. Django is configured to collect the `index.html` and static files from the `build` folder and put them with other static files. All of that is automated for deployement to Heroku.
The only required step for the front-end is to build static files with `npm run build`.
Django is configured to collect the `translate.html` and static files
from the `build` folder and put them with other static files.
All of that is automated for deployement to Heroku.
### Development
@ -87,7 +100,7 @@ A common case during development is to have 2 terminals open: one for the dev se
$ make run
# terminal 2
$ make test-frontend
$ make test-translate
```
#### Enabling websocket and warm-reloading for dev
@ -101,35 +114,25 @@ If you can't turn on websockets, you will see errors in the console (that's not
## Dependencies
We manage our JavaScript dependencies with `yarn`. Dependencies and their versions are listed in the `package.json` file, under `dependencies` for production and `devDependencies` for development.
We manage our JavaScript dependencies with `npm`.
Dependencies and their versions are listed in the `package.json` file,
under `dependencies` for production and `devDependencies` for development.
To add a new dependency, run the following commands:
To add or upgrade a dependency,
run the following commands at the repo root:
```shell
# Add the dependency. Make sure to use an exact version.
yarn add my-package@version
# Go back to the root folder.
cd ..
# Add or upgrade the dependency. Make sure to use an exact version.
npm install my-package@version -w translate
# Rebuild the Pontoon image so that it has the new dependencies.
make build-frontend
make build-translate
```
To upgrade dependency to a specific version, run the following commands:
```shell
# Upgrade dependency to the specified version.
yarn upgrade my-package@version
# Go back to the root folder.
cd ..
# Rebuild the Pontoon image so that it has the upgraded dependencies.
make build-frontend
```
Note that, in order to add new dependencies, you need to have `yarn` installed and perform the actions locally (outside docker). You might want to remove the `node_modules` folder after you've run the install or update command (and the `package.json` and `yarn.lock` files have been updated) and before rebuilding the image, to reduce the size of the docker context.
Note that, in order to add new dependencies, you need to have `npm` installed and perform the actions locally (outside docker).
You might want to remove the `translate/node_modules` folder after you've run the install or update command
(and the `package.json` and `package-lock.json` files have been updated)
and before rebuilding the image, to reduce the size of the docker context.
## Type checking
@ -147,7 +150,7 @@ Tests are run using [`jest`](https://facebook.github.io/jest/). We use [`enzyme`
To run the test suite, use:
$ make test-frontend
$ make test-translate
It will start an auto-reloading test runner, that will refresh every time you make a change to the code or tests.
@ -194,7 +197,10 @@ class Editor extends React.Component {
The second thing you need to do is to add that new string into our main translation files. Translations available to localizers are based on those files, so if you forget to put the new content there, no one will be able to translate that content.
The source language for our application is `en-US` (that is what you should use by default when writing content in the code, and in the language files). You can find them in `frontend/public/static/locale/en-US/`. We currently have only one file, named `translate.ftl`, and that is where all content should be added.
The source language for our application is `en-US`
(that is what you should use by default when writing content in the code, and in the language files).
You can find them in `translate/public/static/locale/en-US/`.
We currently have only one file, named `translate.ftl`, and that is where all content should be added.
Those files use the FTL format. In its simplest form, a string in such a file (called a `message` in Fluent vocabulary) looks like this: `message-id = Content`. There are, however, lots of tools that you can use in that syntax, and you are encouraged to read [Fluent's Syntax Guide](https://projectfluent.org/fluent/guide/) to familiarize yourself with them. Make sure you also take a look at the [Good Practices for Developers](https://github.com/projectfluent/fluent/wiki/Good-Practices-for-Developers#prefer-separate-messages-over-variants-for-ui-logic) guide.

40
translate/jest.config.js Normal file
Просмотреть файл

@ -0,0 +1,40 @@
/* eslint-env commonjs */
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
verbose: true,
roots: ['<rootDir>/src'],
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'],
coveragePathIgnorePatterns: ['<rootDir>/node_modules/'],
testMatch: [
'<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}',
'<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}',
],
testEnvironment: 'jsdom',
preset: 'ts-jest',
globals: {
'ts-jest': {
isolatedModules: true,
},
},
transform: {
'\\.jsx?$': ['babel-jest', { configFile: '../babel.config.json' }],
'\\.tsx?$': 'ts-jest',
},
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$',
],
resetMocks: true,
moduleNameMapper: {
'.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$':
'identity-obj-proxy',
'\\.svg$': '<rootDir>/__mocks__/svg.js',
'~(.*)$': '<rootDir>/src/$1',
},
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
testTimeout: 10000, // optional
};

56
translate/package.json Normal file
Просмотреть файл

@ -0,0 +1,56 @@
{
"name": "translate",
"private": true,
"dependencies": {
"@fluent/bundle": "^0.16.1",
"@fluent/langneg": "^0.5.1",
"@fluent/react": "^0.13.1",
"@fluent/syntax": "^0.17.0",
"@reduxjs/toolkit": "^1.6.1",
"connected-react-router": "^6.8.0",
"date-and-time": "^0.14.2",
"diff-match-patch": "^1.0.4",
"escape-html": "^1.0.3",
"highcharts": "^9.0.0",
"highcharts-react-official": "^2.2.2",
"html-react-parser": "^1.4.8",
"javascript-time-ago": "^2.3.9",
"linkify-it": "^2.1.0",
"lodash.clonedeep": "^4.5.0",
"lodash.escaperegexp": "^4.1.2",
"lodash.flattendeep": "^4.4.0",
"lodash.isempty": "^4.4.0",
"lodash.isequal": "^4.5.0",
"nprogress": "^0.2.0",
"react": "^16.14.0",
"react-content-marker": "^2.0.0",
"react-dom": "^16.14.0",
"react-infinite-scroll-hook": "^4.0.1",
"react-infinite-scroller": "^1.2.4",
"react-linkify": "^0.2.2",
"react-redux": "^7.2.6",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-tabs": "^3.0.0",
"react-time-ago": "^7.1.3",
"reactour": "^1.15.1",
"redux": "^4.1.1",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",
"slate": "^0.63.0",
"slate-react": "^0.58.4",
"styled-components": "^5.3.3",
"tlds": "^1.218.0"
},
"scripts": {
"start": "rollup -c --watch",
"build": "rollup -c",
"build:prod": "rollup -c --environment BUILD:production",
"lint": "eslint 'src/**/*.{js,ts,tsx}'",
"prettier": "prettier --write src/",
"check-prettier": "prettier --check src/",
"test": "jest",
"types": "tsc --noEmit"
}
}

Просмотреть файл

Просмотреть файл

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html{% if locale %} lang="{{ locale }}"{% endif %}>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="description" content="Mozillas Localization Platform">
<meta name="author" content="Mozilla">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" href="/favicon.ico">
<title>Pontoon</title>
<!-- Inlining CSS mitigates/prevents fouc. -->
<style> body { background: #3f4752 !important; } </style>
<link href="/static/css/fontawesome-all.css" rel="stylesheet" />
<link href="/static/css/boilerplate.css" rel="stylesheet" />
<link href="/static/css/fonts.css" rel="stylesheet" />
{% stylesheet 'translate' %}
{% include "tracker.html" %}
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div
id="root"
data-csrf-token="{{ csrf_token }}"
data-notifications="{{ notifications | to_json }}"
></div>
{% javascript 'translate' %}
</body>
</html>

Просмотреть файл

@ -0,0 +1,46 @@
/* eslint-env node */
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import typescript from '@rollup/plugin-typescript';
import css from 'rollup-plugin-css-only';
/** @type {import('rollup').RollupOptions} */
const config = {
input: 'src/index.tsx',
output: { file: 'dist/translate.js' },
treeshake: 'recommended',
plugins: [
json(),
typescript(),
replace({
preventAssignment: true,
'process.env.NODE_ENV': JSON.stringify(
process.env.BUILD ?? 'development',
),
}),
resolve(),
commonjs(),
css({ output: 'translate.css' }),
],
onwarn(warning, warn) {
// https://github.com/reduxjs/redux-toolkit/issues/1466
if (warning.id?.includes('@reduxjs/toolkit')) {
switch (warning.code) {
case 'SOURCEMAP_ERROR':
return;
case 'THIS_IS_UNDEFINED':
if (warning.frame?.includes('this && this')) return;
}
}
warn(warning);
},
};
export default config;

Просмотреть файл

Просмотреть файл

@ -5,7 +5,7 @@
// import sinon from 'sinon';
// import App from './App';
// import store from 'store';
// import store from '~/store';
describe('<App>', () => {
it('renders without crashing', () => {

Просмотреть файл

@ -3,35 +3,35 @@ import { connect } from 'react-redux';
import './App.css';
import * as l10n from 'core/l10n';
import { Lightbox } from 'core/lightbox';
import { WaveLoader } from 'core/loaders';
import * as locale from 'core/locale';
import * as navigation from 'core/navigation';
import * as notification from 'core/notification';
import * as project from 'core/project';
import * as resource from 'core/resource';
import * as stats from 'core/stats';
import * as user from 'core/user';
import { AddonPromotion } from 'modules/addonpromotion';
import * as batchactions from 'modules/batchactions';
import { UserControls } from 'core/user';
import { BatchActions } from 'modules/batchactions';
import { EntitiesList } from 'modules/entitieslist';
import { EntityDetails } from 'modules/entitydetails';
import { Navigation } from 'modules/navbar';
import { ProjectInfo } from 'modules/projectinfo';
import { ResourceProgress } from 'modules/resourceprogress';
import { SearchBox } from 'modules/search';
import { InteractiveTour } from 'modules/interactivetour';
import * as l10n from '~/core/l10n';
import { Lightbox } from '~/core/lightbox';
import { WaveLoader } from '~/core/loaders';
import * as locale from '~/core/locale';
import * as navigation from '~/core/navigation';
import * as notification from '~/core/notification';
import * as project from '~/core/project';
import * as resource from '~/core/resource';
import * as stats from '~/core/stats';
import * as user from '~/core/user';
import { AddonPromotion } from '~/modules/addonpromotion';
import * as batchactions from '~/modules/batchactions';
import { UserControls } from '~/core/user';
import { BatchActions } from '~/modules/batchactions';
import { EntitiesList } from '~/modules/entitieslist';
import { EntityDetails } from '~/modules/entitydetails';
import { Navigation } from '~/modules/navbar';
import { ProjectInfo } from '~/modules/projectinfo';
import { ResourceProgress } from '~/modules/resourceprogress';
import { SearchBox } from '~/modules/search';
import { InteractiveTour } from '~/modules/interactivetour';
import type { BatchActionsState } from 'modules/batchactions';
import type { L10nState } from 'core/l10n';
import type { LocaleState } from 'core/locale';
import type { NavigationParams } from 'core/navigation';
import type { ProjectState } from 'core/project';
import type { Stats } from 'core/stats';
import { AppDispatch, RootState } from 'store';
import type { BatchActionsState } from '~/modules/batchactions';
import type { L10nState } from '~/core/l10n';
import type { LocaleState } from '~/core/locale';
import type { NavigationParams } from '~/core/navigation';
import type { ProjectState } from '~/core/project';
import type { Stats } from '~/core/stats';
import { AppDispatch, RootState } from '~/store';
type Props = {
batchactions: BatchActionsState;

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,6 +1,6 @@
import APIBase from './base';
import type { Locale } from 'core/locale';
import type { Locale } from '~/core/locale';
import type { MachineryTranslation } from './types';
type Translations = Array<MachineryTranslation>;

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -4,11 +4,11 @@ export default class UXActionAPI extends APIBase {
/**
* Log UX action.
*/
log(
async log(
action_type: string,
experiment: string | null | undefined,
data: any | null | undefined,
): Promise<any> {
): Promise<void> {
const csrfToken = this.getCSRFToken();
const payload = new URLSearchParams();
@ -27,6 +27,10 @@ export default class UXActionAPI extends APIBase {
headers.append('X-Requested-With', 'XMLHttpRequest');
headers.append('X-CSRFToken', csrfToken);
return this.fetch('/log-ux-action/', 'POST', payload, headers);
try {
await this.fetch('/log-ux-action/', 'POST', payload, headers);
} catch (_) {
/* Ignore errors during UX action logging */
}
}
}

Просмотреть файл

@ -1,11 +1,11 @@
import NProgress from 'nprogress';
import api from 'core/api';
import * as notification from 'core/notification';
import * as history from 'modules/history';
import * as teamcomments from 'modules/teamcomments';
import api from '~/core/api';
import * as notification from '~/core/notification';
import * as history from '~/modules/history';
import * as teamcomments from '~/modules/teamcomments';
import type { AppDispatch } from 'store';
import type { AppDispatch } from '~/store';
export function addComment(
entity: number,

Просмотреть файл

@ -17,10 +17,10 @@ import escapeHtml from 'escape-html';
import './AddComment.css';
import { UserAvatar } from 'core/user';
import { UserAvatar } from '~/core/user';
import type { NavigationParams } from 'core/navigation';
import type { UserState } from 'core/user';
import type { NavigationParams } from '~/core/navigation';
import type { UserState } from '~/core/user';
type Props = {
parameters: NavigationParams | null | undefined;

Просмотреть файл

@ -5,10 +5,10 @@ import { Localized } from '@fluent/react';
import './Comment.css';
import { Linkify } from 'core/linkify';
import { UserAvatar } from 'core/user';
import { Linkify } from '~/core/linkify';
import { UserAvatar } from '~/core/user';
import type { TranslationComment } from 'core/api';
import type { TranslationComment } from '~/core/api';
type Props = {
comment: TranslationComment;

Просмотреть файл

@ -3,12 +3,12 @@ import { Localized } from '@fluent/react';
import './CommentsList.css';
import { Comment, AddComment } from 'core/comments';
import { Comment, AddComment } from '~/core/comments';
import type { NavigationParams } from 'core/navigation';
import type { TranslationComment } from 'core/api';
import type { UserState } from 'core/user';
import type { HistoryTranslation } from 'modules/history';
import type { NavigationParams } from '~/core/navigation';
import type { TranslationComment } from '~/core/api';
import type { UserState } from '~/core/user';
import type { HistoryTranslation } from '~/modules/history';
type Props = {
comments: Array<TranslationComment>;

Просмотреть файл

Просмотреть файл

@ -1,18 +1,18 @@
import NProgress from 'nprogress';
import api from 'core/api';
import api from '~/core/api';
import { actions as entitiesActions } from 'core/entities';
import * as notification from 'core/notification';
import { actions as pluralActions } from 'core/plural';
import { actions as resourceActions } from 'core/resource';
import { actions as statsActions } from 'core/stats';
import * as unsavedchanges from 'modules/unsavedchanges';
import { actions as entitiesActions } from '~/core/entities';
import * as notification from '~/core/notification';
import { actions as pluralActions } from '~/core/plural';
import { actions as resourceActions } from '~/core/resource';
import { actions as statsActions } from '~/core/stats';
import * as unsavedchanges from '~/modules/unsavedchanges';
import type { Entity, SourceType } from 'core/api';
import type { Locale } from 'core/locale';
import type { Entity, SourceType } from '~/core/api';
import type { Locale } from '~/core/locale';
import type { Entry } from '@fluent/syntax';
import { AppThunk } from 'store';
import { AppThunk } from '~/store';
export const END_UPDATE_TRANSLATION: 'editor/END_UPDATE_TRANSLATION' =
'editor/END_UPDATE_TRANSLATION';

Просмотреть файл

@ -1,13 +1,13 @@
import sinon from 'sinon';
import * as user from 'core/user';
import * as history from 'modules/history';
import * as user from '~/core/user';
import * as history from '~/modules/history';
import {
createDefaultUser,
createReduxStore,
mountComponentWithStore,
} from 'test/store';
} from '~/test/store';
import * as editor from '..';
import EditorMainAction from './EditorMainAction';

Просмотреть файл

@ -1,8 +1,8 @@
import * as React from 'react';
import { Localized } from '@fluent/react';
import { useAppSelector } from 'hooks';
import * as user from 'core/user';
import { useAppSelector } from '~/hooks';
import * as user from '~/core/user';
import * as editor from '..';

Просмотреть файл

@ -1,7 +1,7 @@
import React from 'react';
import * as entities from 'core/entities';
import * as navigation from 'core/navigation';
import * as entities from '~/core/entities';
import * as navigation from '~/core/navigation';
import EditorMenu from './EditorMenu';
import EditorSettings from './EditorSettings';
@ -12,7 +12,7 @@ import {
createDefaultUser,
createReduxStore,
mountComponentWithStore,
} from 'test/store';
} from '~/test/store';
const SELECTED_ENTITY = {
pk: 1,

Просмотреть файл

@ -3,10 +3,10 @@ import { Localized } from '@fluent/react';
import './EditorMenu.css';
import { useAppDispatch, useAppSelector } from 'hooks';
import * as entities from 'core/entities';
import * as user from 'core/user';
import * as unsavedchanges from 'modules/unsavedchanges';
import { useAppDispatch, useAppSelector } from '~/hooks';
import * as entities from '~/core/entities';
import * as user from '~/core/user';
import * as unsavedchanges from '~/modules/unsavedchanges';
import EditorMainAction from './EditorMainAction';
import EditorSettings from './EditorSettings';

Просмотреть файл

@ -1,11 +1,11 @@
import * as React from 'react';
import { Localized } from '@fluent/react';
import { useOnDiscard } from 'core/utils';
import { useOnDiscard } from '~/core/utils';
import './EditorSettings.css';
import type { Settings } from 'core/user';
import type { Settings } from '~/core/user';
type Props = {
settings: Settings;

Просмотреть файл

@ -1,15 +1,15 @@
import sinon from 'sinon';
import * as editor from 'core/editor';
import * as locale from 'core/locale';
import * as project from 'core/project';
import * as user from 'core/user';
import * as editor from '~/core/editor';
import * as locale from '~/core/locale';
import * as project from '~/core/project';
import * as user from '~/core/user';
import {
createDefaultUser,
createReduxStore,
mountComponentWithStore,
} from 'test/store';
} from '~/test/store';
import FailedChecks from './FailedChecks';

Просмотреть файл

@ -3,13 +3,13 @@ import { Localized } from '@fluent/react';
import './FailedChecks.css';
import { useAppDispatch, useAppSelector } from 'hooks';
import * as user from 'core/user';
import { useAppDispatch, useAppSelector } from '~/hooks';
import * as user from '~/core/user';
import { actions, useUpdateTranslationStatus } from '..';
import type { EditorState } from 'core/editor';
import type { UserState } from 'core/user';
import type { EditorState } from '~/core/editor';
import type { UserState } from '~/core/user';
type FailedChecksProps = {
sendTranslation: (ignoreWarnings?: boolean) => void;

Просмотреть файл

@ -1,7 +1,7 @@
import * as React from 'react';
import { Localized } from '@fluent/react';
import { useOnDiscard } from 'core/utils';
import { useOnDiscard } from '~/core/utils';
import './KeyboardShortcuts.css';

Просмотреть файл

@ -1,6 +1,6 @@
import { useCallback } from 'react';
import { useAppDispatch } from 'hooks';
import { useAppDispatch } from '~/hooks';
import { actions } from '..';
/**

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше