@bleedingdev/modern-js-server-utils 3.8.2-ultramodern.9 → 3.8.3-ultramodern.2
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/cjs/compilers/typescript/importRewriter.js +102 -194
- package/dist/cjs/compilers/typescript/index.js +15 -11
- package/dist/cjs/compilers/typescript/tsconfigPathsPlugin.js +37 -8
- package/dist/esm/compilers/typescript/importRewriter.mjs +97 -192
- package/dist/esm/compilers/typescript/index.mjs +16 -12
- package/dist/esm/compilers/typescript/tsconfigPathsPlugin.mjs +37 -8
- package/dist/esm-node/compilers/typescript/importRewriter.mjs +97 -192
- package/dist/esm-node/compilers/typescript/index.mjs +16 -12
- package/dist/esm-node/compilers/typescript/tsconfigPathsPlugin.mjs +37 -8
- package/dist/types/compilers/typescript/importRewriter.d.ts +2 -7
- package/package.json +5 -4
|
@@ -1,203 +1,108 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
'typeof',
|
|
6
|
-
'instanceof',
|
|
7
|
-
'in',
|
|
8
|
-
'of',
|
|
9
|
-
'new',
|
|
10
|
-
'delete',
|
|
11
|
-
'void',
|
|
12
|
-
'throw',
|
|
13
|
-
'case',
|
|
14
|
-
'do',
|
|
15
|
-
'else',
|
|
16
|
-
'yield',
|
|
17
|
-
'await'
|
|
18
|
-
]);
|
|
19
|
-
const PUNCTUATORS_BEFORE_REGEX = new Set('{}(,;[=:?!&|+-*%^~<>/');
|
|
20
|
-
const isRegexAllowed = (lastToken)=>{
|
|
21
|
-
if (!lastToken) return true;
|
|
22
|
-
if (KEYWORDS_BEFORE_REGEX.has(lastToken)) return true;
|
|
23
|
-
return 1 === lastToken.length && PUNCTUATORS_BEFORE_REGEX.has(lastToken);
|
|
1
|
+
import { parseSync } from "@swc/core";
|
|
2
|
+
const assertNoNativeModuleOutputCollision = (sourceFile, fileExists)=>{
|
|
3
|
+
const rawModuleFile = sourceFile.endsWith('.mts') ? `${sourceFile.slice(0, -4)}.mjs` : sourceFile.endsWith('.cts') ? `${sourceFile.slice(0, -4)}.cjs` : void 0;
|
|
4
|
+
if (rawModuleFile && fileExists(rawModuleFile)) throw new Error(`Native module sources collide: "${sourceFile}" and "${rawModuleFile}" publish the same output.`);
|
|
24
5
|
};
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
6
|
+
const isObjectRecord = (value)=>'object' == typeof value && null !== value;
|
|
7
|
+
const isAstNode = (value)=>isObjectRecord(value) && 'string' == typeof value.type;
|
|
8
|
+
const isStringLiteral = (value)=>isAstNode(value) && 'StringLiteral' === value.type && 'string' == typeof value.value && 'object' == typeof value.span && null !== value.span && 'number' == typeof value.span.start && 'number' == typeof value.span.end;
|
|
9
|
+
const getCallSpecifier = (node)=>{
|
|
10
|
+
if ('CallExpression' !== node.type || !Array.isArray(node.arguments)) return;
|
|
11
|
+
const callee = node.callee;
|
|
12
|
+
const isDynamicImport = isAstNode(callee) && 'Import' === callee.type;
|
|
13
|
+
const isCommonJsRequire = isAstNode(callee) && 'Identifier' === callee.type && 'require' === callee.value;
|
|
14
|
+
if (!isDynamicImport && !isCommonJsRequire) return;
|
|
15
|
+
const firstArgument = node.arguments[0];
|
|
16
|
+
if (!isObjectRecord(firstArgument)) return;
|
|
17
|
+
return isStringLiteral(firstArgument.expression) ? firstArgument.expression : void 0;
|
|
28
18
|
};
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
19
|
+
const collectSpecifierLiterals = (content)=>{
|
|
20
|
+
const parserOptions = {
|
|
21
|
+
decorators: true,
|
|
22
|
+
syntax: "typescript",
|
|
23
|
+
target: 'esnext'
|
|
24
|
+
};
|
|
25
|
+
let program;
|
|
26
|
+
try {
|
|
27
|
+
program = parseSync(content, parserOptions);
|
|
28
|
+
} catch (typescriptError) {
|
|
29
|
+
try {
|
|
30
|
+
program = parseSync(content, {
|
|
31
|
+
...parserOptions,
|
|
32
|
+
tsx: true
|
|
33
|
+
});
|
|
34
|
+
} catch {
|
|
35
|
+
throw typescriptError;
|
|
46
36
|
}
|
|
47
|
-
return content[i];
|
|
48
37
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
i += 2;
|
|
58
|
-
continue;
|
|
38
|
+
const literals = [];
|
|
39
|
+
const seenSpans = new Set();
|
|
40
|
+
const add = (value)=>{
|
|
41
|
+
if (!isStringLiteral(value)) return;
|
|
42
|
+
const spanKey = `${value.span.start}:${value.span.end}`;
|
|
43
|
+
if (!seenSpans.has(spanKey)) {
|
|
44
|
+
seenSpans.add(spanKey);
|
|
45
|
+
literals.push(value);
|
|
59
46
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
};
|
|
48
|
+
const visit = (value)=>{
|
|
49
|
+
if (Array.isArray(value)) return void value.forEach(visit);
|
|
50
|
+
if (!isObjectRecord(value)) return;
|
|
51
|
+
if (isAstNode(value)) switch(value.type){
|
|
52
|
+
case 'ImportDeclaration':
|
|
53
|
+
case 'ExportAllDeclaration':
|
|
54
|
+
case 'ExportNamedDeclaration':
|
|
55
|
+
add(value.source);
|
|
56
|
+
break;
|
|
57
|
+
case 'TsImportType':
|
|
58
|
+
add(value.argument);
|
|
59
|
+
break;
|
|
60
|
+
case 'TsExternalModuleReference':
|
|
61
|
+
add(value.expression);
|
|
62
|
+
break;
|
|
63
|
+
case 'CallExpression':
|
|
64
|
+
add(getCallSpecifier(value));
|
|
65
|
+
break;
|
|
67
66
|
}
|
|
68
|
-
|
|
67
|
+
for (const [key, child] of Object.entries(value))if ('span' !== key) visit(child);
|
|
68
|
+
};
|
|
69
|
+
visit(program);
|
|
70
|
+
return literals.sort((left, right)=>left.span.start - right.span.start);
|
|
71
|
+
};
|
|
72
|
+
const mapUtf8ByteOffsets = (content, byteOffsets)=>{
|
|
73
|
+
const pending = new Set(byteOffsets);
|
|
74
|
+
const result = new Map();
|
|
75
|
+
let byteOffset = 0;
|
|
76
|
+
let stringOffset = 0;
|
|
77
|
+
while(stringOffset <= content.length && pending.size > 0){
|
|
78
|
+
if (pending.delete(byteOffset)) result.set(byteOffset, stringOffset);
|
|
79
|
+
if (stringOffset === content.length) break;
|
|
80
|
+
const codePoint = content.codePointAt(stringOffset);
|
|
81
|
+
if (void 0 === codePoint) break;
|
|
82
|
+
const character = String.fromCodePoint(codePoint);
|
|
83
|
+
byteOffset += Buffer.byteLength(character);
|
|
84
|
+
stringOffset += character.length;
|
|
69
85
|
}
|
|
70
|
-
|
|
86
|
+
if (pending.size > 0) throw new Error('SWC returned an import specifier span outside its source');
|
|
87
|
+
return result;
|
|
71
88
|
};
|
|
72
89
|
const scanSpecifiers = (content)=>{
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if ('\\' === ch) {
|
|
90
|
-
i += 2;
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
if ('`' === ch) {
|
|
94
|
-
mode = 'code';
|
|
95
|
-
pushToken('`');
|
|
96
|
-
i++;
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if ('$' === ch && '{' === content[i + 1]) {
|
|
100
|
-
templateExpressionDepths.push(0);
|
|
101
|
-
mode = 'code';
|
|
102
|
-
pushToken('{');
|
|
103
|
-
i += 2;
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
i++;
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (WHITESPACE.test(ch)) {
|
|
110
|
-
i++;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if ('/' === ch) {
|
|
114
|
-
const next = content[i + 1];
|
|
115
|
-
if ('/' === next) {
|
|
116
|
-
const newline = content.indexOf('\n', i + 2);
|
|
117
|
-
i = -1 === newline ? length : newline + 1;
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
if ('*' === next) {
|
|
121
|
-
const end = content.indexOf('*/', i + 2);
|
|
122
|
-
i = -1 === end ? length : end + 2;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (isRegexAllowed(prev1)) {
|
|
126
|
-
i = skipRegexLiteral(content, i);
|
|
127
|
-
pushToken('/regex/');
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
pushToken('/');
|
|
131
|
-
i++;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if ('`' === ch) {
|
|
135
|
-
mode = 'template';
|
|
136
|
-
i++;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if ('{' === ch) {
|
|
140
|
-
if (templateExpressionDepths.length > 0) templateExpressionDepths[templateExpressionDepths.length - 1]++;
|
|
141
|
-
pushToken('{');
|
|
142
|
-
i++;
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if ('}' === ch) {
|
|
146
|
-
if (templateExpressionDepths.length > 0) {
|
|
147
|
-
const top = templateExpressionDepths.length - 1;
|
|
148
|
-
if (0 === templateExpressionDepths[top]) {
|
|
149
|
-
templateExpressionDepths.pop();
|
|
150
|
-
mode = 'template';
|
|
151
|
-
i++;
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
templateExpressionDepths[top]--;
|
|
155
|
-
}
|
|
156
|
-
pushToken('}');
|
|
157
|
-
i++;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
if ('"' === ch || "'" === ch) {
|
|
161
|
-
const start = i;
|
|
162
|
-
let j = i + 1;
|
|
163
|
-
let terminated = false;
|
|
164
|
-
while(j < length){
|
|
165
|
-
const sch = content[j];
|
|
166
|
-
if ('\\' === sch) {
|
|
167
|
-
j += 2;
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
if (sch === ch) {
|
|
171
|
-
terminated = true;
|
|
172
|
-
j++;
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
if ('\n' === sch) break;
|
|
176
|
-
j++;
|
|
177
|
-
}
|
|
178
|
-
const callSpecifier = isCallSpecifierContext(prev1, prev2, prev3);
|
|
179
|
-
const nextChar = callSpecifier ? nextNonTriviaChar(content, j) : '';
|
|
180
|
-
const isCompleteCallSpecifier = !callSpecifier || ')' === nextChar || ',' === nextChar;
|
|
181
|
-
if (terminated && isSpecifierContext(prev1, prev2, prev3) && isCompleteCallSpecifier) specifiers.push({
|
|
182
|
-
start: start + 1,
|
|
183
|
-
end: j - 1,
|
|
184
|
-
value: content.slice(start + 1, j - 1)
|
|
185
|
-
});
|
|
186
|
-
pushToken('"string"');
|
|
187
|
-
i = j;
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
if (IDENTIFIER_CHAR.test(ch)) {
|
|
191
|
-
let j = i + 1;
|
|
192
|
-
while(j < length && IDENTIFIER_CHAR.test(content[j]))j++;
|
|
193
|
-
pushToken(content.slice(i, j));
|
|
194
|
-
i = j;
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
pushToken(ch);
|
|
198
|
-
i++;
|
|
199
|
-
}
|
|
200
|
-
return specifiers;
|
|
90
|
+
const literals = collectSpecifierLiterals(content);
|
|
91
|
+
const byteOffsets = literals.flatMap((literal)=>[
|
|
92
|
+
literal.span.start,
|
|
93
|
+
literal.span.end - 2
|
|
94
|
+
]);
|
|
95
|
+
const stringOffsets = mapUtf8ByteOffsets(content, byteOffsets);
|
|
96
|
+
return literals.map((literal)=>{
|
|
97
|
+
const start = stringOffsets.get(literal.span.start);
|
|
98
|
+
const end = stringOffsets.get(literal.span.end - 2);
|
|
99
|
+
if (void 0 === start || void 0 === end) throw new Error('Could not locate an SWC import specifier in its source');
|
|
100
|
+
return {
|
|
101
|
+
start,
|
|
102
|
+
end,
|
|
103
|
+
value: content.slice(start, end)
|
|
104
|
+
};
|
|
105
|
+
});
|
|
201
106
|
};
|
|
202
107
|
const rewriteImportSpecifiers = (content, rewrite)=>{
|
|
203
108
|
const specifiers = scanSpecifiers(content);
|
|
@@ -226,4 +131,4 @@ const rewriteImportSpecifiers = (content, rewrite)=>{
|
|
|
226
131
|
changed: true
|
|
227
132
|
};
|
|
228
133
|
};
|
|
229
|
-
export { rewriteImportSpecifiers };
|
|
134
|
+
export { assertNoNativeModuleOutputCollision, rewriteImportSpecifiers };
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { fs, getAliasConfig, logger } from "@modern-js/utils";
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { rewriteImportSpecifiers } from "./importRewriter.mjs";
|
|
4
|
+
import { assertNoNativeModuleOutputCollision, rewriteImportSpecifiers } from "./importRewriter.mjs";
|
|
5
5
|
import { createTsconfigPathsMatcher, getNotAliasedPath } from "./tsconfigPathsPlugin.mjs";
|
|
6
6
|
const copyFiles = async (from, to, appDirectory)=>{
|
|
7
7
|
if (await fs.pathExists(from)) {
|
|
8
8
|
const relativePath = path.relative(appDirectory, from);
|
|
9
9
|
const targetDir = path.join(to, relativePath);
|
|
10
10
|
await fs.copy(from, targetDir, {
|
|
11
|
-
filter: (src)
|
|
12
|
-
'.ts',
|
|
13
|
-
'.tsx',
|
|
14
|
-
'.js',
|
|
15
|
-
'.jsx'
|
|
16
|
-
].includes(path.extname(src)) && !src.endsWith('tsconfig.json')
|
|
11
|
+
filter: (src)=>!/\.(?:[cm]?ts|tsx|jsx?)$/u.test(src) && !src.endsWith('tsconfig.json')
|
|
17
12
|
});
|
|
18
13
|
}
|
|
19
14
|
};
|
|
@@ -110,7 +105,7 @@ const runTsgo = (tsgoBinPath, args, options)=>new Promise((resolve, reject)=>{
|
|
|
110
105
|
});
|
|
111
106
|
const getTsgoBinEntry = (pkg)=>{
|
|
112
107
|
if ('string' == typeof pkg.bin) return pkg.bin;
|
|
113
|
-
return pkg.bin?.tsgo;
|
|
108
|
+
return pkg.bin?.tsc ?? pkg.bin?.tsgo;
|
|
114
109
|
};
|
|
115
110
|
const resolveTsgoBinPath = (pkgPath)=>{
|
|
116
111
|
const pkgDir = path.dirname(pkgPath);
|
|
@@ -127,12 +122,19 @@ const getTsgoBinPath = (appDirectory, resolvePaths = [
|
|
|
127
122
|
__dirname
|
|
128
123
|
])=>{
|
|
129
124
|
try {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
let pkgPath;
|
|
126
|
+
try {
|
|
127
|
+
pkgPath = require.resolve("@typescript/native/package.json", {
|
|
128
|
+
paths: resolvePaths
|
|
129
|
+
});
|
|
130
|
+
} catch {
|
|
131
|
+
pkgPath = require.resolve("@typescript/native-preview/package.json", {
|
|
132
|
+
paths: resolvePaths
|
|
133
|
+
});
|
|
134
|
+
}
|
|
133
135
|
return resolveTsgoBinPath(pkgPath);
|
|
134
136
|
} catch {
|
|
135
|
-
throw new Error('tsgo could not be found! Please install "@typescript/native-preview" in your project to compile BFF/server code.');
|
|
137
|
+
throw new Error('tsgo could not be found! Please install "@typescript/native" (or legacy "@typescript/native-preview") in your project to compile BFF/server code.');
|
|
136
138
|
}
|
|
137
139
|
};
|
|
138
140
|
const OUTPUT_SOURCE_EXTENSIONS = {
|
|
@@ -192,6 +194,7 @@ const rewriteOutputSpecifiers = async (appDirectory, distDir, baseUrl, paths, mo
|
|
|
192
194
|
await Promise.all(files.map(async (file)=>{
|
|
193
195
|
const sourceFile = getSourceFileForOutput(appDirectory, distDir, file);
|
|
194
196
|
if (!sourceFile) return;
|
|
197
|
+
assertNoNativeModuleOutputCollision(sourceFile, fs.existsSync);
|
|
195
198
|
const content = await fs.readFile(file, 'utf8');
|
|
196
199
|
const { content: rewritten, changed } = rewriteImportSpecifiers(content, (specifier)=>getNotAliasedPath(sourceFile, matcher, specifier, moduleType));
|
|
197
200
|
if (changed) await fs.writeFile(file, await dropStaleSourceMap(file, rewritten));
|
|
@@ -248,6 +251,7 @@ const compileByTs = async (appDirectory, config, compileOptions)=>{
|
|
|
248
251
|
} else process.exit(1);
|
|
249
252
|
}
|
|
250
253
|
for (const source of sourceDirs)await copyFiles(source, distDir, appDirectory);
|
|
254
|
+
await rewriteOutputSpecifiers(appDirectory, distDir, absoluteBaseUrl, paths, compileOptions.moduleType);
|
|
251
255
|
logger.info("TS-Go compile succeed");
|
|
252
256
|
};
|
|
253
257
|
export { compileByTs, createResolvedTsgoConfig, getTsgoBinPath, rewriteOutputSpecifiers };
|
|
@@ -9,18 +9,47 @@ const toImportSpecifier = (sourceFile, resolvedPath)=>{
|
|
|
9
9
|
const relativePath = pathApi.relative(pathApi.dirname(sourceFile), resolvedPath).split(pathApi.sep).join(path.posix.sep);
|
|
10
10
|
return '.' === relativePath[0] ? relativePath : `./${relativePath}`;
|
|
11
11
|
};
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const SOURCE_TO_OUTPUT_EXTENSION = new Map([
|
|
13
|
+
[
|
|
14
|
+
'.ts',
|
|
15
|
+
'.js'
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
'.tsx',
|
|
19
|
+
'.js'
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
'.js',
|
|
23
|
+
'.js'
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
'.jsx',
|
|
27
|
+
'.js'
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
'.mts',
|
|
31
|
+
'.mjs'
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
'.mjs',
|
|
35
|
+
'.mjs'
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
'.cts',
|
|
39
|
+
'.cjs'
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
'.cjs',
|
|
43
|
+
'.cjs'
|
|
44
|
+
]
|
|
17
45
|
]);
|
|
18
46
|
const toEsmOutputPath = (resolvedPath)=>{
|
|
19
47
|
const sourcePath = (findSourceEntry(resolvedPath) || resolvedPath).replace(/\\/g, '/');
|
|
20
48
|
const ext = path.extname(sourcePath);
|
|
21
49
|
if (!ext) return `${sourcePath}.js`;
|
|
22
|
-
|
|
23
|
-
return
|
|
50
|
+
const outputExtension = SOURCE_TO_OUTPUT_EXTENSION.get(ext);
|
|
51
|
+
if (!outputExtension) return sourcePath;
|
|
52
|
+
return `${sourcePath.slice(0, -ext.length)}${outputExtension}`;
|
|
24
53
|
};
|
|
25
54
|
const resolveRelativeEsmSpecifier = (sourceFile, text)=>{
|
|
26
55
|
if (!text.startsWith('./') && !text.startsWith('../')) return;
|
|
@@ -108,7 +137,7 @@ function getNotAliasedPath(sourceFile, matcher, text, moduleType) {
|
|
|
108
137
|
if (packagePath) return text;
|
|
109
138
|
} catch {}
|
|
110
139
|
}
|
|
111
|
-
if ('module' === moduleType) result = toEsmOutputPath(result);
|
|
140
|
+
if ('module' === moduleType || /\.[cm]ts$/iu.test(result)) result = toEsmOutputPath(result);
|
|
112
141
|
return toImportSpecifier(sourceFile, result) || './';
|
|
113
142
|
}
|
|
114
143
|
export { createTsconfigPathsMatcher, getNotAliasedPath };
|