зеркало из https://github.com/mozilla/pontoon.git
Refactor the frontend build (#2437)
* 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:
Родитель
8d2714d5db
Коммит
9fc9430cc7
|
@ -8,7 +8,7 @@ flags:
|
|||
|
||||
frontend:
|
||||
paths:
|
||||
- frontend/
|
||||
- translate/
|
||||
carryforward: true
|
||||
|
||||
non-frontend-js:
|
||||
|
|
3
.flake8
3
.flake8
|
@ -11,4 +11,5 @@ exclude =
|
|||
setup.py,
|
||||
node_modules,
|
||||
bin,
|
||||
frontend
|
||||
tag-admin,
|
||||
translate
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
61
Makefile
61
Makefile
|
@ -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
|
||||
|
|
|
@ -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="Mozilla’s 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 +0,0 @@
|
|||
/// <reference types="react-scripts" />
|
12625
frontend/yarn.lock
12625
frontend/yarn.lock
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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"
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
67
package.json
67
package.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
|
||||
"""
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
};
|
|
@ -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="Mozilla’s 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 '..';
|
||||
|
||||
/**
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче