diff --git a/.github/workflows/localization.yml b/.github/workflows/localization.yml new file mode 100644 index 00000000000..8f6e21c6ec4 --- /dev/null +++ b/.github/workflows/localization.yml @@ -0,0 +1,49 @@ +name: CI + +on: + push: + branches: + - master + - localization + pull_request: + +jobs: + locales: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: 'yarn' + + - name: Install gettext + run: sudo apt-get install gettext + + - name: Yarn install + run: yarn install --frozen-lockfile --prefer-offline + + - name: Extract locales + run: yarn extract-locales + + - name: Push Locales + if: github.event_name == 'push' + env: + # The default branch of the repository, in this case "master" + default_branch: ${{ github.event.repository.default_branch }} + run: | + # Stable check for if the workflow is running on the default branch + # https://stackoverflow.com/questions/64781462/github-actions-default-branch-variable + is_default_branch="${{ format('refs/heads/{0}', env.default_branch) == github.ref }}" + + ARGS="" + + if [[ "$is_default_branch" == 'false' ]]; then + ARGS="--dry-run" + fi + + ./bin/push-locales $ARGS + + + diff --git a/.prettierignore b/.prettierignore index b7923be159e..a65d5d6fe25 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,7 @@ *.* # exclude these files Dockerfile +src/fonts/LICENSE # exclude these directories /assets/ /bin/ diff --git a/babel.config.locales.js b/babel.config.locales.js new file mode 100644 index 00000000000..c05c53235ce --- /dev/null +++ b/babel.config.locales.js @@ -0,0 +1,41 @@ +// Create UTC creation date in the correct format. +const potCreationDate = new Date() + .toISOString() + .replace('T', ' ') + .replace(/:\d{2}.\d{3}Z/, '+0000'); + +module.exports = { + extends: './babel.config.js', + plugins: [ + [ + 'module:babel-gettext-extractor', + { + headers: { + 'Project-Id-Version': 'amo', + 'Report-Msgid-Bugs-To': 'EMAIL@ADDRESS', + 'POT-Creation-Date': potCreationDate, + 'PO-Revision-Date': 'YEAR-MO-DA HO:MI+ZONE', + 'Last-Translator': 'FULL NAME ', + 'Language-Team': 'LANGUAGE ', + 'MIME-Version': '1.0', + 'Content-Type': 'text/plain; charset=utf-8', + 'Content-Transfer-Encoding': '8bit', + 'plural-forms': 'nplurals=2; plural=(n!=1);', + }, + functionNames: { + gettext: ['msgid'], + dgettext: ['domain', 'msgid'], + ngettext: ['msgid', 'msgid_plural', 'count'], + dngettext: ['domain', 'msgid', 'msgid_plural', 'count'], + pgettext: ['msgctxt', 'msgid'], + dpgettext: ['domain', 'msgctxt', 'msgid'], + npgettext: ['msgctxt', 'msgid', 'msgid_plural', 'count'], + dnpgettext: ['domain', 'msgctxt', 'msgid', 'msgid_plural', 'count'], + }, + fileName: './locale/templates/LC_MESSAGES/amo.pot', + baseDirectory: process.cwd(), + stripTemplateLiteralIndent: true, + }, + ], + ], +}; diff --git a/bin/extract-locales b/bin/extract-locales deleted file mode 100755 index 4d00c81a105..00000000000 --- a/bin/extract-locales +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env sh - -yarn extract-locales diff --git a/bin/locales.mjs b/bin/locales.mjs new file mode 100755 index 00000000000..282d7b10d08 --- /dev/null +++ b/bin/locales.mjs @@ -0,0 +1,59 @@ +#!/usr/bin/env zx + +import {$, path, echo, within, glob} from 'zx'; + +const root = path.join(__dirname, '..'); +const localeDir = path.join(root, 'locale'); +const templateFile = path.join(localeDir, '/templates/LC_MESSAGES/amo.pot'); + +within(async () => { + echo('Extracting locales...'); + + const sourceDir = path.join(root, 'src', 'amo'); + const outputDir = path.join(root, 'dist', 'locales'); + const localesConfig = path.join(root, 'babel.config.locales.js'); + + await $`babel ${sourceDir} \ + --out-dir ${outputDir} \ + --config-file ${localesConfig} \ + --verbose \ + `; + + const {stdout: output} = await $`git diff --numstat -- ${templateFile}`; + + // git diff --numstat returns the number of insertions and deletions for each file + // this regex extracts the numbers from the output + const regex = /([0-9]+).*([0-9]+)/; + + const [, insertions = 0, deletions = 0] = output.match(regex) || []; + + const isLocaleClean = insertions < 2 && deletions < 2; + + if (isLocaleClean) { + return echo('No locale changes, nothing to update, ending process'); + } + + echo(`Found ${insertions} insertions and ${deletions} deletions in ${templateFile}.`); + + const poFiles = await glob(`${localeDir}/**/amo.po`); + + echo(`Merging ${poFiles.length} translation files.`); + + for await (const poFile of poFiles) { + const dir = path.dirname(poFile); + const stem = path.basename(poFile, '.po'); + const tempFile = path.join(dir, `${stem}.po.tmp`); + echo(`merging: ${poFile}`); + + try { + await $`msgmerge --no-fuzzy-matching -q -o ${tempFile} ${poFile} ${templateFile}` + await $`mv ${tempFile} ${poFile}` + } catch (error) { + await $`rm ${tempFile}`; + throw new Error(`Error merging ${poFile}`); + } + } + + return true; +}); + diff --git a/bin/merge-locales b/bin/merge-locales deleted file mode 100755 index a156aa2e644..00000000000 --- a/bin/merge-locales +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env node - -/* eslint-disable no-console */ -require('@babel/register'); - -const { globSync } = require('glob'); -const path = require('path'); -const shell = require('shelljs'); - -const localeDir = path.join(__dirname, '../locale'); - -const poFiles = globSync(`${localeDir}/**/amo.po`); -const template = - path.join(localeDir, `templates/LC_MESSAGES/amo.pot`); - -poFiles.forEach((poFile) => { - const dir = path.dirname(poFile); - const stem = path.basename(poFile, '.po'); - const tempFile = path.join(dir, `${stem}.po.tmp`); - shell.exec( - `msgmerge --no-fuzzy-matching -q -o ${tempFile} ${poFile} ${template}` - ); - shell.mv(tempFile, poFile); -}); diff --git a/bin/push-locales b/bin/push-locales new file mode 100755 index 00000000000..1f8ed66030e --- /dev/null +++ b/bin/push-locales @@ -0,0 +1,58 @@ +#! /bin/bash + +# Exit immediately when a command fails. +set -e + +# Make sure exit code are respected in a pipeline. +set -o pipefail + +# Treat unset variables as an error an exit immediately. +set -u + +info() { + local message="$1" + + echo "" + echo "INFO: $message" + echo "" +} + +ROBOT_EMAIL="addons-dev-automation+github@mozilla.com" +ROBOT_NAME="Mozilla Add-ons Robot" + +# Set git committer/author to the robot. +export GIT_AUTHOR_NAME="$ROBOT_NAME" +export GIT_AUTHOR_EMAIL="$ROBOT_EMAIL" +export GIT_COMMITTER_NAME="$ROBOT_NAME" +export GIT_COMMITTER_EMAIL="$ROBOT_EMAIL" + +DATE=$(date -u +%Y-%m-%d) +REV=$(git rev-parse --short HEAD) +MESSAGE="Extracted l10n messages from $DATE at $REV" +DIFF_WITH_ONE_LINE_CHANGE="2 files changed, 2 insertions(+), 2 deletions(-)" + +git_diff_stat=$(git diff --shortstat locale/templates/LC_MESSAGES) + +info "git_diff_stat: $git_diff_stat" + +# IF there are no uncommitted local changes, exit early. +if [[ -z "$git_diff_stat" ]] || [[ "$git_diff_stat" == *"$DIFF_WITH_ONE_LINE_CHANGE"* ]]; then + info """ + No substantial changes to l10n strings found. Exiting the process. + """ + exit 0 +fi + +info """ +GIT_AUTHOR_NAME: $GIT_AUTHOR_NAME +GIT_AUTHOR_EMAIL: $GIT_AUTHOR_EMAIL +GIT_COMMITTER_NAME: $GIT_COMMITTER_NAME +GIT_COMMITTER_EMAIL: $GIT_COMMITTER_EMAIL + +This script passes arguments directly to Git commands. We can pass --dry-mode to test this script +Without actually committing or pushing. Make sure to only pass arguments supported on both commit and push.. +ARGS: $@ +""" + +git commit -am "$MESSAGE" "$@" +git push "$@" diff --git a/bin/run-l10n-extraction b/bin/run-l10n-extraction deleted file mode 100755 index 6388b2bc879..00000000000 --- a/bin/run-l10n-extraction +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env bash - -# Exit immediately when a command fails. -set -e -# Make sure exit code are respected in a pipeline. -set -o pipefail -# Treat unset variables as an error an exit immediately. -set -u - -INITIAL_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) -GIT_CHANGES=$(git status --porcelain) -DIFF_WITH_ONE_LINE_CHANGE="1 file changed, 1 insertion(+), 1 deletion(-)" -GIT_REMOTE="${GIT_REMOTE:-git@github.com:mozilla/addons-frontend.git}" -LOG_FILE="l10n-extraction.log" - -info() { - local message="$1" - - echo "" - echo "INFO: $message" - echo "" -} - -error() { - local message="$1" - - echo "ERROR: $message" - exit 1 -} - -run_l10n_extraction() { - local branch="amo-locales" - - info "Starting l10n extraction" - - # Detect local (uncommitted) changes. - if [[ ! -z "$GIT_CHANGES" ]]; then - error "You have local changes, therefore this script cannot continue." - fi - - # Switch to the `master` branch if we are not on it already. - if [[ "$INITIAL_GIT_BRANCH" != "master" ]]; then - git checkout master - fi - - # Make sure the 'master' branch is up-to-date. - git pull "$GIT_REMOTE" master - - # Update dependencies - yarn >> "$LOG_FILE" 2>&1 - - # Ensure the branch to extract the locales is clean. - if [[ $(git branch --list "$branch") ]]; then - info "Deleting branch '$branch' because it already exists" - git branch -D "$branch" - fi - - info "Creating and switching to branch '$branch'" - git checkout -b "$branch" - - info "Extracting locales (this can take several minutes)" - bin/extract-locales >> "$LOG_FILE" 2>&1 - - local git_diff_stat - git_diff_stat=$(git diff --shortstat) - - if [[ -z "$git_diff_stat" ]] || [[ "$git_diff_stat" == *"$DIFF_WITH_ONE_LINE_CHANGE"* ]]; then - info "No locale changes, nothing to update, ending process" - git checkout -- . - git checkout "$INITIAL_GIT_BRANCH" - git branch -d "$branch" - return - fi - - git commit -a -m "Extract locales" --no-verify - - info "Merging locales" - bin/merge-locales >> "$LOG_FILE" 2>&1 - - git commit -a -m "Merge locales" --no-verify - - # We use force-push in case the branch already exists. - git push -f "$GIT_REMOTE" "$branch" - - git checkout "$INITIAL_GIT_BRANCH" - git branch -D "$branch" - - echo "" - echo "----------------------------------------------------------------------" - echo "" - echo " You can now open the link below to submit your Pull Request:" - echo " https://github.com/mozilla/addons-frontend/pull/new/$branch" - echo "" - echo "----------------------------------------------------------------------" -} - -# Clear log file -echo "" > "$LOG_FILE" - -run_l10n_extraction diff --git a/docs/i18n.md b/docs/i18n.md index 9479c947250..dd830246160 100644 --- a/docs/i18n.md +++ b/docs/i18n.md @@ -20,96 +20,11 @@ NODE_PATH='./:./src' bin/create-locales ## Updating locales -TL;DR: run the following script from the `master` branch: `./bin/run-l10n-extraction` +Locales are updated automatically as a part of our CI. On every push to master `yarn extract-locales` is run which extracts locale strings from our codebase, merges any changes to the source language files and commits the changes. -### The long story +You can run this command manually on your local environment any time to check the output strings. -Once a week right after the forthcoming release [is tagged](http://addons.readthedocs.io/en/latest/server/push-duty.html), the locales for each app must be generated. - -This is a semi-automated process: a team member must create a pull request with the following commits: - -1. A commit containing the extraction of newly added strings -2. A commit containing a merge of localizations - -Each one of these steps are detailed in the sections below. Let's begin... - -#### Extracting newly added strings - -Start the process by creating a git branch and extracting the locales. - -``` -git checkout master -git pull -git checkout -b amo-locales -bin/extract-locales -``` - -This extracts all strings wrapped with `i18n.gettext()` or any other function supported by [Jed][jed] (the library we use in JavaScript to carry out replacements for the string keys in the current locale). - -The strings are extracted using a babel plugin via webpack. Extracted strings are added to a pot template file. This file is used to seed the po for each locale with the strings needing translating when merging locales. - -Run `git diff` to see what the extraction did. **If no strings were updated then you do not have to continue creating the pull request. You can revert the changes made to the `pot` timestamp.** Here is an example of a diff where no strings were changed. It just shows a single change to the timestamp: - -```diff -diff --git a/locale/templates/LC_MESSAGES/amo.pot b/locale/templates/LC_MESSAGES/amo.pot -index 31e113f2..c7da4e34 100644 ---- a/locale/templates/LC_MESSAGES/amo.pot -+++ b/locale/templates/LC_MESSAGES/amo.pot -@@ -2,7 +2,7 @@ msgid "" - msgstr "" - "Project-Id-Version: amo\n" - "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" --"POT-Creation-Date: 2017-06-08 14:01+0000\n" -+"POT-Creation-Date: 2017-06-08 14:43+0000\n" - "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" - "Last-Translator: FULL NAME \n" - "Language-Team: LANGUAGE \n" -``` - -When the application is under active development it's more likely that you will see a diff containing new strings or at least strings that have shifted to different line numbers in the source. If so, commit your change and continue to the next step: - -``` -git commit -a -m "Extract AMO locales" -``` - -#### Merging locale files - -After extracting new strings, you have to merge them into the existing locale files. Do this in your branch and commit: - -``` -bin/merge-locales -``` - -Keep an eye out for [fuzzy strings](https://www.gnu.org/software/gettext/manual/html_node/Fuzzy-Entries.html) by running `git diff` and searching for a comment that looks like `# fuzzy`. This comment means the localization may not exactly match the source text; a localizer needs to review it. As per our configuration, the application will not display fuzzy translations. These strings will fall back to English. - -In some rare cases you may wish to remove the `fuzzy` marker to prevent falling back to English. Discuss it with a team member before removing `fuzzy` markers. - -Commit and continue to the next step: - -``` -git commit -a -m "Merged AMO locales" -``` - -#### Finalizing the extract/merge process - -Now that you have extracted and merged locales for one application, it's time to create a pull request for your branch. For example: - -``` -git push origin amo-locales -``` - -If the pull request passes all of our CI tests it is likely good to merge. You don't need to ask for a review unless you're unsure of something because often locale updates will be thousands of lines of minor diffs that can't be reasonably reviewed by a human. 🙂 If the pull request passes all of our CI tests it is likely good to merge. - -#### Building the JS locale files - -This command creates the JSON files which are then built into JS bundles by webpack when the build step is run. This happens automatically as part of the deployment process. - -Since dist files are created when needed you only need to build and commit the JSON to the repo. - -``` -# build the JSON. -bin/build-locales -``` +Github actions internally prevent infinite loops by default. ## Setting up translations diff --git a/package.json b/package.json index aa270240056..611e352af56 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ } }, "extract-locales": { - "command": "webpack --progress --color --config webpack.l10n.config.babel.js", + "command": "zx ./bin/locales.mjs", "env": { "NODE_ENV": "production", "NODE_ICU_DATA": "./node_modules/full-icu", @@ -253,6 +253,7 @@ "webpack-isomorphic-tools": "4.0.0" }, "devDependencies": { + "@babel/cli": "7.23.4", "@babel/core": "^7.24.7", "@babel/eslint-parser": "^7.24.7", "@babel/preset-env": "^7.24.7", @@ -329,7 +330,8 @@ "webpack-cli": "^4.0.0", "webpack-dev-middleware": "^6.1.2", "webpack-hot-middleware": "^2.26.1", - "webpack-subresource-integrity": "5.1.0" + "webpack-subresource-integrity": "5.1.0", + "zx": "^8.1.4" }, "bundlewatch": [ { diff --git a/yarn.lock b/yarn.lock index 08f1f8260b0..b1b3abc019a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,6 +20,22 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@babel/cli@7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.23.4.tgz#f5cc90487278065fa0c3b1267cf0c1d44ddf85a7" + integrity sha512-j3luA9xGKCXVyCa5R7lJvOMM+Kc2JEnAEIgz2ggtjQ/j5YUVgfsg/WsG95bbsgq7YLHuiCOzMnoSasuY16qiCw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + commander "^4.0.1" + convert-source-map "^2.0.0" + fs-readdir-recursive "^1.1.0" + glob "^7.2.0" + make-dir "^2.1.0" + slash "^2.0.0" + optionalDependencies: + "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" + chokidar "^3.4.0" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" @@ -1466,7 +1482,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.21", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.21", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.9": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -1479,6 +1495,11 @@ resolved "https://registry.yarnpkg.com/@mozilla-protocol/tokens/-/tokens-5.0.5.tgz#92bc2e5fe61e9706c95f9f3546bed556526dba2a" integrity sha512-VJ2fYJs09m0x1yWHVGhbqp2ZBOfEZy8vbOGYqNXCVrsYriLZp20CdKJq3+jj/chEVz+S5b0wdIOKkLqvgqIr/A== +"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": + version "2.1.8-no-fsevents.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" + integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -1799,6 +1820,14 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/fs-extra@>=11": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + "@types/graceful-fs@^4.1.3": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -1857,6 +1886,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/jsonfile@*": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== + dependencies: + "@types/node" "*" + "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -1872,6 +1908,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.17.tgz#5c009e1d9c38f4a2a9d45c0b0c493fe6cdb4bcb5" integrity sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng== +"@types/node@>=20": + version "20.14.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a" + integrity sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -3114,7 +3157,7 @@ chokidar-cli@^3.0.0: lodash.throttle "^4.1.1" yargs "^13.3.0" -chokidar@3.6.0, chokidar@^3.5.2: +chokidar@3.6.0, chokidar@^3.4.0, chokidar@^3.5.2: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -3287,6 +3330,11 @@ commander@^2.20.0, commander@^2.9.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + commander@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" @@ -5173,6 +5221,11 @@ fs-monkey@^1.0.3: resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5355,7 +5408,7 @@ glob@^10.3.7, glob@^10.4.2: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -9805,6 +9858,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -10015,16 +10073,7 @@ string-natural-compare@3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10138,7 +10187,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10152,13 +10201,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -10815,6 +10857,11 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -11340,7 +11387,7 @@ wordwrap@0.0.2: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" integrity sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11358,15 +11405,6 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -11511,3 +11549,11 @@ yocto-queue@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + +zx@^8.1.4: + version "8.1.4" + resolved "https://registry.yarnpkg.com/zx/-/zx-8.1.4.tgz#19bcd543fab34d01d7d4174a314b33cde9115a17" + integrity sha512-QFDYYpnzdpRiJ3dL2102Cw26FpXpWshW4QLTGxiYfIcwdAqg084jRCkK/kuP/NOSkxOjydRwNFG81qzA5r1a6w== + optionalDependencies: + "@types/fs-extra" ">=11" + "@types/node" ">=20"