@midwayjs/mock 4.0.3 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/creator.js +17 -4
- package/dist/interface.d.ts +8 -0
- package/dist/sourceLoader.d.ts +3 -0
- package/dist/sourceLoader.js +232 -0
- package/package.json +4 -4
package/dist/creator.js
CHANGED
|
@@ -15,6 +15,7 @@ const fs_1 = require("fs");
|
|
|
15
15
|
const yaml = require("js-yaml");
|
|
16
16
|
const getRawBody = require("raw-body");
|
|
17
17
|
const functional_1 = require("@midwayjs/core/functional");
|
|
18
|
+
const sourceLoader_1 = require("./sourceLoader");
|
|
18
19
|
const debug = (0, util_1.debuglog)('midway:debug');
|
|
19
20
|
process.setMaxListeners(0);
|
|
20
21
|
function formatPath(baseDir, p) {
|
|
@@ -128,18 +129,24 @@ async function create(appDir, options = {}) {
|
|
|
128
129
|
});
|
|
129
130
|
options.moduleLoadType = pkgJSON?.type === 'module' ? 'esm' : 'commonjs';
|
|
130
131
|
}
|
|
132
|
+
if (!options.moduleLoader &&
|
|
133
|
+
options.moduleLoadType === 'esm' &&
|
|
134
|
+
(0, core_1.isTypeScriptEnvironment)()) {
|
|
135
|
+
options.moduleLoader = (0, sourceLoader_1.createSourceModuleLoader)();
|
|
136
|
+
}
|
|
137
|
+
const moduleLoader = options.moduleLoader ?? core_1.loadModule;
|
|
131
138
|
if (options.baseDir) {
|
|
132
139
|
if (!(0, path_1.isAbsolute)(options.baseDir)) {
|
|
133
140
|
options.baseDir = (0, path_1.join)(appDir, options.baseDir);
|
|
134
141
|
}
|
|
135
|
-
await (
|
|
142
|
+
await moduleLoader((0, path_1.join)(`${options.baseDir}`, getFileNameWithSuffix('interface')), {
|
|
136
143
|
safeLoad: true,
|
|
137
144
|
loadMode: options.moduleLoadType,
|
|
138
145
|
});
|
|
139
146
|
}
|
|
140
147
|
else if (appDir) {
|
|
141
148
|
options.baseDir = (0, path_1.join)(appDir, 'src');
|
|
142
|
-
await (
|
|
149
|
+
await moduleLoader((0, path_1.join)(`${options.baseDir}`, getFileNameWithSuffix('interface')), {
|
|
143
150
|
safeLoad: true,
|
|
144
151
|
loadMode: options.moduleLoadType,
|
|
145
152
|
});
|
|
@@ -292,15 +299,21 @@ async function createFunctionApp(baseDir, options = {}, customFrameworkModule) {
|
|
|
292
299
|
enableCache: false,
|
|
293
300
|
});
|
|
294
301
|
options.moduleLoadType = pkgJSON?.type === 'module' ? 'esm' : 'commonjs';
|
|
302
|
+
if (!options.moduleLoader &&
|
|
303
|
+
options.moduleLoadType === 'esm' &&
|
|
304
|
+
(0, core_1.isTypeScriptEnvironment)()) {
|
|
305
|
+
options.moduleLoader = (0, sourceLoader_1.createSourceModuleLoader)();
|
|
306
|
+
}
|
|
307
|
+
const moduleLoader = options.moduleLoader ?? core_1.loadModule;
|
|
295
308
|
if (options.baseDir) {
|
|
296
|
-
await (
|
|
309
|
+
await moduleLoader((0, path_1.join)(`${options.baseDir}`, getFileNameWithSuffix('interface')), {
|
|
297
310
|
safeLoad: true,
|
|
298
311
|
loadMode: options.moduleLoadType,
|
|
299
312
|
});
|
|
300
313
|
}
|
|
301
314
|
else if (options.appDir) {
|
|
302
315
|
options.baseDir = `${options.appDir}/src`;
|
|
303
|
-
await (
|
|
316
|
+
await moduleLoader((0, path_1.join)(`${options.baseDir}`, getFileNameWithSuffix('interface')), {
|
|
304
317
|
safeLoad: true,
|
|
305
318
|
loadMode: options.moduleLoadType,
|
|
306
319
|
});
|
package/dist/interface.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ export interface MockBootstrapOptions extends IMidwayBootstrapOptions, ILifeCycl
|
|
|
8
8
|
entryFile?: string;
|
|
9
9
|
bootstrapMode?: 'faas' | 'app';
|
|
10
10
|
initializeMethodName?: string;
|
|
11
|
+
moduleLoader?: (p: string, options?: {
|
|
12
|
+
enableCache?: boolean;
|
|
13
|
+
loadMode?: 'commonjs' | 'esm';
|
|
14
|
+
safeLoad?: boolean;
|
|
15
|
+
warnOnLoadError?: boolean;
|
|
16
|
+
extraModuleRoot?: string[];
|
|
17
|
+
importQuery?: string;
|
|
18
|
+
}) => Promise<any>;
|
|
11
19
|
}
|
|
12
20
|
export type ComponentModule = {
|
|
13
21
|
Configuration: new () => any;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSourceModuleLoader = createSourceModuleLoader;
|
|
4
|
+
const core_1 = require("@midwayjs/core");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const util_1 = require("util");
|
|
8
|
+
const url_1 = require("url");
|
|
9
|
+
const crypto = require("crypto");
|
|
10
|
+
const debug = (0, util_1.debuglog)('midway:debug');
|
|
11
|
+
let cachedTypeScriptCompiler;
|
|
12
|
+
function resolveRelativeEsmSpecifierPath(importerFile, specifier) {
|
|
13
|
+
if (!specifier ||
|
|
14
|
+
(!specifier.startsWith('./') && !specifier.startsWith('../'))) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const absolute = (0, path_1.resolve)((0, path_1.dirname)(importerFile), specifier);
|
|
18
|
+
const candidates = [absolute];
|
|
19
|
+
if (/\.(mjs|cjs|js)$/i.test(specifier)) {
|
|
20
|
+
candidates.push(absolute.replace(/\.(mjs|cjs|js)$/i, '.mts'), absolute.replace(/\.(mjs|cjs|js)$/i, '.cts'), absolute.replace(/\.(mjs|cjs|js)$/i, '.ts'), absolute.replace(/\.(mjs|cjs|js)$/i, '.tsx'));
|
|
21
|
+
}
|
|
22
|
+
else if (!/\.[a-z0-9]+$/i.test(specifier)) {
|
|
23
|
+
candidates.push(`${absolute}.mts`, `${absolute}.cts`, `${absolute}.ts`, `${absolute}.tsx`, `${absolute}.mjs`, `${absolute}.cjs`, `${absolute}.js`, `${absolute}.json`, (0, path_1.join)(absolute, 'index.mts'), (0, path_1.join)(absolute, 'index.cts'), (0, path_1.join)(absolute, 'index.ts'), (0, path_1.join)(absolute, 'index.tsx'), (0, path_1.join)(absolute, 'index.mjs'), (0, path_1.join)(absolute, 'index.cjs'), (0, path_1.join)(absolute, 'index.js'), (0, path_1.join)(absolute, 'index.json'));
|
|
24
|
+
}
|
|
25
|
+
for (const item of candidates) {
|
|
26
|
+
if ((0, fs_1.existsSync)(item)) {
|
|
27
|
+
return item;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function shouldUseEsmSourceFallback(originErr, filePath, rewritten, source) {
|
|
33
|
+
if (rewritten !== source) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (!/\.(mts|cts|ts|tsx)$/i.test(filePath)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return (originErr?.code === 'ERR_UNKNOWN_FILE_EXTENSION' ||
|
|
40
|
+
originErr instanceof SyntaxError ||
|
|
41
|
+
originErr?.name === 'SyntaxError');
|
|
42
|
+
}
|
|
43
|
+
function formatFallbackImportSpecifier(fromFile, toFile) {
|
|
44
|
+
let specifier = (0, path_1.relative)((0, path_1.dirname)(fromFile), toFile).split(path_1.sep).join('/');
|
|
45
|
+
if (!specifier.startsWith('.')) {
|
|
46
|
+
specifier = `./${specifier}`;
|
|
47
|
+
}
|
|
48
|
+
return specifier;
|
|
49
|
+
}
|
|
50
|
+
function loadTypeScriptCompiler(sourceFile) {
|
|
51
|
+
if (cachedTypeScriptCompiler) {
|
|
52
|
+
return cachedTypeScriptCompiler;
|
|
53
|
+
}
|
|
54
|
+
const searchPaths = [(0, path_1.dirname)(sourceFile), process.cwd(), __dirname];
|
|
55
|
+
for (const item of searchPaths) {
|
|
56
|
+
try {
|
|
57
|
+
cachedTypeScriptCompiler = require(require.resolve('typescript', {
|
|
58
|
+
paths: [item],
|
|
59
|
+
}));
|
|
60
|
+
return cachedTypeScriptCompiler;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// try next path
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function createCompiledEsmFallbackGraph(entryFile) {
|
|
68
|
+
const tempDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, path_1.dirname)(entryFile), '.midway-esm-fallback-'));
|
|
69
|
+
const compiledFileMap = new Map();
|
|
70
|
+
const tsCompiler = loadTypeScriptCompiler(entryFile);
|
|
71
|
+
const compileFile = (sourceFile) => {
|
|
72
|
+
const existed = compiledFileMap.get(sourceFile);
|
|
73
|
+
if (existed) {
|
|
74
|
+
return existed;
|
|
75
|
+
}
|
|
76
|
+
const compiledFile = (0, path_1.join)(tempDir, `${crypto.createHash('sha1').update(sourceFile).digest('hex')}.mjs`);
|
|
77
|
+
compiledFileMap.set(sourceFile, compiledFile);
|
|
78
|
+
if (sourceFile.endsWith('.json')) {
|
|
79
|
+
const jsonSource = (0, fs_1.readFileSync)(sourceFile, { encoding: 'utf-8' });
|
|
80
|
+
(0, fs_1.writeFileSync)(compiledFile, `export default ${jsonSource};`, {
|
|
81
|
+
encoding: 'utf-8',
|
|
82
|
+
});
|
|
83
|
+
return compiledFile;
|
|
84
|
+
}
|
|
85
|
+
const source = (0, fs_1.readFileSync)(sourceFile, { encoding: 'utf-8' });
|
|
86
|
+
const rewriteByPattern = (pattern, input) => {
|
|
87
|
+
return input.replace(pattern, (full, head, spec, tail) => {
|
|
88
|
+
const resolved = resolveRelativeEsmSpecifierPath(sourceFile, spec);
|
|
89
|
+
if (!resolved) {
|
|
90
|
+
return full;
|
|
91
|
+
}
|
|
92
|
+
const compiledDependency = compileFile(resolved);
|
|
93
|
+
const fallbackSpecifier = formatFallbackImportSpecifier(compiledFile, compiledDependency);
|
|
94
|
+
return `${head}${fallbackSpecifier}${tail}`;
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
let rewritten = source;
|
|
98
|
+
rewritten = rewriteByPattern(/(from\s+['"])([^'"]+)(['"])/g, rewritten);
|
|
99
|
+
rewritten = rewriteByPattern(/(import\s*\(\s*['"])([^'"]+)(['"]\s*\))/g, rewritten);
|
|
100
|
+
let output = rewritten;
|
|
101
|
+
if (/\.(mts|cts|ts|tsx)$/i.test(sourceFile)) {
|
|
102
|
+
if (!tsCompiler) {
|
|
103
|
+
throw new Error(`[mock]: can not transpile esm typescript file "${sourceFile}", please install "typescript" in current project`);
|
|
104
|
+
}
|
|
105
|
+
output = tsCompiler.transpileModule(rewritten, {
|
|
106
|
+
fileName: sourceFile,
|
|
107
|
+
compilerOptions: {
|
|
108
|
+
module: tsCompiler.ModuleKind.ESNext,
|
|
109
|
+
target: tsCompiler.ScriptTarget.ES2020,
|
|
110
|
+
moduleResolution: tsCompiler.ModuleResolutionKind.NodeNext,
|
|
111
|
+
esModuleInterop: true,
|
|
112
|
+
allowSyntheticDefaultImports: true,
|
|
113
|
+
resolveJsonModule: true,
|
|
114
|
+
experimentalDecorators: true,
|
|
115
|
+
emitDecoratorMetadata: true,
|
|
116
|
+
useDefineForClassFields: false,
|
|
117
|
+
jsx: tsCompiler.JsxEmit.ReactJSX,
|
|
118
|
+
},
|
|
119
|
+
}).outputText;
|
|
120
|
+
}
|
|
121
|
+
(0, fs_1.writeFileSync)(compiledFile, output, { encoding: 'utf-8' });
|
|
122
|
+
return compiledFile;
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
entryFile: compileFile(entryFile),
|
|
126
|
+
cleanup() {
|
|
127
|
+
(0, fs_1.rmSync)(tempDir, {
|
|
128
|
+
recursive: true,
|
|
129
|
+
force: true,
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function rewriteRelativeEsmSource(importerFile, source) {
|
|
135
|
+
let changed = false;
|
|
136
|
+
const rewriteByPattern = (pattern, input) => {
|
|
137
|
+
return input.replace(pattern, (full, head, spec, tail) => {
|
|
138
|
+
const resolved = resolveRelativeEsmSpecifierPath(importerFile, spec);
|
|
139
|
+
if (!resolved) {
|
|
140
|
+
return full;
|
|
141
|
+
}
|
|
142
|
+
let fallback = (0, path_1.relative)((0, path_1.dirname)(importerFile), resolved)
|
|
143
|
+
.split(path_1.sep)
|
|
144
|
+
.join('/');
|
|
145
|
+
if (!fallback.startsWith('.')) {
|
|
146
|
+
fallback = `./${fallback}`;
|
|
147
|
+
}
|
|
148
|
+
if (fallback === spec) {
|
|
149
|
+
return full;
|
|
150
|
+
}
|
|
151
|
+
changed = true;
|
|
152
|
+
return `${head}${fallback}${tail}`;
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
let output = source;
|
|
156
|
+
output = rewriteByPattern(/(from\s+['"])([^'"]+)(['"])/g, output);
|
|
157
|
+
output = rewriteByPattern(/(import\s*\(\s*['"])([^'"]+)(['"]\s*\))/g, output);
|
|
158
|
+
return changed ? output : source;
|
|
159
|
+
}
|
|
160
|
+
async function importWithSpecifierFallback(p, importQuery) {
|
|
161
|
+
const fileUrl = (0, url_1.pathToFileURL)(p);
|
|
162
|
+
if (importQuery) {
|
|
163
|
+
fileUrl.searchParams.set('mwImportQuery', importQuery);
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
return await import(fileUrl.href);
|
|
167
|
+
}
|
|
168
|
+
catch (originErr) {
|
|
169
|
+
const source = (0, fs_1.readFileSync)(p, { encoding: 'utf-8' });
|
|
170
|
+
const rewritten = rewriteRelativeEsmSource(p, source);
|
|
171
|
+
if (!shouldUseEsmSourceFallback(originErr, p, rewritten, source)) {
|
|
172
|
+
throw originErr;
|
|
173
|
+
}
|
|
174
|
+
const fallbackGraph = createCompiledEsmFallbackGraph(p);
|
|
175
|
+
try {
|
|
176
|
+
const fallbackUrl = (0, url_1.pathToFileURL)(fallbackGraph.entryFile);
|
|
177
|
+
if (importQuery) {
|
|
178
|
+
fallbackUrl.searchParams.set('mwImportQuery', importQuery);
|
|
179
|
+
}
|
|
180
|
+
return await import(fallbackUrl.href);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
fallbackGraph.cleanup();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function createSourceModuleLoader(baseLoader = core_1.loadModule) {
|
|
188
|
+
return async (p, options = {}) => {
|
|
189
|
+
options.enableCache = options.enableCache ?? true;
|
|
190
|
+
options.safeLoad = options.safeLoad ?? false;
|
|
191
|
+
options.loadMode = options.loadMode ?? 'commonjs';
|
|
192
|
+
if (p.startsWith(`.${path_1.sep}`) || p.startsWith(`..${path_1.sep}`)) {
|
|
193
|
+
p = (0, path_1.resolve)((0, path_1.dirname)(module.parent.filename), p);
|
|
194
|
+
}
|
|
195
|
+
debug(`[mock]: source load module ${p}, cache: ${options.enableCache}, mode: ${options.loadMode}, safeLoad: ${options.safeLoad}`);
|
|
196
|
+
try {
|
|
197
|
+
if (options.enableCache &&
|
|
198
|
+
options.loadMode === 'esm' &&
|
|
199
|
+
!p.endsWith('.json')) {
|
|
200
|
+
try {
|
|
201
|
+
return await baseLoader(p, {
|
|
202
|
+
...options,
|
|
203
|
+
safeLoad: false,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Mock dev mode loads TS source files directly. This fallback stays in
|
|
208
|
+
// mock on purpose so core.loadModule() can remain a plain loader.
|
|
209
|
+
return await importWithSpecifierFallback(p, options.importQuery);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return await baseLoader(p, {
|
|
213
|
+
...options,
|
|
214
|
+
safeLoad: false,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
if (!options.safeLoad) {
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
if (options.warnOnLoadError &&
|
|
222
|
+
err.code !== 'MODULE_NOT_FOUND' &&
|
|
223
|
+
err.code !== 'ERR_MODULE_NOT_FOUND' &&
|
|
224
|
+
err.code !== 'ENOENT') {
|
|
225
|
+
console.warn(err);
|
|
226
|
+
}
|
|
227
|
+
debug(`[mock]: SafeLoadModule Warning\n\n${err.message}\n`);
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=sourceLoader.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midwayjs/mock",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "create your test app from midway framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@midwayjs/core": "^4.0
|
|
53
|
+
"@midwayjs/core": "^4.1.0",
|
|
54
54
|
"@midwayjs/logger": "^4.0.0",
|
|
55
55
|
"@types/amqplib": "0.10.8",
|
|
56
56
|
"amqplib": "0.10.9",
|
|
@@ -64,12 +64,12 @@
|
|
|
64
64
|
"@types/supertest": "2.0.16",
|
|
65
65
|
"js-yaml": "4.1.1",
|
|
66
66
|
"raw-body": "2.5.2",
|
|
67
|
-
"supertest": "6.3.
|
|
67
|
+
"supertest": "6.3.4"
|
|
68
68
|
},
|
|
69
69
|
"author": "Harry Chen <czy88840616@gmail.com>",
|
|
70
70
|
"repository": {
|
|
71
71
|
"type": "git",
|
|
72
72
|
"url": "https://github.com/midwayjs/midway.git"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "0e6259f761c64b844c4dcab49372c64902fbd7d8"
|
|
75
75
|
}
|