From 0592afb768158bbaffe34e6b301842c395e2595b Mon Sep 17 00:00:00 2001 From: Pankaj Yadav Date: Thu, 31 Aug 2023 19:23:13 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20changes=20for=20creating=20executab?= =?UTF-8?q?le=20binaries=20of=20`percy`=20CLI=20(#1343)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * initial commit * Fix install.js * Fix lint * Revert for oclif * Revert code * Add build exec script * upload artifacts * Mac env for creating executable * Update output filename * Update output filename * Add signing & notrizing steps * Install gon * Update env name * Update permissions * Run on release only * Mac & linux x64, arm64 executables * Update to include current src * Update notrize config template * Use node 14 * Code cleanup * Fix code for codesign * Remove signing * Add entitlement.plist * Run only on publish * Use yarn build to generate dist folder * Add cleanup * Use code based on env * Add steps to create .env file * Skip coverage for Win path * add tests for install.js * add tests for install.js * remove dotenv installation * Update env var name * Add executable.js as entrypoint * Fix win executable and tests * Resolve comments * Resolve comments * convert artifacts to tar to retain permissions * convert artifacts to tar to retain permissions * Update script for correct naming * debug unit test * remove console log * update logic based on platform * Fix lint * refactoring code * Update to use gsed * Fix empty elements in array * Run executable version before uploading * Run executable version before uploading * Run on publish * Verify executable in seprate step --- .github/workflows/executable.yml | 42 +++++ babel.config.cjs | 4 + package.json | 2 + packages/cli/bin/run.cjs | 1 + packages/cli/src/commands.js | 16 +- packages/cli/test/commands.test.js | 66 ++++++++ packages/core/src/install.js | 27 ++++ packages/core/test/unit/install.test.js | 202 ++++++++++++++++++++++++ packages/env/test/helpers.js | 2 + scripts/executable.sh | 73 +++++++++ scripts/files/entitlement.plist | 10 ++ scripts/files/notarize_config.json.tmpl | 10 ++ yarn.lock | 60 ++++++- 13 files changed, 513 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/executable.yml create mode 100755 scripts/executable.sh create mode 100644 scripts/files/entitlement.plist create mode 100644 scripts/files/notarize_config.json.tmpl diff --git a/.github/workflows/executable.yml b/.github/workflows/executable.yml new file mode 100644 index 000000000..ebadb0e90 --- /dev/null +++ b/.github/workflows/executable.yml @@ -0,0 +1,42 @@ +name: Build Executables +on: + release: + types: [published] +jobs: + lint: + name: Build Executables + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v3 + with: + node-version: 14 + - name: Install gon via HomeBrew for code signing and app notarization + run: | + brew tap mitchellh/gon + brew install mitchellh/gon/gon + - run: ./scripts/executable.sh + env: + APPLE_DEV_CERT: ${{secrets.APPLE_DEV_CERT}} + APPLE_ID_USERNAME: ${{secrets.APPLE_ID_USERNAME}} + APPLE_ID_KEY: ${{secrets.APPLE_ID_KEY}} + - name: Create tar files + run: | + tar -cvf percy-linux.tar percy + mv percy-osx percy + tar -cvf percy-osx.tar percy + tar -cvf percy-win.tar percy.exe + - name: Verify executable + run: ./percy --version + - uses: actions/upload-artifact@v3 + with: + name: percy-osx + path: percy-osx.tar + - uses: actions/upload-artifact@v3 + with: + name: percy-linux + path: percy-linux.tar + - uses: actions/upload-artifact@v3 + with: + name: percy-win + path: percy-win.tar diff --git a/babel.config.cjs b/babel.config.cjs index 08077861a..ce06e2862 100644 --- a/babel.config.cjs +++ b/babel.config.cjs @@ -59,6 +59,10 @@ module.exports = { exclude: ['dist', 'test'] }] ] + }, + dev: { + presets: ["@babel/preset-env"], + plugins: ["babel-plugin-transform-import-meta"] } } }; diff --git a/package.json b/package.json index d29fe9ac7..86e999bf5 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "url": "https://github.com/percy/cli" }, "scripts": { + "build_cjs": "BABEL_ENV=dev babel packages -d build", "build": "lerna run build --stream", "build:watch": "lerna run build --parallel -- --watch", "build:pack": "mkdir -p ./packs && lerna exec npm pack && mv ./packages/*/*.tgz ./packs", @@ -30,6 +31,7 @@ "@babel/eslint-parser": "^7.14.7", "@babel/preset-env": "^7.14.7", "@babel/register": "^7.17.7", + "babel-plugin-transform-import-meta": "^2.2.1", "@rollup/plugin-alias": "^4.0.0", "@rollup/plugin-babel": "^6.0.0", "@rollup/plugin-commonjs": "^21.0.0", diff --git a/packages/cli/bin/run.cjs b/packages/cli/bin/run.cjs index fa1a47b71..eb657c068 100755 --- a/packages/cli/bin/run.cjs +++ b/packages/cli/bin/run.cjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// DO NOT REMOVE: Update NODE_ENV for executable // ensure that we're running within a supported node version if (parseInt(process.version.split('.')[0].substring(1), 10) < 14) { console.error(`Node ${process.version} is not supported. Percy only ` + ( diff --git a/packages/cli/src/commands.js b/packages/cli/src/commands.js index 5f2844212..8cdb972e4 100644 --- a/packages/cli/src/commands.js +++ b/packages/cli/src/commands.js @@ -92,6 +92,14 @@ function importLegacyCommands(commandsPath) { }); } +function formatFilepath(filepath) { + /* istanbul ignore next */ + if (!(process.platform.startsWith('win'))) { + filepath = '/' + filepath; + } + return filepath; +} + // Imports and returns compatibile CLI commands from various sources export async function importCommands() { let root = path.resolve(url.fileURLToPath(import.meta.url), '../..'); @@ -140,7 +148,13 @@ export async function importCommands() { pkgs.set(pkg.name, () => Promise.all( pkg['@percy/cli'].commands.map(async cmdPath => { let modulePath = path.join(pkgPath, cmdPath); - let module = await import(url.pathToFileURL(modulePath).href); + // Below code is used in scripts/build.sh to update href + let module = null; + if (process.env.NODE_ENV === 'executable') { + module = await import(formatFilepath(modulePath)); + } else { + module = await import(url.pathToFileURL(modulePath).href); + } module.default.packageInformation ||= pkg; return module.default; }) diff --git a/packages/cli/test/commands.test.js b/packages/cli/test/commands.test.js index 3fc49fe00..f3d66b3ae 100644 --- a/packages/cli/test/commands.test.js +++ b/packages/cli/test/commands.test.js @@ -81,6 +81,72 @@ describe('CLI commands', () => { }); }); + describe('from node_modules with executable', () => { + beforeEach(async () => { + process.env.NODE_ENV = 'executable'; + }); + + afterEach(() => { + delete process.env.NODE_ENV; + }); + + const mockCmds = { + '@percy/cli-exec': { name: 'exec' }, + '@percy/cli-config': { name: 'config' }, + '@percy/storybook': { name: 'storybook' }, + '@percy/core': null, + '@percy/cli': null, + 'percy-cli-custom': { name: 'custom' }, + 'percy-cli-other': null, + 'other-dep': null + }; + + const expectedCmds = [ + jasmine.objectContaining({ name: 'config' }), + jasmine.objectContaining({ name: 'custom' }), + jasmine.objectContaining({ name: 'exec' }), + jasmine.objectContaining({ name: 'storybook' }) + ]; + + it('imports from dependencies', async () => { + await mockModuleCommands(path.resolve('.'), mockCmds); + await expectAsync(importCommands()).toBeResolvedTo(expectedCmds); + expect(logger.stdout).toEqual([]); + expect(logger.stderr).toEqual([]); + }); + + it('imports from a parent directory', async () => { + await mockModuleCommands(path.resolve('../..'), mockCmds); + await expectAsync(importCommands()).toBeResolvedTo(expectedCmds); + expect(logger.stdout).toEqual([]); + expect(logger.stderr).toEqual([]); + }); + + it('imports from the current project', async () => { + await mockModuleCommands(process.cwd(), mockCmds); + await expectAsync(importCommands()).toBeResolvedTo(expectedCmds); + expect(logger.stdout).toEqual([]); + expect(logger.stderr).toEqual([]); + }); + + it('automatically includes package information', async () => { + await mockModuleCommands(path.resolve('.'), mockCmds); + let cmds = await importCommands(); + + expect(cmds[0].packageInformation.name).toEqual('@percy/cli-config'); + }); + + it('handles errors and logs debug info', async () => { + fs.$vol.fromJSON({ './node_modules': null }); + fs.readdirSync.and.throwError(new Error('EACCES')); + await expectAsync(importCommands()).toBeResolvedTo([]); + expect(logger.stdout).toEqual([]); + expect(logger.stderr).toEqual([ + jasmine.stringContaining('[percy:cli:plugins] Error: EACCES') + ]); + }); + }); + describe('from yarn pnp', () => { let Module, plugPnpApi; diff --git a/packages/core/src/install.js b/packages/core/src/install.js index a5d1b8853..88b7937f1 100644 --- a/packages/core/src/install.js +++ b/packages/core/src/install.js @@ -3,6 +3,7 @@ import url from 'url'; import path from 'path'; import https from 'https'; import logger from '@percy/logger'; +import cp from 'child_process'; import { ProxyHttpsAgent } from '@percy/client/utils'; // Formats a raw byte integer as a string @@ -63,7 +64,22 @@ export async function download({ executable }) { let outdir = path.join(directory, revision); + if (process.env.NODE_ENV === 'executable') { + if (outdir.charAt(0) === '/') { + outdir = outdir.replace('/', ''); + } + } + + let command = 'pwd'; let archive = path.join(outdir, decodeURIComponent(url.split('/').pop())); + if (process.env.NODE_ENV === 'executable') { + /* istanbul ignore next */ + if (process.platform.startsWith('win')) { + command = 'cd'; + } + outdir = outdir.replace('C:\\', ''); + archive = archive.replace('C:\\', ''); + } let exec = path.join(outdir, executable); if (!fs.existsSync(exec)) { @@ -106,6 +122,17 @@ export async function download({ ); }).on('error', reject)); + if (process.env.NODE_ENV === 'executable') { + let output = cp.execSync(command, { encoding: 'utf-8' }).trim(); + let prefix = null; + if (process.platform.startsWith('win')) { + prefix = '\\'; + } else { + prefix = '/'; + } + archive = output.concat(prefix, archive); + outdir = output.concat(prefix, outdir); + } // extract the downloaded file await extract(archive, outdir); diff --git a/packages/core/test/unit/install.test.js b/packages/core/test/unit/install.test.js index d2df10c31..0056a92c5 100644 --- a/packages/core/test/unit/install.test.js +++ b/packages/core/test/unit/install.test.js @@ -3,6 +3,7 @@ import logger from '@percy/logger/test/helpers'; import { mockfs, fs } from '@percy/config/test/helpers'; import { mockRequests } from '@percy/client/test/helpers'; import install from '../../src/install.js'; +import { mockgit } from '@percy/env/test/helpers'; const CHROMIUM_REVISIONS = install.chromium.revisions; @@ -200,3 +201,204 @@ describe('Unit / Install', () => { } }); }); + +describe('Unit / Install in executable', () => { + let dl, options; + + beforeEach(async () => { + process.env.NODE_ENV = 'executable'; + await logger.mock({ isTTY: true }); + await mockfs(); + await mockgit(); + + // mock a fake download api + dl = await mockRequests('https://fake-download.org/archive.zip'); + + // all options are required + options = { + name: 'Archive', + revision: 'v0', + url: 'https://fake-download.org/archive.zip', + extract: jasmine.createSpy('extract').and.resolveTo(), + directory: '.downloads', + executable: 'extracted/bin.exe' + }; + }); + + afterEach(() => { + delete process.env.NODE_ENV; + }); + + it('does nothing if the executable already exists in the output directory', async () => { + fs.existsSync.and.returnValue(true); + await install.download(options); + + expect(fs.promises.mkdir).not.toHaveBeenCalled(); + expect(fs.promises.unlink).not.toHaveBeenCalled(); + expect(fs.createWriteStream).not.toHaveBeenCalled(); + expect(dl).not.toHaveBeenCalled(); + }); + + it('creates the output directory when it does not exist', async () => { + await install.download(options); + + expect(fs.promises.mkdir).toHaveBeenCalledOnceWith( + path.join('.downloads', 'v0'), { recursive: true }); + }); + + it('fetches the archive from the provided url', async () => { + await install.download(options); + + expect(dl).toHaveBeenCalled(); + }); + + it('logs progress during the archive download', async () => { + let now = Date.now(); + // eta is calculated by the elapsed time and remaining progress + spyOn(Date, 'now').and.callFake(() => (now += 65000)); + dl.and.returnValue([200, 'partial contents', { 'content-length': 80 }]); + + await install.download(options); + + expect(logger.stderr).toEqual([]); + expect(logger.stdout).toEqual([ + '[percy] Downloading Archive v0...', + '[percy] Downloading Archive v0 [==== ] 16B/80B 20% 04:20', + '[percy] Successfully downloaded Archive v0' + ]); + }); + + it('does not log progress when info logs are disabled', async () => { + logger.loglevel('error'); + + await install.download(options); + + expect(logger.stderr).toEqual([]); + expect(logger.stdout).toEqual([]); + }); + + it('extracts the downloaded archive to the output directory', async () => { + await install.download(options); + + expect(options.extract).toHaveBeenCalledOnceWith( + path.join('/.downloads', 'v0', 'archive.zip'), + path.join('/.downloads', 'v0') + ); + }); + + it('cleans up the archive after downloading and extracting', async () => { + fs.$vol.fromJSON({ '/.downloads/v0/archive.zip': '' }); + expect(fs.existsSync('/.downloads/v0/archive.zip')).toBe(true); + + await install.download(options); + + expect(fs.existsSync('/.downloads/v0/archive.zip')).toBe(false); + }); + + it('handles failed downloads', async () => { + dl.and.returnValue([404]); + + await expectAsync(install.download(options)) + .toBeRejectedWithError('Download failed: 404 - https://fake-download.org/archive.zip'); + }); + + it('logs the file size in a readable format', async () => { + dl.and.returnValue([200, '1'.repeat(20_000_000)]); + + await install.download(options); + + expect(logger.stderr).toEqual([]); + expect(logger.stdout).toContain( + '[percy] Downloading Archive v0 [====================] 19.1MB/19.1MB 100% 00:00' + ); + }); + + it('returns the full path of the executable', async () => { + await expectAsync(install.download(options)).toBeResolvedTo( + path.join('.downloads', 'v0', 'extracted', 'bin.exe')); + }); + + describe('Chromium', () => { + let extractZip; + + beforeEach(async () => { + dl = await mockRequests('https://storage.googleapis.com'); + + // stub extract-zip using esm loader mocks + extractZip = jasmine.createSpy('exports').and.resolveTo(); + global.__MOCK_IMPORTS__.set('extract-zip', { default: extractZip }); + + // make getters for jasmine property spy + let { platform, arch } = process; + Object.defineProperties(process, { + platform: { get: () => platform }, + arch: { get: () => arch } + }); + }); + + it('downloads from the google storage api', async () => { + await install.chromium(); + + expect(dl).toHaveBeenCalled(); + }); + + it('extracts to a .local-chromium directory', async () => { + await install.chromium(); + + expect(extractZip).toHaveBeenCalledOnceWith(jasmine.any(String), { + dir: jasmine.stringMatching('(/|\\\\).local-chromium(/|\\\\)') + }); + }); + + for (let [platform, expected] of Object.entries({ + linux: { + revision: CHROMIUM_REVISIONS.linux, + url: jasmine.stringMatching(`Linux_x64/${CHROMIUM_REVISIONS.linux}/chrome-linux.zip`), + return: path.join('chrome-linux', 'chrome'), + process: { platform: 'linux', arch: 'x64' } + }, + darwin: { + revision: CHROMIUM_REVISIONS.darwin, + url: jasmine.stringMatching(`Mac/${CHROMIUM_REVISIONS.darwin}/chrome-mac.zip`), + return: path.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'), + process: { platform: 'darwin', arch: 'x64' } + }, + darwinArm: { + revision: CHROMIUM_REVISIONS.darwinArm, + url: jasmine.stringMatching(`Mac_Arm/${CHROMIUM_REVISIONS.darwinArm}/chrome-mac.zip`), + return: path.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'), + process: { platform: 'darwin', arch: 'arm64' } + }, + win64: { + revision: CHROMIUM_REVISIONS.win64, + url: jasmine.stringMatching(`Win_x64/${CHROMIUM_REVISIONS.win64}/chrome-win.zip`), + return: path.join('chrome-win', 'chrome.exe'), + process: { platform: 'win32', arch: 'x64' } + }, + win32: { + revision: CHROMIUM_REVISIONS.win32, + url: jasmine.stringMatching(`Win/${CHROMIUM_REVISIONS.win32}/chrome-win.zip`), + return: path.join('chrome-win', 'chrome.exe'), + process: { platform: 'win32', arch: 'x32' } + } + })) { + it(`downloads the correct files for ${platform}`, async () => { + spyOnProperty(process, 'platform').and.returnValue(expected.process.platform); + spyOnProperty(process, 'arch').and.returnValue(expected.process.arch); + + await expectAsync(install.chromium()).toBeResolvedTo( + jasmine.stringMatching(expected.return.replace(/[.\\]/g, '\\$&')) + ); + + expect(dl).toHaveBeenCalledOnceWith( + jasmine.objectContaining({ url: expected.url })); + + expect(logger.stderr).toEqual([]); + expect(logger.stdout).toEqual(jasmine.arrayContaining([ + `[percy] Downloading Chromium ${expected.revision}...`, + `[percy] Successfully downloaded Chromium ${expected.revision}` + ])); + }); + } + }); +}); diff --git a/packages/env/test/helpers.js b/packages/env/test/helpers.js index b6b07fd9e..2c7684a38 100644 --- a/packages/env/test/helpers.js +++ b/packages/env/test/helpers.js @@ -10,6 +10,8 @@ export function mockgit(branch = '') { if (!cmd.includes('rev-parse')) return result; if (cmd.includes('--abbrev-ref')) return branch; return result.match(/^COMMIT_SHA:(.*)$/m)?.[1]; + } else if (cmd.match(/^pwd\b/) || cmd.match(/^cd\b/)) { + return ''; } else { return cp.execSync.and.originalFn.call(this, cmd, options); } diff --git a/scripts/executable.sh b/scripts/executable.sh new file mode 100755 index 000000000..1fd7ad6cf --- /dev/null +++ b/scripts/executable.sh @@ -0,0 +1,73 @@ +#!/bin/bash +set -e -o pipefail + +function cleanup { + rm -rf build + rm AppleDevIDApp.p12 + rm notarize_config.json + security delete-keychain percy.keychain +} + +brew install gnu-sed +npm install -g pkg + +yarn install +yarn build + +# Remove type from package.json files +gsed -i '/"type": "module",/{s///;h};${x;/./{x;q0};x;q1}' ./package.json + +# Create array of package.json files +array=($(ls -d ./packages/*/package.json)) + +# Delete package.json filepath where type module is not defined +delete=(./packages/dom/package.json ./packages/sdk-utils/package.json) +for del in ${delete[@]} +do + array=("${array[@]/$del}") +done + +# Remove type module from package.json where present +for package in "${array[@]}" +do + if [ ! -z "$package" ] + then + gsed -i '/"type": "module",/{s///;h};${x;/./{x;q0};x;q1}' $package + fi +done + +echo "import { cli } from '@percy/cli';\ +$(cat ./packages/cli/dist/percy.js)" > ./packages/cli/dist/percy.js + +gsed -i '/Update NODE_ENV for executable/{s//\nprocess.env.NODE_ENV = "executable";/;h};${x;/./{x;q0};x;q1}' ./packages/cli/bin/run.cjs + +# Convert ES6 code to cjs +npm run build_cjs +cp -R ./build/* packages/ + +# Create executables +pkg ./packages/cli/bin/run.js -d + +# Rename executables +mkdir -p linux && mv run-linux percy-linux && chmod +x percy +mkdir -p osx && mv run-macos percy && chmod +x percy-osx +mkdir -p win && mv run-win.exe percy.exe && chmod +x percy.exe + +# Sign & Notrize mac app +echo "$APPLE_DEV_CERT" | base64 -d > AppleDevIDApp.p12 + +security create-keychain -p percy percy.keychain +security import AppleDevIDApp.p12 -t agg -k percy.keychain -P ChaiTime -A +security list-keychains -s ~/Library/Keychains/percy.keychain +security default-keychain -s ~/Library/Keychains/percy.keychain +security unlock-keychain -p "percy" ~/Library/Keychains/percy.keychain +security set-keychain-settings -t 3600 -l ~/Library/Keychains/percy.keychain +security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k percy ~/Library/Keychains/percy.keychain-db + +codesign --force --verbose=4 -s "Developer ID Application: BrowserStack Inc (763K6K6H44)" --options runtime --entitlements scripts/files/entitlement.plist --keychain ~/Library/Keychains/percy.keychain percy-osx + +zip percy-osx.zip percy-osx +cat scripts/files/notarize_config.json.tmpl | sed -e "s/{{APPLE_ID_USERNAME}}/$APPLE_ID_USERNAME/" | sed -e "s/{{APPLE_ID_KEY}}/$APPLE_ID_KEY/" > notarize_config.json +gon -log-level=error -log-json notarize_config.json + +cleanup diff --git a/scripts/files/entitlement.plist b/scripts/files/entitlement.plist new file mode 100644 index 000000000..852978277 --- /dev/null +++ b/scripts/files/entitlement.plist @@ -0,0 +1,10 @@ + + + + +com.apple.security.cs.allow-jit + +com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/scripts/files/notarize_config.json.tmpl b/scripts/files/notarize_config.json.tmpl new file mode 100644 index 000000000..f425cd654 --- /dev/null +++ b/scripts/files/notarize_config.json.tmpl @@ -0,0 +1,10 @@ +{ + "apple_id": { + "username" : "{{APPLE_ID_USERNAME}}", + "password": "{{APPLE_ID_KEY}}" + }, + "notarize" : [{ + "path": "percy-osx.zip", + "bundle_id": "io.percy.cli" + }] +} diff --git a/yarn.lock b/yarn.lock index 654cba372..bbcb3dbb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,6 +39,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" + integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== + dependencies: + "@babel/highlight" "^7.22.10" + chalk "^2.4.2" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.19.3", "@babel/compat-data@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.4.tgz#95c86de137bf0317f3a570e1b6e996b427299747" @@ -319,6 +327,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" @@ -334,6 +347,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" @@ -386,11 +404,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" + integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.6": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.6.tgz#b923430cb94f58a7eae8facbffa9efd19130e7f8" integrity sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA== +"@babel/parser@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" + integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -1013,6 +1045,15 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" +"@babel/template@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.6", "@babel/traverse@^7.18.9", "@babel/traverse@^7.19.4", "@babel/traverse@^7.19.6": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.6.tgz#7b4c865611df6d99cb131eec2e8ac71656a490dc" @@ -1038,6 +1079,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" + integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -2670,6 +2720,14 @@ babel-plugin-polyfill-regenerator@^0.4.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.3" +babel-plugin-transform-import-meta@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz#eb5b79019ff0a9157b94d8280955121189a2964b" + integrity sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw== + dependencies: + "@babel/template" "^7.4.4" + tslib "^2.4.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -2883,7 +2941,7 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==