ttsc 0.4.4-dev.20260426 → 0.5.0-dev.20260429
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 +35 -3
- package/cmd/ttsc/main.go +2 -2
- package/driver/program.go +120 -15
- package/go.mod +3 -3
- package/go.sum +6 -6
- package/lib/api.d.ts +3 -3
- package/lib/api.js +275 -624
- package/lib/api.js.map +1 -1
- package/lib/cli.js +78 -2
- package/lib/cli.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +3 -3
- package/lib/native.d.ts +5 -0
- package/lib/native.js +8 -0
- package/lib/native.js.map +1 -1
- package/lib/plugin.d.ts +8 -48
- package/lib/plugin.js +79 -132
- package/lib/plugin.js.map +1 -1
- package/lib/runner/cli.js +1 -0
- package/lib/runner/cli.js.map +1 -1
- package/lib/source-build.d.ts +30 -0
- package/lib/source-build.js +365 -0
- package/lib/source-build.js.map +1 -0
- package/package.json +9 -9
- package/shim/ast/go.mod +1 -1
- package/shim/ast/lint.go +328 -0
- package/shim/bundled/go.mod +1 -1
- package/shim/checker/go.mod +1 -1
- package/shim/compiler/go.mod +1 -1
- package/shim/core/go.mod +1 -1
- package/shim/core/shim.go +15 -0
- package/shim/diagnosticwriter/go.mod +1 -1
- package/shim/diagnosticwriter/lint.go +137 -0
- package/shim/parser/go.mod +1 -1
- package/shim/scanner/go.mod +1 -1
- package/shim/tsoptions/go.mod +1 -1
- package/shim/tspath/go.mod +1 -1
- package/shim/vfs/cachedvfs/go.mod +1 -1
- package/shim/vfs/go.mod +1 -1
- package/shim/vfs/osvfs/go.mod +1 -1
- package/src/api.ts +413 -771
- package/src/cli.ts +82 -2
- package/src/index.ts +3 -3
- package/src/native.ts +18 -0
- package/src/plugin.ts +107 -213
- package/src/runner/cli.ts +5 -0
- package/src/source-build.ts +383 -0
- package/test/native.test.ts +43 -0
- package/test/source-build.test.ts +219 -0
package/lib/api.js
CHANGED
|
@@ -107,6 +107,29 @@ function outputText(value) {
|
|
|
107
107
|
}
|
|
108
108
|
return typeof value === "string" ? value : value.toString("utf8");
|
|
109
109
|
}
|
|
110
|
+
function hasCapability(plugin, capability) {
|
|
111
|
+
return plugin.backend.capabilities?.includes(capability) === true;
|
|
112
|
+
}
|
|
113
|
+
function isOutputPlugin(plugin) {
|
|
114
|
+
return hasCapability(plugin, "output");
|
|
115
|
+
}
|
|
116
|
+
function isCheckOnlyPlugin(plugin) {
|
|
117
|
+
const capabilities = plugin.backend.capabilities ?? [];
|
|
118
|
+
return (capabilities.length > 0 &&
|
|
119
|
+
capabilities.every((capability) => capability === "check"));
|
|
120
|
+
}
|
|
121
|
+
function isCompilerPlugin(plugin) {
|
|
122
|
+
return !isOutputPlugin(plugin) && !isCheckOnlyPlugin(plugin);
|
|
123
|
+
}
|
|
124
|
+
function outputPlugins(plugins) {
|
|
125
|
+
return plugins.filter(isOutputPlugin);
|
|
126
|
+
}
|
|
127
|
+
function checkOnlyPlugins(plugins) {
|
|
128
|
+
return plugins.filter(isCheckOnlyPlugin);
|
|
129
|
+
}
|
|
130
|
+
function compilerPlugins(plugins) {
|
|
131
|
+
return plugins.filter(isCompilerPlugin);
|
|
132
|
+
}
|
|
110
133
|
/**
|
|
111
134
|
* Transform a single .ts file and return the rewritten JS as a string.
|
|
112
135
|
*
|
|
@@ -122,14 +145,8 @@ function transform(options) {
|
|
|
122
145
|
const sourceFile = realpathIfExists(path.isAbsolute(options.file)
|
|
123
146
|
? options.file
|
|
124
147
|
: path.resolve(options.cwd ?? process.cwd(), options.file));
|
|
125
|
-
if (execution.
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
if (execution.nativeBinary) {
|
|
129
|
-
return transformWithNativeBinary(options, execution, sourceFile);
|
|
130
|
-
}
|
|
131
|
-
if (execution.nativeMode !== "none") {
|
|
132
|
-
throw new Error(`ttsc.transform: native rewrite mode "${execution.nativeMode}" requires a plugin-provided version-matched binary`);
|
|
148
|
+
if (execution.nativePlugins.length > 0) {
|
|
149
|
+
return transformWithNativePlugins(options, execution, sourceFile);
|
|
133
150
|
}
|
|
134
151
|
const tempOutDir = fs.mkdtempSync(path.join(os.tmpdir(), "ttsc-transform-"));
|
|
135
152
|
try {
|
|
@@ -150,14 +167,7 @@ function transform(options) {
|
|
|
150
167
|
if (!emitted) {
|
|
151
168
|
throw new Error(`ttsc.transform: no output produced for ${sourceFile}`);
|
|
152
169
|
}
|
|
153
|
-
const transformed =
|
|
154
|
-
command: "transform",
|
|
155
|
-
compilerOptions: execution.compilerOptions,
|
|
156
|
-
cwd: execution.cwd,
|
|
157
|
-
projectRoot: execution.projectRoot,
|
|
158
|
-
sourceFile,
|
|
159
|
-
tsconfig: execution.tsconfig,
|
|
160
|
-
}, fs.readFileSync(emitted, "utf8"));
|
|
170
|
+
const transformed = fs.readFileSync(emitted, "utf8");
|
|
161
171
|
if (options.out) {
|
|
162
172
|
fs.mkdirSync(path.dirname(options.out), { recursive: true });
|
|
163
173
|
fs.writeFileSync(options.out, transformed, "utf8");
|
|
@@ -168,91 +178,65 @@ function transform(options) {
|
|
|
168
178
|
fs.rmSync(tempOutDir, { recursive: true, force: true });
|
|
169
179
|
}
|
|
170
180
|
}
|
|
171
|
-
function
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
"
|
|
175
|
-
"--tsconfig=" + execution.tsconfig,
|
|
176
|
-
"--rewrite-mode=" + execution.nativeMode,
|
|
177
|
-
];
|
|
178
|
-
const res = spawnBinary(execution.nativeBinary, args, {
|
|
179
|
-
cwd: options.cwd,
|
|
180
|
-
env: mergeEnv(options.env),
|
|
181
|
-
encoding: "utf8",
|
|
182
|
-
});
|
|
183
|
-
if (res.error) {
|
|
184
|
-
throw new Error("ttsc.transform: failed to spawn " +
|
|
185
|
-
execution.nativeBinary +
|
|
186
|
-
": " +
|
|
187
|
-
res.error.message);
|
|
181
|
+
function transformWithNativePlugins(options, execution, sourceFile) {
|
|
182
|
+
const checked = runNativeCheckPlugins(options, execution);
|
|
183
|
+
if (checked.status !== 0) {
|
|
184
|
+
throw new Error("ttsc.transform exited " + checked.status + "\n" + checked.stderr);
|
|
188
185
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const transformed = finalizeTransformText(execution.outputPlugins, {
|
|
193
|
-
command: "transform",
|
|
194
|
-
compilerOptions: execution.compilerOptions,
|
|
195
|
-
cwd: execution.cwd,
|
|
196
|
-
projectRoot: execution.projectRoot,
|
|
197
|
-
sourceFile,
|
|
198
|
-
tsconfig: execution.tsconfig,
|
|
199
|
-
}, outputText(res.stdout));
|
|
200
|
-
if (options.out) {
|
|
201
|
-
fs.mkdirSync(path.dirname(options.out), { recursive: true });
|
|
202
|
-
fs.writeFileSync(options.out, transformed, "utf8");
|
|
203
|
-
}
|
|
204
|
-
return transformed;
|
|
205
|
-
}
|
|
206
|
-
function transformWithSourceTransforms(options, execution, sourceFile) {
|
|
207
|
-
if (execution.nativeBinary || execution.nativeMode !== "none") {
|
|
208
|
-
throw new Error("ttsc.transform: transformSource cannot be combined with native rewrite mode yet");
|
|
209
|
-
}
|
|
210
|
-
const materialized = materializeSourceTransformedProject(execution, "transform", sourceFile);
|
|
211
|
-
const tempOutDir = path.join(materialized.root, ".ttsc-transform-out");
|
|
186
|
+
const compilers = compilerPlugins(execution.nativePlugins);
|
|
187
|
+
const outputs = outputPlugins(execution.nativePlugins);
|
|
188
|
+
const tempOutDir = fs.mkdtempSync(path.join(os.tmpdir(), "ttsc-transform-"));
|
|
212
189
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
throw new Error("ttsc.transform exited " +
|
|
225
|
-
result.status +
|
|
226
|
-
"\n" +
|
|
227
|
-
(result.stderr || result.stdout));
|
|
190
|
+
let emitted = null;
|
|
191
|
+
if (compilers.length !== 0) {
|
|
192
|
+
assertSingleCompilerHost(compilers);
|
|
193
|
+
emitted = path.join(tempOutDir, "transform.js");
|
|
194
|
+
const transformed = runNativeCompilerTransform(options, execution, sourceFile, emitted, compilers);
|
|
195
|
+
if (transformed.status !== 0) {
|
|
196
|
+
throw new Error("ttsc.transform exited " +
|
|
197
|
+
transformed.status +
|
|
198
|
+
"\n" +
|
|
199
|
+
transformed.stderr);
|
|
200
|
+
}
|
|
228
201
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
202
|
+
else {
|
|
203
|
+
const result = build({
|
|
204
|
+
...options,
|
|
205
|
+
emit: true,
|
|
206
|
+
outDir: tempOutDir,
|
|
207
|
+
plugins: false,
|
|
208
|
+
skipDiagnosticsCheck: checkOnlyPlugins(execution.nativePlugins).length !== 0,
|
|
209
|
+
tsconfig: execution.tsconfig,
|
|
210
|
+
});
|
|
211
|
+
if (result.status !== 0) {
|
|
212
|
+
throw new Error("ttsc.transform exited " +
|
|
213
|
+
result.status +
|
|
214
|
+
"\n" +
|
|
215
|
+
(result.stderr || result.stdout));
|
|
216
|
+
}
|
|
217
|
+
emitted = findEmittedFile(tempOutDir, execution.projectRoot, sourceFile);
|
|
218
|
+
if (!emitted) {
|
|
219
|
+
throw new Error(`ttsc.transform: no output produced for ${sourceFile}`);
|
|
220
|
+
}
|
|
232
221
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}, fs.readFileSync(emitted, "utf8"));
|
|
241
|
-
if (options.out) {
|
|
242
|
-
const mapFile = `${emitted}.map`;
|
|
243
|
-
if (fs.existsSync(mapFile)) {
|
|
244
|
-
const outMap = `${options.out}.map`;
|
|
245
|
-
fs.mkdirSync(path.dirname(outMap), { recursive: true });
|
|
246
|
-
fs.writeFileSync(outMap, patchSourceMapText(fs.readFileSync(mapFile, "utf8"), materialized, outMap, mapFile), "utf8");
|
|
247
|
-
transformed = rewriteSourceMapReference(transformed, path.basename(outMap));
|
|
222
|
+
for (const plugin of outputs) {
|
|
223
|
+
const result = runNativeOutputPlugin(options, execution, plugin, emitted);
|
|
224
|
+
if (result.status !== 0) {
|
|
225
|
+
throw new Error("ttsc.transform exited " +
|
|
226
|
+
result.status +
|
|
227
|
+
"\n" +
|
|
228
|
+
(result.stderr || result.stdout));
|
|
248
229
|
}
|
|
230
|
+
}
|
|
231
|
+
const transformed = fs.readFileSync(emitted, "utf8");
|
|
232
|
+
if (options.out) {
|
|
249
233
|
fs.mkdirSync(path.dirname(options.out), { recursive: true });
|
|
250
234
|
fs.writeFileSync(options.out, transformed, "utf8");
|
|
251
235
|
}
|
|
252
236
|
return transformed;
|
|
253
237
|
}
|
|
254
238
|
finally {
|
|
255
|
-
fs.rmSync(
|
|
239
|
+
fs.rmSync(tempOutDir, { recursive: true, force: true });
|
|
256
240
|
}
|
|
257
241
|
}
|
|
258
242
|
function realpathIfExists(file) {
|
|
@@ -270,19 +254,47 @@ function realpathIfExists(file) {
|
|
|
270
254
|
*/
|
|
271
255
|
function build(options = {}) {
|
|
272
256
|
const execution = resolveExecutionContext(options);
|
|
273
|
-
if (execution.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
257
|
+
if (execution.nativePlugins.length > 0) {
|
|
258
|
+
const compilers = compilerPlugins(execution.nativePlugins);
|
|
259
|
+
const outputs = outputPlugins(execution.nativePlugins);
|
|
260
|
+
const checked = runNativeCheckPlugins(options, execution);
|
|
261
|
+
if (checked.status !== 0) {
|
|
262
|
+
return checked;
|
|
263
|
+
}
|
|
264
|
+
if (options.emit === false) {
|
|
265
|
+
if (compilers.length !== 0) {
|
|
266
|
+
assertSingleCompilerHost(compilers);
|
|
267
|
+
return appendBuildOutput(checked, buildWithNativeCompilerPlugins(options, execution, compilers));
|
|
268
|
+
}
|
|
269
|
+
if (checked.stdout !== "" || checked.stderr !== "") {
|
|
270
|
+
return checked;
|
|
271
|
+
}
|
|
272
|
+
return runTsgo(execution, ["--noEmit"], options);
|
|
273
|
+
}
|
|
274
|
+
let result;
|
|
275
|
+
if (compilers.length !== 0) {
|
|
276
|
+
assertSingleCompilerHost(compilers);
|
|
277
|
+
result = appendBuildOutput(checked, buildWithNativeCompilerPlugins(options, execution, compilers));
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
if (checked.stdout === "" &&
|
|
281
|
+
checked.stderr === "" &&
|
|
282
|
+
options.skipDiagnosticsCheck !== true) {
|
|
283
|
+
const tsgoChecked = runTsgo(execution, ["--noEmit"], options);
|
|
284
|
+
if (tsgoChecked.status !== 0) {
|
|
285
|
+
return tsgoChecked;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const args = createTsgoBuildArgs(execution, options, {
|
|
289
|
+
listEmittedFiles: outputs.length !== 0 || options.forceListEmittedFiles === true,
|
|
290
|
+
});
|
|
291
|
+
const emitted = runTsgoBuild(execution, options, args);
|
|
292
|
+
result = appendBuildOutput(checked, emitted);
|
|
293
|
+
}
|
|
294
|
+
if (result.status !== 0 || outputs.length === 0) {
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
return appendBuildOutput(result, applyOutputPlugins(options, execution, result.emittedFiles ?? [], outputs));
|
|
286
298
|
}
|
|
287
299
|
if (options.emit !== false && options.skipDiagnosticsCheck !== true) {
|
|
288
300
|
const checked = runTsgo(execution, ["--noEmit"], options);
|
|
@@ -291,84 +303,12 @@ function build(options = {}) {
|
|
|
291
303
|
}
|
|
292
304
|
}
|
|
293
305
|
const args = createTsgoBuildArgs(execution, options, {
|
|
294
|
-
listEmittedFiles: options.emit !== false &&
|
|
295
|
-
(execution.outputPlugins.length > 0 || options.forceListEmittedFiles === true),
|
|
296
|
-
});
|
|
297
|
-
const res = spawnBinary(execution.tsgo.binary, args, {
|
|
298
|
-
cwd: execution.projectRoot,
|
|
299
|
-
env: mergeEnv(options.env),
|
|
300
|
-
encoding: "utf8",
|
|
306
|
+
listEmittedFiles: options.emit !== false && options.forceListEmittedFiles === true,
|
|
301
307
|
});
|
|
302
|
-
|
|
303
|
-
throw new Error("ttsc.build: failed to spawn " +
|
|
304
|
-
execution.tsgo.binary +
|
|
305
|
-
": " +
|
|
306
|
-
res.error.message);
|
|
307
|
-
}
|
|
308
|
-
const result = {
|
|
309
|
-
status: res.status ?? 1,
|
|
310
|
-
stdout: outputText(res.stdout),
|
|
311
|
-
stderr: outputText(res.stderr),
|
|
312
|
-
};
|
|
313
|
-
const emittedFiles = parseEmittedFiles(result.stdout);
|
|
314
|
-
if (emittedFiles.length !== 0) {
|
|
315
|
-
result.stdout = stripEmittedFileLines(result.stdout);
|
|
316
|
-
}
|
|
317
|
-
if (result.status === 0 && options.emit !== false && execution.outputPlugins.length > 0) {
|
|
318
|
-
applyBuildPlugins(execution.outputPlugins, execution, emittedFiles);
|
|
319
|
-
}
|
|
320
|
-
return normalizeFailedDiagnostics({ ...result, emittedFiles });
|
|
308
|
+
return runTsgoBuild(execution, options, args);
|
|
321
309
|
}
|
|
322
|
-
function
|
|
323
|
-
|
|
324
|
-
const res = spawnBinary(execution.nativeBinary, args, {
|
|
325
|
-
cwd: execution.projectRoot,
|
|
326
|
-
env: mergeEnv(options.env),
|
|
327
|
-
encoding: "utf8",
|
|
328
|
-
});
|
|
329
|
-
if (res.error) {
|
|
330
|
-
throw new Error("ttsc.build: failed to spawn " +
|
|
331
|
-
execution.nativeBinary +
|
|
332
|
-
": " +
|
|
333
|
-
res.error.message);
|
|
334
|
-
}
|
|
335
|
-
return normalizeFailedDiagnostics({
|
|
336
|
-
status: res.status ?? 1,
|
|
337
|
-
stdout: outputText(res.stdout),
|
|
338
|
-
stderr: outputText(res.stderr),
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
function buildWithSourceTransforms(options, execution) {
|
|
342
|
-
if (execution.nativeBinary || execution.nativeMode !== "none") {
|
|
343
|
-
return {
|
|
344
|
-
status: 2,
|
|
345
|
-
stdout: "",
|
|
346
|
-
stderr: "ttsc.build: transformSource cannot be combined with native rewrite mode yet\n",
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
const materialized = materializeSourceTransformedProject(execution, "build");
|
|
350
|
-
try {
|
|
351
|
-
const outDir = mapOutDirToMaterializedProject(options.outDir, execution, materialized);
|
|
352
|
-
const result = build({
|
|
353
|
-
...options,
|
|
354
|
-
cwd: materialized.root,
|
|
355
|
-
forceListEmittedFiles: true,
|
|
356
|
-
outDir,
|
|
357
|
-
plugins: false,
|
|
358
|
-
tsconfig: materialized.tsconfig,
|
|
359
|
-
});
|
|
360
|
-
if (result.status === 0 && options.emit !== false) {
|
|
361
|
-
const copied = copyMaterializedEmittedFiles(materialized, result.emittedFiles ?? []);
|
|
362
|
-
result.emittedFiles = copied;
|
|
363
|
-
if (execution.outputPlugins.length > 0) {
|
|
364
|
-
applyBuildPlugins(execution.outputPlugins, execution, copied);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return result;
|
|
368
|
-
}
|
|
369
|
-
finally {
|
|
370
|
-
fs.rmSync(materialized.root, { recursive: true, force: true });
|
|
371
|
-
}
|
|
310
|
+
function buildWithNativeCompilerPlugins(options, execution, plugins) {
|
|
311
|
+
return runNativePluginCommand(plugins[0], createNativeBuildArgs(execution, options, plugins), options, execution, "ttsc.build");
|
|
372
312
|
}
|
|
373
313
|
/**
|
|
374
314
|
* Run `ttsc check` (build without emit) — CI gate / pre-commit hook use.
|
|
@@ -392,6 +332,29 @@ function runTsgo(execution, extraArgs, options) {
|
|
|
392
332
|
stderr: outputText(res.stderr),
|
|
393
333
|
});
|
|
394
334
|
}
|
|
335
|
+
function runTsgoBuild(execution, options, args) {
|
|
336
|
+
const res = spawnBinary(execution.tsgo.binary, args, {
|
|
337
|
+
cwd: execution.projectRoot,
|
|
338
|
+
env: mergeEnv(options.env),
|
|
339
|
+
encoding: "utf8",
|
|
340
|
+
});
|
|
341
|
+
if (res.error) {
|
|
342
|
+
throw new Error("ttsc.build: failed to spawn " +
|
|
343
|
+
execution.tsgo.binary +
|
|
344
|
+
": " +
|
|
345
|
+
res.error.message);
|
|
346
|
+
}
|
|
347
|
+
const result = {
|
|
348
|
+
status: res.status ?? 1,
|
|
349
|
+
stdout: outputText(res.stdout),
|
|
350
|
+
stderr: outputText(res.stderr),
|
|
351
|
+
};
|
|
352
|
+
const emittedFiles = parseEmittedFiles(result.stdout);
|
|
353
|
+
if (emittedFiles.length !== 0) {
|
|
354
|
+
result.stdout = stripEmittedFileLines(result.stdout);
|
|
355
|
+
}
|
|
356
|
+
return normalizeFailedDiagnostics({ ...result, emittedFiles });
|
|
357
|
+
}
|
|
395
358
|
function createTsgoBuildArgs(execution, options, flags) {
|
|
396
359
|
const args = ["-p", execution.tsconfig];
|
|
397
360
|
if (options.emit === true) {
|
|
@@ -408,11 +371,12 @@ function createTsgoBuildArgs(execution, options, flags) {
|
|
|
408
371
|
}
|
|
409
372
|
return args;
|
|
410
373
|
}
|
|
411
|
-
function createNativeBuildArgs(execution, options) {
|
|
374
|
+
function createNativeBuildArgs(execution, options, plugins) {
|
|
412
375
|
const args = [
|
|
413
376
|
options.emit === false ? "check" : "build",
|
|
414
377
|
"--tsconfig=" + execution.tsconfig,
|
|
415
|
-
"--rewrite-mode=" +
|
|
378
|
+
"--rewrite-mode=" + (options.rewriteMode ?? plugins[0]?.backend.mode ?? "none"),
|
|
379
|
+
"--plugins-json=" + serializeNativePlugins(plugins),
|
|
416
380
|
"--cwd=" + execution.projectRoot,
|
|
417
381
|
];
|
|
418
382
|
if (options.emit === true) {
|
|
@@ -432,6 +396,132 @@ function createNativeBuildArgs(execution, options) {
|
|
|
432
396
|
}
|
|
433
397
|
return args;
|
|
434
398
|
}
|
|
399
|
+
function createNativeCheckArgs(execution, options, plugin) {
|
|
400
|
+
const args = [
|
|
401
|
+
"check",
|
|
402
|
+
"--tsconfig=" + execution.tsconfig,
|
|
403
|
+
"--rewrite-mode=" + (options.rewriteMode ?? plugin.backend.mode),
|
|
404
|
+
"--plugins-json=" + serializeNativePlugins(execution.nativePlugins),
|
|
405
|
+
"--cwd=" + execution.projectRoot,
|
|
406
|
+
];
|
|
407
|
+
if ("outDir" in options && options.outDir) {
|
|
408
|
+
args.push("--outDir=" + path.resolve(execution.cwd, options.outDir));
|
|
409
|
+
}
|
|
410
|
+
if ("quiet" in options && options.quiet === false) {
|
|
411
|
+
args.push("--verbose");
|
|
412
|
+
}
|
|
413
|
+
else if ("quiet" in options && options.quiet === true) {
|
|
414
|
+
args.push("--quiet");
|
|
415
|
+
}
|
|
416
|
+
return args;
|
|
417
|
+
}
|
|
418
|
+
function createNativeOutputArgs(execution, options, plugin, file) {
|
|
419
|
+
const args = [
|
|
420
|
+
"output",
|
|
421
|
+
"--file=" + file,
|
|
422
|
+
"--tsconfig=" + execution.tsconfig,
|
|
423
|
+
"--rewrite-mode=" + (options.rewriteMode ?? plugin.backend.mode),
|
|
424
|
+
"--plugins-json=" + serializeNativePlugins(execution.nativePlugins),
|
|
425
|
+
"--cwd=" + execution.projectRoot,
|
|
426
|
+
];
|
|
427
|
+
if ("outDir" in options && options.outDir) {
|
|
428
|
+
args.push("--outDir=" + path.resolve(execution.cwd, options.outDir));
|
|
429
|
+
}
|
|
430
|
+
return args;
|
|
431
|
+
}
|
|
432
|
+
function createNativeTransformArgs(execution, options, sourceFile, out, plugins) {
|
|
433
|
+
return [
|
|
434
|
+
"transform",
|
|
435
|
+
"--file=" + sourceFile,
|
|
436
|
+
"--out=" + out,
|
|
437
|
+
"--tsconfig=" + execution.tsconfig,
|
|
438
|
+
"--rewrite-mode=" + (options.rewriteMode ?? plugins[0]?.backend.mode ?? "none"),
|
|
439
|
+
"--plugins-json=" + serializeNativePlugins(plugins),
|
|
440
|
+
];
|
|
441
|
+
}
|
|
442
|
+
function serializeNativePlugins(plugins) {
|
|
443
|
+
return JSON.stringify(plugins.map((plugin) => ({
|
|
444
|
+
config: plugin.config,
|
|
445
|
+
contractVersion: plugin.backend.contractVersion,
|
|
446
|
+
mode: plugin.backend.mode,
|
|
447
|
+
name: plugin.name,
|
|
448
|
+
})));
|
|
449
|
+
}
|
|
450
|
+
function runNativeCheckPlugins(options, execution) {
|
|
451
|
+
let out = { status: 0, stdout: "", stderr: "" };
|
|
452
|
+
for (const plugin of checkOnlyPlugins(execution.nativePlugins)) {
|
|
453
|
+
const result = runNativePluginCommand(plugin, createNativeCheckArgs(execution, options, plugin), options, execution, "ttsc.check");
|
|
454
|
+
out = appendBuildOutput(out, result);
|
|
455
|
+
if (result.status !== 0) {
|
|
456
|
+
return out;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return out;
|
|
460
|
+
}
|
|
461
|
+
function runNativeCompilerTransform(options, execution, sourceFile, out, plugins) {
|
|
462
|
+
return runNativePluginCommand(plugins[0], createNativeTransformArgs(execution, options, sourceFile, out, plugins), options, execution, "ttsc.transform");
|
|
463
|
+
}
|
|
464
|
+
function applyOutputPlugins(options, execution, emittedFiles, plugins) {
|
|
465
|
+
let out = { status: 0, stdout: "", stderr: "" };
|
|
466
|
+
for (const plugin of plugins) {
|
|
467
|
+
for (const file of emittedFiles) {
|
|
468
|
+
if (!fs.existsSync(file)) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
const result = runNativeOutputPlugin(options, execution, plugin, file);
|
|
472
|
+
out = appendBuildOutput(out, result);
|
|
473
|
+
if (result.status !== 0) {
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
function runNativeOutputPlugin(options, execution, plugin, file) {
|
|
481
|
+
return runNativePluginCommand(plugin, createNativeOutputArgs(execution, options, plugin, file), options, execution, "ttsc.output");
|
|
482
|
+
}
|
|
483
|
+
function runNativePluginCommand(plugin, args, options, execution, label) {
|
|
484
|
+
const binary = plugin.backend.binary;
|
|
485
|
+
if (!binary) {
|
|
486
|
+
return {
|
|
487
|
+
status: 2,
|
|
488
|
+
stdout: "",
|
|
489
|
+
stderr: `${label}: plugin "${plugin.name}" requires a version-matched binary\n`,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
const res = spawnBinary(binary, args, {
|
|
493
|
+
cwd: execution.projectRoot,
|
|
494
|
+
env: mergeEnv(options.env),
|
|
495
|
+
encoding: "utf8",
|
|
496
|
+
});
|
|
497
|
+
if (res.error) {
|
|
498
|
+
throw new Error(`${label}: failed to spawn ${binary}: ${res.error.message}`);
|
|
499
|
+
}
|
|
500
|
+
return normalizeFailedDiagnostics({
|
|
501
|
+
status: res.status ?? 1,
|
|
502
|
+
stdout: outputText(res.stdout),
|
|
503
|
+
stderr: outputText(res.stderr),
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function appendBuildOutput(left, right) {
|
|
507
|
+
return normalizeFailedDiagnostics({
|
|
508
|
+
emittedFiles: right.emittedFiles !== undefined ? right.emittedFiles : left.emittedFiles,
|
|
509
|
+
status: right.status !== 0 ? right.status : left.status,
|
|
510
|
+
stdout: left.stdout + right.stdout,
|
|
511
|
+
stderr: left.stderr + right.stderr,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
function assertSingleCompilerHost(plugins) {
|
|
515
|
+
const binaries = [
|
|
516
|
+
...new Set(plugins
|
|
517
|
+
.map((plugin) => plugin.backend.binary)
|
|
518
|
+
.filter((binary) => typeof binary === "string")),
|
|
519
|
+
];
|
|
520
|
+
if (binaries.length > 1) {
|
|
521
|
+
throw new Error("ttsc: multiple compiler native backends cannot share one emit pass; " +
|
|
522
|
+
"use output-capability plugins for post-emit transforms");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
435
525
|
/** Ask the binary for its version banner. Handy for user-agent strings. */
|
|
436
526
|
function version(options = {}) {
|
|
437
527
|
const tsgo = (0, tsgo_1.resolveTsgo)(options);
|
|
@@ -444,9 +534,9 @@ function version(options = {}) {
|
|
|
444
534
|
return `ttsc ${readOwnPackageVersion()} (${outputText(res.stdout).trim()})`;
|
|
445
535
|
}
|
|
446
536
|
/**
|
|
447
|
-
* Promise-facing variant of `transform()`. The
|
|
448
|
-
*
|
|
449
|
-
*
|
|
537
|
+
* Promise-facing variant of `transform()`. The host path stays synchronous so
|
|
538
|
+
* plugin descriptors can stay dependency-free, but many adapter surfaces still
|
|
539
|
+
* prefer a Promise-returning function.
|
|
450
540
|
*/
|
|
451
541
|
function transformAsync(options) {
|
|
452
542
|
return Promise.resolve().then(() => transform(options));
|
|
@@ -469,47 +559,12 @@ function resolveExecutionContext(options) {
|
|
|
469
559
|
return {
|
|
470
560
|
compilerOptions: loaded.project.compilerOptions,
|
|
471
561
|
cwd,
|
|
472
|
-
|
|
473
|
-
nativeMode: options.rewriteMode ?? loaded.nativeMode,
|
|
474
|
-
outputPlugins: loaded.plugins.filter((plugin) => plugin.transformOutput),
|
|
562
|
+
nativePlugins: loaded.nativePlugins,
|
|
475
563
|
projectRoot,
|
|
476
|
-
sourcePlugins: loaded.plugins.filter((plugin) => plugin.transformSource),
|
|
477
564
|
tsgo,
|
|
478
565
|
tsconfig,
|
|
479
566
|
};
|
|
480
567
|
}
|
|
481
|
-
function finalizeTransformText(plugins, context, text) {
|
|
482
|
-
if (plugins.length === 0) {
|
|
483
|
-
return text;
|
|
484
|
-
}
|
|
485
|
-
return (0, plugin_1.applyPluginTransforms)(plugins, {
|
|
486
|
-
...context,
|
|
487
|
-
code: text,
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
function applyBuildPlugins(plugins, execution, emittedFiles) {
|
|
491
|
-
if (plugins.length === 0) {
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
for (const file of emittedFiles) {
|
|
495
|
-
if (!isJavaScriptOutput(file) || !fs.existsSync(file)) {
|
|
496
|
-
continue;
|
|
497
|
-
}
|
|
498
|
-
const current = fs.readFileSync(file, "utf8");
|
|
499
|
-
const next = (0, plugin_1.applyPluginTransforms)(plugins, {
|
|
500
|
-
code: current,
|
|
501
|
-
command: "build",
|
|
502
|
-
compilerOptions: execution.compilerOptions,
|
|
503
|
-
cwd: execution.cwd,
|
|
504
|
-
outputFile: file,
|
|
505
|
-
projectRoot: execution.projectRoot,
|
|
506
|
-
tsconfig: execution.tsconfig,
|
|
507
|
-
});
|
|
508
|
-
if (next !== current) {
|
|
509
|
-
fs.writeFileSync(file, next, "utf8");
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
568
|
function isJavaScriptOutput(file) {
|
|
514
569
|
return /\.(?:[cm]?js)$/i.test(file);
|
|
515
570
|
}
|
|
@@ -611,408 +666,4 @@ function readOwnPackageVersion() {
|
|
|
611
666
|
return "0.0.0";
|
|
612
667
|
}
|
|
613
668
|
}
|
|
614
|
-
function materializeSourceTransformedProject(execution, command, onlySourceFile) {
|
|
615
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), "ttsc-source-"));
|
|
616
|
-
const materialized = {
|
|
617
|
-
mappers: new Map(),
|
|
618
|
-
originalRoot: execution.projectRoot,
|
|
619
|
-
root,
|
|
620
|
-
tsconfig: path.join(root, path.relative(execution.projectRoot, execution.tsconfig)),
|
|
621
|
-
};
|
|
622
|
-
copyProjectForSourceTransforms(execution.projectRoot, root, execution.compilerOptions);
|
|
623
|
-
const files = onlySourceFile
|
|
624
|
-
? [toMaterializedPath(onlySourceFile, materialized)]
|
|
625
|
-
: listSourceTransformFiles(root);
|
|
626
|
-
for (const tempFile of files) {
|
|
627
|
-
if (!fs.existsSync(tempFile) || !fs.statSync(tempFile).isFile()) {
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
const originalFile = fromMaterializedPath(tempFile, materialized);
|
|
631
|
-
const current = fs.readFileSync(tempFile, "utf8");
|
|
632
|
-
const transformed = (0, plugin_1.applyPluginSourceTransformsWithMap)(execution.sourcePlugins, {
|
|
633
|
-
code: current,
|
|
634
|
-
command,
|
|
635
|
-
compilerOptions: execution.compilerOptions,
|
|
636
|
-
cwd: execution.cwd,
|
|
637
|
-
projectRoot: execution.projectRoot,
|
|
638
|
-
sourceFile: originalFile,
|
|
639
|
-
tsconfig: execution.tsconfig,
|
|
640
|
-
});
|
|
641
|
-
const next = transformed.code;
|
|
642
|
-
if (next !== current) {
|
|
643
|
-
fs.writeFileSync(tempFile, next, "utf8");
|
|
644
|
-
}
|
|
645
|
-
if (transformed.stages.length > 0) {
|
|
646
|
-
materialized.mappers.set(filepathKey(tempFile), {
|
|
647
|
-
finalCode: next,
|
|
648
|
-
originalCode: current,
|
|
649
|
-
stages: transformed.stages,
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
return materialized;
|
|
654
|
-
}
|
|
655
|
-
function copyProjectForSourceTransforms(source, target, compilerOptions) {
|
|
656
|
-
const excluded = new Set([".git", "node_modules"]);
|
|
657
|
-
const outDir = typeof compilerOptions.outDir === "string" ? compilerOptions.outDir : "";
|
|
658
|
-
const resolvedOutDir = outDir ? path.resolve(outDir) : "";
|
|
659
|
-
copyDirectoryFiltered(source, target, (entry) => {
|
|
660
|
-
if (excluded.has(path.basename(entry))) {
|
|
661
|
-
return false;
|
|
662
|
-
}
|
|
663
|
-
if (resolvedOutDir && isPathInside(entry, resolvedOutDir)) {
|
|
664
|
-
return false;
|
|
665
|
-
}
|
|
666
|
-
return true;
|
|
667
|
-
});
|
|
668
|
-
const nodeModules = path.join(source, "node_modules");
|
|
669
|
-
if (fs.existsSync(nodeModules)) {
|
|
670
|
-
const targetNodeModules = path.join(target, "node_modules");
|
|
671
|
-
try {
|
|
672
|
-
fs.symlinkSync(nodeModules, targetNodeModules, "junction");
|
|
673
|
-
}
|
|
674
|
-
catch {
|
|
675
|
-
copyDirectoryFiltered(nodeModules, targetNodeModules, () => true);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
function copyDirectoryFiltered(source, target, shouldCopy) {
|
|
680
|
-
if (!shouldCopy(source)) {
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
const stat = fs.lstatSync(source);
|
|
684
|
-
if (stat.isSymbolicLink()) {
|
|
685
|
-
const link = fs.readlinkSync(source);
|
|
686
|
-
fs.symlinkSync(link, target);
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
if (stat.isDirectory()) {
|
|
690
|
-
fs.mkdirSync(target, { recursive: true });
|
|
691
|
-
for (const entry of fs.readdirSync(source)) {
|
|
692
|
-
copyDirectoryFiltered(path.join(source, entry), path.join(target, entry), shouldCopy);
|
|
693
|
-
}
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
if (stat.isFile()) {
|
|
697
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
698
|
-
fs.copyFileSync(source, target);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
function listSourceTransformFiles(root) {
|
|
702
|
-
const out = [];
|
|
703
|
-
const stack = [root];
|
|
704
|
-
while (stack.length !== 0) {
|
|
705
|
-
const current = stack.pop();
|
|
706
|
-
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
707
|
-
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
708
|
-
continue;
|
|
709
|
-
}
|
|
710
|
-
const next = path.join(current, entry.name);
|
|
711
|
-
if (entry.isDirectory()) {
|
|
712
|
-
stack.push(next);
|
|
713
|
-
}
|
|
714
|
-
else if (entry.isFile() && isTransformableSource(next)) {
|
|
715
|
-
out.push(next);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
return out;
|
|
720
|
-
}
|
|
721
|
-
function isTransformableSource(file) {
|
|
722
|
-
return /\.(?:[cm]?tsx?)$/i.test(file) && !/\.d\.[cm]?ts$/i.test(file);
|
|
723
|
-
}
|
|
724
|
-
function mapOutDirToMaterializedProject(outDir, execution, materialized) {
|
|
725
|
-
if (!outDir) {
|
|
726
|
-
return undefined;
|
|
727
|
-
}
|
|
728
|
-
const resolved = path.resolve(execution.cwd, outDir);
|
|
729
|
-
if (isPathInside(resolved, execution.projectRoot)) {
|
|
730
|
-
return toMaterializedPath(resolved, materialized);
|
|
731
|
-
}
|
|
732
|
-
return path.join(materialized.root, ".ttsc-out");
|
|
733
|
-
}
|
|
734
|
-
function copyMaterializedEmittedFiles(materialized, emittedFiles) {
|
|
735
|
-
const copied = [];
|
|
736
|
-
for (const emitted of emittedFiles) {
|
|
737
|
-
if (!isPathInside(emitted, materialized.root)) {
|
|
738
|
-
continue;
|
|
739
|
-
}
|
|
740
|
-
const target = fromMaterializedPath(emitted, materialized);
|
|
741
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
742
|
-
if (/\.map$/i.test(emitted)) {
|
|
743
|
-
fs.writeFileSync(target, patchSourceMapText(fs.readFileSync(emitted, "utf8"), materialized, target), "utf8");
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
fs.copyFileSync(emitted, target);
|
|
747
|
-
}
|
|
748
|
-
copied.push(target);
|
|
749
|
-
}
|
|
750
|
-
return copied;
|
|
751
|
-
}
|
|
752
|
-
function toMaterializedPath(file, materialized) {
|
|
753
|
-
const relative = path.relative(materialized.originalRoot, file);
|
|
754
|
-
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
755
|
-
return file;
|
|
756
|
-
}
|
|
757
|
-
return path.join(materialized.root, relative);
|
|
758
|
-
}
|
|
759
|
-
function fromMaterializedPath(file, materialized) {
|
|
760
|
-
const relative = path.relative(materialized.root, file);
|
|
761
|
-
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
762
|
-
return file;
|
|
763
|
-
}
|
|
764
|
-
return path.join(materialized.originalRoot, relative);
|
|
765
|
-
}
|
|
766
|
-
function isPathInside(file, parent) {
|
|
767
|
-
const relative = path.relative(parent, file);
|
|
768
|
-
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
769
|
-
}
|
|
770
|
-
function filepathKey(file) {
|
|
771
|
-
return path.resolve(file).replace(/\\/g, "/");
|
|
772
|
-
}
|
|
773
|
-
function patchSourceMapText(text, materialized, outputFile, tempOutputFile = toMaterializedPath(outputFile, materialized)) {
|
|
774
|
-
let map;
|
|
775
|
-
try {
|
|
776
|
-
map = JSON.parse(text);
|
|
777
|
-
}
|
|
778
|
-
catch {
|
|
779
|
-
return text;
|
|
780
|
-
}
|
|
781
|
-
if (!Array.isArray(map.sources)) {
|
|
782
|
-
return text;
|
|
783
|
-
}
|
|
784
|
-
const tempMapDir = path.dirname(tempOutputFile);
|
|
785
|
-
const originalMapDir = path.dirname(outputFile);
|
|
786
|
-
const nextSources = [];
|
|
787
|
-
const nextSourcesContent = Array.isArray(map.sourcesContent)
|
|
788
|
-
? [...map.sourcesContent]
|
|
789
|
-
: undefined;
|
|
790
|
-
const sourceMappings = new Map();
|
|
791
|
-
map.sources.forEach((source, index) => {
|
|
792
|
-
const tempSource = path.isAbsolute(source)
|
|
793
|
-
? source
|
|
794
|
-
: path.resolve(tempMapDir, source);
|
|
795
|
-
if (isPathInside(tempSource, materialized.root)) {
|
|
796
|
-
const originalSource = fromMaterializedPath(tempSource, materialized);
|
|
797
|
-
const mapper = materialized.mappers.get(filepathKey(tempSource));
|
|
798
|
-
if (mapper) {
|
|
799
|
-
sourceMappings.set(index, mapper);
|
|
800
|
-
}
|
|
801
|
-
nextSources.push(relativeSourceMapPath(originalMapDir, originalSource));
|
|
802
|
-
if (nextSourcesContent) {
|
|
803
|
-
try {
|
|
804
|
-
nextSourcesContent[index] = fs.readFileSync(originalSource, "utf8");
|
|
805
|
-
}
|
|
806
|
-
catch {
|
|
807
|
-
/* keep emitted sourcesContent */
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
nextSources.push(source);
|
|
813
|
-
}
|
|
814
|
-
});
|
|
815
|
-
if (sourceMappings.size > 0 && typeof map.mappings === "string") {
|
|
816
|
-
map.mappings = remapSourceMapMappings(map.mappings, sourceMappings);
|
|
817
|
-
}
|
|
818
|
-
map.sources = nextSources;
|
|
819
|
-
if (nextSourcesContent) {
|
|
820
|
-
map.sourcesContent = nextSourcesContent;
|
|
821
|
-
}
|
|
822
|
-
delete map.sourceRoot;
|
|
823
|
-
return JSON.stringify(map);
|
|
824
|
-
}
|
|
825
|
-
function relativeSourceMapPath(fromDir, file) {
|
|
826
|
-
const relative = path.relative(fromDir, file).replace(/\\/g, "/");
|
|
827
|
-
return relative.startsWith(".") ? relative : `./${relative}`;
|
|
828
|
-
}
|
|
829
|
-
function rewriteSourceMapReference(code, mapFileName) {
|
|
830
|
-
const reference = `//# sourceMappingURL=${mapFileName}`;
|
|
831
|
-
if (/\/\/# sourceMappingURL=.*(?:\r?\n)?$/m.test(code)) {
|
|
832
|
-
return code.replace(/\/\/# sourceMappingURL=.*(?:\r?\n)?$/m, `${reference}\n`);
|
|
833
|
-
}
|
|
834
|
-
return code.endsWith("\n") ? `${code}${reference}\n` : `${code}\n${reference}\n`;
|
|
835
|
-
}
|
|
836
|
-
function remapSourceMapMappings(mappings, sourceMappings) {
|
|
837
|
-
const decoded = decodeMappings(mappings);
|
|
838
|
-
for (const line of decoded) {
|
|
839
|
-
for (const segment of line) {
|
|
840
|
-
if (segment.length < 4) {
|
|
841
|
-
continue;
|
|
842
|
-
}
|
|
843
|
-
const mapper = sourceMappings.get(segment[1]);
|
|
844
|
-
if (!mapper) {
|
|
845
|
-
continue;
|
|
846
|
-
}
|
|
847
|
-
const mapped = mapTransformedPositionToOriginal(mapper, {
|
|
848
|
-
column: segment[3],
|
|
849
|
-
line: segment[2],
|
|
850
|
-
});
|
|
851
|
-
segment[2] = mapped.line;
|
|
852
|
-
segment[3] = mapped.column;
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
return encodeMappings(decoded);
|
|
856
|
-
}
|
|
857
|
-
function mapTransformedPositionToOriginal(mapper, position) {
|
|
858
|
-
let offset = lineColumnToOffset(mapper.finalCode, position.line, position.column);
|
|
859
|
-
for (let index = mapper.stages.length - 1; index >= 0; index -= 1) {
|
|
860
|
-
offset = mapOffsetToPreviousStage(offset, mapper.stages[index]);
|
|
861
|
-
}
|
|
862
|
-
return offsetToLineColumn(mapper.originalCode, offset);
|
|
863
|
-
}
|
|
864
|
-
function mapOffsetToPreviousStage(offset, stage) {
|
|
865
|
-
let delta = 0;
|
|
866
|
-
for (const edit of stage.edits) {
|
|
867
|
-
if (offset < edit.newStart) {
|
|
868
|
-
return clampOffset(offset - delta, stage.before.length);
|
|
869
|
-
}
|
|
870
|
-
if (offset < edit.newEnd) {
|
|
871
|
-
return edit.start;
|
|
872
|
-
}
|
|
873
|
-
delta += edit.code.length - (edit.end - edit.start);
|
|
874
|
-
}
|
|
875
|
-
return clampOffset(offset - delta, stage.before.length);
|
|
876
|
-
}
|
|
877
|
-
function lineColumnToOffset(source, line, column) {
|
|
878
|
-
const starts = lineStarts(source);
|
|
879
|
-
const start = starts[Math.min(Math.max(line, 0), starts.length - 1)] ?? 0;
|
|
880
|
-
return clampOffset(start + Math.max(column, 0), source.length);
|
|
881
|
-
}
|
|
882
|
-
function offsetToLineColumn(source, offset) {
|
|
883
|
-
const starts = lineStarts(source);
|
|
884
|
-
const clamped = clampOffset(offset, source.length);
|
|
885
|
-
let low = 0;
|
|
886
|
-
let high = starts.length - 1;
|
|
887
|
-
while (low <= high) {
|
|
888
|
-
const mid = (low + high) >> 1;
|
|
889
|
-
const start = starts[mid];
|
|
890
|
-
const next = starts[mid + 1] ?? Number.POSITIVE_INFINITY;
|
|
891
|
-
if (clamped < start) {
|
|
892
|
-
high = mid - 1;
|
|
893
|
-
}
|
|
894
|
-
else if (clamped >= next) {
|
|
895
|
-
low = mid + 1;
|
|
896
|
-
}
|
|
897
|
-
else {
|
|
898
|
-
return { column: clamped - start, line: mid };
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
const last = starts.length - 1;
|
|
902
|
-
return { column: clamped - (starts[last] ?? 0), line: last };
|
|
903
|
-
}
|
|
904
|
-
function lineStarts(source) {
|
|
905
|
-
const starts = [0];
|
|
906
|
-
for (let index = 0; index < source.length; index += 1) {
|
|
907
|
-
const ch = source.charCodeAt(index);
|
|
908
|
-
if (ch === 13 /* \r */) {
|
|
909
|
-
if (source.charCodeAt(index + 1) === 10 /* \n */) {
|
|
910
|
-
index += 1;
|
|
911
|
-
}
|
|
912
|
-
starts.push(index + 1);
|
|
913
|
-
}
|
|
914
|
-
else if (ch === 10 /* \n */) {
|
|
915
|
-
starts.push(index + 1);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
return starts;
|
|
919
|
-
}
|
|
920
|
-
function clampOffset(offset, length) {
|
|
921
|
-
return Math.min(Math.max(offset, 0), length);
|
|
922
|
-
}
|
|
923
|
-
const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
924
|
-
const BASE64_VALUES = new Map([...BASE64_CHARS].map((char, index) => [char, index]));
|
|
925
|
-
function decodeMappings(mappings) {
|
|
926
|
-
const lines = [];
|
|
927
|
-
let line = [];
|
|
928
|
-
let segment = [];
|
|
929
|
-
let index = 0;
|
|
930
|
-
const state = [0, 0, 0, 0, 0];
|
|
931
|
-
while (index < mappings.length) {
|
|
932
|
-
const char = mappings[index];
|
|
933
|
-
if (char === ";") {
|
|
934
|
-
if (segment.length > 0) {
|
|
935
|
-
line.push(segment);
|
|
936
|
-
segment = [];
|
|
937
|
-
}
|
|
938
|
-
lines.push(line);
|
|
939
|
-
line = [];
|
|
940
|
-
state[0] = 0;
|
|
941
|
-
index += 1;
|
|
942
|
-
continue;
|
|
943
|
-
}
|
|
944
|
-
if (char === ",") {
|
|
945
|
-
if (segment.length > 0) {
|
|
946
|
-
line.push(segment);
|
|
947
|
-
segment = [];
|
|
948
|
-
}
|
|
949
|
-
index += 1;
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
const read = readVlq(mappings, index);
|
|
953
|
-
index = read.next;
|
|
954
|
-
const field = segment.length;
|
|
955
|
-
state[field] = (state[field] ?? 0) + read.value;
|
|
956
|
-
segment.push(state[field]);
|
|
957
|
-
}
|
|
958
|
-
if (segment.length > 0) {
|
|
959
|
-
line.push(segment);
|
|
960
|
-
}
|
|
961
|
-
lines.push(line);
|
|
962
|
-
return lines;
|
|
963
|
-
}
|
|
964
|
-
function encodeMappings(lines) {
|
|
965
|
-
const state = [0, 0, 0, 0, 0];
|
|
966
|
-
return lines
|
|
967
|
-
.map((line) => {
|
|
968
|
-
state[0] = 0;
|
|
969
|
-
return line
|
|
970
|
-
.map((segment) => segment
|
|
971
|
-
.map((value, index) => {
|
|
972
|
-
const relative = value - (state[index] ?? 0);
|
|
973
|
-
state[index] = value;
|
|
974
|
-
return writeVlq(relative);
|
|
975
|
-
})
|
|
976
|
-
.join(""))
|
|
977
|
-
.join(",");
|
|
978
|
-
})
|
|
979
|
-
.join(";");
|
|
980
|
-
}
|
|
981
|
-
function readVlq(input, start) {
|
|
982
|
-
let index = start;
|
|
983
|
-
let shift = 0;
|
|
984
|
-
let value = 0;
|
|
985
|
-
while (index < input.length) {
|
|
986
|
-
const digit = BASE64_VALUES.get(input[index]);
|
|
987
|
-
if (digit === undefined) {
|
|
988
|
-
throw new Error(`ttsc: invalid source map VLQ digit ${input[index]}`);
|
|
989
|
-
}
|
|
990
|
-
index += 1;
|
|
991
|
-
const continuation = (digit & 32) !== 0;
|
|
992
|
-
value += (digit & 31) << shift;
|
|
993
|
-
shift += 5;
|
|
994
|
-
if (!continuation) {
|
|
995
|
-
const negative = (value & 1) === 1;
|
|
996
|
-
const decoded = value >> 1;
|
|
997
|
-
return { next: index, value: negative ? -decoded : decoded };
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
throw new Error("ttsc: unterminated source map VLQ segment");
|
|
1001
|
-
}
|
|
1002
|
-
function writeVlq(value) {
|
|
1003
|
-
let vlq = Math.abs(value) << 1;
|
|
1004
|
-
if (value < 0) {
|
|
1005
|
-
vlq |= 1;
|
|
1006
|
-
}
|
|
1007
|
-
let out = "";
|
|
1008
|
-
do {
|
|
1009
|
-
let digit = vlq & 31;
|
|
1010
|
-
vlq >>>= 5;
|
|
1011
|
-
if (vlq > 0) {
|
|
1012
|
-
digit |= 32;
|
|
1013
|
-
}
|
|
1014
|
-
out += BASE64_CHARS[digit];
|
|
1015
|
-
} while (vlq > 0);
|
|
1016
|
-
return out;
|
|
1017
|
-
}
|
|
1018
669
|
//# sourceMappingURL=api.js.map
|