diff --git a/dev-packages/rollup-utils/npmHelpers.mjs b/dev-packages/rollup-utils/npmHelpers.mjs index 4e6483364ee4..4cb8deaa61e0 100644 --- a/dev-packages/rollup-utils/npmHelpers.mjs +++ b/dev-packages/rollup-utils/npmHelpers.mjs @@ -15,7 +15,6 @@ import { defineConfig } from 'rollup'; import { makeCleanupPlugin, makeDebugBuildStatementReplacePlugin, - makeExtractPolyfillsPlugin, makeImportMetaUrlReplacePlugin, makeNodeResolvePlugin, makeRrwebBuildPlugin, @@ -44,7 +43,6 @@ export function makeBaseNPMConfig(options = {}) { const debugBuildStatementReplacePlugin = makeDebugBuildStatementReplacePlugin(); const importMetaUrlReplacePlugin = makeImportMetaUrlReplacePlugin(); const cleanupPlugin = makeCleanupPlugin(); - const extractPolyfillsPlugin = makeExtractPolyfillsPlugin(); const rrwebBuildPlugin = makeRrwebBuildPlugin({ excludeShadowDom: undefined, excludeIframe: undefined, @@ -121,10 +119,6 @@ export function makeBaseNPMConfig(options = {}) { ], }; - if (addPolyfills) { - defaultBaseConfig.plugins.push(extractPolyfillsPlugin); - } - return deepMerge(defaultBaseConfig, packageSpecificConfig, { // Plugins have to be in the correct order or everything breaks, so when merging we have to manually re-order them customMerge: key => (key === 'plugins' ? mergePlugins : undefined), diff --git a/dev-packages/rollup-utils/plugins/extractPolyfillsPlugin.mjs b/dev-packages/rollup-utils/plugins/extractPolyfillsPlugin.mjs deleted file mode 100644 index e0a21b400f35..000000000000 --- a/dev-packages/rollup-utils/plugins/extractPolyfillsPlugin.mjs +++ /dev/null @@ -1,214 +0,0 @@ -import * as path from 'path'; - -import * as acorn from 'acorn'; -import * as recast from 'recast'; - -const POLYFILL_NAMES = new Set([ - '_asyncNullishCoalesce', - '_asyncOptionalChain', - '_asyncOptionalChainDelete', - '_nullishCoalesce', - '_optionalChain', - '_optionalChainDelete', -]); - -/** - * Create a plugin which will replace function definitions of any of the above functions with an `import` or `require` - * statement pulling them in from a central source. Mimics tsc's `importHelpers` option. - */ -export function makeExtractPolyfillsPlugin() { - let moduleFormat; - - // For more on the hooks used in this plugin, see https://rollupjs.org/guide/en/#output-generation-hooks - return { - name: 'extractPolyfills', - - // Figure out which build we're currently in (esm or cjs) - outputOptions(options) { - moduleFormat = options.format; - }, - - // This runs after both the sucrase transpilation (which happens in the `transform` hook) and rollup's own - // esm-i-fying or cjs-i-fying work (which happens right before `renderChunk`), in other words, after all polyfills - // will have been injected - renderChunk(code, chunk) { - const sourceFile = chunk.fileName; - - // We don't want to pull the function definitions out of their actual sourcefiles, just the places where they've - // been injected - if (sourceFile.includes('buildPolyfills')) { - return null; - } - - // The index.js file of the core package will include identifiers named after polyfills so we would inject the - // polyfills, however that would override the exports so we should just skip that file. - const isCorePackage = process.cwd().endsWith(`packages${path.sep}core`); - if (isCorePackage && sourceFile === 'index.js') { - return null; - } - - const parserOptions = { - sourceFileName: sourceFile, - // We supply a custom parser which wraps the provided `acorn` parser in order to override the `ecmaVersion` value. - // See https://github.com/benjamn/recast/issues/578. - parser: { - parse(source, options) { - return acorn.parse(source, { - ...options, - // By this point in the build, everything should already have been down-compiled to whatever JS version - // we're targeting. Setting this parser to `latest` just means that whatever that version is (or changes - // to in the future), this parser will be able to handle the generated code. - ecmaVersion: 'latest', - }); - }, - }, - }; - - const ast = recast.parse(code, parserOptions); - - // Find function definitions and function expressions whose identifiers match a known polyfill name - const polyfillNodes = findPolyfillNodes(ast); - - if (polyfillNodes.length === 0) { - return null; - } - - console.log(`${sourceFile} - polyfills: ${polyfillNodes.map(node => node.name)}`); - - // Depending on the output format, generate `import { x, y, z } from '...'` or `var { x, y, z } = require('...')` - const importOrRequireNode = createImportOrRequireNode(polyfillNodes, sourceFile, moduleFormat); - - // Insert our new `import` or `require` node at the top of the file, and then delete the function definitions it's - // meant to replace (polyfill nodes get marked for deletion in `findPolyfillNodes`) - ast.program.body = [importOrRequireNode, ...ast.program.body.filter(node => !node.shouldDelete)]; - - // In spite of the name, this doesn't actually print anything - it just stringifies the code, and keeps track of - // where original nodes end up in order to generate a sourcemap. - const result = recast.print(ast, { - sourceMapName: `${sourceFile}.map`, - quote: 'single', - }); - - return { code: result.code, map: result.map }; - }, - }; -} - -/** - * Extract the function name, regardless of the format in which the function is declared - */ -function getNodeName(node) { - // Function expressions and functions pulled from objects - if (node.type === 'VariableDeclaration') { - // In practice sucrase and rollup only ever declare one polyfill at a time, so it's safe to just grab the first - // entry here - const declarationId = node.declarations[0].id; - - // Note: Sucrase and rollup seem to only use the first type of variable declaration for their polyfills, but good to - // cover our bases - - // Declarations of the form - // `const dogs = function() { return "are great"; };` - // or - // `const dogs = () => "are great"; - if (declarationId.type === 'Identifier') { - return declarationId.name; - } - // Declarations of the form - // `const { dogs } = { dogs: function() { return "are great"; } }` - // or - // `const { dogs } = { dogs: () => "are great" }` - else if (declarationId.type === 'ObjectPattern') { - return declarationId.properties[0].key.name; - } - // Any other format - else { - return 'unknown variable'; - } - } - - // Regular old functions, of the form - // `function dogs() { return "are great"; }` - else if (node.type === 'FunctionDeclaration') { - return node.id.name; - } - - // If we get here, this isn't a node we're interested in, so just return a string we know will never match any of the - // polyfill names - else { - return 'nope'; - } -} - -/** - * Find all nodes whose identifiers match a known polyfill name. - * - * Note: In theory, this could yield false positives, if any of the magic names were assigned to something other than a - * polyfill function, but the chances of that are slim. Also, it only searches the module global scope, but that's - * always where the polyfills appear, so no reason to traverse the whole tree. - */ -function findPolyfillNodes(ast) { - const isPolyfillNode = node => { - const nodeName = getNodeName(node); - if (POLYFILL_NAMES.has(nodeName)) { - // Mark this node for later deletion, since we're going to replace it with an import statement - node.shouldDelete = true; - // Store the name in a consistent spot, regardless of node type - node.name = nodeName; - - return true; - } - - return false; - }; - - return ast.program.body.filter(isPolyfillNode); -} - -/** - * Create a node representing an `import` or `require` statement of the form - * - * import { < polyfills > } from '...' - * or - * var { < polyfills > } = require('...') - * - * @param polyfillNodes The nodes from the current version of the code, defining the polyfill functions - * @param currentSourceFile The path, relative to `src/`, of the file currently being transpiled - * @param moduleFormat Either 'cjs' or 'esm' - * @returns A single node which can be subbed in for the polyfill definition nodes - */ -function createImportOrRequireNode(polyfillNodes, currentSourceFile, moduleFormat) { - const { - callExpression, - identifier, - importDeclaration, - importSpecifier, - literal, - objectPattern, - property, - variableDeclaration, - variableDeclarator, - } = recast.types.builders; - - // Since our polyfills live in `@sentry/core`, if we're importing or requiring them there the path will have to be - // relative - const isCorePackage = process.cwd().endsWith(path.join('packages', 'core')); - const importSource = literal( - isCorePackage ? `.${path.sep}${path.relative(path.dirname(currentSourceFile), 'buildPolyfills')}` : '@sentry/core', - ); - - // This is the `x, y, z` of inside of `import { x, y, z }` or `var { x, y, z }` - const importees = polyfillNodes.map(({ name: fnName }) => - moduleFormat === 'esm' - ? importSpecifier(identifier(fnName)) - : property.from({ kind: 'init', key: identifier(fnName), value: identifier(fnName), shorthand: true }), - ); - - const requireFn = identifier('require'); - - return moduleFormat === 'esm' - ? importDeclaration(importees, importSource) - : variableDeclaration('var', [ - variableDeclarator(objectPattern(importees), callExpression(requireFn, [importSource])), - ]); -} diff --git a/dev-packages/rollup-utils/plugins/npmPlugins.mjs b/dev-packages/rollup-utils/plugins/npmPlugins.mjs index 6597e2244ab8..5f577507b102 100644 --- a/dev-packages/rollup-utils/plugins/npmPlugins.mjs +++ b/dev-packages/rollup-utils/plugins/npmPlugins.mjs @@ -173,5 +173,3 @@ export function makeCodeCovPlugin() { uploadToken: process.env.CODECOV_TOKEN, }); } - -export { makeExtractPolyfillsPlugin } from './extractPolyfillsPlugin.mjs'; diff --git a/packages/core/src/utils-hoist/buildPolyfills/README.md b/packages/core/src/utils-hoist/buildPolyfills/README.md deleted file mode 100644 index 3b18ad989133..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/README.md +++ /dev/null @@ -1,30 +0,0 @@ -## 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. - -Note that not all polyfills are currently used by the SDK, but all are included here for future compatibility, 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/core/src/utils-hoist/buildPolyfills/_asyncNullishCoalesce.ts b/packages/core/src/utils-hoist/buildPolyfills/_asyncNullishCoalesce.ts deleted file mode 100644 index 032b31011f96..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/_asyncNullishCoalesce.ts +++ /dev/null @@ -1,51 +0,0 @@ -// https://github.com/alangpierce/sucrase/tree/265887868966917f3b924ce38dfad01fbab1329f -// -// The MIT License (MIT) -// -// Copyright (c) 2012-2018 various contributors (see AUTHORS) -// -// 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. - -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 - */ -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/core/src/utils-hoist/buildPolyfills/_asyncOptionalChain.ts b/packages/core/src/utils-hoist/buildPolyfills/_asyncOptionalChain.ts deleted file mode 100644 index 37489b5c9232..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/_asyncOptionalChain.ts +++ /dev/null @@ -1,82 +0,0 @@ -// https://github.com/alangpierce/sucrase/tree/265887868966917f3b924ce38dfad01fbab1329f -// -// The MIT License (MIT) -// -// Copyright (c) 2012-2018 various contributors (see AUTHORS) -// -// 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. - -import type { 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 - */ -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/core/src/utils-hoist/buildPolyfills/_asyncOptionalChainDelete.ts b/packages/core/src/utils-hoist/buildPolyfills/_asyncOptionalChainDelete.ts deleted file mode 100644 index 9cef4fd791f0..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/_asyncOptionalChainDelete.ts +++ /dev/null @@ -1,51 +0,0 @@ -// https://github.com/alangpierce/sucrase/tree/265887868966917f3b924ce38dfad01fbab1329f -// -// The MIT License (MIT) -// -// Copyright (c) 2012-2018 various contributors (see AUTHORS) -// -// 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. - -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`. - */ -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/core/src/utils-hoist/buildPolyfills/_nullishCoalesce.ts b/packages/core/src/utils-hoist/buildPolyfills/_nullishCoalesce.ts deleted file mode 100644 index a11cd469bf11..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/_nullishCoalesce.ts +++ /dev/null @@ -1,49 +0,0 @@ -// https://github.com/alangpierce/sucrase/tree/265887868966917f3b924ce38dfad01fbab1329f -// -// The MIT License (MIT) -// -// Copyright (c) 2012-2018 various contributors (see AUTHORS) -// -// 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. - -/** - * 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/core/src/utils-hoist/buildPolyfills/_optionalChain.ts b/packages/core/src/utils-hoist/buildPolyfills/_optionalChain.ts deleted file mode 100644 index a7ea8338d744..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/_optionalChain.ts +++ /dev/null @@ -1,82 +0,0 @@ -// https://github.com/alangpierce/sucrase/tree/265887868966917f3b924ce38dfad01fbab1329f -// -// The MIT License (MIT) -// -// Copyright (c) 2012-2018 various contributors (see AUTHORS) -// -// 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. - -import type { 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/core/src/utils-hoist/buildPolyfills/_optionalChainDelete.ts b/packages/core/src/utils-hoist/buildPolyfills/_optionalChainDelete.ts deleted file mode 100644 index 04bffc8ab385..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/_optionalChainDelete.ts +++ /dev/null @@ -1,52 +0,0 @@ -// https://github.com/alangpierce/sucrase/tree/265887868966917f3b924ce38dfad01fbab1329f -// -// The MIT License (MIT) -// -// Copyright (c) 2012-2018 various contributors (see AUTHORS) -// -// 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. - -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/core/src/utils-hoist/buildPolyfills/index.ts b/packages/core/src/utils-hoist/buildPolyfills/index.ts deleted file mode 100644 index 2017dcbd9592..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { _asyncNullishCoalesce } from './_asyncNullishCoalesce'; -export { _asyncOptionalChain } from './_asyncOptionalChain'; -export { _asyncOptionalChainDelete } from './_asyncOptionalChainDelete'; -export { _nullishCoalesce } from './_nullishCoalesce'; -export { _optionalChain } from './_optionalChain'; -export { _optionalChainDelete } from './_optionalChainDelete'; diff --git a/packages/core/src/utils-hoist/buildPolyfills/types.ts b/packages/core/src/utils-hoist/buildPolyfills/types.ts deleted file mode 100644 index 10e2f4a944a4..000000000000 --- a/packages/core/src/utils-hoist/buildPolyfills/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Primitive } from '../../types-hoist'; - -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/core/src/utils-hoist/index.ts b/packages/core/src/utils-hoist/index.ts index e53cd0edb59b..befb934905a2 100644 --- a/packages/core/src/utils-hoist/index.ts +++ b/packages/core/src/utils-hoist/index.ts @@ -179,10 +179,3 @@ export { SDK_VERSION } from './version'; export { getDebugImagesForResources, getFilenameToDebugIdMap } from './debug-ids'; export { escapeStringForRegex } from './vendor/escapeStringForRegex'; export { supportsHistory } from './vendor/supportsHistory'; - -export { _asyncNullishCoalesce } from './buildPolyfills/_asyncNullishCoalesce'; -export { _asyncOptionalChain } from './buildPolyfills/_asyncOptionalChain'; -export { _asyncOptionalChainDelete } from './buildPolyfills/_asyncOptionalChainDelete'; -export { _nullishCoalesce } from './buildPolyfills/_nullishCoalesce'; -export { _optionalChain } from './buildPolyfills/_optionalChain'; -export { _optionalChainDelete } from './buildPolyfills/_optionalChainDelete'; diff --git a/packages/core/test/utils-hoist/buildPolyfills/nullishCoalesce.test.ts b/packages/core/test/utils-hoist/buildPolyfills/nullishCoalesce.test.ts deleted file mode 100644 index 11c83b0711d9..000000000000 --- a/packages/core/test/utils-hoist/buildPolyfills/nullishCoalesce.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// TODO(v9): Remove this test - -import { _nullishCoalesce } from '../../../src/utils-hoist/buildPolyfills'; -import type { Value } from '../../../src/utils-hoist/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/core/test/utils-hoist/buildPolyfills/optionalChain.test.ts b/packages/core/test/utils-hoist/buildPolyfills/optionalChain.test.ts deleted file mode 100644 index bd7a7bb052fb..000000000000 --- a/packages/core/test/utils-hoist/buildPolyfills/optionalChain.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -// TODO(v9): Remove this test - -import { shim as arrayFlatShim } from 'array.prototype.flat'; - -import { _optionalChain } from '../../../src/utils-hoist/buildPolyfills'; -import type { GenericFunction, GenericObject, Value } from '../../../src/utils-hoist/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. -arrayFlatShim(); - -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. - // @ts-expect-error this is what we're testing - expect(_optionalChain([initialChainComponent, ...operations.flat()])).toEqual( - // @ts-expect-error this is what we're testing - _optionalChainOrig([initialChainComponent, ...operations.flat()]), - ); - // @ts-expect-error this is what we're testing - expect(_optionalChain([initialChainComponent, ...operations.flat()])).toEqual(expectedValue); - }); - }); -}); diff --git a/packages/core/test/utils-hoist/buildPolyfills/originals.ts b/packages/core/test/utils-hoist/buildPolyfills/originals.ts deleted file mode 100644 index a504d5f0d871..000000000000 --- a/packages/core/test/utils-hoist/buildPolyfills/originals.ts +++ /dev/null @@ -1,85 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck this is just used for tests - -// TODO(v9): Remove this file - -// 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 function _asyncNullishCoalesce(lhs, rhsFn) { - if (lhs != null) { - return lhs; - } else { - return 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 _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/eslint-plugin-sdk/src/rules/no-nullish-coalescing.js b/packages/eslint-plugin-sdk/src/rules/no-nullish-coalescing.js deleted file mode 100644 index 8944f2755632..000000000000 --- a/packages/eslint-plugin-sdk/src/rules/no-nullish-coalescing.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @fileoverview disallow nullish coalescing operators as they were introduced only in ES2020 and hence require - * us to add a polyfill. This increases bundle size more than avoiding nullish coalescing operators all together. - * - * @author Lukas Stracke - * - * Based on: https://github.com/mysticatea/eslint-plugin-es/blob/v4.1.0/lib/rules/no-nullish-coalescing-operators.js - */ -'use strict'; - -// ------------------------------------------------------------------------------ -// Rule Definition -// ------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'problem', - docs: { - description: 'disallow nullish coalescing operators.', - category: 'Best Practices', - recommended: true, - }, - messages: { - forbidden: 'Avoid using nullish coalescing operators.', - }, - fixable: null, - schema: [], - }, - create(context) { - return { - "LogicalExpression[operator='??']"(node) { - context.report({ - node: context.getSourceCode().getTokenAfter(node.left, isNullishCoalescingOperator), - messageId: 'forbidden', - }); - }, - }; - }, -}; - -/** - * Checks if the given token is a nullish coalescing operator or not. - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a nullish coalescing operator. - */ -function isNullishCoalescingOperator(token) { - return token.value === '??' && token.type === 'Punctuator'; -} diff --git a/packages/eslint-plugin-sdk/src/rules/no-optional-chaining.js b/packages/eslint-plugin-sdk/src/rules/no-optional-chaining.js deleted file mode 100644 index 67da656bc782..000000000000 --- a/packages/eslint-plugin-sdk/src/rules/no-optional-chaining.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @fileoverview Rule to disallow using optional chaining - because this is transpiled into verbose code. - * @author Francesco Novy - * - * Based on https://github.com/facebook/lexical/pull/3233 - */ -'use strict'; - -// ------------------------------------------------------------------------------ -// Rule Definition -// ------------------------------------------------------------------------------ - -/** @type {import('eslint').Rule.RuleModule} */ -module.exports = { - meta: { - type: 'problem', - docs: { - description: 'disallow usage of optional chaining', - category: 'Best Practices', - recommended: true, - }, - messages: { - forbidden: 'Avoid using optional chaining', - }, - fixable: null, - schema: [], - }, - - create(context) { - const sourceCode = context.getSourceCode(); - - /** - * Checks if the given token is a `?.` token or not. - * @param {Token} token The token to check. - * @returns {boolean} `true` if the token is a `?.` token. - */ - function isQuestionDotToken(token) { - return ( - token.value === '?.' && - (token.type === 'Punctuator' || // espree has been parsed well. - // espree@7.1.0 doesn't parse "?." tokens well. Therefore, get the string from the source code and check it. - sourceCode.getText(token) === '?.') - ); - } - - return { - 'CallExpression[optional=true]'(node) { - context.report({ - messageId: 'forbidden', - node: sourceCode.getTokenAfter(node.callee, isQuestionDotToken), - }); - }, - 'MemberExpression[optional=true]'(node) { - context.report({ - messageId: 'forbidden', - node: sourceCode.getTokenAfter(node.object, isQuestionDotToken), - }); - }, - }; - }, -}; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 1d8d712e5b0f..842f8475905d 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -14,13 +14,7 @@ import { SyncPromise as SyncPromise_imported, TRACEPARENT_REGEXP as TRACEPARENT_REGEXP_imported, UNKNOWN_FUNCTION as UNKNOWN_FUNCTION_imported, - _asyncNullishCoalesce as _asyncNullishCoalesce_imported, - _asyncOptionalChain as _asyncOptionalChain_imported, - _asyncOptionalChainDelete as _asyncOptionalChainDelete_imported, _browserPerformanceTimeOriginMode as _browserPerformanceTimeOriginMode_imported, - _nullishCoalesce as _nullishCoalesce_imported, - _optionalChain as _optionalChain_imported, - _optionalChainDelete as _optionalChainDelete_imported, addConsoleInstrumentationHandler as addConsoleInstrumentationHandler_imported, addContextToFrame as addContextToFrame_imported, addExceptionMechanism as addExceptionMechanism_imported, @@ -646,24 +640,6 @@ export const extractRequestData = extractRequestData_imported; // eslint-disable-next-line deprecation/deprecation export const addRequestDataToEvent = addRequestDataToEvent_imported; -/** @deprecated Import from `@sentry/core` instead. */ -export const _asyncNullishCoalesce = _asyncNullishCoalesce_imported; - -/** @deprecated Import from `@sentry/core` instead. */ -export const _asyncOptionalChain = _asyncOptionalChain_imported; - -/** @deprecated Import from `@sentry/core` instead. */ -export const _asyncOptionalChainDelete = _asyncOptionalChainDelete_imported; - -/** @deprecated Import from `@sentry/core` instead. */ -export const _nullishCoalesce = _nullishCoalesce_imported; - -/** @deprecated Import from `@sentry/core` instead. */ -export const _optionalChain = _optionalChain_imported; - -/** @deprecated Import from `@sentry/core` instead. */ -export const _optionalChainDelete = _optionalChainDelete_imported; - /** @deprecated Import from `@sentry/core` instead. */ // eslint-disable-next-line deprecation/deprecation export const BAGGAGE_HEADER_NAME = BAGGAGE_HEADER_NAME_imported;