mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-24 02:28:34 +01:00
@@ -19,16 +19,20 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { platform } from 'vs/base/common/process';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
|
||||
|
||||
interface LoadFunction {
|
||||
(request: string): any;
|
||||
}
|
||||
|
||||
interface INodeModuleFactory {
|
||||
interface IAlternativeModuleProvider {
|
||||
alternativeModuleName(name: string): string | undefined;
|
||||
}
|
||||
|
||||
interface INodeModuleFactory extends Partial<IAlternativeModuleProvider> {
|
||||
readonly nodeModuleName: string | string[];
|
||||
load(request: string, parent: URI, original: LoadFunction): any;
|
||||
alternativeModuleName?(name: string): string | undefined;
|
||||
}
|
||||
|
||||
export abstract class RequireInterceptor {
|
||||
@@ -60,6 +64,7 @@ export abstract class RequireInterceptor {
|
||||
|
||||
this.register(new VSCodeNodeModuleFactory(this._apiFactory, extensionPaths, this._extensionRegistry, configProvider, this._logService));
|
||||
this.register(this._instaService.createInstance(KeytarNodeModuleFactory, extensionPaths));
|
||||
this.register(this._instaService.createInstance(NodeModuleAliasingModuleFactory));
|
||||
if (this._initData.remote.isRemote) {
|
||||
this.register(this._instaService.createInstance(OpenNodeModuleFactory, extensionPaths, this._initData.environment.appUriScheme));
|
||||
}
|
||||
@@ -67,14 +72,17 @@ export abstract class RequireInterceptor {
|
||||
|
||||
protected abstract _installInterceptor(): void;
|
||||
|
||||
public register(interceptor: INodeModuleFactory): void {
|
||||
if (Array.isArray(interceptor.nodeModuleName)) {
|
||||
for (let moduleName of interceptor.nodeModuleName) {
|
||||
this._factories.set(moduleName, interceptor);
|
||||
public register(interceptor: INodeModuleFactory | IAlternativeModuleProvider): void {
|
||||
if ('nodeModuleName' in interceptor) {
|
||||
if (Array.isArray(interceptor.nodeModuleName)) {
|
||||
for (let moduleName of interceptor.nodeModuleName) {
|
||||
this._factories.set(moduleName, interceptor);
|
||||
}
|
||||
} else {
|
||||
this._factories.set(interceptor.nodeModuleName, interceptor);
|
||||
}
|
||||
} else {
|
||||
this._factories.set(interceptor.nodeModuleName, interceptor);
|
||||
}
|
||||
|
||||
if (typeof interceptor.alternativeModuleName === 'function') {
|
||||
this._alternatives.push((moduleName) => {
|
||||
return interceptor.alternativeModuleName!(moduleName);
|
||||
@@ -83,6 +91,60 @@ export abstract class RequireInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
//#region --- module renames
|
||||
|
||||
class NodeModuleAliasingModuleFactory implements IAlternativeModuleProvider {
|
||||
/**
|
||||
* Map of aliased internal node_modules, used to allow for modules to be
|
||||
* renamed without breaking extensions. In the form "original -> new name".
|
||||
*/
|
||||
private static readonly aliased: ReadonlyMap<string, string> = new Map([
|
||||
['vscode-ripgrep', '@vscode/ripgrep'],
|
||||
]);
|
||||
|
||||
private readonly re?: RegExp;
|
||||
|
||||
constructor(@IExtHostInitDataService initData: IExtHostInitDataService) {
|
||||
if (initData.environment.appRoot && NodeModuleAliasingModuleFactory.aliased.size) {
|
||||
const root = escapeRegExpCharacters(this.forceForwardSlashes(initData.environment.appRoot.fsPath));
|
||||
// decompose ${appRoot}/node_modules/foo/bin to ['${appRoot}/node_modules/', 'foo', '/bin'],
|
||||
// and likewise the more complex form ${appRoot}/node_modules.asar.unpacked/@vcode/foo/bin
|
||||
// to ['${appRoot}/node_modules.asar.unpacked/',' @vscode/foo', '/bin'].
|
||||
const npmIdChrs = `[a-z0-9_.-]`;
|
||||
const npmModuleName = `@${npmIdChrs}+\\/${npmIdChrs}+|${npmIdChrs}+`;
|
||||
const moduleFolders = 'node_modules|node_modules\\.asar(?:\\.unpacked)?';
|
||||
this.re = new RegExp(`^(${root}/${moduleFolders}\\/)(${npmModuleName})(.*)$`, 'i');
|
||||
}
|
||||
}
|
||||
|
||||
public alternativeModuleName(name: string): string | undefined {
|
||||
if (!this.re) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = this.re.exec(this.forceForwardSlashes(name));
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [, prefix, moduleName, suffix] = result;
|
||||
const dealiased = NodeModuleAliasingModuleFactory.aliased.get(moduleName);
|
||||
if (dealiased === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(`${moduleName} as been renamed to ${dealiased}, please update your imports`);
|
||||
|
||||
return prefix + dealiased + suffix;
|
||||
}
|
||||
|
||||
private forceForwardSlashes(str: string) {
|
||||
return str.replace(/\\/g, '/');
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region --- vscode-module
|
||||
|
||||
class VSCodeNodeModuleFactory implements INodeModuleFactory {
|
||||
|
||||
@@ -23,8 +23,25 @@ class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
protected _installInterceptor(): void {
|
||||
const that = this;
|
||||
const node_module = <any>require.__$__nodeRequire('module');
|
||||
const original = node_module._load;
|
||||
const originalLoad = node_module._load;
|
||||
node_module._load = function load(request: string, parent: { filename: string; }, isMain: boolean) {
|
||||
request = applyAlternatives(request);
|
||||
if (!that._factories.has(request)) {
|
||||
return originalLoad.apply(this, arguments);
|
||||
}
|
||||
return that._factories.get(request)!.load(
|
||||
request,
|
||||
URI.file(realpathSync(parent.filename)),
|
||||
request => originalLoad.apply(this, [request, parent, isMain])
|
||||
);
|
||||
};
|
||||
|
||||
const originalLookup = node_module._resolveLookupPaths;
|
||||
node_module._resolveLookupPaths = (request: string, parent: unknown) => {
|
||||
return originalLookup.call(this, applyAlternatives(request), parent);
|
||||
};
|
||||
|
||||
const applyAlternatives = (request: string) => {
|
||||
for (let alternativeModuleName of that._alternatives) {
|
||||
let alternative = alternativeModuleName(request);
|
||||
if (alternative) {
|
||||
@@ -32,14 +49,7 @@ class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!that._factories.has(request)) {
|
||||
return original.apply(this, arguments);
|
||||
}
|
||||
return that._factories.get(request)!.load(
|
||||
request,
|
||||
URI.file(realpathSync(parent.filename)),
|
||||
request => original.apply(this, [request, parent, isMain])
|
||||
);
|
||||
return request;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user