ttsc 0.8.0 → 0.9.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/README.md +75 -66
- package/cmd/ttsc/api_compile.go +5 -1
- package/driver/program.go +23 -1
- package/driver/rewrite.go +126 -9
- package/driver/rewrite_test.go +52 -0
- package/lib/TtscCompiler.js +28 -46
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.js +8 -38
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +36 -63
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/project/readProjectConfig.js +108 -60
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +15 -45
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.js +10 -40
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +21 -48
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.js +14 -44
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +7 -0
- package/lib/compiler/internal/runBuild.js +47 -66
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +18 -48
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +41 -75
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/getCompilerVersionText.js +7 -37
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +48 -69
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +53 -64
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +328 -57
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +8 -0
- package/lib/plugin/internal/buildSourcePlugin.js +179 -125
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +2 -0
- package/lib/plugin/internal/loadProjectPlugins.js +197 -9
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +16 -3
- package/lib/structures/ITtscCompilerResult.d.ts +4 -0
- package/lib/structures/ITtscCompilerTransformation.d.ts +2 -0
- package/lib/structures/ITtscPluginFactoryContext.d.ts +6 -3
- package/lib/structures/ITtscProjectPluginConfig.d.ts +8 -7
- package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
- package/lib/structures/internal/ITtscProjectLocatorOptions.d.ts +2 -0
- package/lib/structures/internal/TtscCommonOptions.d.ts +2 -0
- package/package.json +9 -14
- package/shim/diagnosticwriter/lint.go +8 -0
- package/src/TtscCompiler.ts +20 -5
- package/src/compiler/internal/buildNativeCompiler.ts +2 -2
- package/src/compiler/internal/compileProjectInMemory.ts +21 -16
- package/src/compiler/internal/project/readProjectConfig.ts +134 -35
- package/src/compiler/internal/project/resolveProjectConfig.ts +2 -2
- package/src/compiler/internal/resolveBinary.ts +2 -2
- package/src/compiler/internal/resolveEmittedJavaScript.ts +11 -7
- package/src/compiler/internal/resolveTsgo.ts +2 -2
- package/src/compiler/internal/runBuild.ts +34 -21
- package/src/compiler/internal/runSingleFileEmit.ts +3 -3
- package/src/compiler/internal/transformProjectInMemory.ts +37 -37
- package/src/launcher/internal/getCompilerVersionText.ts +2 -2
- package/src/launcher/internal/prepareExecution.ts +14 -4
- package/src/launcher/internal/runTtsc.ts +30 -4
- package/src/launcher/internal/runTtsx.ts +377 -26
- package/src/plugin/internal/buildSourcePlugin.ts +127 -32
- package/src/plugin/internal/loadProjectPlugins.ts +266 -10
- package/src/structures/ITtscCompilerContext.ts +17 -3
- package/src/structures/ITtscCompilerResult.ts +5 -0
- package/src/structures/ITtscCompilerTransformation.ts +3 -0
- package/src/structures/ITtscPluginFactoryContext.ts +6 -3
- package/src/structures/ITtscProjectPluginConfig.ts +8 -7
- package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
- package/src/structures/internal/ITtscProjectLocatorOptions.ts +2 -0
- package/src/structures/internal/TtscCommonOptions.ts +2 -0
- package/utility/host.go +610 -18
- package/utility/host_test.go +162 -0
- package/test/compiler.test.ts +0 -422
- package/test/platform.test.ts +0 -12
- package/test/project.test.ts +0 -197
- package/test/source-build.test.ts +0 -45
- package/test/tsgo.test.ts +0 -69
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { TtscCompiler } from "../../TtscCompiler";
|
|
6
6
|
import { resolveBinary } from "../../compiler/internal/resolveBinary";
|
|
@@ -72,7 +72,7 @@ function runCompatibleBuild(
|
|
|
72
72
|
argv: readonly string[],
|
|
73
73
|
checkOnly: boolean,
|
|
74
74
|
): number {
|
|
75
|
-
const options = parseBuildArgs(argv, checkOnly);
|
|
75
|
+
const options = normalizeBuildOptions(parseBuildArgs(argv, checkOnly));
|
|
76
76
|
if (options.watch) {
|
|
77
77
|
return runWatch(options, checkOnly);
|
|
78
78
|
}
|
|
@@ -85,6 +85,17 @@ function runCompatibleBuild(
|
|
|
85
85
|
return result.status;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
function normalizeBuildOptions(
|
|
89
|
+
options: ReturnType<typeof parseBuildArgs>,
|
|
90
|
+
): ReturnType<typeof parseBuildArgs> {
|
|
91
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
92
|
+
return {
|
|
93
|
+
...options,
|
|
94
|
+
cacheDir: resolveCacheDir(cwd, options.cacheDir),
|
|
95
|
+
cwd,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
88
99
|
function runPrepare(argv: readonly string[]): number {
|
|
89
100
|
const options = parseProjectArgs(argv);
|
|
90
101
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
@@ -153,12 +164,20 @@ function resolveCleanProjectRoot(cwd: string, tsconfig?: string): string {
|
|
|
153
164
|
|
|
154
165
|
function formatProjectPath(cwd: string, target: string): string {
|
|
155
166
|
const relative = path.relative(cwd, target);
|
|
156
|
-
if (!relative ||
|
|
167
|
+
if (!relative || isOutsideRelativePath(relative)) {
|
|
157
168
|
return target;
|
|
158
169
|
}
|
|
159
170
|
return relative;
|
|
160
171
|
}
|
|
161
172
|
|
|
173
|
+
function isOutsideRelativePath(relative: string): boolean {
|
|
174
|
+
return (
|
|
175
|
+
relative === ".." ||
|
|
176
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
177
|
+
path.isAbsolute(relative)
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
162
181
|
function delegateToNative(argv: readonly string[]): number {
|
|
163
182
|
const bin = resolveBinary();
|
|
164
183
|
if (!bin) {
|
|
@@ -498,6 +517,13 @@ function runWatch(
|
|
|
498
517
|
return 0;
|
|
499
518
|
}
|
|
500
519
|
|
|
520
|
+
function resolveCacheDir(cwd: string, cacheDir?: string): string | undefined {
|
|
521
|
+
if (!cacheDir) {
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
524
|
+
return path.isAbsolute(cacheDir) ? cacheDir : path.resolve(cwd, cacheDir);
|
|
525
|
+
}
|
|
526
|
+
|
|
501
527
|
function collectWatchDirectories(root: string): string[] {
|
|
502
528
|
const out: string[] = [];
|
|
503
529
|
const stack: string[] = [root];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from "node:fs";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
-
import
|
|
3
|
+
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { getCompilerVersionText } from "./getCompilerVersionText";
|
|
6
6
|
import { prepareExecution } from "./prepareExecution";
|
|
@@ -38,7 +38,7 @@ function run(argv: readonly string[]): number {
|
|
|
38
38
|
|
|
39
39
|
const prepared = prepareExecution(entry, {
|
|
40
40
|
binary: parsed.binary,
|
|
41
|
-
cacheDir: parsed.cacheDir,
|
|
41
|
+
cacheDir: resolveCacheDir(cwd, parsed.cacheDir),
|
|
42
42
|
cwd,
|
|
43
43
|
project: parsed.project,
|
|
44
44
|
});
|
|
@@ -140,9 +140,9 @@ function printHelp(): void {
|
|
|
140
140
|
"Options:",
|
|
141
141
|
" -P, --project <file> Use an explicit tsconfig.json",
|
|
142
142
|
" --cwd <dir> Resolve entry/project relative to this directory",
|
|
143
|
-
" --cache-dir <dir> Override the
|
|
143
|
+
" --cache-dir <dir> Override the runner and source-plugin cache root",
|
|
144
144
|
" --binary <path> Use an explicit tsgo binary",
|
|
145
|
-
" -r, --require <
|
|
145
|
+
" -r, --require <module> Preload a module before the entrypoint",
|
|
146
146
|
" -h, --help Show this help",
|
|
147
147
|
" -v, --version Print the runner version",
|
|
148
148
|
"",
|
|
@@ -155,16 +155,30 @@ function printHelp(): void {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
function resolvePreload(cwd: string, preload: string): string {
|
|
158
|
-
if (
|
|
159
|
-
preload.startsWith(".") ||
|
|
160
|
-
preload.startsWith("/") ||
|
|
161
|
-
preload.includes(path.sep)
|
|
162
|
-
) {
|
|
158
|
+
if (path.isAbsolute(preload) || isRelativeSpecifier(preload)) {
|
|
163
159
|
return path.resolve(cwd, preload);
|
|
164
160
|
}
|
|
165
161
|
return preload;
|
|
166
162
|
}
|
|
167
163
|
|
|
164
|
+
function isRelativeSpecifier(specifier: string): boolean {
|
|
165
|
+
return (
|
|
166
|
+
specifier === "." ||
|
|
167
|
+
specifier === ".." ||
|
|
168
|
+
specifier.startsWith("./") ||
|
|
169
|
+
specifier.startsWith("../") ||
|
|
170
|
+
specifier.startsWith(".\\") ||
|
|
171
|
+
specifier.startsWith("..\\")
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function resolveCacheDir(cwd: string, cacheDir?: string): string | undefined {
|
|
176
|
+
if (!cacheDir) {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
return path.isAbsolute(cacheDir) ? cacheDir : path.resolve(cwd, cacheDir);
|
|
180
|
+
}
|
|
181
|
+
|
|
168
182
|
function takeValue(flag: string, rest: string[]): string {
|
|
169
183
|
const value = rest.shift();
|
|
170
184
|
if (!value) {
|
|
@@ -196,6 +210,7 @@ function runPreparedEntry(
|
|
|
196
210
|
...parsed.passthrough,
|
|
197
211
|
];
|
|
198
212
|
const result = spawnSync(process.execPath, args, {
|
|
213
|
+
cwd,
|
|
199
214
|
stdio: "inherit",
|
|
200
215
|
env: process.env,
|
|
201
216
|
windowsHide: true,
|
|
@@ -221,22 +236,7 @@ function rewriteEsmSpecifiers(root: string): void {
|
|
|
221
236
|
continue;
|
|
222
237
|
}
|
|
223
238
|
const before = fs.readFileSync(next, "utf8");
|
|
224
|
-
const after = before
|
|
225
|
-
.replace(
|
|
226
|
-
/\bfrom\s+(['"])(\.[^'"]+)\1/g,
|
|
227
|
-
(_, quote: string, specifier: string) =>
|
|
228
|
-
`from ${quote}${withResolvableExtension(next, specifier)}${quote}`,
|
|
229
|
-
)
|
|
230
|
-
.replace(
|
|
231
|
-
/\bimport\s+(['"])(\.[^'"]+)\1/g,
|
|
232
|
-
(_, quote: string, specifier: string) =>
|
|
233
|
-
`import ${quote}${withResolvableExtension(next, specifier)}${quote}`,
|
|
234
|
-
)
|
|
235
|
-
.replace(
|
|
236
|
-
/\bimport\(\s*(['"])(\.[^'"]+)\1\s*\)/g,
|
|
237
|
-
(_, quote: string, specifier: string) =>
|
|
238
|
-
`import(${quote}${withResolvableExtension(next, specifier)}${quote})`,
|
|
239
|
-
);
|
|
239
|
+
const after = rewriteEsmSpecifiersInText(next, before);
|
|
240
240
|
if (after !== before) {
|
|
241
241
|
fs.writeFileSync(next, after, "utf8");
|
|
242
242
|
}
|
|
@@ -244,6 +244,193 @@ function rewriteEsmSpecifiers(root: string): void {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
function rewriteEsmSpecifiersInText(fromFile: string, input: string): string {
|
|
248
|
+
const replacements: Array<{ start: number; end: number; text: string }> = [];
|
|
249
|
+
scanEsmSpecifiers(fromFile, input, 0, input.length, replacements);
|
|
250
|
+
if (replacements.length === 0) {
|
|
251
|
+
return input;
|
|
252
|
+
}
|
|
253
|
+
let output = "";
|
|
254
|
+
let last = 0;
|
|
255
|
+
for (const replacement of replacements) {
|
|
256
|
+
output += input.slice(last, replacement.start);
|
|
257
|
+
output += replacement.text;
|
|
258
|
+
last = replacement.end;
|
|
259
|
+
}
|
|
260
|
+
return output + input.slice(last);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function scanEsmSpecifiers(
|
|
264
|
+
fromFile: string,
|
|
265
|
+
input: string,
|
|
266
|
+
start: number,
|
|
267
|
+
end: number,
|
|
268
|
+
replacements: Array<{ start: number; end: number; text: string }>,
|
|
269
|
+
): void {
|
|
270
|
+
let i = start;
|
|
271
|
+
while (i < end) {
|
|
272
|
+
if (input[i] === "`") {
|
|
273
|
+
i = scanTemplateExpressions(fromFile, input, i, replacements);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const skipped = skipNonCode(input, i);
|
|
277
|
+
if (skipped !== i) {
|
|
278
|
+
i = skipped;
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (isKeywordAt(input, i, "import")) {
|
|
282
|
+
const next = skipWhitespace(input, i + "import".length);
|
|
283
|
+
if (input[next] === "(") {
|
|
284
|
+
rewriteDynamicImportSpecifier(fromFile, input, next, replacements);
|
|
285
|
+
} else if (isQuote(input[next])) {
|
|
286
|
+
rewriteStringSpecifier(fromFile, input, next, replacements);
|
|
287
|
+
} else {
|
|
288
|
+
rewriteFromSpecifier(fromFile, input, next, replacements);
|
|
289
|
+
}
|
|
290
|
+
i += "import".length;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (isKeywordAt(input, i, "export")) {
|
|
294
|
+
rewriteFromSpecifier(fromFile, input, i + "export".length, replacements);
|
|
295
|
+
i += "export".length;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
i += 1;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function rewriteDynamicImportSpecifier(
|
|
303
|
+
fromFile: string,
|
|
304
|
+
input: string,
|
|
305
|
+
openParen: number,
|
|
306
|
+
replacements: Array<{ start: number; end: number; text: string }>,
|
|
307
|
+
): void {
|
|
308
|
+
const specifier = skipWhitespace(input, openParen + 1);
|
|
309
|
+
if (isQuote(input[specifier])) {
|
|
310
|
+
rewriteStringSpecifier(fromFile, input, specifier, replacements);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function rewriteFromSpecifier(
|
|
315
|
+
fromFile: string,
|
|
316
|
+
input: string,
|
|
317
|
+
start: number,
|
|
318
|
+
replacements: Array<{ start: number; end: number; text: string }>,
|
|
319
|
+
): void {
|
|
320
|
+
let i = start;
|
|
321
|
+
let depth = 0;
|
|
322
|
+
while (i < input.length) {
|
|
323
|
+
const skipped = skipNonCode(input, i);
|
|
324
|
+
if (skipped !== i) {
|
|
325
|
+
i = skipped;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const current = input[i]!;
|
|
329
|
+
if (depth === 0 && current === ";") {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (current === "{" || current === "[" || current === "(") {
|
|
333
|
+
depth += 1;
|
|
334
|
+
i += 1;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (current === "}" || current === "]" || current === ")") {
|
|
338
|
+
depth = Math.max(0, depth - 1);
|
|
339
|
+
i += 1;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (depth === 0 && isKeywordAt(input, i, "from")) {
|
|
343
|
+
const specifier = skipWhitespace(input, i + "from".length);
|
|
344
|
+
if (isQuote(input[specifier])) {
|
|
345
|
+
rewriteStringSpecifier(fromFile, input, specifier, replacements);
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
i += 1;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function rewriteStringSpecifier(
|
|
354
|
+
fromFile: string,
|
|
355
|
+
input: string,
|
|
356
|
+
quoteIndex: number,
|
|
357
|
+
replacements: Array<{ start: number; end: number; text: string }>,
|
|
358
|
+
): void {
|
|
359
|
+
const literal = readSimpleStringLiteral(input, quoteIndex);
|
|
360
|
+
if (literal === null) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const next = withResolvableExtension(fromFile, literal.value);
|
|
364
|
+
if (next === literal.value) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
replacements.push({
|
|
368
|
+
start: literal.start,
|
|
369
|
+
end: literal.end,
|
|
370
|
+
text: next,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function scanTemplateExpressions(
|
|
375
|
+
fromFile: string,
|
|
376
|
+
input: string,
|
|
377
|
+
start: number,
|
|
378
|
+
replacements: Array<{ start: number; end: number; text: string }>,
|
|
379
|
+
): number {
|
|
380
|
+
for (let i = start + 1; i < input.length; i += 1) {
|
|
381
|
+
const current = input[i]!;
|
|
382
|
+
if (current === "\\") {
|
|
383
|
+
i += 1;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (current === "`") {
|
|
387
|
+
return i + 1;
|
|
388
|
+
}
|
|
389
|
+
if (current === "$" && input[i + 1] === "{") {
|
|
390
|
+
const expressionStart = i + 2;
|
|
391
|
+
const expressionEnd = findTemplateExpressionEnd(input, expressionStart);
|
|
392
|
+
if (expressionEnd === null) {
|
|
393
|
+
return input.length;
|
|
394
|
+
}
|
|
395
|
+
scanEsmSpecifiers(
|
|
396
|
+
fromFile,
|
|
397
|
+
input,
|
|
398
|
+
expressionStart,
|
|
399
|
+
expressionEnd,
|
|
400
|
+
replacements,
|
|
401
|
+
);
|
|
402
|
+
i = expressionEnd;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return input.length;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function findTemplateExpressionEnd(
|
|
409
|
+
input: string,
|
|
410
|
+
expressionStart: number,
|
|
411
|
+
): number | null {
|
|
412
|
+
let depth = 1;
|
|
413
|
+
for (let i = expressionStart; i < input.length; i += 1) {
|
|
414
|
+
const skipped = skipNonCode(input, i);
|
|
415
|
+
if (skipped !== i) {
|
|
416
|
+
i = skipped - 1;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const current = input[i]!;
|
|
420
|
+
if (current === "{") {
|
|
421
|
+
depth += 1;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (current === "}") {
|
|
425
|
+
depth -= 1;
|
|
426
|
+
if (depth === 0) {
|
|
427
|
+
return i;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
|
|
247
434
|
function withResolvableExtension(fromFile: string, specifier: string): string {
|
|
248
435
|
if (!specifier.startsWith(".")) {
|
|
249
436
|
return specifier;
|
|
@@ -258,6 +445,12 @@ function withResolvableExtension(fromFile: string, specifier: string): string {
|
|
|
258
445
|
return pathname + extension + suffix;
|
|
259
446
|
}
|
|
260
447
|
}
|
|
448
|
+
const directory = pathname.replace(/\/+$/, "");
|
|
449
|
+
for (const extension of [".js", ".mjs", ".cjs"]) {
|
|
450
|
+
if (fs.existsSync(path.resolve(fromDir, directory, "index" + extension))) {
|
|
451
|
+
return `${directory}/index${extension}${suffix}`;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
261
454
|
return `${specifier}.js`;
|
|
262
455
|
}
|
|
263
456
|
|
|
@@ -272,3 +465,161 @@ function splitSpecifierSuffix(specifier: string): [string, string?] {
|
|
|
272
465
|
function isJavaScriptOutput(filename: string): boolean {
|
|
273
466
|
return /\.(?:[cm]?js)$/i.test(filename);
|
|
274
467
|
}
|
|
468
|
+
|
|
469
|
+
function skipNonCode(input: string, start: number): number {
|
|
470
|
+
const current = input[start];
|
|
471
|
+
const next = input[start + 1];
|
|
472
|
+
if (current === '"' || current === "'") {
|
|
473
|
+
return skipString(input, start);
|
|
474
|
+
}
|
|
475
|
+
if (current === "`") {
|
|
476
|
+
return skipTemplate(input, start);
|
|
477
|
+
}
|
|
478
|
+
if (current === "/" && next === "/") {
|
|
479
|
+
return skipLineComment(input, start);
|
|
480
|
+
}
|
|
481
|
+
if (current === "/" && next === "*") {
|
|
482
|
+
return skipBlockComment(input, start);
|
|
483
|
+
}
|
|
484
|
+
if (current === "/" && looksLikeRegexStart(input, start)) {
|
|
485
|
+
return skipRegex(input, start);
|
|
486
|
+
}
|
|
487
|
+
return start;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function skipString(input: string, start: number): number {
|
|
491
|
+
const quote = input[start]!;
|
|
492
|
+
for (let i = start + 1; i < input.length; i += 1) {
|
|
493
|
+
const current = input[i]!;
|
|
494
|
+
if (current === "\\") {
|
|
495
|
+
i += 1;
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
if (current === quote) {
|
|
499
|
+
return i + 1;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return input.length;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function skipTemplate(input: string, start: number): number {
|
|
506
|
+
for (let i = start + 1; i < input.length; i += 1) {
|
|
507
|
+
const current = input[i]!;
|
|
508
|
+
if (current === "\\") {
|
|
509
|
+
i += 1;
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (current === "`") {
|
|
513
|
+
return i + 1;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return input.length;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function skipLineComment(input: string, start: number): number {
|
|
520
|
+
const end = input.indexOf("\n", start + 2);
|
|
521
|
+
return end === -1 ? input.length : end + 1;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function skipBlockComment(input: string, start: number): number {
|
|
525
|
+
const end = input.indexOf("*/", start + 2);
|
|
526
|
+
return end === -1 ? input.length : end + 2;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function skipRegex(input: string, start: number): number {
|
|
530
|
+
let inClass = false;
|
|
531
|
+
for (let i = start + 1; i < input.length; i += 1) {
|
|
532
|
+
const current = input[i]!;
|
|
533
|
+
if (current === "\\") {
|
|
534
|
+
i += 1;
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (current === "[") {
|
|
538
|
+
inClass = true;
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (current === "]") {
|
|
542
|
+
inClass = false;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (current === "/" && !inClass) {
|
|
546
|
+
i += 1;
|
|
547
|
+
while (/[A-Za-z]/.test(input[i] ?? "")) {
|
|
548
|
+
i += 1;
|
|
549
|
+
}
|
|
550
|
+
return i;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return input.length;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function looksLikeRegexStart(input: string, start: number): boolean {
|
|
557
|
+
let i = start - 1;
|
|
558
|
+
while (i >= 0 && /\s/.test(input[i]!)) {
|
|
559
|
+
i -= 1;
|
|
560
|
+
}
|
|
561
|
+
if (i < 0) {
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
const previous = input[i]!;
|
|
565
|
+
if ("([{=,:;!?&|+-*~^<>".includes(previous)) {
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
const word = readPreviousWord(input, i);
|
|
569
|
+
return /^(?:return|throw|case|delete|void|typeof|instanceof|in|of|yield|await)$/.test(
|
|
570
|
+
word,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function readPreviousWord(input: string, end: number): string {
|
|
575
|
+
let start = end;
|
|
576
|
+
while (start >= 0 && isIdentifierPart(input[start]!)) {
|
|
577
|
+
start -= 1;
|
|
578
|
+
}
|
|
579
|
+
return input.slice(start + 1, end + 1);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function readSimpleStringLiteral(
|
|
583
|
+
input: string,
|
|
584
|
+
quoteIndex: number,
|
|
585
|
+
): { start: number; end: number; value: string } | null {
|
|
586
|
+
const quote = input[quoteIndex]!;
|
|
587
|
+
for (let i = quoteIndex + 1; i < input.length; i += 1) {
|
|
588
|
+
const current = input[i]!;
|
|
589
|
+
if (current === "\\") {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
if (current === quote) {
|
|
593
|
+
return {
|
|
594
|
+
start: quoteIndex + 1,
|
|
595
|
+
end: i,
|
|
596
|
+
value: input.slice(quoteIndex + 1, i),
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function skipWhitespace(input: string, start: number): number {
|
|
604
|
+
let i = start;
|
|
605
|
+
while (i < input.length && /\s/.test(input[i]!)) {
|
|
606
|
+
i += 1;
|
|
607
|
+
}
|
|
608
|
+
return i;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function isKeywordAt(input: string, start: number, keyword: string): boolean {
|
|
612
|
+
return (
|
|
613
|
+
input.startsWith(keyword, start) &&
|
|
614
|
+
!isIdentifierPart(input[start - 1] ?? "") &&
|
|
615
|
+
!isIdentifierPart(input[start + keyword.length] ?? "")
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function isIdentifierPart(value: string): boolean {
|
|
620
|
+
return /[$\p{ID_Continue}]/u.test(value);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function isQuote(value: string | undefined): value is '"' | "'" {
|
|
624
|
+
return value === '"' || value === "'";
|
|
625
|
+
}
|