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.
Files changed (84) hide show
  1. package/README.md +75 -66
  2. package/cmd/ttsc/api_compile.go +5 -1
  3. package/driver/program.go +23 -1
  4. package/driver/rewrite.go +126 -9
  5. package/driver/rewrite_test.go +52 -0
  6. package/lib/TtscCompiler.js +28 -46
  7. package/lib/TtscCompiler.js.map +1 -1
  8. package/lib/compiler/internal/buildNativeCompiler.js +8 -38
  9. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  10. package/lib/compiler/internal/compileProjectInMemory.js +36 -63
  11. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  12. package/lib/compiler/internal/project/readProjectConfig.js +108 -60
  13. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  14. package/lib/compiler/internal/project/resolveProjectConfig.js +15 -45
  15. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  16. package/lib/compiler/internal/resolveBinary.js +10 -40
  17. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  18. package/lib/compiler/internal/resolveEmittedJavaScript.js +21 -48
  19. package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
  20. package/lib/compiler/internal/resolveTsgo.js +14 -44
  21. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  22. package/lib/compiler/internal/runBuild.d.ts +7 -0
  23. package/lib/compiler/internal/runBuild.js +47 -66
  24. package/lib/compiler/internal/runBuild.js.map +1 -1
  25. package/lib/compiler/internal/runSingleFileEmit.js +18 -48
  26. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  27. package/lib/compiler/internal/transformProjectInMemory.js +41 -75
  28. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  29. package/lib/launcher/internal/getCompilerVersionText.js +7 -37
  30. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  31. package/lib/launcher/internal/prepareExecution.js +48 -69
  32. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  33. package/lib/launcher/internal/runTtsc.js +53 -64
  34. package/lib/launcher/internal/runTtsc.js.map +1 -1
  35. package/lib/launcher/internal/runTtsx.js +328 -57
  36. package/lib/launcher/internal/runTtsx.js.map +1 -1
  37. package/lib/plugin/internal/buildSourcePlugin.d.ts +8 -0
  38. package/lib/plugin/internal/buildSourcePlugin.js +179 -125
  39. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  40. package/lib/plugin/internal/loadProjectPlugins.d.ts +2 -0
  41. package/lib/plugin/internal/loadProjectPlugins.js +197 -9
  42. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  43. package/lib/structures/ITtscCompilerContext.d.ts +16 -3
  44. package/lib/structures/ITtscCompilerResult.d.ts +4 -0
  45. package/lib/structures/ITtscCompilerTransformation.d.ts +2 -0
  46. package/lib/structures/ITtscPluginFactoryContext.d.ts +6 -3
  47. package/lib/structures/ITtscProjectPluginConfig.d.ts +8 -7
  48. package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
  49. package/lib/structures/internal/ITtscProjectLocatorOptions.d.ts +2 -0
  50. package/lib/structures/internal/TtscCommonOptions.d.ts +2 -0
  51. package/package.json +9 -14
  52. package/shim/diagnosticwriter/lint.go +8 -0
  53. package/src/TtscCompiler.ts +20 -5
  54. package/src/compiler/internal/buildNativeCompiler.ts +2 -2
  55. package/src/compiler/internal/compileProjectInMemory.ts +21 -16
  56. package/src/compiler/internal/project/readProjectConfig.ts +134 -35
  57. package/src/compiler/internal/project/resolveProjectConfig.ts +2 -2
  58. package/src/compiler/internal/resolveBinary.ts +2 -2
  59. package/src/compiler/internal/resolveEmittedJavaScript.ts +11 -7
  60. package/src/compiler/internal/resolveTsgo.ts +2 -2
  61. package/src/compiler/internal/runBuild.ts +34 -21
  62. package/src/compiler/internal/runSingleFileEmit.ts +3 -3
  63. package/src/compiler/internal/transformProjectInMemory.ts +37 -37
  64. package/src/launcher/internal/getCompilerVersionText.ts +2 -2
  65. package/src/launcher/internal/prepareExecution.ts +14 -4
  66. package/src/launcher/internal/runTtsc.ts +30 -4
  67. package/src/launcher/internal/runTtsx.ts +377 -26
  68. package/src/plugin/internal/buildSourcePlugin.ts +127 -32
  69. package/src/plugin/internal/loadProjectPlugins.ts +266 -10
  70. package/src/structures/ITtscCompilerContext.ts +17 -3
  71. package/src/structures/ITtscCompilerResult.ts +5 -0
  72. package/src/structures/ITtscCompilerTransformation.ts +3 -0
  73. package/src/structures/ITtscPluginFactoryContext.ts +6 -3
  74. package/src/structures/ITtscProjectPluginConfig.ts +8 -7
  75. package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
  76. package/src/structures/internal/ITtscProjectLocatorOptions.ts +2 -0
  77. package/src/structures/internal/TtscCommonOptions.ts +2 -0
  78. package/utility/host.go +610 -18
  79. package/utility/host_test.go +162 -0
  80. package/test/compiler.test.ts +0 -422
  81. package/test/platform.test.ts +0 -12
  82. package/test/project.test.ts +0 -197
  83. package/test/source-build.test.ts +0 -45
  84. package/test/tsgo.test.ts +0 -69
@@ -1,6 +1,6 @@
1
- import * as fs from "node:fs";
1
+ import fs from "node:fs";
2
2
  import { createRequire } from "node:module";
3
- import * as path from "node:path";
3
+ import path from "node:path";
4
4
 
5
5
  import type { ITtscProjectPluginConfig } from "../../../structures/ITtscProjectPluginConfig";
6
6
  import type { ITtscParsedProjectConfig } from "../../../structures/internal/ITtscParsedProjectConfig";
@@ -8,12 +8,25 @@ import type { ITtscProjectLocatorOptions } from "../../../structures/internal/IT
8
8
 
9
9
  import { resolveProjectConfig } from "./resolveProjectConfig";
10
10
 
11
+ const PATH_OPTIONS = new Set(["baseUrl", "declarationDir", "rootDir"]);
12
+
13
+ type ResolvedCompilerOptions = {
14
+ options: Record<string, unknown>;
15
+ optionBaseDirs: Record<string, string>;
16
+ outDir?: string;
17
+ pluginBaseDirs: string[];
18
+ pluginsDeclared: boolean;
19
+ plugins: ITtscProjectPluginConfig[];
20
+ };
21
+
11
22
  /** Read the resolved project config subset used by ttsc. */
12
23
  export function readProjectConfig(
13
24
  opts: ITtscProjectLocatorOptions = {},
14
25
  ): ITtscParsedProjectConfig {
15
26
  const tsconfig = resolveProjectConfig(opts);
16
- const root = path.dirname(tsconfig);
27
+ const root = opts.projectRoot
28
+ ? path.resolve(opts.cwd ?? process.cwd(), opts.projectRoot)
29
+ : path.dirname(tsconfig);
17
30
  const compilerOptions = readResolvedCompilerOptions(tsconfig);
18
31
  return {
19
32
  compilerOptions: {
@@ -22,6 +35,7 @@ export function readProjectConfig(
22
35
  plugins: compilerOptions.plugins,
23
36
  },
24
37
  path: tsconfig,
38
+ pluginBaseDirs: compilerOptions.pluginBaseDirs,
25
39
  root,
26
40
  };
27
41
  }
@@ -47,46 +61,131 @@ function resolveAbsolutePath(cwd: string, target: string): string {
47
61
  function readResolvedCompilerOptions(
48
62
  tsconfig: string,
49
63
  seen: Set<string> = new Set(),
50
- ): {
51
- options: Record<string, unknown>;
52
- outDir?: string;
53
- plugins: ITtscProjectPluginConfig[];
54
- } {
64
+ ): ResolvedCompilerOptions {
55
65
  const canonical = resolveRealPath(tsconfig);
56
66
  if (seen.has(canonical)) {
57
67
  throw new Error(`ttsc: circular tsconfig extends detected: ${canonical}`);
58
68
  }
59
69
  seen.add(canonical);
70
+ try {
71
+ const parsed = parseJsonc(fs.readFileSync(canonical, "utf8")) as {
72
+ extends?: unknown;
73
+ compilerOptions?: Record<string, unknown> & {
74
+ outDir?: unknown;
75
+ plugins?: unknown;
76
+ };
77
+ };
78
+ const own = parsed.compilerOptions;
79
+ const base = resolveBaseCompilerOptions(canonical, parsed.extends, seen);
80
+ const ownBaseDir = path.dirname(canonical);
81
+ const ownOptionBaseDirs =
82
+ own === undefined
83
+ ? {}
84
+ : Object.fromEntries(
85
+ Object.keys(own).map((key) => [key, ownBaseDir] as const),
86
+ );
87
+ const optionBaseDirs = {
88
+ ...base.optionBaseDirs,
89
+ ...ownOptionBaseDirs,
90
+ };
91
+ const options = resolvePathOptions(
92
+ {
93
+ ...base.options,
94
+ ...(own ?? {}),
95
+ },
96
+ optionBaseDirs,
97
+ );
98
+ const ownPlugins = own?.plugins;
99
+ const pluginsDeclared = Array.isArray(ownPlugins);
100
+ const plugins = pluginsDeclared
101
+ ? ownPlugins.filter(isProjectPluginConfig)
102
+ : base.plugins;
103
+ return {
104
+ optionBaseDirs,
105
+ options,
106
+ outDir:
107
+ typeof own?.outDir === "string"
108
+ ? resolveAbsolutePath(ownBaseDir, own.outDir)
109
+ : base.outDir,
110
+ pluginBaseDirs: pluginsDeclared
111
+ ? plugins.map(() => ownBaseDir)
112
+ : base.pluginBaseDirs,
113
+ pluginsDeclared: pluginsDeclared || base.pluginsDeclared,
114
+ plugins,
115
+ };
116
+ } finally {
117
+ seen.delete(canonical);
118
+ }
119
+ }
60
120
 
61
- const parsed = parseJsonc(fs.readFileSync(canonical, "utf8")) as {
62
- extends?: unknown;
63
- compilerOptions?: Record<string, unknown> & {
64
- outDir?: unknown;
65
- plugins?: unknown;
121
+ function resolveBaseCompilerOptions(
122
+ tsconfig: string,
123
+ extended: unknown,
124
+ seen: Set<string>,
125
+ ): ResolvedCompilerOptions {
126
+ if (typeof extended === "string") {
127
+ return readResolvedCompilerOptions(
128
+ resolveExtendsConfig(tsconfig, extended),
129
+ seen,
130
+ );
131
+ }
132
+ if (!Array.isArray(extended)) {
133
+ return {
134
+ optionBaseDirs: {},
135
+ options: {},
136
+ pluginBaseDirs: [],
137
+ pluginsDeclared: false,
138
+ plugins: [],
66
139
  };
140
+ }
141
+ let merged: ResolvedCompilerOptions = {
142
+ optionBaseDirs: {},
143
+ options: {},
144
+ pluginBaseDirs: [],
145
+ pluginsDeclared: false,
146
+ plugins: [],
67
147
  };
68
- const own = parsed.compilerOptions;
69
- const base =
70
- typeof parsed.extends === "string"
71
- ? readResolvedCompilerOptions(
72
- resolveExtendsConfig(canonical, parsed.extends),
73
- seen,
74
- )
75
- : { options: {}, plugins: [] };
76
- const options = {
77
- ...base.options,
78
- ...(own ?? {}),
79
- };
80
- return {
81
- options,
82
- outDir:
83
- typeof own?.outDir === "string"
84
- ? resolveAbsolutePath(path.dirname(canonical), own.outDir)
85
- : base.outDir,
86
- plugins: Array.isArray(own?.plugins)
87
- ? own.plugins.filter(isProjectPluginConfig)
88
- : base.plugins,
89
- };
148
+ for (const specifier of extended) {
149
+ if (typeof specifier !== "string") {
150
+ continue;
151
+ }
152
+ const current = readResolvedCompilerOptions(
153
+ resolveExtendsConfig(tsconfig, specifier),
154
+ seen,
155
+ );
156
+ merged = {
157
+ optionBaseDirs: {
158
+ ...merged.optionBaseDirs,
159
+ ...current.optionBaseDirs,
160
+ },
161
+ options: {
162
+ ...merged.options,
163
+ ...current.options,
164
+ },
165
+ outDir: current.outDir ?? merged.outDir,
166
+ pluginBaseDirs: current.pluginsDeclared
167
+ ? current.pluginBaseDirs
168
+ : merged.pluginBaseDirs,
169
+ plugins: current.pluginsDeclared ? current.plugins : merged.plugins,
170
+ pluginsDeclared: merged.pluginsDeclared || current.pluginsDeclared,
171
+ };
172
+ }
173
+ return merged;
174
+ }
175
+
176
+ function resolvePathOptions(
177
+ options: Record<string, unknown>,
178
+ baseDirs: Record<string, string>,
179
+ ): Record<string, unknown> {
180
+ const resolved = { ...options };
181
+ for (const key of PATH_OPTIONS) {
182
+ const value = resolved[key];
183
+ const baseDir = baseDirs[key];
184
+ if (typeof value === "string" && baseDir !== undefined) {
185
+ resolved[key] = resolveAbsolutePath(baseDir, value);
186
+ }
187
+ }
188
+ return resolved;
90
189
  }
91
190
 
92
191
  function resolveExtendsConfig(tsconfig: string, specifier: string): string {
@@ -1,5 +1,5 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
3
 
4
4
  import type { ITtscProjectLocatorOptions } from "../../../structures/internal/ITtscProjectLocatorOptions";
5
5
 
@@ -1,5 +1,5 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
3
 
4
4
  /** Resolve the ttsc helper binary path, or null when unavailable. */
5
5
  export function resolveBinary(
@@ -1,5 +1,5 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
3
 
4
4
  /** Locate the JavaScript file emitted for a TypeScript source file. */
5
5
  export function resolveEmittedJavaScript(options: {
@@ -37,11 +37,7 @@ function resolveExactEmittedFile(
37
37
  sourceFile: string,
38
38
  ): string | null {
39
39
  const relative = path.relative(projectRoot, sourceFile);
40
- if (
41
- relative === "" ||
42
- relative.startsWith("..") ||
43
- path.isAbsolute(relative)
44
- ) {
40
+ if (relative === "" || isOutsideRelativePath(relative)) {
45
41
  return null;
46
42
  }
47
43
  return path.resolve(
@@ -87,6 +83,14 @@ function sharedSourceStemSegments(outPath: string, srcPath: string): number {
87
83
  return shared;
88
84
  }
89
85
 
86
+ function isOutsideRelativePath(relative: string): boolean {
87
+ return (
88
+ relative === ".." ||
89
+ relative.startsWith(`..${path.sep}`) ||
90
+ path.isAbsolute(relative)
91
+ );
92
+ }
93
+
90
94
  function emittedJavaScriptExtension(filename: string): string {
91
95
  switch (path.extname(filename).toLowerCase()) {
92
96
  case ".mts":
@@ -1,6 +1,6 @@
1
- import * as fs from "node:fs";
1
+ import fs from "node:fs";
2
2
  import { createRequire } from "node:module";
3
- import * as path from "node:path";
3
+ import path from "node:path";
4
4
 
5
5
  /** Resolve the consumer project's TypeScript-Go preview binary. */
6
6
  export function resolveTsgo(
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
5
 
6
6
  import { loadProjectPlugins } from "../../plugin/internal/loadProjectPlugins";
7
7
  import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiagnostic";
@@ -112,10 +112,10 @@ export function runBuild(
112
112
  buildWithNativeCompilerPlugins(options, execution, compilers),
113
113
  );
114
114
  }
115
- if (checked.stdout !== "" || checked.stderr !== "") {
116
- return checked;
117
- }
118
- return runTsgo(execution, ["--noEmit"], options);
115
+ return appendBuildOutput(
116
+ checked,
117
+ runTsgo(execution, ["--noEmit"], options),
118
+ );
119
119
  }
120
120
 
121
121
  let result: TtscBuildResult;
@@ -126,14 +126,10 @@ export function runBuild(
126
126
  buildWithNativeCompilerPlugins(options, execution, compilers),
127
127
  );
128
128
  } else {
129
- if (
130
- checked.stdout === "" &&
131
- checked.stderr === "" &&
132
- options.skipDiagnosticsCheck !== true
133
- ) {
129
+ if (options.skipDiagnosticsCheck !== true) {
134
130
  const tsgoChecked = runTsgo(execution, ["--noEmit"], options);
135
131
  if (tsgoChecked.status !== 0) {
136
- return tsgoChecked;
132
+ return appendBuildOutput(checked, tsgoChecked);
137
133
  }
138
134
  }
139
135
  const args = createTsgoBuildArgs(execution, options, {
@@ -201,7 +197,7 @@ function runTsgo(
201
197
  res.error.message,
202
198
  );
203
199
  }
204
- return normalizeFailedDiagnostics(
200
+ return normalizeBuildOutput(
205
201
  {
206
202
  status: res.status ?? 1,
207
203
  stdout: outputText(res.stdout),
@@ -238,7 +234,7 @@ function runTsgoBuild(
238
234
  if (emittedFiles.length !== 0) {
239
235
  result.stdout = stripEmittedFileLines(result.stdout);
240
236
  }
241
- return normalizeFailedDiagnostics(
237
+ return normalizeBuildOutput(
242
238
  { ...result, emittedFiles },
243
239
  execution.projectRoot,
244
240
  );
@@ -374,7 +370,7 @@ function runNativePluginCommand(
374
370
  `${label}: failed to spawn ${plugin.binary}: ${res.error.message}`,
375
371
  );
376
372
  }
377
- return normalizeFailedDiagnostics(
373
+ return normalizeBuildOutput(
378
374
  {
379
375
  status: res.status ?? 1,
380
376
  stdout: outputText(res.stdout),
@@ -384,11 +380,11 @@ function runNativePluginCommand(
384
380
  );
385
381
  }
386
382
 
387
- function appendBuildOutput(
383
+ export function appendBuildOutput(
388
384
  left: TtscBuildResult,
389
385
  right: TtscBuildResult,
390
386
  ): TtscBuildResult {
391
- return normalizeFailedDiagnostics({
387
+ return normalizeBuildOutput({
392
388
  diagnostics: [...left.diagnostics, ...right.diagnostics],
393
389
  emittedFiles:
394
390
  right.emittedFiles !== undefined ? right.emittedFiles : left.emittedFiles,
@@ -461,7 +457,9 @@ function resolveExecutionContext(
461
457
  cwd,
462
458
  tsconfig: options.tsconfig,
463
459
  });
464
- const projectRoot = path.dirname(tsconfig);
460
+ const projectRoot = options.projectRoot
461
+ ? path.resolve(cwd, options.projectRoot)
462
+ : path.dirname(tsconfig);
465
463
  const tsgo = resolveTsgo({ ...options, cwd: projectRoot });
466
464
  const fallbackBinary = resolveBinary(options);
467
465
  const loaded = loadProjectPlugins({
@@ -469,6 +467,7 @@ function resolveExecutionContext(
469
467
  cacheDir: options.cacheDir ?? options.env?.TTSC_CACHE_DIR,
470
468
  cwd,
471
469
  entries: options.plugins,
470
+ projectRoot,
472
471
  tsconfig,
473
472
  });
474
473
  return {
@@ -503,7 +502,7 @@ type PartialBuildResult = Omit<TtscBuildResult, "diagnostics"> & {
503
502
  diagnostics?: ITtscCompilerDiagnostic[];
504
503
  };
505
504
 
506
- function normalizeFailedDiagnostics(
505
+ export function normalizeBuildOutput(
507
506
  result: PartialBuildResult,
508
507
  cwd?: string,
509
508
  ): TtscBuildResult {
@@ -557,6 +556,20 @@ function parseDiagnosticLine(
557
556
  line: string,
558
557
  cwd: string | undefined,
559
558
  ): ITtscCompilerDiagnostic | null {
559
+ const colonMatch = line.match(
560
+ /^(.+):(\d+):(\d+)\s+-\s+(error|warning|suggestion|message)\s+([A-Z]+)?(\d+|[A-Z][A-Z0-9_-]*):\s+(.+)$/i,
561
+ );
562
+ if (colonMatch) {
563
+ return {
564
+ category: normalizeDiagnosticCategory(colonMatch[4]!),
565
+ character: Number(colonMatch[3]),
566
+ code: normalizeDiagnosticCode(colonMatch[6]!),
567
+ file: normalizeDiagnosticFile(colonMatch[1]!, cwd),
568
+ line: Number(colonMatch[2]),
569
+ messageText: colonMatch[7]!,
570
+ };
571
+ }
572
+
560
573
  const fileMatch = line.match(
561
574
  /^(.+?)\((\d+),(\d+)\):\s+(error|warning|suggestion|message)\s+([A-Z]+)?(\d+|[A-Z][A-Z0-9_-]*):\s+(.+)$/i,
562
575
  );
@@ -611,5 +624,5 @@ function normalizeDiagnosticCode(value: string): number | string {
611
624
  }
612
625
 
613
626
  function stripAnsi(text: string): string {
614
- return text.replace(/\x1b\[[0-9;]*m/g, "");
627
+ return text.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
615
628
  }
@@ -1,6 +1,6 @@
1
- import * as fs from "node:fs";
2
- import * as os from "node:os";
3
- import * as path from "node:path";
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
4
 
5
5
  import type { TtscSingleFileEmitOptions } from "../../structures/internal/TtscSingleFileEmitOptions";
6
6
  import { resolveProjectConfig } from "./project/resolveProjectConfig";
@@ -1,11 +1,13 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import * as fs from "node:fs";
3
- import * as path from "node:path";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
4
 
5
- import { loadProjectPlugins } from "../../plugin/internal/loadProjectPlugins";
5
+ import {
6
+ hasProjectPluginEntries,
7
+ loadProjectPlugins,
8
+ } from "../../plugin/internal/loadProjectPlugins";
6
9
  import type { ITtscCompilerContext } from "../../structures/ITtscCompilerContext";
7
10
  import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiagnostic";
8
- import type { ITtscProjectPluginConfig } from "../../structures/ITtscProjectPluginConfig";
9
11
  import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoadedNativePlugin";
10
12
  import type { ITtscParsedProjectConfig } from "../../structures/internal/ITtscParsedProjectConfig";
11
13
  import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
@@ -13,6 +15,7 @@ import { buildNativeCompiler } from "./buildNativeCompiler";
13
15
  import { readProjectConfig } from "./project/readProjectConfig";
14
16
  import { resolveBinary } from "./resolveBinary";
15
17
  import { resolveTsgo } from "./resolveTsgo";
18
+ import { appendBuildOutput, normalizeBuildOutput } from "./runBuild";
16
19
 
17
20
  /** Transform a project and capture TypeScript source output in memory. */
18
21
  export function transformProjectInMemory(options: ITtscCompilerContext): {
@@ -22,23 +25,20 @@ export function transformProjectInMemory(options: ITtscCompilerContext): {
22
25
  const cwd = path.resolve(options.cwd ?? process.cwd());
23
26
  const project = readProjectConfig({
24
27
  cwd,
28
+ projectRoot: options.projectRoot,
25
29
  tsconfig: options.tsconfig,
26
30
  });
27
- if (configuredPlugins(options, project).length !== 0) {
31
+ if (hasConfiguredPlugins(options, project)) {
28
32
  return transformProjectWithPlugins(options, cwd, project);
29
33
  }
30
34
  return transformProjectWithNativeHost(options, project);
31
35
  }
32
36
 
33
- function configuredPlugins(
37
+ function hasConfiguredPlugins(
34
38
  options: ITtscCompilerContext,
35
39
  project: ITtscParsedProjectConfig,
36
- ): ITtscProjectPluginConfig[] {
37
- if (options.plugins === false) {
38
- return [];
39
- }
40
- const entries = options.plugins ?? project.compilerOptions.plugins;
41
- return entries.filter((entry) => entry.enabled !== false);
40
+ ): boolean {
41
+ return hasProjectPluginEntries(project, options.plugins);
42
42
  }
43
43
 
44
44
  function transformProjectWithNativeHost(
@@ -95,6 +95,7 @@ function transformProjectWithPlugins(
95
95
  cacheDir: options.cacheDir ?? options.env?.TTSC_CACHE_DIR,
96
96
  cwd,
97
97
  entries: options.plugins,
98
+ projectRoot: options.projectRoot,
98
99
  tsconfig: project.path,
99
100
  });
100
101
  const checks = loaded.nativePlugins.filter(
@@ -116,7 +117,11 @@ function transformProjectWithPlugins(
116
117
  };
117
118
  }
118
119
  if (transformers.length === 0) {
119
- return transformProjectWithNativeHost(options, project);
120
+ const transformed = transformProjectWithNativeHost(options, project);
121
+ return {
122
+ result: appendBuildOutput(checked, transformed.result),
123
+ typescript: transformed.typescript,
124
+ };
120
125
  }
121
126
  assertTransformHostCompatibility(transformers);
122
127
 
@@ -138,13 +143,14 @@ function transformProjectWithPlugins(
138
143
  outputText(res.stdout),
139
144
  outputText(res.stderr),
140
145
  );
146
+ const result = {
147
+ diagnostics: output.diagnostics,
148
+ status: res.status ?? 1,
149
+ stdout: "",
150
+ stderr: outputText(res.stderr),
151
+ };
141
152
  return {
142
- result: {
143
- diagnostics: output.diagnostics,
144
- status: res.status ?? 1,
145
- stdout: "",
146
- stderr: outputText(res.stderr),
147
- },
153
+ result: appendBuildOutput(checked, result),
148
154
  typescript: output.typescript,
149
155
  };
150
156
  }
@@ -175,12 +181,17 @@ function runNativeChecks(
175
181
  `ttsc.transform.check: failed to spawn ${plugin.binary}: ${res.error.message}`,
176
182
  );
177
183
  }
178
- result = appendBuildResult(result, {
179
- diagnostics: [],
180
- status: res.status ?? 1,
181
- stdout: outputText(res.stdout),
182
- stderr: outputText(res.stderr),
183
- });
184
+ result = appendBuildOutput(
185
+ result,
186
+ normalizeBuildOutput(
187
+ {
188
+ status: res.status ?? 1,
189
+ stdout: outputText(res.stdout),
190
+ stderr: outputText(res.stderr),
191
+ },
192
+ project.root,
193
+ ),
194
+ );
184
195
  if (result.status !== 0) {
185
196
  return result;
186
197
  }
@@ -279,18 +290,6 @@ function readNearestPackageManifest(
279
290
  return undefined;
280
291
  }
281
292
 
282
- function appendBuildResult(
283
- left: TtscBuildResult,
284
- right: TtscBuildResult,
285
- ): TtscBuildResult {
286
- return {
287
- diagnostics: [...left.diagnostics, ...right.diagnostics],
288
- status: right.status !== 0 ? right.status : left.status,
289
- stdout: left.stdout + right.stdout,
290
- stderr: left.stderr + right.stderr,
291
- };
292
- }
293
-
294
293
  function nativePluginEnv(
295
294
  options: ITtscCompilerContext,
296
295
  projectRoot: string,
@@ -390,6 +389,7 @@ function isTextRecord(value: unknown): value is Record<string, string> {
390
389
  return (
391
390
  typeof value === "object" &&
392
391
  value !== null &&
392
+ !Array.isArray(value) &&
393
393
  Object.values(value).every((entry) => typeof entry === "string")
394
394
  );
395
395
  }
@@ -1,6 +1,6 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import * as fs from "node:fs";
3
- import * as path from "node:path";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
4
 
5
5
  import { resolveTsgo } from "../../compiler/internal/resolveTsgo";
6
6
  import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOptions";
@@ -1,6 +1,6 @@
1
- import * as fs from "node:fs";
2
- import * as os from "node:os";
3
- import * as path from "node:path";
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
4
 
5
5
  import { resolveEmittedJavaScript } from "../../compiler/internal/resolveEmittedJavaScript";
6
6
  import { runBuild } from "../../compiler/internal/runBuild";
@@ -57,8 +57,9 @@ function createProjectContext(
57
57
  );
58
58
  const tsconfig = project.path;
59
59
  const root = project.root;
60
+ const explicitCacheDir = resolveCacheDir(cwd, options.cacheDir);
60
61
  const cacheDir =
61
- options.cacheDir ??
62
+ explicitCacheDir ??
62
63
  path.join(root, "node_modules", ".cache", "ttsc", "ttsx");
63
64
  const processDir = path.join(cacheDir, "project", PROCESS_CACHE_KEY);
64
65
  const virtualRoot = path.join(processDir, "fs");
@@ -67,6 +68,7 @@ function createProjectContext(
67
68
  root,
68
69
  cacheDir,
69
70
  processDir,
71
+ pluginCacheDir: explicitCacheDir,
70
72
  virtualRoot,
71
73
  emitDir: project.compilerOptions.outDir
72
74
  ? virtualPath(virtualRoot, project.compilerOptions.outDir)
@@ -104,6 +106,7 @@ function buildProject(
104
106
  emit: true,
105
107
  env: options.env,
106
108
  forceListEmittedFiles: true,
109
+ cacheDir: context.pluginCacheDir,
107
110
  outDir: context.emitDir,
108
111
  plugins: options.plugins,
109
112
  quiet: true,
@@ -129,6 +132,13 @@ function buildProject(
129
132
  throw new Error(detail);
130
133
  }
131
134
 
135
+ function resolveCacheDir(cwd: string, cacheDir?: string): string | undefined {
136
+ if (!cacheDir) {
137
+ return undefined;
138
+ }
139
+ return path.isAbsolute(cacheDir) ? cacheDir : path.resolve(cwd, cacheDir);
140
+ }
141
+
132
142
  function linkVirtualProjectLayout(
133
143
  context: ReturnType<typeof createProjectContext>,
134
144
  ): void {