@ttsc/lint 0.26.0 → 0.26.2

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/src/index.ts CHANGED
@@ -1789,8 +1789,9 @@ function evaluateTtsxConfigPlugins(
1789
1789
  "--no-plugins",
1790
1790
  loaderPath,
1791
1791
  ];
1792
- if (process.env.TTSC_TSGO_BINARY) {
1793
- args.unshift("--binary", process.env.TTSC_TSGO_BINARY);
1792
+ const tsgoBinary = resolveConfigTsgo(configPath, context);
1793
+ if (tsgoBinary) {
1794
+ args.unshift("--binary", tsgoBinary);
1794
1795
  }
1795
1796
  const env = {
1796
1797
  ...nodeConfigLoaderEnv(configPath),
@@ -2453,6 +2454,73 @@ function nodeConfigLoaderEnv(configPath: string): NodeJS.ProcessEnv {
2453
2454
  * and is not for a host that loaded this descriptor in process. The bare name
2454
2455
  * remains the last resort for an installation none of them reach.
2455
2456
  */
2457
+ /**
2458
+ * The native TypeScript compiler the config evaluator must run.
2459
+ *
2460
+ * The evaluator builds the loader in an ephemeral directory, so the child it
2461
+ * spawns cannot discover `typescript` the way an ordinary invocation does: the
2462
+ * directory is not in the project, and `linkNearestNodeModules` is the only
2463
+ * thing that puts the project's modules within reach of it. Leaving the child
2464
+ * to re-derive the compiler from there made this work by inheritance —
2465
+ * `TTSC_TSGO_BINARY` is exported by `ttsx` to its own descendants, so a build
2466
+ * launched under `ttsx` passed a binary down and a build launched any other way
2467
+ * did not. A published consumer exports no such variable, and neither does a
2468
+ * process that deliberately sheds the launcher's runtime state, and for those
2469
+ * the evaluator failed with `ttsc: typescript is required` before it read a
2470
+ * line of the config.
2471
+ *
2472
+ * The host has already resolved a compiler for the project it is linting, so
2473
+ * the answer is asked of the project rather than of the environment. The config
2474
+ * comes first and the descriptor's own location second, the same order and for
2475
+ * the same reason as {@link resolveTtsxLauncher}. An explicit `TTSC_TSGO_BINARY`
2476
+ * still wins, so an embedder that pins a compiler keeps pinning it.
2477
+ *
2478
+ * Returning `undefined` leaves the child to resolve for itself, which is what
2479
+ * it did before: a project that cannot answer here could not answer there
2480
+ * either, and the child's own diagnostic is the one that names the missing
2481
+ * package.
2482
+ */
2483
+ function resolveConfigTsgo(
2484
+ configPath: string,
2485
+ context: TtscPluginFactoryContext<ITtscLintPluginConfig>,
2486
+ ): string | undefined {
2487
+ const explicit = process.env.TTSC_TSGO_BINARY?.trim();
2488
+ if (explicit) return explicit;
2489
+ const anchors = [
2490
+ configPath,
2491
+ path.join(context.projectRoot, "package.json"),
2492
+ context.dirname,
2493
+ ];
2494
+ for (const anchor of anchors) {
2495
+ const binary = tsgoBinaryFrom(anchor);
2496
+ if (binary !== undefined) return binary;
2497
+ }
2498
+ return undefined;
2499
+ }
2500
+
2501
+ /**
2502
+ * The platform compiler binary of the `typescript` install `anchor` can see, or
2503
+ * `undefined` when this anchor reaches neither the package nor its platform
2504
+ * dependency. Mirrors ttsc's own resolution order so both name one file.
2505
+ */
2506
+ function tsgoBinaryFrom(anchor: string): string | undefined {
2507
+ try {
2508
+ const manifest = createRequire(anchor).resolve("typescript/package.json");
2509
+ const platform = createRequire(manifest).resolve(
2510
+ `@typescript/typescript-${process.platform}-${process.arch}/package.json`,
2511
+ );
2512
+ const binary = path.join(
2513
+ path.dirname(platform),
2514
+ "lib",
2515
+ process.platform === "win32" ? "tsc.exe" : "tsc",
2516
+ );
2517
+ return fs.existsSync(binary) ? binary : undefined;
2518
+ } catch {
2519
+ // This anchor cannot see the compiler; the caller tries the next one.
2520
+ return undefined;
2521
+ }
2522
+ }
2523
+
2456
2524
  function resolveTtsxLauncher(anchors: readonly string[]): string {
2457
2525
  const explicit = process.env.TTSC_TTSX_BINARY?.trim();
2458
2526
  if (explicit) return explicit;
@@ -30,9 +30,10 @@ export interface ITtscLintFormat {
30
30
  severity?: TtscLintSeverity;
31
31
 
32
32
  /**
33
- * Insert trailing semicolons on ASI-terminated statements. Mirrors Prettier's
34
- * `semi`. `false` flips the rule to require _no_ trailing semicolon (rare;
35
- * matches prettier's `semi: false`).
33
+ * Insert trailing semicolons on ASI-terminated statements, and on the
34
+ * interface, type-literal, and class members that carry no body. Mirrors
35
+ * Prettier's `semi`. `false` flips the rule to require _no_ trailing
36
+ * semicolon (rare; matches prettier's `semi: false`).
36
37
  *
37
38
  * @default true
38
39
  */