From b8019b98bee74c75131c77007737725103d2970c Mon Sep 17 00:00:00 2001 From: Joel Chen Date: Mon, 31 May 2021 16:12:15 -0700 Subject: [PATCH 1/3] feat: add pathMapping to node.js --- src/build/generate-contributions.ts | 8 ++++ src/build/strings.ts | 2 + src/configuration.ts | 11 +++++- src/targets/node/nodeSourcePathResolver.ts | 38 +++++++++++++++++-- .../node/node-source-path-resolver.test.ts | 30 +++++++++++++++ 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/build/generate-contributions.ts b/src/build/generate-contributions.ts index 67b040fcc..0c10ce78d 100644 --- a/src/build/generate-contributions.ts +++ b/src/build/generate-contributions.ts @@ -357,6 +357,14 @@ const nodeBaseConfigurationAttributes: ConfigurationAttributes { + private hasPathMapping: boolean; + public static shouldWarnAboutSymlinks(config: AnyLaunchConfiguration) { return 'runtimeArgs' in config && !config.runtimeArgs?.includes('--preserve-symlinks'); } @@ -37,6 +41,7 @@ export class NodeSourcePathResolver extends SourcePathResolverBase { sourceMapOverrides: c.sourceMapPathOverrides, remoteRoot: c.remoteRoot, localRoot: c.localRoot, + pathMapping: c.pathMapping, }; } @@ -47,6 +52,7 @@ export class NodeSourcePathResolver extends SourcePathResolverBase { protected readonly logger: ILogger, ) { super(options, logger); + this.hasPathMapping = Object.keys(this.options.pathMapping).length > 0; } /** @@ -65,6 +71,30 @@ export class NodeSourcePathResolver extends SourcePathResolverBase { return this.options; } + /** + * map remote path to local path and apply the pathMapping option + * + * @param scriptPath remote path + * @returns mapped path + */ + private async rebaseRemoteWithPathMapping(scriptPath: string): Promise { + const mapped = this.rebaseRemoteToLocal(scriptPath); + + if (this.hasPathMapping) { + const mapped2 = await defaultPathMappingResolver( + mapped, + this.options.pathMapping, + this.logger, + ); + + if (mapped2 && (await this.fsUtils.exists(mapped2))) { + return mapped2; + } + } + + return mapped; + } + /** * @override */ @@ -89,7 +119,7 @@ export class NodeSourcePathResolver extends SourcePathResolverBase { const absolutePath = urlUtils.fileUrlToAbsolutePath(url); if (absolutePath) { - return this.rebaseRemoteToLocal(absolutePath); + return await this.rebaseRemoteWithPathMapping(absolutePath); } // It's possible the source might be an HTTP if using the `sourceURL` @@ -111,7 +141,7 @@ export class NodeSourcePathResolver extends SourcePathResolverBase { } const withBase = properResolve(this.options.basePath ?? '', url); - return this.rebaseRemoteToLocal(withBase); + return await this.rebaseRemoteWithPathMapping(withBase); } /** @@ -175,6 +205,6 @@ export class NodeSourcePathResolver extends SourcePathResolverBase { ); } - return this.rebaseRemoteToLocal(url) || url; + return await this.rebaseRemoteWithPathMapping(url); } } diff --git a/src/test/node/node-source-path-resolver.test.ts b/src/test/node/node-source-path-resolver.test.ts index 79782f243..7f9007ef7 100644 --- a/src/test/node/node-source-path-resolver.test.ts +++ b/src/test/node/node-source-path-resolver.test.ts @@ -20,6 +20,7 @@ describe('node source path resolver', () => { remoteRoot: null, localRoot: null, sourceMapOverrides: { 'webpack:///*': `${__dirname}/*` }, + pathMapping: {}, }; it('resolves absolute', async () => { @@ -95,6 +96,35 @@ describe('node source path resolver', () => { ); }); + it('applies pathMapping option', async () => { + const r = new NodeSourcePathResolver( + { + realPath: fsUtils.realPath, + readFile: fsUtils.readFile, + exists: async p => p === '/lib/index.js', + }, + undefined, + { + ...defaultOptions, + pathMapping: { + '/src': '/lib', + }, + }, + Logger.null, + ); + + expect(await r.urlToAbsolutePath({ url: 'file:///src/index.js' })).to.equal('/lib/index.js'); + expect(await r.urlToAbsolutePath({ url: 'file:///src/not-exist.js' })).to.equal( + '/src/not-exist.js', + ); + expect(await r.urlToAbsolutePath({ url: 'file:///src2/index.js' })).to.equal( + '/src2/index.js', + ); + expect(await r.urlToAbsolutePath({ url: 'file:///test/src/index.js' })).to.equal( + '/test/src/index.js', + ); + }); + describe('source map filtering', () => { const testTable = { 'matches paths': { From b141468e84d418e56564a22a52603660f79ea71f Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 9 Jun 2021 18:47:22 -0700 Subject: [PATCH 2/3] merge some pathMapping options --- OPTIONS.md | 12 ++++++--- src/build/generate-contributions.ts | 15 +++++------ src/configuration.ts | 29 ++++++++++------------ src/targets/browser/browserPathResolver.ts | 2 -- src/targets/node/nodeSourcePathResolver.ts | 3 +-- src/targets/sourcePathResolver.ts | 2 ++ 6 files changed, 32 insertions(+), 31 deletions(-) diff --git a/OPTIONS.md b/OPTIONS.md index 6ef5ce2d1..45e1d3fe7 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -20,7 +20,8 @@ "${workspaceFolder}/**/*.js", "!**/node_modules/**" ]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

-
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
"console"

pathMapping

A mapping of folders from one path to another. To resolve scripts to their original locations. Typical use is to map scripts in node_modules to their sources that locate in another folder.

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

Default value:
false

port

Debug port to attach to. Default is 9229.

Default value:
9229

processId

ID of process to attach to.

Default value:
undefined

remoteRoot

Absolute path to the remote directory containing the program.

@@ -68,7 +69,8 @@ "${workspaceFolder}/**/*.js", "!**/node_modules/**" ]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

-
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
"console"

pathMapping

A mapping of folders from one path to another. To resolve scripts to their original locations. Typical use is to map scripts in node_modules to their sources that locate in another folder.

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

Default value:
false

profileStartup

If true, will start profiling as soon as the process launches

Default value:
false

program

Absolute path to the program. Generated value is guessed by looking at package.json and opened files. Edit this attribute.

Default value:
""

remoteRoot

Absolute path to the remote directory containing the program.

@@ -116,7 +118,8 @@ "${workspaceFolder}/**/*.js", "!**/node_modules/**" ]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

-
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
"console"

pathMapping

A mapping of folders from one path to another. To resolve scripts to their original locations. Typical use is to map scripts in node_modules to their sources that locate in another folder.

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

Default value:
false

remoteRoot

Absolute path to the remote directory containing the program.

Default value:
null

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

Default value:
[
@@ -162,7 +165,8 @@
 
Default value:
[
   "${workspaceFolder}/out/**/*.js"
 ]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

-
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
"console"

pathMapping

A mapping of folders from one path to another. To resolve scripts to their original locations. Typical use is to map scripts in node_modules to their sources that locate in another folder.

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

Default value:
false

remoteRoot

Absolute path to the remote directory containing the program.

Default value:
null

rendererDebugOptions

Chrome launch options used when attaching to the renderer process, with debugWebviews or debugWebWorkerHost.

Default value:
{}

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

diff --git a/src/build/generate-contributions.ts b/src/build/generate-contributions.ts index 0c10ce78d..1f3eea779 100644 --- a/src/build/generate-contributions.ts +++ b/src/build/generate-contributions.ts @@ -298,6 +298,12 @@ const baseConfigurationAttributes: ConfigurationAttributes = default: [], description: refString('base.cascadeTerminateToConfigurations.label'), }, + pathMapping: { + type: 'object', + additionalProperties: { type: 'string' }, + description: refString('browser.pathMapping.description'), + default: {}, + }, }; /** @@ -358,12 +364,8 @@ const nodeBaseConfigurationAttributes: ConfigurationAttributes !o.includes(token)) ?? null; } + if ('pathMapping' in cast) { + cast.pathMapping = filterValues(cast.pathMapping, v => v.includes(token)); + } + if ('cwd' in cast && cast.cwd?.includes(token)) { cast.cwd = undefined; } diff --git a/src/targets/browser/browserPathResolver.ts b/src/targets/browser/browserPathResolver.ts index ae30a407f..05311e6b6 100644 --- a/src/targets/browser/browserPathResolver.ts +++ b/src/targets/browser/browserPathResolver.ts @@ -23,12 +23,10 @@ import { import { IUrlResolution } from '../../common/sourcePathResolver'; import * as utils from '../../common/urlUtils'; import { urlToRegex } from '../../common/urlUtils'; -import { PathMapping } from '../../configuration'; import { ISourcePathResolverOptions, SourcePathResolverBase } from '../sourcePathResolver'; export interface IOptions extends ISourcePathResolverOptions { baseUrl?: string; - pathMapping: PathMapping; clientID: string | undefined; remoteFilePrefix: string | undefined; } diff --git a/src/targets/node/nodeSourcePathResolver.ts b/src/targets/node/nodeSourcePathResolver.ts index 144a0de57..6bebcc9db 100644 --- a/src/targets/node/nodeSourcePathResolver.ts +++ b/src/targets/node/nodeSourcePathResolver.ts @@ -16,13 +16,12 @@ import { } from '../../common/sourceMaps/sourceMapResolutionUtils'; import { IUrlResolution } from '../../common/sourcePathResolver'; import * as urlUtils from '../../common/urlUtils'; -import { AnyLaunchConfiguration, AnyNodeConfiguration, PathMapping } from '../../configuration'; +import { AnyLaunchConfiguration, AnyNodeConfiguration } from '../../configuration'; import { ILinkedBreakpointLocation } from '../../ui/linkedBreakpointLocation'; import { ISourcePathResolverOptions, SourcePathResolverBase } from '../sourcePathResolver'; interface IOptions extends ISourcePathResolverOptions { basePath?: string; - pathMapping: PathMapping; } const localNodeInternalsPrefix = 'node:'; diff --git a/src/targets/sourcePathResolver.ts b/src/targets/sourcePathResolver.ts index 120eeb351..8c8fa7a86 100644 --- a/src/targets/sourcePathResolver.ts +++ b/src/targets/sourcePathResolver.ts @@ -25,9 +25,11 @@ import { urlToRegex, } from '../common/urlUtils'; import { SourceMapOverrides } from './sourceMapOverrides'; +import { PathMapping } from '../configuration'; export interface ISourcePathResolverOptions { resolveSourceMapLocations: ReadonlyArray | null; + pathMapping: PathMapping; sourceMapOverrides: { [key: string]: string }; localRoot: string | null; remoteRoot: string | null; From b348e2b33aac690ced8cc3da35ea21ef7a907441 Mon Sep 17 00:00:00 2001 From: Joel Chen Date: Thu, 15 Jul 2021 16:05:33 -0700 Subject: [PATCH 3/3] fix breakpoints prediction for path mappings --- src/adapter/breakpointPredictor.ts | 86 ++++++++++++++++--- src/common/sourceMaps/codeSearchStrategy.ts | 38 +++++++- src/common/sourceMaps/mtimeCorrelatedCache.ts | 8 +- src/common/sourceMaps/nodeSearchStrategy.ts | 22 +++-- src/common/sourceMaps/sourceMapRepository.ts | 12 +++ src/test/browser/browserPathResolverTest.ts | 3 + 6 files changed, 145 insertions(+), 24 deletions(-) diff --git a/src/adapter/breakpointPredictor.ts b/src/adapter/breakpointPredictor.ts index f35f9b399..c2b9d21f2 100644 --- a/src/adapter/breakpointPredictor.ts +++ b/src/adapter/breakpointPredictor.ts @@ -16,7 +16,7 @@ import { ISearchStrategy } from '../common/sourceMaps/sourceMapRepository'; import { ISourcePathResolver } from '../common/sourcePathResolver'; import { getOptimalCompiledPosition } from '../common/sourceUtils'; import * as urlUtils from '../common/urlUtils'; -import { AnyLaunchConfiguration } from '../configuration'; +import { AnyLaunchConfiguration, PathMapping } from '../configuration'; import Dap from '../dap/api'; import { logPerf } from '../telemetry/performance'; @@ -26,7 +26,12 @@ export interface IWorkspaceLocation { columnNumber: number; // 1-based } -type DiscoveredMetadata = ISourceMapMetadata & { sourceUrl: string; resolvedPath: string }; +type DiscoveredMetadata = ISourceMapMetadata & { + sourceUrl: string; + resolvedPath: string; + // is the meta for source map or path map + type: 'source' | 'path'; +}; type MetadataMap = Map>; const longPredictionWarning = 10 * 1000; @@ -162,6 +167,7 @@ export class BreakpointsPredictor implements IBreakpointsPredictor { private readonly longParseEmitter = new EventEmitter(); private sourcePathToCompiled?: Promise; private cache?: CorrelatedCache; + private readonly pathMapping: PathMapping; /** * Event that fires if it takes a long time to predict sourcemaps. @@ -182,6 +188,8 @@ export class BreakpointsPredictor implements IBreakpointsPredictor { path.join(launchConfig.__workspaceCachePath, 'bp-predict.json'), ); } + + this.pathMapping = launchConfig.pathMapping; } private async createInitialMapping(): Promise { @@ -218,8 +226,9 @@ export class BreakpointsPredictor implements IBreakpointsPredictor { try { await this.repo.streamChildrenWithSourcemaps(this.outFiles, async metadata => { const cached = await this.cache?.lookup(metadata.compiledPath, metadata.mtime); + if (cached) { - cached.forEach(c => addDiscovery({ ...c, ...metadata })); + cached.forEach(c => addDiscovery({ ...c, ...metadata, type: 'source' })); return; } @@ -240,7 +249,12 @@ export class BreakpointsPredictor implements IBreakpointsPredictor { continue; } - const discovery = { ...metadata, resolvedPath, sourceUrl: url }; + const discovery: DiscoveredMetadata = { + ...metadata, + resolvedPath, + sourceUrl: url, + type: 'source', + }; discovered.push(discovery); addDiscovery(discovery); } @@ -255,6 +269,40 @@ export class BreakpointsPredictor implements IBreakpointsPredictor { this.logger.warn(LogTag.RuntimeException, 'Error reading sourcemaps from disk', { error }); } + // search for files that path mapping affects, and add break point predictions for them + // if they don't have source mapping data + if (this.pathMapping && Object.keys(this.pathMapping).length > 0) { + await this.repo.streamChildrenWithPathMaps( + this.pathMapping, + async metadata => { + const cached = await this.cache?.lookup(metadata.compiledPath, -1); + if (cached) { + cached.forEach(c => addDiscovery({ ...c, ...metadata, type: 'path' })); + return; + } + + const sourceMapped = await this.cache?.lookup(metadata.compiledPath); + + // If a file that has sourcemap meta info, then use that for breakpoint prediction, and not + // the path map info + if (!sourceMapped) { + const sourceUrl = path.relative(metadata.compiledPath, metadata.sourceMapUrl); + addDiscovery({ + compiledPath: metadata.compiledPath, + resolvedPath: metadata.sourceMapUrl, + sourceUrl, + sourceMapUrl: '', + type: 'path', + }); + } + }, + // search for normal .js files and node's ES6 module .mjs + // other extensions like .ts etc should have sourcemap info and should not + // be subjected to breakpoint prediction with path maps. + '**/*.{js,mjs}', + ); + } + clearTimeout(warnLongRuntime); return sourcePathToCompiled; } @@ -288,14 +336,20 @@ export class BreakpointsPredictor implements IBreakpointsPredictor { return; } - const sourceMaps = await Promise.all( - [...set].map(metadata => - this.sourceMapFactory - .load(metadata) - .then(map => ({ map, metadata })) - .catch(() => undefined), - ), - ); + const byPathMap = [...set].filter(s => s.type === 'path'); + + const bySourceMap = [...set].filter(s => s.type === 'source'); + const sourceMaps = + bySourceMap.length > 0 + ? await Promise.all( + bySourceMap.map(metadata => + this.sourceMapFactory + .load(metadata) + .then(map => ({ map, metadata })) + .catch(() => undefined), + ), + ) + : []; for (const b of params.breakpoints ?? []) { const key = `${params.source.path}:${b.line}:${b.column || 1}`; @@ -306,6 +360,14 @@ export class BreakpointsPredictor implements IBreakpointsPredictor { const locations: IWorkspaceLocation[] = []; this.predictedLocations.set(key, locations); + for (const pathMapMeta of byPathMap) { + locations.push({ + absolutePath: pathMapMeta.compiledPath, + lineNumber: b.line, + columnNumber: b.column || 1, + }); + } + for (const sourceMapLoad of sourceMaps) { if (!sourceMapLoad) { continue; diff --git a/src/common/sourceMaps/codeSearchStrategy.ts b/src/common/sourceMaps/codeSearchStrategy.ts index be48a3050..2f6d26bd5 100644 --- a/src/common/sourceMaps/codeSearchStrategy.ts +++ b/src/common/sourceMaps/codeSearchStrategy.ts @@ -2,14 +2,15 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import { injectable } from 'inversify'; import type * as vscodeType from 'vscode'; -import { LogTag, ILogger } from '../logging'; +import { PathMapping } from '../../configuration'; +import { FileGlobList } from '../fileGlobList'; +import { ILogger, LogTag } from '../logging'; import { forceForwardSlashes } from '../pathUtils'; import { NodeSearchStrategy } from './nodeSearchStrategy'; import { ISourceMapMetadata } from './sourceMap'; import { createMetadataForFile, ISearchStrategy } from './sourceMapRepository'; -import { injectable } from 'inversify'; -import { FileGlobList } from '../fileGlobList'; /** * A source map repository that uses VS Code's proposed search API to @@ -46,6 +47,37 @@ export class CodeSearchStrategy implements ISearchStrategy { return this.nodeStrategy.streamAllChildren(files, onChild); } + /** + * @inheritdoc + */ + public async streamChildrenWithPathMaps( + pathMapping: PathMapping, + onChild: (child: Required) => Promise, + pattern = '**', + ): Promise { + const todo: Promise[] = []; + + // process pathMapping config + const mappedPaths = Object.keys(pathMapping); + for (const path of mappedPaths) { + const files = await this.vscode.workspace.findFiles( + new this.vscode.RelativePattern(path, pattern), + ); + for (const file of files) { + const sourceMapUrl = file.path.replace(path, pathMapping[path]); + todo.push( + onChild({ + compiledPath: file.path, + mtime: -1, + sourceMapUrl, + }), + ); + } + } + + return (await Promise.all(todo)).filter((t): t is T => t !== undefined); + } + /** * @inheritdoc */ diff --git a/src/common/sourceMaps/mtimeCorrelatedCache.ts b/src/common/sourceMaps/mtimeCorrelatedCache.ts index 782efc42c..603e8a2e0 100644 --- a/src/common/sourceMaps/mtimeCorrelatedCache.ts +++ b/src/common/sourceMaps/mtimeCorrelatedCache.ts @@ -3,9 +3,9 @@ *--------------------------------------------------------*/ import { mkdirSync } from 'fs'; import { dirname } from 'path'; -import { debounce } from '../objUtils'; import { IDisposable } from '../disposable'; import { readfile, writeFile } from '../fsUtils'; +import { debounce } from '../objUtils'; export class CorrelatedCache implements IDisposable { private cacheData?: Promise<{ [key: string]: { correlation: C; value: V } }>; @@ -26,10 +26,12 @@ export class CorrelatedCache implements IDisposable { /** * Gets the value from the map if it exists and the correlation matches. */ - public async lookup(key: string, correlation: C): Promise { + public async lookup(key: string, correlation?: C): Promise { const data = await this.getData(); const entry = data[key]; - return entry && entry.correlation === correlation ? entry.value : undefined; + return entry && (correlation === undefined || entry.correlation === correlation) + ? entry.value + : undefined; } /** diff --git a/src/common/sourceMaps/nodeSearchStrategy.ts b/src/common/sourceMaps/nodeSearchStrategy.ts index 54bd8710c..fe2a2c58c 100644 --- a/src/common/sourceMaps/nodeSearchStrategy.ts +++ b/src/common/sourceMaps/nodeSearchStrategy.ts @@ -2,14 +2,14 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import { ISourceMapMetadata } from './sourceMap'; -import { ISearchStrategy, createMetadataForFile } from './sourceMapRepository'; import globStream from 'glob-stream'; -import { LogTag, ILogger } from '../logging'; -import { forceForwardSlashes, fixDriveLetterAndSlashes } from '../pathUtils'; -import { injectable, inject } from 'inversify'; +import { inject, injectable } from 'inversify'; +import { PathMapping } from '../../configuration'; import { FileGlobList } from '../fileGlobList'; - +import { ILogger, LogTag } from '../logging'; +import { fixDriveLetterAndSlashes, forceForwardSlashes } from '../pathUtils'; +import { ISourceMapMetadata } from './sourceMap'; +import { createMetadataForFile, ISearchStrategy } from './sourceMapRepository'; /** * A source map repository that uses globbing to find candidate files. */ @@ -38,6 +38,16 @@ export class NodeSearchStrategy implements ISearchStrategy { return (await Promise.all(todo)).filter((t): t is T => t !== undefined); } + /** + * @inheritdoc + */ + public async streamChildrenWithPathMaps( + _pathMapping: PathMapping, + _onChild: (child: Required) => T | Promise, + ): Promise { + return Promise.resolve([]); + } + /** * @inheritdoc */ diff --git a/src/common/sourceMaps/sourceMapRepository.ts b/src/common/sourceMaps/sourceMapRepository.ts index cfd0b3a03..e5c1a0a96 100644 --- a/src/common/sourceMaps/sourceMapRepository.ts +++ b/src/common/sourceMaps/sourceMapRepository.ts @@ -2,6 +2,7 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import { PathMapping } from '../../configuration'; import { FileGlobList } from '../fileGlobList'; import { readfile, stat } from '../fsUtils'; import { parseSourceMappingUrl } from '../sourceUtils'; @@ -29,6 +30,17 @@ export interface ISearchStrategy { onChild: (child: Required) => T | Promise, ): Promise; + /** + * Recursively finds all children that match the pathMap setting, calling + * `onChild` when children are found and returning promise that resolves + * once all children have been discovered. + */ + streamChildrenWithPathMaps( + pathMapping: PathMapping, + onChild: (child: Required) => T | Promise, + pattern?: string, + ): Promise; + /** * Recursively finds all children, calling `onChild` when children are found * and returning promise that resolves once all children have been discovered. diff --git a/src/test/browser/browserPathResolverTest.ts b/src/test/browser/browserPathResolverTest.ts index 856fb1127..3f3016259 100644 --- a/src/test/browser/browserPathResolverTest.ts +++ b/src/test/browser/browserPathResolverTest.ts @@ -99,6 +99,9 @@ describe('browserPathResolver.urlToAbsolutePath', () => { streamChildrenWithSourcemaps() { return Promise.resolve([]); }, + streamChildrenWithPathMaps() { + return Promise.resolve([]); + }, streamAllChildren(_files, onChild) { return Promise.all([ onChild(path.join(testFixturesDir, 'web', 'a.vue')),