diff --git a/lib/detector.ts b/lib/detector.ts index 35fbf11c..e6efaae4 100644 --- a/lib/detector.ts +++ b/lib/detector.ts @@ -1,49 +1,43 @@ -import * as babelTypes from '@babel/types'; -import * as babel from '@babel/parser'; -import generate from '@babel/generator'; +import * as acorn from 'acorn'; import { log } from './log'; import { ALIAS_AS_RELATIVE, ALIAS_AS_RESOLVABLE } from './common'; -function isLiteral(node: babelTypes.Node): node is babelTypes.Literal { +// Minimal ESTree node types used by the detector +interface AcornNode { + type: string; + start: number; + end: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} + +function isLiteral(node: AcornNode): boolean { if (node == null) { return false; } - if (!node.type.endsWith('Literal')) { - return false; + if (node.type === 'Literal') { + // Exclude null, regex + return node.value !== null && !(node.value instanceof RegExp); } - if (node.type === 'TemplateLiteral' && node.expressions.length !== 0) { - return false; + if (node.type === 'TemplateLiteral') { + return node.expressions.length === 0; } - return true; + return false; } -function getLiteralValue(node: babelTypes.Literal) { +function getLiteralValue(node: AcornNode) { if (node.type === 'TemplateLiteral') { return node.quasis[0].value.raw; } - if (node.type === 'NullLiteral') { - throw new Error('Unexpected null in require expression'); - } - - if (node.type === 'RegExpLiteral') { - throw new Error('Unexpected regexp in require expression'); - } - return node.value; } -function reconstructSpecifiers( - specs: ( - | babelTypes.ImportDefaultSpecifier - | babelTypes.ImportNamespaceSpecifier - | babelTypes.ImportSpecifier - )[], -) { +function reconstructSpecifiers(specs: AcornNode[]) { if (!specs || !specs.length) { return ''; } @@ -51,7 +45,7 @@ function reconstructSpecifiers( const defaults = []; for (const spec of specs) { - if (babelTypes.isImportDefaultSpecifier(spec)) { + if (spec.type === 'ImportDefaultSpecifier') { defaults.push(spec.local.name); } } @@ -59,10 +53,8 @@ function reconstructSpecifiers( const nonDefaults = []; for (const spec of specs) { - if (babelTypes.isImportSpecifier(spec)) { - const importedName = babelTypes.isIdentifier(spec.imported) - ? spec.imported.name - : spec.imported.value; + if (spec.type === 'ImportSpecifier') { + const importedName = spec.imported.name; if (spec.local.name === importedName) { nonDefaults.push(spec.local.name); @@ -79,8 +71,8 @@ function reconstructSpecifiers( return defaults.join(', '); } -function reconstruct(node: babelTypes.Node) { - let v = generate(node, { comments: false }).code.replace(/\n/g, ''); +function reconstruct(node: AcornNode, source: string) { + let v = source.slice(node.start, node.end).replace(/\n/g, ''); let v2; while (true) { @@ -121,12 +113,12 @@ function valid2(v2?: Was['v2']) { ); } -function visitorRequireResolve(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +function visitorRequireResolve(n: AcornNode) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isMemberExpression(n.callee)) { + if (n.callee.type !== 'MemberExpression') { return null; } @@ -150,12 +142,12 @@ function visitorRequireResolve(n: babelTypes.Node) { }; } -function visitorRequire(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +function visitorRequire(n: AcornNode) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isIdentifier(n.callee)) { + if (n.callee.type !== 'Identifier') { return null; } @@ -173,20 +165,20 @@ function visitorRequire(n: babelTypes.Node) { }; } -function visitorImport(n: babelTypes.Node) { - if (!babelTypes.isImportDeclaration(n)) { +function visitorImport(n: AcornNode) { + if (n.type !== 'ImportDeclaration') { return null; } return { v1: n.source.value, v3: reconstructSpecifiers(n.specifiers) }; } -function visitorPathJoin(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +function visitorPathJoin(n: AcornNode) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isMemberExpression(n.callee)) { + if (n.callee.type !== 'MemberExpression') { return null; } @@ -218,10 +210,10 @@ function visitorPathJoin(n: babelTypes.Node) { return null; } - return { v1: getLiteralValue(n.arguments[1] as babelTypes.StringLiteral) }; + return { v1: getLiteralValue(n.arguments[1]) }; } -export function visitorSuccessful(node: babelTypes.Node, test = false) { +export function visitorSuccessful(node: AcornNode, test = false) { let was: Was | null = visitorRequireResolve(node); if (was) { @@ -283,12 +275,12 @@ export function visitorSuccessful(node: babelTypes.Node, test = false) { return null; } -function nonLiteralRequireResolve(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +function nonLiteralRequireResolve(n: AcornNode, source: string) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isMemberExpression(n.callee)) { + if (n.callee.type !== 'MemberExpression') { return null; } @@ -309,7 +301,7 @@ function nonLiteralRequireResolve(n: babelTypes.Node) { const m = n.arguments[1]; if (!m) { - return { v1: reconstruct(n.arguments[0]) }; + return { v1: reconstruct(n.arguments[0], source) }; } if (!isLiteral(n.arguments[1])) { @@ -317,17 +309,17 @@ function nonLiteralRequireResolve(n: babelTypes.Node) { } return { - v1: reconstruct(n.arguments[0]), + v1: reconstruct(n.arguments[0], source), v2: getLiteralValue(n.arguments[1]), }; } -function nonLiteralRequire(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +function nonLiteralRequire(n: AcornNode, source: string) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isIdentifier(n.callee)) { + if (n.callee.type !== 'Identifier') { return null; } @@ -342,7 +334,7 @@ function nonLiteralRequire(n: babelTypes.Node) { const m = n.arguments[1]; if (!m) { - return { v1: reconstruct(n.arguments[0]) }; + return { v1: reconstruct(n.arguments[0], source) }; } if (!isLiteral(n.arguments[1])) { @@ -350,13 +342,14 @@ function nonLiteralRequire(n: babelTypes.Node) { } return { - v1: reconstruct(n.arguments[0]), + v1: reconstruct(n.arguments[0], source), v2: getLiteralValue(n.arguments[1]), }; } -export function visitorNonLiteral(n: babelTypes.Node) { - const was = nonLiteralRequireResolve(n) || nonLiteralRequire(n); +export function visitorNonLiteral(n: AcornNode, source: string) { + const was = + nonLiteralRequireResolve(n, source) || nonLiteralRequire(n, source); if (was) { if (!valid2(was.v2)) { @@ -373,12 +366,12 @@ export function visitorNonLiteral(n: babelTypes.Node) { return null; } -function isRequire(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +function isRequire(n: AcornNode, source: string) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isIdentifier(n.callee)) { + if (n.callee.type !== 'Identifier') { return null; } @@ -392,15 +385,15 @@ function isRequire(n: babelTypes.Node) { return null; } - return { v1: reconstruct(n.arguments[0]) }; + return { v1: reconstruct(n.arguments[0], source) }; } -function isRequireResolve(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +function isRequireResolve(n: AcornNode, source: string) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isMemberExpression(n.callee)) { + if (n.callee.type !== 'MemberExpression') { return null; } @@ -420,11 +413,11 @@ function isRequireResolve(n: babelTypes.Node) { return null; } - return { v1: reconstruct(n.arguments[0]) }; + return { v1: reconstruct(n.arguments[0], source) }; } -export function visitorMalformed(n: babelTypes.Node) { - const was = isRequireResolve(n) || isRequire(n); +export function visitorMalformed(n: AcornNode, source: string) { + const was = isRequireResolve(n, source) || isRequire(n, source); if (was) { return { alias: was.v1 }; @@ -433,12 +426,12 @@ export function visitorMalformed(n: babelTypes.Node) { return null; } -export function visitorUseSCWD(n: babelTypes.Node) { - if (!babelTypes.isCallExpression(n)) { +export function visitorUseSCWD(n: AcornNode, source: string) { + if (n.type !== 'CallExpression') { return null; } - if (!babelTypes.isMemberExpression(n.callee)) { + if (n.callee.type !== 'MemberExpression') { return null; } @@ -452,7 +445,9 @@ export function visitorUseSCWD(n: babelTypes.Node) { return null; } - const was = { v1: n.arguments.map(reconstruct).join(', ') }; + const was = { + v1: n.arguments.map((a: AcornNode) => reconstruct(a, source)).join(', '), + }; if (was) { return { alias: was.v1 }; @@ -461,25 +456,24 @@ export function visitorUseSCWD(n: babelTypes.Node) { return null; } -type VisitorFunction = (node: babelTypes.Node, trying?: boolean) => boolean; +type VisitorFunction = (node: AcornNode, trying?: boolean) => boolean; -function traverse(ast: babelTypes.File, visitor: VisitorFunction) { +function traverse(ast: AcornNode, visitor: VisitorFunction) { // modified esprima-walk to support // visitor return value and "trying" flag - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const stack: Array<[any, boolean]> = [[ast, false]]; + const stack: Array<[AcornNode, boolean]> = [[ast, false]]; for (let i = 0; i < stack.length; i += 1) { const item = stack[i]; const [node] = item; if (node) { - const trying = item[1] || babelTypes.isTryStatement(node); + const trying = item[1] || node.type === 'TryStatement'; if (visitor(node, trying)) { for (const key in node) { - if (node[key as keyof babelTypes.File]) { - const child = node[key as keyof babelTypes.File]; + if (node[key]) { + const child = node[key]; if (child instanceof Array) { for (let j = 0; j < child.length; j += 1) { @@ -496,25 +490,40 @@ function traverse(ast: babelTypes.File, visitor: VisitorFunction) { } export function parse(body: string) { - return babel.parse(body, { - allowImportExportEverywhere: true, - allowReturnOutsideFunction: true, - }); + // Try module mode first (handles import/export), fall back to script mode + // for legacy code that uses strict-mode-incompatible syntax (e.g. `with` + // statements, octal escapes). This matches Babel's permissive behavior. + try { + return acorn.parse(body, { + ecmaVersion: 'latest', + sourceType: 'module', + allowReturnOutsideFunction: true, + allowImportExportEverywhere: true, + allowHashBang: true, + }) as unknown as AcornNode; + } catch (_) { + return acorn.parse(body, { + ecmaVersion: 'latest', + sourceType: 'script', + allowReturnOutsideFunction: true, + allowHashBang: true, + }) as unknown as AcornNode; + } } export function detect(body: string, visitor: VisitorFunction, file?: string) { - let json; + let ast: AcornNode | undefined; try { - json = parse(body); + ast = parse(body); } catch (error) { const fileInfo = file ? ` in ${file}` : ''; - log.warn(`Babel parse has failed: ${(error as Error).message}${fileInfo}`); + log.warn(`Acorn parse has failed: ${(error as Error).message}${fileInfo}`); } - if (!json) { + if (!ast) { return; } - traverse(json, visitor); + traverse(ast, visitor); } diff --git a/lib/walker.ts b/lib/walker.ts index 4f68def1..92acd403 100644 --- a/lib/walker.ts +++ b/lib/walker.ts @@ -261,9 +261,11 @@ function stepDetect( body = body.toString(); } + const source = body as string; + try { detector.detect( - body, + source, (node, trying) => { const { toplevel } = marker; let d = detector.visitorSuccessful(node) as unknown as Derivative; @@ -279,7 +281,7 @@ function stepDetect( return false; } - d = detector.visitorNonLiteral(node) as unknown as Derivative; + d = detector.visitorNonLiteral(node, source) as unknown as Derivative; if (d) { if (typeof d === 'object' && d.mustExclude) { @@ -298,7 +300,7 @@ function stepDetect( return false; } - d = detector.visitorMalformed(node) as unknown as Derivative; + d = detector.visitorMalformed(node, source) as unknown as Derivative; if (d) { // there is no 'mustExclude' @@ -308,7 +310,7 @@ function stepDetect( return false; } - d = detector.visitorUseSCWD(node) as unknown as Derivative; + d = detector.visitorUseSCWD(node, source) as unknown as Derivative; if (d) { // there is no 'mustExclude' @@ -378,6 +380,37 @@ class Walker { private dictionary: ConfigDictionary; + // Module resolution cache: avoids redundant follow() calls for the same + // specifier resolved from the same directory. + private resolveCache = new Map< + string, + { + newFile: string; + newPackages: { packageJson: string; marker?: Marker }[]; + newPackageForNewRecords?: { packageJson: string; marker?: Marker }; + } + >(); + + // Performance timing accumulators (milliseconds) + private timings = { + read: 0, + strip: 0, + detect: 0, + patch: 0, + derivatives: 0, + esmTransform: 0, + stat: 0, + total: 0, + }; + + private fileCounts = { + jsParsed: 0, + json: 0, + nativeAddon: 0, + other: 0, + total: 0, + }; + constructor() { this.tasks = []; this.records = {}; @@ -541,9 +574,11 @@ class Walker { if (scripts) { scripts = expandFiles(scripts, base); - for (const script of scripts) { - const stat = await fs.stat(script); + const scriptStats = await Promise.all( + scripts.map((s) => fs.stat(s).then((stat) => ({ path: s, stat }))), + ); + for (const { path: script, stat } of scriptStats) { if (stat.isFile()) { if (!isDotJS(script) && !isDotJSON(script) && !isDotNODE(script)) { log.warn("Non-javascript file is specified in 'scripts'.", [ @@ -567,9 +602,12 @@ class Walker { if (assets) { assets = expandFiles(assets, base); - for (const asset of assets) { + const assetStats = await Promise.all( + assets.map((a) => fs.stat(a).then((stat) => ({ path: a, stat }))), + ); + + for (const { path: asset, stat } of assetStats) { log.debug(' Adding asset : .... ', asset); - const stat = await fs.stat(asset); if (stat.isFile()) { await this.appendBlobOrContent({ @@ -587,10 +625,14 @@ class Walker { if (files) { files = expandFiles(files, base); - for (let file of files) { - file = normalizePath(file); - const stat = await fs.stat(file); + const normalizedFiles = files.map((f) => normalizePath(f)); + const fileStats = await Promise.all( + normalizedFiles.map((f) => + fs.stat(f).then((stat) => ({ path: f, stat })), + ), + ); + for (const { path: file, stat } of fileStats) { if (stat.isFile()) { // 1) remove sources of top-level(!) package 'files' i.e. ship as BLOB // 2) non-source (non-js) files of top-level package are shipped as CONTENT @@ -812,87 +854,130 @@ class Walker { marker: Marker, derivative: Derivative, ) { - const newPackages: { packageJson: string; marker?: Marker }[] = []; + const basedir = path.dirname(record.file); + const cacheKey = `${derivative.alias}\0${basedir}`; + const cached = this.resolveCache.get(cacheKey); + + let newFile: string; + let newPackages: { packageJson: string; marker?: Marker }[]; + let newPackageForNewRecords: + | { packageJson: string; marker?: Marker } + | undefined; + + if (cached) { + // Cache hit — reuse prior resolution. Deep-clone marker objects to + // prevent shared mutation (stepActivate modifies marker.config). + // The appendBlobOrContent calls below are no-ops for already-seen + // files (deduped by appendRecord). + newFile = cached.newFile; + newPackages = cached.newPackages.map((p) => ({ + packageJson: p.packageJson, + marker: p.marker + ? { + config: p.marker.config + ? JSON.parse(JSON.stringify(p.marker.config)) + : undefined, + configPath: p.marker.configPath, + base: p.marker.base, + } + : undefined, + })); + newPackageForNewRecords = cached.newPackageForNewRecords + ? newPackages.find( + (p) => + p.packageJson === cached.newPackageForNewRecords!.packageJson, + ) + : undefined; + } else { + // Cache miss — perform full resolution + newPackages = []; - const catchReadFile = (file: string) => { - assert(isPackageJson(file), `walker: ${file} must be package.json`); - newPackages.push({ packageJson: file }); - }; + const catchReadFile = (file: string) => { + assert(isPackageJson(file), `walker: ${file} must be package.json`); + newPackages.push({ packageJson: file }); + }; - const catchPackageFilter = (config: PackageJson, base: string) => { - const newPackage = newPackages[newPackages.length - 1]; - newPackage.marker = { - config, - configPath: newPackage.packageJson, - base, + const catchPackageFilter = (config: PackageJson, base: string) => { + const newPackage = newPackages[newPackages.length - 1]; + newPackage.marker = { + config, + configPath: newPackage.packageJson, + base, + }; }; - }; - let newFile = ''; - let failure: Error | undefined; + newFile = ''; + let failure: Error | undefined; - const basedir = path.dirname(record.file); - try { - newFile = await follow(derivative.alias, { - basedir, - // default is extensions: ['.js'], but - // it is not enough because 'typos.json' - // is not taken in require('./typos') - // in 'normalize-package-data/lib/fixer.js' - // Also include .mjs to support ESM files that get transformed to .js - extensions: MODULE_RESOLVE_EXTENSIONS, - catchReadFile, - catchPackageFilter, - }); - } catch (error) { - failure = error as Error; - } + try { + newFile = await follow(derivative.alias, { + basedir, + // default is extensions: ['.js'], but + // it is not enough because 'typos.json' + // is not taken in require('./typos') + // in 'normalize-package-data/lib/fixer.js' + // Also include .mjs to support ESM files that get transformed to .js + extensions: MODULE_RESOLVE_EXTENSIONS, + catchReadFile, + catchPackageFilter, + }); + } catch (error) { + failure = error as Error; + } - if (failure) { - const { toplevel } = marker; - const mainNotFound = - newPackages.length > 0 && !newPackages[0].marker?.config?.main; - const debug = - !toplevel || - derivative.mayExclude || - (mainNotFound && derivative.fromDependencies); - const level = debug ? 'debug' : 'warn'; + if (failure) { + const { toplevel } = marker; + const mainNotFound = + newPackages.length > 0 && !newPackages[0].marker?.config?.main; + const debug = + !toplevel || + derivative.mayExclude || + (mainNotFound && derivative.fromDependencies); + const level = debug ? 'debug' : 'warn'; + + if (mainNotFound) { + const message = "Entry 'main' not found in %1"; + log[level](message, [ + `%1: ${newPackages[0].packageJson}`, + `%2: ${record.file}`, + ]); + } else { + log[level](`${pc.yellow(failure.message)} in ${record.file}`); + } - if (mainNotFound) { - const message = "Entry 'main' not found in %1"; - log[level](message, [ - `%1: ${newPackages[0].packageJson}`, - `%2: ${record.file}`, - ]); - } else { - log[level](`${pc.yellow(failure.message)} in ${record.file}`); + return; } - return; - } + newPackageForNewRecords = undefined; - let newPackageForNewRecords; + for (const newPackage of newPackages) { + let newFile2; - for (const newPackage of newPackages) { - let newFile2; + try { + newFile2 = await follow(derivative.alias, { + basedir, + extensions: MODULE_RESOLVE_EXTENSIONS, + ignoreFile: newPackage.packageJson, + }); + if (strictVerify) { + assert(newFile2 === normalizePath(newFile2)); + } + } catch (_) { + // not setting is enough + } - try { - newFile2 = await follow(derivative.alias, { - basedir: path.dirname(record.file), - extensions: MODULE_RESOLVE_EXTENSIONS, - ignoreFile: newPackage.packageJson, - }); - if (strictVerify) { - assert(newFile2 === normalizePath(newFile2)); + if (newFile2 !== newFile) { + newPackageForNewRecords = newPackage; + break; } - } catch (_) { - // not setting is enough } - if (newFile2 !== newFile) { - newPackageForNewRecords = newPackage; - break; - } + // Store in cache for future lookups from the same directory + this.resolveCache.set(cacheKey, { + newFile, + newPackages, + newPackageForNewRecords, + }); } // Add all discovered package.json files, not just the one determined by the double-resolution logic @@ -975,6 +1060,17 @@ class Walker { if (record[store] !== undefined) return; record[store] = false; // default is discard + this.fileCounts.total += 1; + if (isDotJSON(record.file)) { + this.fileCounts.json += 1; + } else if (isDotNODE(record.file)) { + this.fileCounts.nativeAddon += 1; + } else if (isDotJS(record.file) || record.file.endsWith('.mjs')) { + this.fileCounts.jsParsed += 1; + } else { + this.fileCounts.other += 1; + } + this.appendStat({ file: record.file, store: STORE_STAT, @@ -1012,11 +1108,18 @@ class Walker { this.hasPatch(record) ) { if (!record.body) { + let t0 = performance.now(); await stepRead(record); + this.timings.read += performance.now() - t0; + + t0 = performance.now(); this.stepPatch(record); + this.timings.patch += performance.now() - t0; if (store === STORE_BLOB || needsSeaRead) { + t0 = performance.now(); stepStrip(record); + this.timings.strip += performance.now() - t0; } } @@ -1108,6 +1211,7 @@ class Walker { (isDotJS(record.file) || record.file.endsWith('.mjs')) ) { if (isESMFile(record.file)) { + const t0 = performance.now(); try { const result = transformESMtoCJS( record.body.toString('utf8'), @@ -1127,13 +1231,19 @@ class Walker { `Failed to transform ESM module to CJS for file "${record.file}": ${message}`, ); } + this.timings.esmTransform += performance.now() - t0; } } if (store === STORE_BLOB || needsSeaRead) { const derivatives2: Derivative[] = []; + let t0 = performance.now(); stepDetect(record, marker, derivatives2); + this.timings.detect += performance.now() - t0; + + t0 = performance.now(); await this.stepDerivatives(record, marker, derivatives2); + this.timings.derivatives += performance.now() - t0; // After dependencies are resolved, rewrite .mjs require paths to .js // since the packer renames .mjs files to .js in the snapshot. @@ -1185,6 +1295,7 @@ class Walker { }); } + const t0 = performance.now(); try { const valueStat = await fs.stat(record.file); @@ -1202,6 +1313,7 @@ class Walker { log.error(`Cannot stat, ${exception.code}`, record.file); throw wasReported(exception.message); } + this.timings.stat += performance.now() - t0; if (path.dirname(record.file) !== record.file) { // root directory @@ -1298,12 +1410,30 @@ class Walker { const { tasks } = this; + const walkerStart = performance.now(); + for (let i = 0; i < tasks.length; i += 1) { // NO MULTIPLE WORKERS! THIS WILL LEAD TO NON-DETERMINISTIC // ORDER. one-by-one fifo is the only way to iterate tasks await this.step(tasks[i]); } + this.timings.total = performance.now() - walkerStart; + + const t = this.timings; + const c = this.fileCounts; + log.debug('Walker performance summary:'); + log.debug( + ` Files: ${c.total} total (${c.jsParsed} JS, ${c.json} JSON, ${c.nativeAddon} native, ${c.other} other)`, + ); + log.debug( + ` Timings: read=${t.read.toFixed(0)}ms detect=${t.detect.toFixed(0)}ms derivatives=${t.derivatives.toFixed(0)}ms`, + ); + log.debug( + ` esmTransform=${t.esmTransform.toFixed(0)}ms patch=${t.patch.toFixed(0)}ms strip=${t.strip.toFixed(0)}ms stat=${t.stat.toFixed(0)}ms`, + ); + log.debug(` Total walker time: ${t.total.toFixed(0)}ms`); + return { symLinks: this.symLinks, records: this.records, diff --git a/package.json b/package.json index 8ea997ac..c12685ca 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@babel/types": "^7.23.0", "@roberts_lando/vfs": "^0.3.2", "@yao-pkg/pkg-fetch": "3.5.33", + "acorn": "^8.16.0", "esbuild": "^0.27.3", "into-stream": "^9.1.0", "minimist": "^1.2.6", diff --git a/yarn.lock b/yarn.lock index 77e8a894..1eca3386 100644 --- a/yarn.lock +++ b/yarn.lock @@ -874,6 +874,11 @@ acorn@^8.15.0: resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +acorn@^8.16.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz"