-
-
Notifications
You must be signed in to change notification settings - Fork 257
Expand file tree
/
Copy pathcjs.ts
More file actions
129 lines (115 loc) · 3.68 KB
/
cjs.ts
File metadata and controls
129 lines (115 loc) · 3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import path from 'node:path';
import { require } from '../../helpers';
import { BasicRunner } from './basic';
import type {
BasicGlobalContext,
BasicModuleScope,
BasicRunnerFile,
ModuleObject,
RunnerRequirer,
} from './type';
const define = (...args: unknown[]) => {
const factory = args.pop() as () => void;
factory();
};
export class CommonJsRunner extends BasicRunner {
protected createGlobalContext(): BasicGlobalContext {
return {
console: console,
setTimeout: ((...args: Parameters<typeof setTimeout>) => {
const timeout = setTimeout(...args);
timeout.unref();
return timeout;
}) as typeof setTimeout,
clearTimeout: clearTimeout,
queueMicrotask,
};
}
protected createBaseModuleScope(): BasicModuleScope {
const baseModuleScope: BasicModuleScope = {
console: this.globalContext!.console,
setTimeout: this.globalContext!.setTimeout,
clearTimeout: this.globalContext!.clearTimeout,
nsObj: (m: Record<string, unknown>) => {
Object.defineProperty(m, Symbol.toStringTag, {
value: 'Module',
});
return m;
},
queueMicrotask,
};
return baseModuleScope;
}
protected createModuleScope(
requireFn: RunnerRequirer,
m: ModuleObject,
file: BasicRunnerFile,
): BasicModuleScope {
return {
...this.baseModuleScope!,
require: requireFn.bind(null, path.dirname(file.path)),
module: m,
exports: m.exports,
__dirname: path.dirname(file.path),
__filename: file.path,
define,
};
}
protected createRunner(): void {
this.requirers.set('miss', this.createMissRequirer());
this.requirers.set('entry', this.createCjsRequirer());
}
protected createMissRequirer(): RunnerRequirer {
return (_currentDirectory, modulePath, _context = {}) => {
const modulePathStr = modulePath as string;
const resolvedPath = require.resolve(modulePathStr, {
paths: [_currentDirectory],
});
// rslint-disable-next-line @typescript-eslint/no-require-imports
return require(
resolvedPath.startsWith('node:') ? resolvedPath.slice(5) : resolvedPath,
);
};
}
protected createCjsRequirer(): RunnerRequirer {
const requireCache: Record<string, ModuleObject> = Object.create(null);
// rslint-disable-next-line @typescript-eslint/no-require-imports
const vm = require('node:vm') as typeof import('node:vm');
return (currentDirectory, modulePath, context = {}) => {
const file = context.file || this.getFile(modulePath, currentDirectory);
if (!file) {
return this.requirers.get('miss')!(currentDirectory, modulePath);
}
if (file.path in requireCache) {
return requireCache[file.path].exports;
}
const m = {
exports: {},
};
requireCache[file.path] = m;
const currentModuleScope = this.createModuleScope(
this.getRequire(),
m,
file,
);
const args = Object.keys(currentModuleScope);
const argValues = args.map((arg) => currentModuleScope[arg]);
this.preExecute(file.content, file);
const dynamicImport = new Function(
'specifier',
'return import(specifier)',
);
const fn = vm.compileFunction(file.content, args, {
filename: file.path,
// Specify how the modules should be loaded during the evaluation of this script when `import()` is called.
importModuleDynamically: async (specifier) => {
const result = await dynamicImport(specifier);
return result;
},
});
fn.call(m.exports, ...argValues);
this.postExecute(m, file);
return m.exports;
};
}
}