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
@@ -15,12 +15,25 @@ type TtscPluginFactory<T = ITtscProjectPluginConfig> = (
15
15
  context: ITtscPluginFactoryContext<T>,
16
16
  ) => ITtscPlugin;
17
17
 
18
+ type ProjectPluginEntry = {
19
+ baseDir: string;
20
+ config: ITtscProjectPluginConfig;
21
+ };
22
+
23
+ type PackageManifest = {
24
+ dependencies?: Record<string, unknown>;
25
+ devDependencies?: Record<string, unknown>;
26
+ name?: unknown;
27
+ ttsc?: unknown;
28
+ };
29
+
18
30
  export function loadProjectPlugins(options: {
19
31
  binary: string;
20
32
  cacheDir?: string;
21
33
  cwd?: string;
22
34
  entries?: readonly ITtscProjectPluginConfig[] | false;
23
35
  file?: string;
36
+ projectRoot?: string;
24
37
  tsconfig?: string;
25
38
  }): {
26
39
  nativePlugins: ITtscLoadedNativePlugin[];
@@ -29,13 +42,14 @@ export function loadProjectPlugins(options: {
29
42
  const project = readProjectConfig({
30
43
  cwd: options.cwd,
31
44
  file: options.file,
45
+ projectRoot: options.projectRoot,
32
46
  tsconfig: options.tsconfig,
33
47
  });
34
- const entries =
48
+ const entries: ProjectPluginEntry[] =
35
49
  options.entries === false
36
50
  ? []
37
- : [...(options.entries ?? project.compilerOptions.plugins)].filter(
38
- (entry) => entry.enabled !== false,
51
+ : resolvePluginEntries(project, options.entries).filter(
52
+ (entry) => entry.config.enabled !== false,
39
53
  );
40
54
  if (entries.length === 0) {
41
55
  return {
@@ -51,7 +65,11 @@ export function loadProjectPlugins(options: {
51
65
  tsconfig: project.path,
52
66
  };
53
67
  const plugins = entries.map((entry) =>
54
- loadPluginEntry(entry, { ...context, plugin: entry }),
68
+ loadPluginEntry(
69
+ entry.config,
70
+ { ...context, plugin: entry.config },
71
+ entry.baseDir,
72
+ ),
55
73
  );
56
74
 
57
75
  const nativePlugins: ITtscLoadedNativePlugin[] = [];
@@ -70,28 +88,258 @@ export function loadProjectPlugins(options: {
70
88
  });
71
89
  nativePlugins.push({
72
90
  binary,
73
- config: entries[index]!,
91
+ config: entries[index]!.config,
74
92
  name: plugin.name,
75
93
  source: plugin.source,
76
94
  stage,
77
95
  });
78
96
  });
79
97
  return {
80
- nativePlugins,
98
+ nativePlugins: orderNativePlugins(nativePlugins),
81
99
  project,
82
100
  };
83
101
  }
84
102
 
103
+ export function hasProjectPluginEntries(
104
+ project: ITtscParsedProjectConfig,
105
+ entries?: readonly ITtscProjectPluginConfig[] | false,
106
+ ): boolean {
107
+ if (entries === false) {
108
+ return false;
109
+ }
110
+ return resolvePluginEntries(project, entries).some(
111
+ (entry) => entry.config.enabled !== false,
112
+ );
113
+ }
114
+
115
+ function resolvePluginEntries(
116
+ project: ITtscParsedProjectConfig,
117
+ entries?: readonly ITtscProjectPluginConfig[],
118
+ ): ProjectPluginEntry[] {
119
+ if (entries !== undefined) {
120
+ return entries.map((config) => ({
121
+ baseDir: project.root,
122
+ config,
123
+ }));
124
+ }
125
+ const configured = project.compilerOptions.plugins.map((config, index) => ({
126
+ baseDir: project.pluginBaseDirs[index] ?? project.root,
127
+ config,
128
+ }));
129
+ return [...configured, ...discoverPackagePluginEntries(project, configured)];
130
+ }
131
+
132
+ function discoverPackagePluginEntries(
133
+ project: ITtscParsedProjectConfig,
134
+ configured: readonly ProjectPluginEntry[],
135
+ ): ProjectPluginEntry[] {
136
+ const projectPackageJson = findNearestPackageJson(project.root);
137
+ if (projectPackageJson === undefined) {
138
+ return [];
139
+ }
140
+ const projectPackageRoot = path.dirname(projectPackageJson);
141
+ const projectManifest = readPackageManifest(projectPackageJson);
142
+ if (projectManifest === undefined) {
143
+ return [];
144
+ }
145
+
146
+ const configuredTransforms = createConfiguredTransformSet(configured);
147
+ const out: ProjectPluginEntry[] = [];
148
+ for (const name of directDependencyNames(projectManifest)) {
149
+ const packageJson = resolveDependencyPackageJson(name, projectPackageRoot);
150
+ if (packageJson === undefined) {
151
+ continue;
152
+ }
153
+ const manifest = readPackageManifest(packageJson);
154
+ const config = readPackagePluginConfig(name, manifest);
155
+ if (config === undefined || config.enabled === false) {
156
+ continue;
157
+ }
158
+ const packageRoot = path.dirname(packageJson);
159
+ const transform = config.transform;
160
+ if (typeof transform !== "string") {
161
+ continue;
162
+ }
163
+ const baseDir = isRelativePluginSpecifier(transform)
164
+ ? packageRoot
165
+ : projectPackageRoot;
166
+ const resolved = resolvePluginRequest(transform, baseDir);
167
+ if (hasConfiguredTransform(configuredTransforms, transform, resolved)) {
168
+ continue;
169
+ }
170
+ out.push({
171
+ baseDir,
172
+ config,
173
+ });
174
+ addConfiguredTransform(configuredTransforms, transform, resolved);
175
+ }
176
+ return out;
177
+ }
178
+
179
+ type ConfiguredTransformSet = {
180
+ raw: Set<string>;
181
+ resolved: Set<string>;
182
+ };
183
+
184
+ function createConfiguredTransformSet(
185
+ entries: readonly ProjectPluginEntry[],
186
+ ): ConfiguredTransformSet {
187
+ const raw = new Set<string>();
188
+ const resolved = new Set<string>();
189
+ for (const entry of entries) {
190
+ const transform = entry.config.transform;
191
+ if (typeof transform !== "string" || transform.length === 0) {
192
+ continue;
193
+ }
194
+ if (!isRelativePluginSpecifier(transform)) {
195
+ raw.add(transform);
196
+ }
197
+ try {
198
+ resolved.add(resolvePluginRequest(transform, entry.baseDir));
199
+ } catch {
200
+ // Keep the normal plugin loading error path for invalid explicit entries.
201
+ }
202
+ }
203
+ return { raw, resolved };
204
+ }
205
+
206
+ function hasConfiguredTransform(
207
+ configuredTransforms: ConfiguredTransformSet,
208
+ transform: string,
209
+ resolved: string,
210
+ ): boolean {
211
+ return (
212
+ configuredTransforms.resolved.has(resolved) ||
213
+ (!isRelativePluginSpecifier(transform) &&
214
+ configuredTransforms.raw.has(transform))
215
+ );
216
+ }
217
+
218
+ function addConfiguredTransform(
219
+ configuredTransforms: ConfiguredTransformSet,
220
+ transform: string,
221
+ resolved: string,
222
+ ): void {
223
+ if (!isRelativePluginSpecifier(transform)) {
224
+ configuredTransforms.raw.add(transform);
225
+ }
226
+ configuredTransforms.resolved.add(resolved);
227
+ }
228
+
229
+ function directDependencyNames(manifest: PackageManifest): string[] {
230
+ const seen = new Set<string>();
231
+ const out: string[] = [];
232
+ for (const dependencies of [
233
+ manifest.dependencies,
234
+ manifest.devDependencies,
235
+ ]) {
236
+ if (!isRecord(dependencies)) {
237
+ continue;
238
+ }
239
+ for (const name of Object.keys(dependencies)) {
240
+ if (seen.has(name)) {
241
+ continue;
242
+ }
243
+ seen.add(name);
244
+ out.push(name);
245
+ }
246
+ }
247
+ return out;
248
+ }
249
+
250
+ function resolveDependencyPackageJson(
251
+ name: string,
252
+ projectRoot: string,
253
+ ): string | undefined {
254
+ const direct = path.join(projectRoot, "node_modules", ...name.split("/"));
255
+ const directManifest = path.join(direct, "package.json");
256
+ if (fs.existsSync(directManifest)) {
257
+ return resolveRealPath(directManifest);
258
+ }
259
+ const projectPackage = path.join(projectRoot, "package.json");
260
+ const projectRequire = createRequire(projectPackage);
261
+ try {
262
+ return resolveRealPath(projectRequire.resolve(`${name}/package.json`));
263
+ } catch {
264
+ try {
265
+ return findNearestPackageJson(projectRequire.resolve(name));
266
+ } catch {
267
+ return undefined;
268
+ }
269
+ }
270
+ }
271
+
272
+ function findNearestPackageJson(location: string): string | undefined {
273
+ let current = fs.statSync(location).isDirectory()
274
+ ? location
275
+ : path.dirname(location);
276
+ while (true) {
277
+ const manifest = path.join(current, "package.json");
278
+ if (fs.existsSync(manifest)) {
279
+ return resolveRealPath(manifest);
280
+ }
281
+ const parent = path.dirname(current);
282
+ if (parent === current) {
283
+ return undefined;
284
+ }
285
+ current = parent;
286
+ }
287
+ }
288
+
289
+ function readPackageManifest(file: string): PackageManifest | undefined {
290
+ if (!fs.existsSync(file)) {
291
+ return undefined;
292
+ }
293
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8")) as unknown;
294
+ return isRecord(parsed) ? (parsed as PackageManifest) : undefined;
295
+ }
296
+
297
+ function readPackagePluginConfig(
298
+ packageName: string,
299
+ manifest: PackageManifest | undefined,
300
+ ): ITtscProjectPluginConfig | undefined {
301
+ const ttsc = manifest?.ttsc;
302
+ if (!isRecord(ttsc) || !("plugin" in ttsc)) {
303
+ return undefined;
304
+ }
305
+ const plugin = ttsc.plugin;
306
+ if (!isRecord(plugin) || Array.isArray(plugin)) {
307
+ throw new Error(
308
+ `ttsc: package ${JSON.stringify(packageName)} declares invalid "ttsc.plugin"; expected an object`,
309
+ );
310
+ }
311
+ if (typeof plugin.transform !== "string" || plugin.transform.length === 0) {
312
+ throw new Error(
313
+ `ttsc: package ${JSON.stringify(packageName)} declares invalid "ttsc.plugin.transform"; expected a non-empty string`,
314
+ );
315
+ }
316
+ return { ...plugin } as ITtscProjectPluginConfig;
317
+ }
318
+
319
+ function isRecord(value: unknown): value is Record<string, unknown> {
320
+ return typeof value === "object" && value !== null;
321
+ }
322
+
323
+ function orderNativePlugins(
324
+ plugins: readonly ITtscLoadedNativePlugin[],
325
+ ): ITtscLoadedNativePlugin[] {
326
+ return [
327
+ ...plugins.filter((plugin) => plugin.stage === "check"),
328
+ ...plugins.filter((plugin) => plugin.stage === "transform"),
329
+ ];
330
+ }
331
+
85
332
  function loadPluginEntry(
86
333
  entry: ITtscProjectPluginConfig,
87
334
  context: ITtscPluginFactoryContext,
335
+ baseDir: string,
88
336
  ): ITtscPlugin {
89
337
  const specifier = entry.transform;
90
338
  if (typeof specifier !== "string" || specifier.length === 0) {
91
339
  throw new Error(`ttsc: plugin entry is missing a string "transform" field`);
92
340
  }
93
341
 
94
- const request = resolvePluginRequest(specifier, context.projectRoot);
342
+ const request = resolvePluginRequest(specifier, baseDir);
95
343
  const mod = require(request) as {
96
344
  createTtscPlugin?: TtscPluginFactory;
97
345
  default?: ITtscPlugin | TtscPluginFactory;
@@ -170,12 +418,20 @@ function isPluginStage(value: string): value is TtscPluginStage {
170
418
 
171
419
  function resolvePluginRequest(specifier: string, projectRoot: string): string {
172
420
  if (path.isAbsolute(specifier)) {
173
- return specifier;
421
+ return resolveRealPath(specifier);
174
422
  }
175
423
  if (isRelativePluginSpecifier(specifier)) {
176
- return path.resolve(projectRoot, specifier);
424
+ return resolveRealPath(path.resolve(projectRoot, specifier));
425
+ }
426
+ return resolveRealPath(require.resolve(specifier, { paths: [projectRoot] }));
427
+ }
428
+
429
+ function resolveRealPath(location: string): string {
430
+ try {
431
+ return fs.realpathSync(location);
432
+ } catch {
433
+ return location;
177
434
  }
178
- return require.resolve(specifier, { paths: [projectRoot] });
179
435
  }
180
436
 
181
437
  function isRelativePluginSpecifier(specifier: string): boolean {
@@ -35,6 +35,17 @@ export interface ITtscCompilerContext {
35
35
  */
36
36
  tsconfig?: string;
37
37
 
38
+ /**
39
+ * Project root override for generated config wrappers.
40
+ *
41
+ * Most callers should leave this unset so the project root is the directory
42
+ * containing {@link ITtscCompilerContext.tsconfig}. Embedders that synthesize
43
+ * a temporary config extending a real project config can set this to keep
44
+ * plugin resolution, cache paths, and native sidecar `--cwd` anchored to the
45
+ * real project.
46
+ */
47
+ projectRoot?: string;
48
+
38
49
  /**
39
50
  * Explicit TypeScript-Go executable for controlled embedding.
40
51
  *
@@ -73,9 +84,12 @@ export interface ITtscCompilerContext {
73
84
  /**
74
85
  * Plugin entries for this compiler instance.
75
86
  *
76
- * - `undefined`: read `compilerOptions.plugins` from the project config.
77
- * - `false`: ignore project plugins for this compiler instance.
78
- * - array: use these plugin entries instead of the project config entries.
87
+ * - `undefined`: read project plugins from `compilerOptions.plugins` and
88
+ * directly installed package markers.
89
+ * - `false`: ignore project config plugins and package markers for this
90
+ * compiler instance.
91
+ * - array: use these plugin entries instead of project config entries and
92
+ * package markers.
79
93
  *
80
94
  * Plugin entries are resolved once per operation from this instance context.
81
95
  * Per-call plugin overrides are intentionally not part of the public API.
@@ -34,6 +34,11 @@ export namespace ITtscCompilerResult {
34
34
  */
35
35
  type: "success";
36
36
 
37
+ /**
38
+ * Non-fatal diagnostics reported during compilation.
39
+ */
40
+ diagnostics?: ITtscCompilerDiagnostic[];
41
+
37
42
  /**
38
43
  * The generated compiler output.
39
44
  *
@@ -19,6 +19,9 @@ export namespace ITtscCompilerTransformation {
19
19
  /** Indicates that transformation completed without diagnostics. */
20
20
  type: "success";
21
21
 
22
+ /** Non-fatal diagnostics reported during transformation. */
23
+ diagnostics?: ITtscCompilerDiagnostic[];
24
+
22
25
  /** Transformed TypeScript source text keyed by project file path. */
23
26
  typescript: Record<string, string>;
24
27
  }
@@ -43,10 +43,13 @@ export interface ITtscPluginFactoryContext<T = ITtscProjectPluginConfig> {
43
43
  plugin: T;
44
44
 
45
45
  /**
46
- * Directory containing the resolved tsconfig/jsconfig.
46
+ * Project root used as the native plugin working directory and package
47
+ * discovery base.
47
48
  *
48
- * Relative plugin module specifiers and plugin source paths are resolved from
49
- * this directory, matching TypeScript plugin config behavior.
49
+ * By default this is the directory containing the resolved tsconfig/jsconfig.
50
+ * Callers may override it when a wrapper config lives outside the project.
51
+ * Relative `source` paths returned by a plugin descriptor resolve from this
52
+ * directory.
50
53
  */
51
54
  projectRoot: string;
52
55
 
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Raw `compilerOptions.plugins[]` entry read from tsconfig/jsconfig.
2
+ * Raw plugin entry read from `compilerOptions.plugins[]` or from a directly
3
+ * installed package's `package.json#ttsc.plugin` marker.
3
4
  *
4
- * This is the project-facing config shape that users write in
5
- * `tsconfig.json`. ttsc deliberately keeps it open-ended because plugin
6
- * packages own their own config fields.
5
+ * This is the project-facing config shape that users write in `tsconfig.json`
6
+ * or plugin packages expose through `package.json`. ttsc deliberately keeps it
7
+ * open-ended because plugin packages own their own config fields.
7
8
  *
8
9
  * ttsc interprets only two properties:
9
10
  *
@@ -28,9 +29,9 @@ export interface ITtscProjectPluginConfig {
28
29
  /**
29
30
  * Plugin module specifier, relative path, or absolute path to load.
30
31
  *
31
- * Relative paths are resolved from the resolved project root, which is the
32
- * directory containing the active tsconfig/jsconfig. Package specifiers are
33
- * resolved with Node's package resolution from that same project root.
32
+ * Relative paths are resolved from the tsconfig/jsconfig file that declared
33
+ * the plugin entry. Package specifiers are resolved with Node's package
34
+ * resolution from that same directory.
34
35
  *
35
36
  * The loaded JavaScript module must export an {@link ITtscPlugin} descriptor
36
37
  * or a `createTtscPlugin(context)` factory. The Go implementation itself is
@@ -11,6 +11,8 @@ export interface ITtscParsedProjectConfig {
11
11
  } & Record<string, unknown>;
12
12
  /** Absolute path to the resolved tsconfig/jsconfig. */
13
13
  path: string;
14
+ /** Directory that declared each inherited plugin entry. */
15
+ pluginBaseDirs: string[];
14
16
  /** Directory containing the resolved tsconfig/jsconfig. */
15
17
  root: string;
16
18
  }
@@ -4,6 +4,8 @@ export interface ITtscProjectLocatorOptions {
4
4
  cwd?: string;
5
5
  /** Source file used as the starting point for nearest-config discovery. */
6
6
  file?: string;
7
+ /** Project root override for generated tsconfig wrappers. */
8
+ projectRoot?: string;
7
9
  /** Explicit tsconfig/jsconfig path. */
8
10
  tsconfig?: string;
9
11
  }
@@ -11,6 +11,8 @@ export interface TtscCommonOptions {
11
11
  binary?: string;
12
12
  /** Working directory for config discovery and relative file paths. */
13
13
  cwd?: string;
14
+ /** Project root override for generated tsconfig wrappers. */
15
+ projectRoot?: string;
14
16
  /** Environment variables merged over `process.env` for child processes. */
15
17
  env?: NodeJS.ProcessEnv;
16
18
  /** Explicit root directory for compiled source-plugin cache artifacts. */