ttsc 0.8.1 → 0.10.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 (88) hide show
  1. package/README.md +39 -68
  2. package/cmd/ttsc/api_compile.go +109 -115
  3. package/cmd/ttsc/api_transform.go +54 -51
  4. package/cmd/ttsc/main.go +103 -121
  5. package/driver/program.go +30 -2
  6. package/driver/rewrite.go +126 -9
  7. package/go.mod +2 -0
  8. package/lib/TtscCompiler.js +28 -46
  9. package/lib/TtscCompiler.js.map +1 -1
  10. package/lib/compiler/internal/buildNativeCompiler.js +8 -38
  11. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  12. package/lib/compiler/internal/compileProjectInMemory.js +36 -63
  13. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  14. package/lib/compiler/internal/project/readProjectConfig.js +108 -60
  15. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  16. package/lib/compiler/internal/project/resolveProjectConfig.js +15 -45
  17. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  18. package/lib/compiler/internal/resolveBinary.js +10 -40
  19. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  20. package/lib/compiler/internal/resolveEmittedJavaScript.js +21 -48
  21. package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
  22. package/lib/compiler/internal/resolveTsgo.js +14 -44
  23. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  24. package/lib/compiler/internal/runBuild.d.ts +7 -0
  25. package/lib/compiler/internal/runBuild.js +47 -66
  26. package/lib/compiler/internal/runBuild.js.map +1 -1
  27. package/lib/compiler/internal/runSingleFileEmit.js +18 -48
  28. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  29. package/lib/compiler/internal/transformProjectInMemory.js +41 -75
  30. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  31. package/lib/launcher/internal/getCompilerVersionText.js +7 -37
  32. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  33. package/lib/launcher/internal/prepareExecution.js +48 -69
  34. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  35. package/lib/launcher/internal/runTtsc.js +53 -64
  36. package/lib/launcher/internal/runTtsc.js.map +1 -1
  37. package/lib/launcher/internal/runTtsx.js +328 -57
  38. package/lib/launcher/internal/runTtsx.js.map +1 -1
  39. package/lib/plugin/internal/buildSourcePlugin.d.ts +8 -0
  40. package/lib/plugin/internal/buildSourcePlugin.js +287 -139
  41. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  42. package/lib/plugin/internal/loadProjectPlugins.d.ts +2 -0
  43. package/lib/plugin/internal/loadProjectPlugins.js +197 -9
  44. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  45. package/lib/structures/ITtscCompilerContext.d.ts +16 -3
  46. package/lib/structures/ITtscCompilerResult.d.ts +4 -0
  47. package/lib/structures/ITtscCompilerTransformation.d.ts +2 -0
  48. package/lib/structures/ITtscPluginFactoryContext.d.ts +6 -3
  49. package/lib/structures/ITtscProjectPluginConfig.d.ts +8 -7
  50. package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
  51. package/lib/structures/internal/ITtscProjectLocatorOptions.d.ts +2 -0
  52. package/lib/structures/internal/TtscCommonOptions.d.ts +2 -0
  53. package/package.json +9 -14
  54. package/shim/diagnosticwriter/lint.go +8 -0
  55. package/shim/printer/go.mod +5 -0
  56. package/shim/printer/shim.go +23 -0
  57. package/src/TtscCompiler.ts +20 -5
  58. package/src/compiler/internal/buildNativeCompiler.ts +2 -2
  59. package/src/compiler/internal/compileProjectInMemory.ts +21 -16
  60. package/src/compiler/internal/project/readProjectConfig.ts +134 -35
  61. package/src/compiler/internal/project/resolveProjectConfig.ts +2 -2
  62. package/src/compiler/internal/resolveBinary.ts +2 -2
  63. package/src/compiler/internal/resolveEmittedJavaScript.ts +11 -7
  64. package/src/compiler/internal/resolveTsgo.ts +2 -2
  65. package/src/compiler/internal/runBuild.ts +34 -21
  66. package/src/compiler/internal/runSingleFileEmit.ts +3 -3
  67. package/src/compiler/internal/transformProjectInMemory.ts +37 -37
  68. package/src/launcher/internal/getCompilerVersionText.ts +2 -2
  69. package/src/launcher/internal/prepareExecution.ts +14 -4
  70. package/src/launcher/internal/runTtsc.ts +30 -4
  71. package/src/launcher/internal/runTtsx.ts +377 -26
  72. package/src/plugin/internal/buildSourcePlugin.ts +321 -50
  73. package/src/plugin/internal/loadProjectPlugins.ts +266 -10
  74. package/src/structures/ITtscCompilerContext.ts +17 -3
  75. package/src/structures/ITtscCompilerResult.ts +5 -0
  76. package/src/structures/ITtscCompilerTransformation.ts +3 -0
  77. package/src/structures/ITtscPluginFactoryContext.ts +6 -3
  78. package/src/structures/ITtscProjectPluginConfig.ts +8 -7
  79. package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
  80. package/src/structures/internal/ITtscProjectLocatorOptions.ts +2 -0
  81. package/src/structures/internal/TtscCommonOptions.ts +2 -0
  82. package/utility/host.go +622 -20
  83. package/cmd/ttsc/main_test.go +0 -288
  84. package/test/compiler.test.ts +0 -422
  85. package/test/platform.test.ts +0 -12
  86. package/test/project.test.ts +0 -197
  87. package/test/source-build.test.ts +0 -45
  88. package/test/tsgo.test.ts +0 -69
@@ -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 { compileProjectInMemory } from "./compiler/internal/compileProjectInMemory";
5
5
  import { resolveProjectConfig } from "./compiler/internal/project/resolveProjectConfig";
@@ -66,6 +66,7 @@ export class TtscCompiler {
66
66
  cacheDir: this.resolvePluginCacheDir(),
67
67
  cwd: execution.cwd,
68
68
  entries: this.context.plugins,
69
+ projectRoot: execution.projectRoot,
69
70
  tsconfig: execution.tsconfig,
70
71
  });
71
72
  return loaded.nativePlugins.map((plugin) => plugin.binary);
@@ -161,7 +162,9 @@ export class TtscCompiler {
161
162
  });
162
163
  return {
163
164
  cwd,
164
- projectRoot: path.dirname(tsconfig),
165
+ projectRoot: this.context.projectRoot
166
+ ? path.resolve(cwd, this.context.projectRoot)
167
+ : path.dirname(tsconfig),
165
168
  tsconfig,
166
169
  };
167
170
  }
@@ -243,8 +246,11 @@ function runTransformation(
243
246
 
244
247
  function toCompilerResult(project: ProjectResult): ITtscCompilerResult {
245
248
  const { output, result } = project;
246
- if (result.status === 0 && result.diagnostics.length === 0) {
249
+ if (result.status === 0 && !hasErrorDiagnostics(result.diagnostics)) {
247
250
  return {
251
+ ...(result.diagnostics.length === 0
252
+ ? {}
253
+ : { diagnostics: result.diagnostics }),
248
254
  output,
249
255
  type: "success",
250
256
  };
@@ -277,8 +283,11 @@ function toCompilerTransformation(
277
283
  project: ProjectTransformation,
278
284
  ): ITtscCompilerTransformation {
279
285
  const { result, typescript } = project;
280
- if (result.status === 0 && result.diagnostics.length === 0) {
286
+ if (result.status === 0 && !hasErrorDiagnostics(result.diagnostics)) {
281
287
  return {
288
+ ...(result.diagnostics.length === 0
289
+ ? {}
290
+ : { diagnostics: result.diagnostics }),
282
291
  type: "success",
283
292
  typescript,
284
293
  };
@@ -293,6 +302,12 @@ function toCompilerTransformation(
293
302
  };
294
303
  }
295
304
 
305
+ function hasErrorDiagnostics(
306
+ diagnostics: readonly ITtscCompilerDiagnostic[],
307
+ ): boolean {
308
+ return diagnostics.some((diagnostic) => diagnostic.category === "error");
309
+ }
310
+
296
311
  function normalizeError(error: unknown): unknown {
297
312
  if (!(error instanceof Error)) {
298
313
  return error;
@@ -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 { buildSourcePlugin } from "../../plugin/internal/buildSourcePlugin";
5
5
 
@@ -1,11 +1,11 @@
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
+ import { hasProjectPluginEntries } from "../../plugin/internal/loadProjectPlugins";
6
7
  import type { ITtscCompilerContext } from "../../structures/ITtscCompilerContext";
7
8
  import type { ITtscCompilerDiagnostic } from "../../structures/ITtscCompilerDiagnostic";
8
- import type { ITtscProjectPluginConfig } from "../../structures/ITtscProjectPluginConfig";
9
9
  import type { ITtscParsedProjectConfig } from "../../structures/internal/ITtscParsedProjectConfig";
10
10
  import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
11
11
  import { buildNativeCompiler } from "./buildNativeCompiler";
@@ -20,22 +20,23 @@ export function compileProjectInMemory(options: ITtscCompilerContext): {
20
20
  const cwd = path.resolve(options.cwd ?? process.cwd());
21
21
  const project = readProjectConfig({
22
22
  cwd,
23
+ projectRoot: options.projectRoot,
23
24
  tsconfig: options.tsconfig,
24
25
  });
25
- if (configuredPlugins(options, project).length !== 0) {
26
+ if (hasConfiguredPlugins(options, project)) {
26
27
  return compileProjectWithPlugins(options, cwd, project);
27
28
  }
28
29
  const tsconfig = project.path;
29
30
  const binary = buildNativeCompiler({
30
- cacheBaseDir: path.dirname(tsconfig),
31
+ cacheBaseDir: project.root,
31
32
  cacheDir: options.cacheDir,
32
33
  packageRoot: packageRootDir(),
33
34
  });
34
35
  const res = spawnSync(
35
36
  binary,
36
- ["api-compile", "--cwd", path.dirname(tsconfig), "--tsconfig", tsconfig],
37
+ ["api-compile", "--cwd", project.root, "--tsconfig", tsconfig],
37
38
  {
38
- cwd: path.dirname(tsconfig),
39
+ cwd: project.root,
39
40
  encoding: "utf8",
40
41
  env: { ...process.env, ...options.env },
41
42
  maxBuffer: 1024 * 1024 * 256,
@@ -60,15 +61,11 @@ export function compileProjectInMemory(options: ITtscCompilerContext): {
60
61
  };
61
62
  }
62
63
 
63
- function configuredPlugins(
64
+ function hasConfiguredPlugins(
64
65
  options: ITtscCompilerContext,
65
66
  project: ITtscParsedProjectConfig,
66
- ): ITtscProjectPluginConfig[] {
67
- if (options.plugins === false) {
68
- return [];
69
- }
70
- const entries = options.plugins ?? project.compilerOptions.plugins;
71
- return entries.filter((entry) => entry.enabled !== false);
67
+ ): boolean {
68
+ return hasProjectPluginEntries(project, options.plugins);
72
69
  }
73
70
 
74
71
  function compileProjectWithPlugins(
@@ -109,13 +106,21 @@ function outputKeyMapper(
109
106
  return (relativePath) => relativePath;
110
107
  }
111
108
  const relativeOutDir = path.relative(project.root, outDir);
112
- if (relativeOutDir !== "" && !relativeOutDir.startsWith("..")) {
109
+ if (relativeOutDir !== "" && !isOutsideRelativePath(relativeOutDir)) {
113
110
  const prefix = pathToKey(relativeOutDir);
114
111
  return (relativePath) => path.posix.join(prefix, relativePath);
115
112
  }
116
113
  return (relativePath) => pathToKey(path.join(outDir, relativePath));
117
114
  }
118
115
 
116
+ function isOutsideRelativePath(relative: string): boolean {
117
+ return (
118
+ relative === ".." ||
119
+ relative.startsWith(`..${path.sep}`) ||
120
+ path.isAbsolute(relative)
121
+ );
122
+ }
123
+
119
124
  function readOutputDirectory(
120
125
  directory: string,
121
126
  keyOf: (relativePath: string) => string,
@@ -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";