diff --git a/packages/utils/.eslintrc.js b/packages/utils/.eslintrc.js index 5a2cc7f1ec08..35c6aab563f5 100644 --- a/packages/utils/.eslintrc.js +++ b/packages/utils/.eslintrc.js @@ -1,3 +1,19 @@ module.exports = { extends: ['../../.eslintrc.js'], + overrides: [ + { + files: ['scripts/**/*.ts'], + parserOptions: { + project: ['../../tsconfig.dev.json'], + }, + }, + { + files: ['test/**'], + parserOptions: { + sourceType: 'module', + }, + }, + ], + // symlinks to the folders inside of `build`, created to simulate what's in the npm package + ignorePatterns: ['cjs/**', 'esm/**'], }; diff --git a/packages/utils/.gitignore b/packages/utils/.gitignore new file mode 100644 index 000000000000..4b89517c06c8 --- /dev/null +++ b/packages/utils/.gitignore @@ -0,0 +1,6 @@ +# symlinks to the folders in `build`, needed for tests +cjs +esm + +# needed so we can test our versions of polyfills against Sucrase and Rollup's originals +!test/buildPolyfills/originals.d.ts diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js index 24f49ab59a4c..f9b7ccfa4502 100644 --- a/packages/utils/jest.config.js +++ b/packages/utils/jest.config.js @@ -1 +1,9 @@ -module.exports = require('../../jest/jest.config.js'); +const baseConfig = require('../../jest/jest.config.js'); + +module.exports = { + ...baseConfig, + transform: { + '^.+\\.ts$': 'ts-jest', + '^.+\\.js$': 'ts-jest', + }, +}; diff --git a/packages/utils/package.json b/packages/utils/package.json index d67412868965..94e477d16fbd 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -20,6 +20,8 @@ "tslib": "^1.9.3" }, "devDependencies": { + "@types/array.prototype.flat": "^1.2.1", + "array.prototype.flat": "^1.3.0", "chai": "^4.1.2" }, "scripts": { @@ -28,7 +30,7 @@ "build:dev": "run-s build", "build:es5": "yarn build:cjs # *** backwards compatibility - remove in v7 ***", "build:esm": "tsc -p tsconfig.esm.json", - "build:rollup": "rollup -c rollup.npm.config.js", + "build:rollup": "yarn ts-node scripts/buildRollup.ts", "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:cjs:watch build:esm:watch build:types:watch", "build:cjs:watch": "tsc -p tsconfig.cjs.json --watch", @@ -39,7 +41,7 @@ "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:npm": "ts-node ../../scripts/prepack.ts && npm pack ./build", "circularDepCheck": "madge --circular src/index.ts", - "clean": "rimraf build coverage", + "clean": "rimraf build coverage cjs esm", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", diff --git a/packages/utils/rollup.npm.config.js b/packages/utils/rollup.npm.config.js index 5a62b528ef44..dcf32469a4e4 100644 --- a/packages/utils/rollup.npm.config.js +++ b/packages/utils/rollup.npm.config.js @@ -1,3 +1,9 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; -export default makeNPMConfigVariants(makeBaseNPMConfig()); +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + // We build the polyfills separately because they're not included in the top-level exports of the package, in order + // to keep them out of the public API. + entrypoints: ['src/index.ts', 'src/buildPolyfills/index.ts'], + }), +); diff --git a/packages/utils/scripts/buildRollup.ts b/packages/utils/scripts/buildRollup.ts new file mode 100644 index 000000000000..f48b0b23d6d9 --- /dev/null +++ b/packages/utils/scripts/buildRollup.ts @@ -0,0 +1,30 @@ +import * as childProcess from 'child_process'; +import * as fs from 'fs'; + +/** + * Run the given shell command, piping the shell process's `stdin`, `stdout`, and `stderr` to that of the current + * process. Returns contents of `stdout`. + */ +function run(cmd: string, options?: childProcess.ExecSyncOptions): string | Buffer { + return childProcess.execSync(cmd, { stdio: 'inherit', ...options }); +} + +run('yarn rollup -c rollup.npm.config.js'); + +// We want to distribute the README because it contains the MIT license blurb from Sucrase and Rollup +fs.copyFileSync('src/buildPolyfills/README.md', 'build/cjs/buildPolyfills/README.md'); +fs.copyFileSync('src/buildPolyfills/README.md', 'build/esm/buildPolyfills/README.md'); + +// Because we import our polyfills from `@sentry/utils/cjs/buildPolyfills` and `@sentry/utils/esm/buildPolyfills` rather +// than straight from `@sentry/utils` (so as to avoid having them in the package's public API), when tests run, they'll +// expect to find `cjs` and `esm` at the root level of the repo. +try { + fs.symlinkSync('build/cjs', 'cjs'); +} catch (oO) { + // if we get here, it's because the symlink already exists, so we're good +} +try { + fs.symlinkSync('build/esm', 'esm'); +} catch (oO) { + // same as above +} diff --git a/packages/utils/src/buildPolyfills/README.md b/packages/utils/src/buildPolyfills/README.md new file mode 100644 index 000000000000..8171e8583a96 --- /dev/null +++ b/packages/utils/src/buildPolyfills/README.md @@ -0,0 +1,15 @@ +## Build Polyfills + +This is a collection of syntax and import/export polyfills either copied directly from or heavily inspired by those used by [Rollup](https://github.com/rollup/rollup) and [Sucrase](https://github.com/alangpierce/sucrase). When either tool uses one of these polyfills during a build, it injects the function source code into each file needing the function, which can lead to a great deal of duplication. For our builds, we have therefore implemented something similar to [`tsc`'s `importHelpers` behavior](https://www.typescriptlang.org/tsconfig#importHelpers): Instead of leaving the polyfills injected in multiple places, we instead replace each injected function with an `import` or `require` statement, pulling from the CJS or ESM builds as appropriate. (In other words, the injected `import` statements import from `@sentry/utils/esm/buildPolyfills` and the injected `require` statements pull from `@sentry/utils/cjs/buildPolyfills/`. Because these functions should never be part of the public API, they're not exported from the package directly.) + +Note that not all polyfills are currently used by the SDK, but all are included here for future compatitibility, should they ever be needed. Also, since we're never going to be calling these directly from within another TS file, their types are fairly generic. In some cases testing required more specific types, which can be found in the test files. + +-------- + +_Code from both Rollup and Sucrase is used under the MIT license, copyright 2017 and 2012-2018, respectively._ + +_Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:_ + +_The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software._ + +_THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._ diff --git a/packages/utils/src/buildPolyfills/_asyncNullishCoalesce.ts b/packages/utils/src/buildPolyfills/_asyncNullishCoalesce.ts new file mode 100644 index 000000000000..c1b075ee5222 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_asyncNullishCoalesce.ts @@ -0,0 +1,30 @@ +// adapted from Sucrase (https://github.com/alangpierce/sucrase) + +import { _nullishCoalesce } from './_nullishCoalesce'; + +/** + * Polyfill for the nullish coalescing operator (`??`), when used in situations where at least one of the values is the + * result of an async operation. + * + * Note that the RHS is wrapped in a function so that if it's a computed value, that evaluation won't happen unless the + * LHS evaluates to a nullish value, to mimic the operator's short-circuiting behavior. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param lhs The value of the expression to the left of the `??` + * @param rhsFn A function returning the value of the expression to the right of the `??` + * @returns The LHS value, unless it's `null` or `undefined`, in which case, the RHS value + */ +// eslint-disable-next-line @sentry-internal/sdk/no-async-await +export async function _asyncNullishCoalesce(lhs: unknown, rhsFn: () => unknown): Promise { + return _nullishCoalesce(lhs, rhsFn); +} + +// Sucrase version: +// async function _asyncNullishCoalesce(lhs, rhsFn) { +// if (lhs != null) { +// return lhs; +// } else { +// return await rhsFn(); +// } +// } diff --git a/packages/utils/src/buildPolyfills/_asyncOptionalChain.ts b/packages/utils/src/buildPolyfills/_asyncOptionalChain.ts new file mode 100644 index 000000000000..10a10aa03f3a --- /dev/null +++ b/packages/utils/src/buildPolyfills/_asyncOptionalChain.ts @@ -0,0 +1,59 @@ +import { GenericFunction } from './types'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions, for situations in which at least one part of the expression is async. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) See + * https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The value of the expression + */ +// eslint-disable-next-line @sentry-internal/sdk/no-async-await +export async function _asyncOptionalChain(ops: unknown[]): Promise { + let lastAccessLHS: unknown = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i] as string; + const fn = ops[i + 1] as (intermediateValue: unknown) => Promise; + i += 2; + // by checking for loose equality to `null`, we catch both `null` and `undefined` + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + // really we're meaning to return `undefined` as an actual value here, but it saves bytes not to write it + return; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = await fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = await fn((...args: unknown[]) => (value as GenericFunction).call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// Sucrase version: +// async function _asyncOptionalChain(ops) { +// let lastAccessLHS = undefined; +// let value = ops[0]; +// let i = 1; +// while (i < ops.length) { +// const op = ops[i]; +// const fn = ops[i + 1]; +// i += 2; +// if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { +// return undefined; +// } +// if (op === 'access' || op === 'optionalAccess') { +// lastAccessLHS = value; +// value = await fn(value); +// } else if (op === 'call' || op === 'optionalCall') { +// value = await fn((...args) => value.call(lastAccessLHS, ...args)); +// lastAccessLHS = undefined; +// } +// } +// return value; +// } diff --git a/packages/utils/src/buildPolyfills/_asyncOptionalChainDelete.ts b/packages/utils/src/buildPolyfills/_asyncOptionalChainDelete.ts new file mode 100644 index 000000000000..bf0260337919 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_asyncOptionalChainDelete.ts @@ -0,0 +1,28 @@ +import { _asyncOptionalChain } from './_asyncOptionalChain'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions, in cases where the value of the expression is to be deleted. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) See + * https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The return value of the `delete` operator: `true`, unless the deletion target is an own, non-configurable + * property (one which can't be deleted or turned into an accessor, and whose enumerability can't be changed), in which + * case `false`. + */ +// eslint-disable-next-line @sentry-internal/sdk/no-async-await +export async function _asyncOptionalChainDelete(ops: unknown[]): Promise { + const result = (await _asyncOptionalChain(ops)) as Promise; + // If `result` is `null`, it means we didn't get to the end of the chain and so nothing was deleted (in which case, + // return `true` since that's what `delete` does when it no-ops). If it's non-null, we know the delete happened, in + // which case we return whatever the `delete` returned, which will be a boolean. + return result == null ? true : (result as Promise); +} + +// Sucrase version: +// async function asyncOptionalChainDelete(ops) { +// const result = await ASYNC_OPTIONAL_CHAIN_NAME(ops); +// return result == null ? true : result; +// } diff --git a/packages/utils/src/buildPolyfills/_createNamedExportFrom.ts b/packages/utils/src/buildPolyfills/_createNamedExportFrom.ts new file mode 100644 index 000000000000..0c632e586aba --- /dev/null +++ b/packages/utils/src/buildPolyfills/_createNamedExportFrom.ts @@ -0,0 +1,21 @@ +import { GenericObject } from './types'; + +declare const exports: GenericObject; + +/** + * Copy a property from the given object into `exports`, under the given name. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param obj The object containing the property to copy. + * @param localName The name under which to export the property + * @param importedName The name under which the property lives in `obj` + */ +export function _createNamedExportFrom(obj: GenericObject, localName: string, importedName: string): void { + exports[localName] = obj[importedName]; +} + +// Sucrase version: +// function _createNamedExportFrom(obj, localName, importedName) { +// Object.defineProperty(exports, localName, {enumerable: true, get: () => obj[importedName]}); +// } diff --git a/packages/utils/src/buildPolyfills/_createStarExport.ts b/packages/utils/src/buildPolyfills/_createStarExport.ts new file mode 100644 index 000000000000..f4f36c8c041a --- /dev/null +++ b/packages/utils/src/buildPolyfills/_createStarExport.ts @@ -0,0 +1,28 @@ +import { GenericObject } from './types'; + +declare const exports: GenericObject; + +/** + * Copy properties from an object into `exports`. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param obj The object containing the properties to copy. + */ +export function _createStarExport(obj: GenericObject): void { + Object.keys(obj) + .filter(key => key !== 'default' && key !== '__esModule' && !(key in exports)) + .forEach(key => (exports[key] = obj[key])); +} + +// Sucrase version: +// function _createStarExport(obj) { +// Object.keys(obj) +// .filter(key => key !== 'default' && key !== '__esModule') +// .forEach(key => { +// if (exports.hasOwnProperty(key)) { +// return; +// } +// Object.defineProperty(exports, key, { enumerable: true, get: () => obj[key] }); +// }); +// } diff --git a/packages/utils/src/buildPolyfills/_interopDefault.ts b/packages/utils/src/buildPolyfills/_interopDefault.ts new file mode 100644 index 000000000000..5bed0ef4e3f1 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopDefault.ts @@ -0,0 +1,18 @@ +import { RequireResult } from './types'; + +/** + * Unwraps a module if it has been wrapped in an object under the key `default`. + * + * Adapted from Rollup (https://github.com/rollup/rollup) + * + * @param requireResult The result of calling `require` on a module + * @returns The full module, unwrapped if necessary. + */ +export function _interopDefault(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? (requireResult.default as RequireResult) : requireResult; +} + +// Rollup version: +// function _interopDefault(e) { +// return e && e.__esModule ? e['default'] : e; +// } diff --git a/packages/utils/src/buildPolyfills/_interopNamespace.ts b/packages/utils/src/buildPolyfills/_interopNamespace.ts new file mode 100644 index 000000000000..2211e21accfa --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopNamespace.ts @@ -0,0 +1,26 @@ +import { RequireResult } from './types'; + +/** + * Adds a self-referential `default` property to CJS modules which aren't the result of transpilation from ESM modules. + * + * Adapted from Rollup (https://github.com/rollup/rollup) + * + * @param requireResult The result of calling `require` on a module + * @returns Either `requireResult` or a copy of `requireResult` with an added self-referential `default` property + */ +export function _interopNamespace(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? requireResult : { ...requireResult, default: requireResult }; +} + +// Rollup version (with `output.externalLiveBindings` and `output.freeze` both set to false) +// function _interopNamespace(e) { +// if (e && e.__esModule) return e; +// var n = Object.create(null); +// if (e) { +// for (var k in e) { +// n[k] = e[k]; +// } +// } +// n["default"] = e; +// return n; +// } diff --git a/packages/utils/src/buildPolyfills/_interopNamespaceDefaultOnly.ts b/packages/utils/src/buildPolyfills/_interopNamespaceDefaultOnly.ts new file mode 100644 index 000000000000..66785a79e92f --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopNamespaceDefaultOnly.ts @@ -0,0 +1,24 @@ +import { RequireResult } from './types'; + +/** + * Wrap a module in an object, as the value under the key `default`. + * + * Adapted from Rollup (https://github.com/rollup/rollup) + * + * @param requireResult The result of calling `require` on a module + * @returns An object containing the key-value pair (`default`, `requireResult`) + */ +export function _interopNamespaceDefaultOnly(requireResult: RequireResult): RequireResult { + return { + __proto__: null, + default: requireResult, + }; +} + +// Rollup version +// function _interopNamespaceDefaultOnly(e) { +// return { +// __proto__: null, +// 'default': e +// }; +// } diff --git a/packages/utils/src/buildPolyfills/_interopRequireDefault.ts b/packages/utils/src/buildPolyfills/_interopRequireDefault.ts new file mode 100644 index 000000000000..9d9a7767cb7c --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopRequireDefault.ts @@ -0,0 +1,18 @@ +import { RequireResult } from './types'; + +/** + * Wraps modules which aren't the result of transpiling an ESM module in an object under the key `default` + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param requireResult The result of calling `require` on a module + * @returns `requireResult` or `requireResult` wrapped in an object, keyed as `default` + */ +export function _interopRequireDefault(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? requireResult : { default: requireResult }; +} + +// Sucrase version +// function _interopRequireDefault(obj) { +// return obj && obj.__esModule ? obj : { default: obj }; +// } diff --git a/packages/utils/src/buildPolyfills/_interopRequireWildcard.ts b/packages/utils/src/buildPolyfills/_interopRequireWildcard.ts new file mode 100644 index 000000000000..411939ab68d6 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_interopRequireWildcard.ts @@ -0,0 +1,31 @@ +import { RequireResult } from './types'; + +/** + * Adds a `default` property to CJS modules which aren't the result of transpilation from ESM modules. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param requireResult The result of calling `require` on a module + * @returns Either `requireResult` or a copy of `requireResult` with an added self-referential `default` property + */ +export function _interopRequireWildcard(requireResult: RequireResult): RequireResult { + return requireResult.__esModule ? requireResult : { ...requireResult, default: requireResult }; +} + +// Sucrase version +// function _interopRequireWildcard(obj) { +// if (obj && obj.__esModule) { +// return obj; +// } else { +// var newObj = {}; +// if (obj != null) { +// for (var key in obj) { +// if (Object.prototype.hasOwnProperty.call(obj, key)) { +// newObj[key] = obj[key]; +// } +// } +// } +// newObj.default = obj; +// return newObj; +// } +// } diff --git a/packages/utils/src/buildPolyfills/_nullishCoalesce.ts b/packages/utils/src/buildPolyfills/_nullishCoalesce.ts new file mode 100644 index 000000000000..70ba98a2bd8f --- /dev/null +++ b/packages/utils/src/buildPolyfills/_nullishCoalesce.ts @@ -0,0 +1,25 @@ +/** + * Polyfill for the nullish coalescing operator (`??`). + * + * Note that the RHS is wrapped in a function so that if it's a computed value, that evaluation won't happen unless the + * LHS evaluates to a nullish value, to mimic the operator's short-circuiting behavior. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * + * @param lhs The value of the expression to the left of the `??` + * @param rhsFn A function returning the value of the expression to the right of the `??` + * @returns The LHS value, unless it's `null` or `undefined`, in which case, the RHS value + */ +export function _nullishCoalesce(lhs: unknown, rhsFn: () => unknown): unknown { + // by checking for loose equality to `null`, we catch both `null` and `undefined` + return lhs != null ? lhs : rhsFn(); +} + +// Sucrase version: +// function _nullishCoalesce(lhs, rhsFn) { +// if (lhs != null) { +// return lhs; +// } else { +// return rhsFn(); +// } +// } diff --git a/packages/utils/src/buildPolyfills/_optionalChain.ts b/packages/utils/src/buildPolyfills/_optionalChain.ts new file mode 100644 index 000000000000..452c6ac110b0 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_optionalChain.ts @@ -0,0 +1,58 @@ +import { GenericFunction } from './types'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) + * See https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The value of the expression + */ +export function _optionalChain(ops: unknown[]): unknown { + let lastAccessLHS: unknown = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i] as string; + const fn = ops[i + 1] as (intermediateValue: unknown) => unknown; + i += 2; + // by checking for loose equality to `null`, we catch both `null` and `undefined` + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + // really we're meaning to return `undefined` as an actual value here, but it saves bytes not to write it + return; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = fn((...args: unknown[]) => (value as GenericFunction).call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// Sucrase version +// function _optionalChain(ops) { +// let lastAccessLHS = undefined; +// let value = ops[0]; +// let i = 1; +// while (i < ops.length) { +// const op = ops[i]; +// const fn = ops[i + 1]; +// i += 2; +// if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { +// return undefined; +// } +// if (op === 'access' || op === 'optionalAccess') { +// lastAccessLHS = value; +// value = fn(value); +// } else if (op === 'call' || op === 'optionalCall') { +// value = fn((...args) => value.call(lastAccessLHS, ...args)); +// lastAccessLHS = undefined; +// } +// } +// return value; +// } diff --git a/packages/utils/src/buildPolyfills/_optionalChainDelete.ts b/packages/utils/src/buildPolyfills/_optionalChainDelete.ts new file mode 100644 index 000000000000..61147bedc5b5 --- /dev/null +++ b/packages/utils/src/buildPolyfills/_optionalChainDelete.ts @@ -0,0 +1,28 @@ +import { _optionalChain } from './_optionalChain'; + +/** + * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values, + * descriptors, and functions, in cases where the value of the expression is to be deleted. + * + * Adapted from Sucrase (https://github.com/alangpierce/sucrase) See + * https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15 + * + * @param ops Array result of expression conversion + * @returns The return value of the `delete` operator: `true`, unless the deletion target is an own, non-configurable + * property (one which can't be deleted or turned into an accessor, and whose enumerability can't be changed), in which + * case `false`. + */ +export function _optionalChainDelete(ops: unknown[]): boolean { + const result = _optionalChain(ops) as boolean | null; + // If `result` is `null`, it means we didn't get to the end of the chain and so nothing was deleted (in which case, + // return `true` since that's what `delete` does when it no-ops). If it's non-null, we know the delete happened, in + // which case we return whatever the `delete` returned, which will be a boolean. + return result == null ? true : result; +} + +// Sucrase version: +// function _optionalChainDelete(ops) { +// const result = _optionalChain(ops); +// // by checking for loose equality to `null`, we catch both `null` and `undefined` +// return result == null ? true : result; +// } diff --git a/packages/utils/src/buildPolyfills/index.ts b/packages/utils/src/buildPolyfills/index.ts new file mode 100644 index 000000000000..9717453e98fa --- /dev/null +++ b/packages/utils/src/buildPolyfills/index.ts @@ -0,0 +1,13 @@ +export { _asyncNullishCoalesce } from './_asyncNullishCoalesce'; +export { _asyncOptionalChain } from './_asyncOptionalChain'; +export { _asyncOptionalChainDelete } from './_asyncOptionalChainDelete'; +export { _createNamedExportFrom } from './_createNamedExportFrom'; +export { _createStarExport } from './_createStarExport'; +export { _interopDefault } from './_interopDefault'; +export { _interopNamespace } from './_interopNamespace'; +export { _interopNamespaceDefaultOnly } from './_interopNamespaceDefaultOnly'; +export { _interopRequireDefault } from './_interopRequireDefault'; +export { _interopRequireWildcard } from './_interopRequireWildcard'; +export { _nullishCoalesce } from './_nullishCoalesce'; +export { _optionalChain } from './_optionalChain'; +export { _optionalChainDelete } from './_optionalChainDelete'; diff --git a/packages/utils/src/buildPolyfills/types.ts b/packages/utils/src/buildPolyfills/types.ts new file mode 100644 index 000000000000..41f1a8a31ee5 --- /dev/null +++ b/packages/utils/src/buildPolyfills/types.ts @@ -0,0 +1,7 @@ +import { Primitive } from '@sentry/types'; + +export type GenericObject = { [key: string]: Value }; +export type GenericFunction = (...args: unknown[]) => Value; +export type Value = Primitive | GenericFunction | GenericObject; + +export type RequireResult = GenericObject | (GenericFunction & GenericObject); diff --git a/packages/utils/test/buildPolyfills/interop.test.ts b/packages/utils/test/buildPolyfills/interop.test.ts new file mode 100644 index 000000000000..917d62daee2f --- /dev/null +++ b/packages/utils/test/buildPolyfills/interop.test.ts @@ -0,0 +1,180 @@ +import { + _interopDefault, + _interopNamespace, + _interopNamespaceDefaultOnly, + _interopRequireDefault, + _interopRequireWildcard, +} from '../../src/buildPolyfills'; +import { RequireResult } from '../../src/buildPolyfills/types'; +import { + _interopDefault as _interopDefaultOrig, + _interopNamespace as _interopNamespaceOrig, + _interopNamespaceDefaultOnly as _interopNamespaceDefaultOnlyOrig, + _interopRequireDefault as _interopRequireDefaultOrig, + _interopRequireWildcard as _interopRequireWildcardOrig, +} from './originals'; + +// This file tests five different functions against a range of test cases. Though the inputs are the same for each +// function's test cases, the expected output differs. The testcases for each function are therefore built from separate +// collections of expected inputs and expected outputs. Further, for readability purposes, the tests labels have also +// been split into their own object. It's also worth noting that in real life, there are some test-case/function +// pairings which would never happen, but by testing all combinations, we're guaranteed to have tested the ones which +// show up in the wild. + +const dogStr = 'dogs are great!'; +const dogFunc = () => dogStr; +const dogAdjectives = { maisey: 'silly', charlie: 'goofy' }; + +const withESModuleFlag = { __esModule: true, ...dogAdjectives }; +const withESModuleFlagAndDefault = { __esModule: true, default: dogFunc, ...dogAdjectives }; +const namedExports = { ...dogAdjectives }; +const withNonEnumerableProp = { ...dogAdjectives }; +// Properties added using `Object.defineProperty` are non-enumerable by default +Object.defineProperty(withNonEnumerableProp, 'hiddenProp', { value: 'shhhhhhhh' }); +const withDefaultExport = { default: dogFunc, ...dogAdjectives }; +const withOnlyDefaultExport = { default: dogFunc }; +const exportsEquals = dogFunc as RequireResult; +const exportsEqualsWithDefault = dogFunc as RequireResult; +exportsEqualsWithDefault.default = exportsEqualsWithDefault; + +const mockRequireResults: Record = { + withESModuleFlag, + withESModuleFlagAndDefault, + namedExports, + withNonEnumerableProp, + withDefaultExport, + withOnlyDefaultExport, + exportsEquals: exportsEquals, + exportsEqualsWithDefault: exportsEqualsWithDefault as unknown as RequireResult, +}; + +const testLabels: Record = { + withESModuleFlag: 'module with `__esModule` flag', + withESModuleFlagAndDefault: 'module with `__esModule` flag and default export', + namedExports: 'module with named exports', + withNonEnumerableProp: 'module with named exports and non-enumerable prop', + withDefaultExport: 'module with default export', + withOnlyDefaultExport: 'module with only default export', + exportsEquals: 'module using `exports =`', + exportsEqualsWithDefault: 'module using `exports =` with default export', +}; + +function makeTestCases(expectedOutputs: Record): Array<[string, RequireResult, RequireResult]> { + return Object.keys(mockRequireResults).map(key => [testLabels[key], mockRequireResults[key], expectedOutputs[key]]); +} + +describe('_interopNamespace', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: withESModuleFlag, + withESModuleFlagAndDefault: withESModuleFlagAndDefault, + namedExports: { ...namedExports, default: namedExports }, + withNonEnumerableProp: { + ...withNonEnumerableProp, + default: withNonEnumerableProp, + }, + withDefaultExport: { ...withDefaultExport, default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopNamespace(requireResult)).toEqual(_interopNamespaceOrig(requireResult)); + expect(_interopNamespace(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopNamespaceDefaultOnly', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: { default: withESModuleFlag }, + withESModuleFlagAndDefault: { default: withESModuleFlagAndDefault }, + namedExports: { default: namedExports }, + withNonEnumerableProp: { default: withNonEnumerableProp }, + withDefaultExport: { default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopNamespaceDefaultOnly(requireResult)).toEqual(_interopNamespaceDefaultOnlyOrig(requireResult)); + expect(_interopNamespaceDefaultOnly(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopRequireWildcard', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: withESModuleFlag, + withESModuleFlagAndDefault: withESModuleFlagAndDefault, + namedExports: { ...namedExports, default: namedExports }, + withNonEnumerableProp: { + ...withNonEnumerableProp, + default: withNonEnumerableProp, + }, + withDefaultExport: { ...withDefaultExport, default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopRequireWildcard(requireResult)).toEqual(_interopRequireWildcardOrig(requireResult)); + expect(_interopRequireWildcard(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopDefault', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: undefined as unknown as RequireResult, + withESModuleFlagAndDefault: withESModuleFlagAndDefault.default as RequireResult, + namedExports: namedExports, + withNonEnumerableProp: withNonEnumerableProp, + withDefaultExport: withDefaultExport, + withOnlyDefaultExport: withOnlyDefaultExport, + exportsEquals: exportsEquals, + exportsEqualsWithDefault: exportsEqualsWithDefault, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopDefault(requireResult)).toEqual(_interopDefaultOrig(requireResult)); + expect(_interopDefault(requireResult)).toEqual(expectedOutput); + }); + }); +}); + +describe('_interopRequireDefault', () => { + describe('returns the same result as the original', () => { + const expectedOutputs: Record = { + withESModuleFlag: withESModuleFlag, + withESModuleFlagAndDefault: withESModuleFlagAndDefault, + namedExports: { default: namedExports }, + withNonEnumerableProp: { default: withNonEnumerableProp }, + withDefaultExport: { default: withDefaultExport }, + withOnlyDefaultExport: { default: withOnlyDefaultExport }, + exportsEquals: { default: exportsEquals }, + exportsEqualsWithDefault: { default: exportsEqualsWithDefault }, + }; + + const testCases = makeTestCases(expectedOutputs); + + it.each(testCases)('%s', (_, requireResult, expectedOutput) => { + expect(_interopRequireDefault(requireResult)).toEqual(_interopRequireDefaultOrig(requireResult)); + expect(_interopRequireDefault(requireResult)).toEqual(expectedOutput); + }); + }); +}); diff --git a/packages/utils/test/buildPolyfills/nullishCoalesce.test.ts b/packages/utils/test/buildPolyfills/nullishCoalesce.test.ts new file mode 100644 index 000000000000..55fd5ee9c996 --- /dev/null +++ b/packages/utils/test/buildPolyfills/nullishCoalesce.test.ts @@ -0,0 +1,29 @@ +import { _nullishCoalesce } from '../../src/buildPolyfills'; +import { Value } from '../../src/buildPolyfills/types'; +import { _nullishCoalesce as _nullishCoalesceOrig } from './originals'; + +const dogStr = 'dogs are great!'; +const dogFunc = () => dogStr; +const dogAdjectives = { maisey: 'silly', charlie: 'goofy' }; +const dogAdjectiveFunc = () => dogAdjectives; + +describe('_nullishCoalesce', () => { + describe('returns the same result as the original', () => { + const testCases: Array<[string, Value, () => Value, Value]> = [ + ['null LHS', null, dogFunc, dogStr], + ['undefined LHS', undefined, dogFunc, dogStr], + ['false LHS', false, dogFunc, false], + ['zero LHS', 0, dogFunc, 0], + ['empty string LHS', '', dogFunc, ''], + ['true LHS', true, dogFunc, true], + ['truthy primitive LHS', 12312012, dogFunc, 12312012], + ['truthy object LHS', dogAdjectives, dogFunc, dogAdjectives], + ['truthy function LHS', dogAdjectiveFunc, dogFunc, dogAdjectiveFunc], + ]; + + it.each(testCases)('%s', (_, lhs, rhs, expectedValue) => { + expect(_nullishCoalesce(lhs, rhs)).toEqual(_nullishCoalesceOrig(lhs, rhs)); + expect(_nullishCoalesce(lhs, rhs)).toEqual(expectedValue); + }); + }); +}); diff --git a/packages/utils/test/buildPolyfills/optionalChain.test.ts b/packages/utils/test/buildPolyfills/optionalChain.test.ts new file mode 100644 index 000000000000..4fec5ee1dc63 --- /dev/null +++ b/packages/utils/test/buildPolyfills/optionalChain.test.ts @@ -0,0 +1,87 @@ +import { default as arrayFlat } from 'array.prototype.flat'; + +import { _optionalChain } from '../../src/buildPolyfills'; +import { GenericFunction, GenericObject, Value } from '../../src/buildPolyfills/types'; +import { _optionalChain as _optionalChainOrig } from './originals'; + +// Older versions of Node don't have `Array.prototype.flat`, which crashes these tests. On newer versions that do have +// it, this is a no-op. +arrayFlat.shim(); + +type OperationType = 'access' | 'call' | 'optionalAccess' | 'optionalCall'; +type OperationExecutor = + | ((intermediateValue: GenericObject) => Value) + | ((intermediateValue: GenericFunction) => Value); +type Operation = [OperationType, OperationExecutor]; + +const truthyObject = { maisey: 'silly', charlie: 'goofy' }; +const nullishObject = null; +const truthyFunc = (): GenericObject => truthyObject; +const nullishFunc = undefined; +const truthyReturn = (): GenericObject => truthyObject; +const nullishReturn = (): null => nullishObject; + +// The polyfill being tested here works under the assumption that the original code containing the optional chain has +// been transformed into an array of values, labels, and functions. For example, `truthyObject?.charlie` will have been +// transformed into `_optionalChain([truthyObject, 'optionalAccess', _ => _.charlie])`. We are not testing the +// transformation here, only what the polyfill does with the already-transformed inputs. + +describe('_optionalChain', () => { + describe('returns the same result as the original', () => { + // In these test cases, the array passed to `_optionalChain` has been broken up into the first entry followed by an + // array of pairs of subsequent elements, because this seemed the easiest way to express the type, which is really + // + // [Value, OperationType, Value => Value, OperationType, Value => Value, OperationType, Value => Value, ...]. + // + // (In other words, `[A, B, C, D, E]` has become `A, [[B, C], [D, E]]`, and these are then the second and third + // entries in each test case.) We then undo this wrapping before passing the data to our functions. + const testCases: Array<[string, Value, Operation[], Value]> = [ + ['truthyObject?.charlie', truthyObject, [['optionalAccess', (_: GenericObject) => _.charlie]], 'goofy'], + ['nullishObject?.maisey', nullishObject, [['optionalAccess', (_: GenericObject) => _.maisey]], undefined], + [ + 'truthyFunc?.().maisey', + truthyFunc, + [ + ['optionalCall', (_: GenericFunction) => _()], + ['access', (_: GenericObject) => _.maisey], + ], + 'silly', + ], + [ + 'nullishFunc?.().charlie', + nullishFunc, + [ + ['optionalCall', (_: GenericFunction) => _()], + ['access', (_: GenericObject) => _.charlie], + ], + undefined, + ], + [ + 'truthyReturn()?.maisey', + truthyReturn, + [ + ['call', (_: GenericFunction) => _()], + ['optionalAccess', (_: GenericObject) => _.maisey], + ], + 'silly', + ], + [ + 'nullishReturn()?.charlie', + nullishReturn, + [ + ['call', (_: GenericFunction) => _()], + ['optionalAccess', (_: GenericObject) => _.charlie], + ], + undefined, + ], + ]; + + it.each(testCases)('%s', (_, initialChainComponent, operations, expectedValue) => { + // `operations` is flattened and spread in order to undo the wrapping done in the test cases for TS purposes. + expect(_optionalChain([initialChainComponent, ...operations.flat()])).toEqual( + _optionalChainOrig([initialChainComponent, ...operations.flat()]), + ); + expect(_optionalChain([initialChainComponent, ...operations.flat()])).toEqual(expectedValue); + }); + }); +}); diff --git a/packages/utils/test/buildPolyfills/originals.d.ts b/packages/utils/test/buildPolyfills/originals.d.ts new file mode 100644 index 000000000000..323d6f26e93c --- /dev/null +++ b/packages/utils/test/buildPolyfills/originals.d.ts @@ -0,0 +1,23 @@ +// NOTE: Unlike other types files, this is NOT auto-generated by our build scripts. Since these functions are the +// originals of functions we adapted from Rollup and Sucrase, there's no reason they should ever change, but if they do, +// this file needs to be regenerated, by running +// `yarn tsc --allowJs --skipLibCheck --declaration --emitDeclarationOnly test/buildPolyfills/originals.js` +// from within the `utils` package. Keep in mind that running that command will clobber this note, so make sure to copy +// it before you regenerate the types, so you can add it back in.) + +export function _asyncNullishCoalesce(lhs: any, rhsFn: any): Promise; +export function _asyncOptionalChain(ops: any): Promise; +export function _asyncOptionalChainDelete(ops: any): Promise; +export function _createNamedExportFrom(obj: any, localName: any, importedName: any): void; +export function _createStarExport(obj: any): void; +export function _interopDefault(e: any): any; +export function _interopNamespace(e: any): any; +export function _interopNamespaceDefaultOnly(e: any): { + __proto__: any; + default: any; +}; +export function _interopRequireDefault(obj: any): any; +export function _interopRequireWildcard(obj: any): any; +export function _nullishCoalesce(lhs: any, rhsFn: any): any; +export function _optionalChain(ops: any): any; +export function _optionalChainDelete(ops: any): any; diff --git a/packages/utils/test/buildPolyfills/originals.js b/packages/utils/test/buildPolyfills/originals.js new file mode 100644 index 000000000000..d3dcb22e8082 --- /dev/null +++ b/packages/utils/test/buildPolyfills/originals.js @@ -0,0 +1,147 @@ +// Originals of the buildPolyfills from Sucrase and Rollup we use (which we have adapted in various ways), preserved here for testing, to prove that +// the modified versions do the same thing the originals do. + +// From Sucrase +export async function _asyncNullishCoalesce(lhs, rhsFn) { + if (lhs != null) { + return lhs; + } else { + return await rhsFn(); + } +} + +// From Sucrase +export async function _asyncOptionalChain(ops) { + let lastAccessLHS = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i]; + const fn = ops[i + 1]; + i += 2; + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + return undefined; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = await fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = await fn((...args) => value.call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// From Sucrase +export async function _asyncOptionalChainDelete(ops) { + const result = await _asyncOptionalChain(ops); + // by checking for loose equality to `null`, we catch both `null` and `undefined` + return result == null ? true : result; +} + +// From Sucrase +export function _createNamedExportFrom(obj, localName, importedName) { + Object.defineProperty(exports, localName, { enumerable: true, get: () => obj[importedName] }); +} + +// From Sucrase +export function _createStarExport(obj) { + Object.keys(obj) + .filter(key => key !== 'default' && key !== '__esModule') + .forEach(key => { + // eslint-disable-next-line no-prototype-builtins + if (exports.hasOwnProperty(key)) { + return; + } + Object.defineProperty(exports, key, { enumerable: true, get: () => obj[key] }); + }); +} + +// From Rollup +export function _interopDefault(e) { + return e && e.__esModule ? e['default'] : e; +} + +// From Rollup +export function _interopNamespace(e) { + if (e && e.__esModule) return e; + var n = Object.create(null); + if (e) { + // eslint-disable-next-line guard-for-in + for (var k in e) { + n[k] = e[k]; + } + } + n['default'] = e; + return n; +} + +export function _interopNamespaceDefaultOnly(e) { + return { + __proto__: null, + default: e, + }; +} + +// From Sucrase +export function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +// From Sucrase +export function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {}; + if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + newObj[key] = obj[key]; + } + } + } + newObj.default = obj; + return newObj; + } +} + +// From Sucrase +export function _nullishCoalesce(lhs, rhsFn) { + if (lhs != null) { + return lhs; + } else { + return rhsFn(); + } +} + +// From Sucrase +export function _optionalChain(ops) { + let lastAccessLHS = undefined; + let value = ops[0]; + let i = 1; + while (i < ops.length) { + const op = ops[i]; + const fn = ops[i + 1]; + i += 2; + if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { + return undefined; + } + if (op === 'access' || op === 'optionalAccess') { + lastAccessLHS = value; + value = fn(value); + } else if (op === 'call' || op === 'optionalCall') { + value = fn((...args) => value.call(lastAccessLHS, ...args)); + lastAccessLHS = undefined; + } + } + return value; +} + +// From Sucrase +export function _optionalChainDelete(ops) { + const result = _optionalChain(ops); + // by checking for loose equality to `null`, we catch both `null` and `undefined` + return result == null ? true : result; +} diff --git a/packages/utils/tsconfig.test.json b/packages/utils/tsconfig.test.json index 87f6afa06b86..0f755903fa64 100644 --- a/packages/utils/tsconfig.test.json +++ b/packages/utils/tsconfig.test.json @@ -5,8 +5,19 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node", "jest"], // other package-specific, test-specific options + // this is necessary in order to be able to handle the buildPolyfills `originals.js` which is used for testing + "allowJs": true, + + // `es2020` is the recommended `lib` and `target` for Node 14 + // see https://github.com/tsconfig/bases/blob/main/bases/node14.json + "lib": ["dom", "es2020"], + "module": "commonjs", + "target": "es2020", + + // so we don't have to worry about how libraries we use export things + "esModuleInterop": true } } diff --git a/rollup/npmHelpers.js b/rollup/npmHelpers.js index 79a14189a880..440223c7e757 100644 --- a/rollup/npmHelpers.js +++ b/rollup/npmHelpers.js @@ -59,6 +59,10 @@ export function makeBaseNPMConfig(options = {}) { // }); externalLiveBindings: false, + // Don't call `Object.freeze` on the results of `import * as someModule from '...'` + // (We don't need it, so why waste the bytes?) + freeze: false, + // Equivalent to `esModuleInterop` in tsconfig. // Controls whether rollup emits helpers to handle special cases where turning // `import * as dogs from 'dogs'` diff --git a/scripts/test.ts b/scripts/test.ts index 1aa4dc6ac8b1..e7058f2b66a8 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -28,6 +28,17 @@ const NODE_8_LEGACY_DEPENDENCIES = [ ]; const NODE_10_LEGACY_DEPENDENCIES = ['jsdom@16.x']; +type JSONValue = string | number | boolean | null | JSONArray | JSONObject; + +type JSONObject = { + [key: string]: JSONValue; +}; +type JSONArray = Array; + +interface TSConfigJSON extends JSONObject { + compilerOptions: { lib: string[]; target: string }; +} + /** * Run the given shell command, piping the shell process's `stdin`, `stdout`, and `stderr` to that of the current * process. Returns contents of `stdout`. @@ -52,7 +63,7 @@ function installLegacyDeps(legacyDeps: string[] = []): void { * it to a `var` solves this by making it redeclarable. * */ -function addTransformer(): void { +function addJestTransformer(): void { // Though newer `ts-jest` versions support transformers written in TS, the legacy version does not. run('yarn tsc --skipLibCheck jest/transformers/constReplacer.ts'); @@ -81,6 +92,34 @@ function addTransformer(): void { fs.writeFileSync(path.resolve('jest/jest.config.js'), code); } +/** + * Modify a json file on disk. + * + * @param filepath The path to the file to be modified + * @param transformer A function which takes the JSON data as input and returns a mutated version. It may mutate the + * JSON data in place, but it isn't required to do so. + */ +export function modifyJSONFile(filepath: string, transformer: (json: JSONObject) => JSONObject): void { + const fileContents = fs + .readFileSync(filepath) + .toString() + // get rid of comments, which the `jsonc` format allows, but which will crash `JSON.parse` + .replace(/\/\/.*\n/g, ''); + const json = JSON.parse(fileContents); + const newJSON = transformer(json); + fs.writeFileSync(filepath, JSON.stringify(newJSON, null, 2)); +} + +const es6ifyTestTSConfig = (pkg: string): void => { + const filepath = `packages/${pkg}/tsconfig.test.json`; + const transformer = (json: JSONObject): JSONObject => { + const tsconfig = json as TSConfigJSON; + tsconfig.compilerOptions.target = 'es6'; + return json; + }; + modifyJSONFile(filepath, transformer); +}; + /** * Skip tests which don't apply to Node and therefore don't need to run in older Node versions. * @@ -107,15 +146,22 @@ function runTests(): void { if (CURRENT_NODE_VERSION === '8') { installLegacyDeps(NODE_8_LEGACY_DEPENDENCIES); // Inject a `const`-to-`var` transformer, in order to stop Node 8 from complaining when we shadow `global` - addTransformer(); + addJestTransformer(); // TODO Right now, this just skips incompatible tests, but it could be skipping more (hence the aspirational name), // and not just in Node 8. See `skipNonNodeTests`'s docstring. skipNonNodeTests(); + es6ifyTestTSConfig('utils'); runWithIgnores(NODE_8_SKIP_TESTS_PACKAGES); } // else if (CURRENT_NODE_VERSION === '10') { installLegacyDeps(NODE_10_LEGACY_DEPENDENCIES); + es6ifyTestTSConfig('utils'); + runWithIgnores(DEFAULT_SKIP_TESTS_PACKAGES); + } + // + else if (CURRENT_NODE_VERSION === '12') { + es6ifyTestTSConfig('utils'); runWithIgnores(DEFAULT_SKIP_TESTS_PACKAGES); } // diff --git a/yarn.lock b/yarn.lock index bf506395377a..4e6d2a0f257d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4468,6 +4468,11 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.1.tgz#78b5433344e2f92e8b306c06a5622c50c245bf6b" integrity sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg== +"@types/array.prototype.flat@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#5433a141730f8e1d7a8e7486458ceb8144ee5edc" + integrity sha512-JOvNJUU/zjfJWcA1aHDnCKHwQjZ7VQ3UNfbcMKXrkQKKyMkJHrQ9vpSVMhgsztrtsbIRJKazMDvg2QggFVwJqw== + "@types/aws-lambda@^8.10.62": version "8.10.73" resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.73.tgz#77773c9accb2cec26fcb7c6b510a555805604a53" @@ -5772,6 +5777,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== +acorn@^8.7.0: + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + adjust-sourcemap-loader@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" @@ -5785,11 +5795,32 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -agent-base@4, agent-base@5, agent-base@6, agent-base@^4.3.0, agent-base@^6.0.2, agent-base@~4.2.1: +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@5: version "5.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== +agent-base@6, agent-base@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + agentkeepalive@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" @@ -6172,6 +6203,16 @@ array.prototype.flat@^1.2.3: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" +array.prototype.flat@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" + integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + array.prototype.flatmap@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" @@ -6272,6 +6313,13 @@ ast-types@0.13.3: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7" integrity sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA== +ast-types@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -10206,6 +10254,14 @@ define-properties@^1.1.2, define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -11672,6 +11728,35 @@ es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.0" +es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5: + version "1.20.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.0.tgz#b2d526489cceca004588296334726329e0a6bfb6" + integrity sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.4.1" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" @@ -11703,6 +11788,13 @@ es-module-lexer@^0.9.0: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -11735,6 +11827,18 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + es6-symbol@^3.1.1, es6-symbol@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" @@ -13168,6 +13272,16 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -13862,6 +13976,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-binary2@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" @@ -13889,6 +14008,13 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" @@ -13899,6 +14025,11 @@ has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + has-to-string-tag-x@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" @@ -15017,6 +15148,11 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + is-number-object@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" @@ -15153,6 +15289,13 @@ is-shared-array-buffer@^1.0.1: resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-ssh@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" @@ -15258,6 +15401,13 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.0" +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-what@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" @@ -18833,6 +18983,11 @@ object-inspect@^1.11.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.1.tgz#d4bd7d7de54b9a75599f59a00bd698c1f1c6549b" integrity sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA== +object-inspect@^1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" @@ -21204,6 +21359,16 @@ recast@^0.18.1: private "^0.1.8" source-map "~0.6.1" +recast@^0.20.5: + version "0.20.5" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" + integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== + dependencies: + ast-types "0.14.2" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -21330,7 +21495,7 @@ regex-parser@^2.2.11: resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== -regexp.prototype.flags@^1.2.0: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -23312,6 +23477,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -23320,6 +23494,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string_decoder@0.10, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -24347,6 +24530,11 @@ tslib@^2.0.0, tslib@^2.0.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tslib@^2.0.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^2.3.0, tslib@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" @@ -24563,6 +24751,16 @@ unbox-primitive@^1.0.0, unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unbzip2-stream@^1.3.3: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"