@lensmcp/cluster 1.17.2 → 1.17.3
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/create-webpack-dev.d.ts.map +1 -1
- package/create-webpack-dev.js +13 -3
- package/package.json +4 -3
- package/tsgo-check-plugin.d.ts +74 -5
- package/tsgo-check-plugin.d.ts.map +1 -1
- package/tsgo-check-plugin.js +187 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-webpack-dev.d.ts","sourceRoot":"","sources":["../../../libs/cluster/src/create-webpack-dev.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-webpack-dev.d.ts","sourceRoot":"","sources":["../../../libs/cluster/src/create-webpack-dev.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAatF,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,qBAAqB,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA2CD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAyKtF"}
|
package/create-webpack-dev.js
CHANGED
|
@@ -49,12 +49,22 @@ function createDevWebpackConfig(options) {
|
|
|
49
49
|
// Type-checking mode (LENSMCP_TYPECHECK): default = the resident fork-ts-checker pair (async,
|
|
50
50
|
// feeds the lens `error TS` service-error signal); 'off' = none (fastest, quietest — the signal
|
|
51
51
|
// is traded away); 'tsgo' = zero-resident cold re-checks via @typescript/native-preview (same
|
|
52
|
-
// signal, no idle memory — see tsgo-check-plugin.ts).
|
|
53
|
-
//
|
|
52
|
+
// signal, no idle memory — see tsgo-check-plugin.ts).
|
|
53
|
+
//
|
|
54
|
+
// tsgo still falls back to fork-ts-checker when no binary resolves — degrading gracefully beats
|
|
55
|
+
// failing a dev server — but that fallback is now an ANOMALY, not the expected "the flag shipped
|
|
56
|
+
// ahead of the devDep" state: @lensmcp/cluster DEPENDS on the compiler. So it also reports itself
|
|
57
|
+
// onto the lens bus. fork-ts-checker publishes to the CONSOLE only, so without this the
|
|
58
|
+
// `typecheck://` resources would sit at `producer: 'none'` with nothing naming the reason —
|
|
59
|
+
// the precise "looks healthy because nobody is watching" state this producer exists to end.
|
|
54
60
|
const typecheckMode = process.env.LENSMCP_TYPECHECK;
|
|
55
61
|
const tsgoBin = typecheckMode === 'tsgo' ? (0, tsgo_check_plugin_1.resolveTsgoBin)(workspaceRoot, process.env.LENSMCP_TSGO_BIN) : null;
|
|
56
62
|
if (typecheckMode === 'tsgo' && !tsgoBin) {
|
|
57
|
-
|
|
63
|
+
// No trailing period: `summariseProducers` renders this INSIDE a sentence it terminates itself.
|
|
64
|
+
const detail = `${tsgo_check_plugin_1.TSGO_MISSING_CAUSE}. Fell back to fork-ts-checker, which reports to the console only — ` +
|
|
65
|
+
`so typecheck:// has no producer for this project until it is fixed`;
|
|
66
|
+
console.warn(`[serve] ${options.appName}: LENSMCP_TYPECHECK=tsgo but ${detail}.`);
|
|
67
|
+
(0, tsgo_check_plugin_1.reportTsgoUnavailable)({ appName: options.appName, tsConfig, workspaceRoot, detail });
|
|
58
68
|
}
|
|
59
69
|
const useTsgo = !!tsgoBin;
|
|
60
70
|
// Build combined allowlist: orgScopes + bundlePackages + user-provided
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/cluster",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.3",
|
|
4
4
|
"description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -65,8 +65,9 @@
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@lensmcp/node-instrumentation": "1.17.
|
|
69
|
-
"@lensmcp/nx-plugin": "1.17.
|
|
68
|
+
"@lensmcp/node-instrumentation": "1.17.3",
|
|
69
|
+
"@lensmcp/nx-plugin": "1.17.3",
|
|
70
|
+
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
|
|
70
71
|
"fork-ts-checker-webpack-plugin": "^9.0.0",
|
|
71
72
|
"glob": "^11.0.0",
|
|
72
73
|
"http-proxy": "^1.18.0",
|
package/tsgo-check-plugin.d.ts
CHANGED
|
@@ -21,8 +21,15 @@
|
|
|
21
21
|
* `module: preserve` + `emitDecoratorMetadata` otherwise flags TS1272 on decorated signatures —
|
|
22
22
|
* an emit-safety rule that doesn't apply here (this checker never emits; webpack owns emit).
|
|
23
23
|
* Parity was verified per adopting workspace (0-error parity with tsc on all spot-checked apps);
|
|
24
|
-
* tsgo is a PREVIEW compiler, which is why this
|
|
25
|
-
*
|
|
24
|
+
* tsgo is a PREVIEW compiler, which is why this MODE is opt-in.
|
|
25
|
+
*
|
|
26
|
+
* OWNERSHIP: `@lensmcp/cluster` DEPENDS on `@typescript/native-preview` — this is our feature, so we
|
|
27
|
+
* ship the compiler that powers it rather than asking every consumer to remember a devDep. That was
|
|
28
|
+
* the old contract, and it made the feature (and, once this plugin became the `typecheck://`
|
|
29
|
+
* producer, the whole typecheck health signal) die quietly in any workspace that forgot. Resolution
|
|
30
|
+
* still PREFERS a workspace's own copy (a workspace pinning a tsgo version keeps winning), and a
|
|
31
|
+
* missing binary is now an ANOMALY: it degrades gracefully — never crashing the dev server — but it
|
|
32
|
+
* reports itself onto the lens bus instead of only warning into a log nobody reads.
|
|
26
33
|
*/
|
|
27
34
|
import { spawn } from 'node:child_process';
|
|
28
35
|
export interface TsgoCheckPluginOptions {
|
|
@@ -31,7 +38,7 @@ export interface TsgoCheckPluginOptions {
|
|
|
31
38
|
tsConfig: string;
|
|
32
39
|
workspaceRoot: string;
|
|
33
40
|
debounceMs?: number;
|
|
34
|
-
/** Binary override (LENSMCP_TSGO_BIN);
|
|
41
|
+
/** Binary override (LENSMCP_TSGO_BIN); otherwise resolved by {@link resolveTsgoBin}. */
|
|
35
42
|
binPath?: string;
|
|
36
43
|
/** Injectable for tests. */
|
|
37
44
|
spawnImpl?: typeof spawn;
|
|
@@ -44,8 +51,70 @@ export interface TsgoCheckPluginOptions {
|
|
|
44
51
|
/** Set false to skip publishing to the lens bus (console output is unchanged). */
|
|
45
52
|
publishEvents?: boolean;
|
|
46
53
|
}
|
|
47
|
-
/** The
|
|
48
|
-
export declare
|
|
54
|
+
/** The npm package that ships the tsgo compiler — a real `dependency` of `@lensmcp/cluster`. */
|
|
55
|
+
export declare const TSGO_PACKAGE = "@typescript/native-preview";
|
|
56
|
+
/** Seams for {@link resolveTsgoBin}, so every branch of the precedence is testable without an install. */
|
|
57
|
+
export interface TsgoResolutionDeps {
|
|
58
|
+
/** Locate OUR `@typescript/native-preview`. Default: {@link resolveTsgoPackageDir}. */
|
|
59
|
+
ownPackageDir?: () => string | null;
|
|
60
|
+
/** Probe a candidate. Default: exists && is a file && is executable. */
|
|
61
|
+
isExecutable?: (candidate: string) => boolean;
|
|
62
|
+
/** Default: `process.platform`. */
|
|
63
|
+
platform?: NodeJS.Platform;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Where OUR copy of {@link TSGO_PACKAGE} lives, or null when it isn't installed.
|
|
67
|
+
*
|
|
68
|
+
* `require.resolve` walks up from THIS module, so it finds the copy installed for
|
|
69
|
+
* `@lensmcp/cluster` whether the consumer's installer HOISTED it
|
|
70
|
+
* (`<root>/node_modules/@typescript/native-preview`) or NESTED it
|
|
71
|
+
* (`<root>/node_modules/@lensmcp/cluster/node_modules/@typescript/native-preview`, the shape you
|
|
72
|
+
* get on a version conflict) or used pnpm's virtual store. A hand-written `../../node_modules/…`
|
|
73
|
+
* relative guess gets exactly one of those right.
|
|
74
|
+
*/
|
|
75
|
+
export declare function resolveTsgoPackageDir(): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* The tsgo binary to run, or null when none is usable.
|
|
78
|
+
*
|
|
79
|
+
* Precedence — first usable wins:
|
|
80
|
+
* 1. `override` (LENSMCP_TSGO_BIN). Deliberately NOT a fallthrough: an override pointing at a
|
|
81
|
+
* missing/non-executable file is an operator mistake to SURFACE, not to paper over by silently
|
|
82
|
+
* running a different compiler than the one that was asked for.
|
|
83
|
+
* 2. The consuming workspace's `node_modules/.bin/tsgo` — a workspace that pins its own tsgo
|
|
84
|
+
* version still beats the one we ship.
|
|
85
|
+
* 3. OURS, via {@link resolveTsgoPackageDir}. `@lensmcp/cluster` depends on {@link TSGO_PACKAGE},
|
|
86
|
+
* so this is the branch that normally answers, and the reason the feature no longer depends on
|
|
87
|
+
* every consumer independently remembering a devDep.
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveTsgoBin(workspaceRoot: string, override?: string, deps?: TsgoResolutionDeps): string | null;
|
|
90
|
+
/**
|
|
91
|
+
* Why tsgo could not run, and what to do — the CAUSE half, shared by both sites that can hit it.
|
|
92
|
+
*
|
|
93
|
+
* Phrased for the new ownership: `@lensmcp/cluster` DEPENDS on the compiler, so "not installed" is
|
|
94
|
+
* no longer an expected opt-out state, it is a broken install (or an unsupported platform, or a bad
|
|
95
|
+
* override). Callers append the CONSEQUENCE, which differs by site.
|
|
96
|
+
*/
|
|
97
|
+
export declare const TSGO_MISSING_CAUSE: string;
|
|
98
|
+
/**
|
|
99
|
+
* Report — onto the LENS BUS, not just the console — that tsgo cannot run.
|
|
100
|
+
*
|
|
101
|
+
* A `console.warn` is invisible to anything reading `typecheck://current`, which is precisely the
|
|
102
|
+
* failure this whole area exists to eliminate: a resource that looks healthy because nobody is
|
|
103
|
+
* watching. Published as a `toolError` run, so the reducer records the producer as reporting with
|
|
104
|
+
* `lastRunStatus: 'error'` — `status` degrades to `unknown` (never a false `clean`) and `detail`
|
|
105
|
+
* names the cause and the fix.
|
|
106
|
+
*
|
|
107
|
+
* Strictly passive: fully swallowed, never blocks or fails the host build.
|
|
108
|
+
*/
|
|
109
|
+
export declare function reportTsgoUnavailable(args: {
|
|
110
|
+
appName: string;
|
|
111
|
+
tsConfig: string;
|
|
112
|
+
workspaceRoot: string;
|
|
113
|
+
/** Full sentence: {@link TSGO_MISSING_CAUSE} plus this site's consequence. */
|
|
114
|
+
detail: string;
|
|
115
|
+
/** Override the lens bus file. Injectable for tests. */
|
|
116
|
+
eventFile?: string;
|
|
117
|
+
}): void;
|
|
49
118
|
/**
|
|
50
119
|
* Write the shim tsconfig (absolute `extends`; see the header) and return its path. It lives under
|
|
51
120
|
* the WORKSPACE's git-ignored `tmp/` — NOT os.tmpdir(): `types: ['node']` (and every other @types
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tsgo-check-plugin.d.ts","sourceRoot":"","sources":["../../../libs/cluster/src/tsgo-check-plugin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tsgo-check-plugin.d.ts","sourceRoot":"","sources":["../../../libs/cluster/src/tsgo-check-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAK9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,gGAAgG;AAChG,eAAO,MAAM,YAAY,+BAA+B,CAAC;AAEzD,0GAA0G;AAC1G,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACpC,wEAAwE;IACxE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B;AAmCD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CA6BrD;AA6BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,GAAE,kBAAuB,GAC5B,MAAM,GAAG,IAAI,CAcf;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,QAG2C,CAAC;AAE3E;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CAeP;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CA0BlG;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,sBAAsB;IAMxC,KAAK,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI;IAO1B,sGAAsG;IACtG,QAAQ,IAAI,IAAI;IAOhB;;;;OAIG;IACH,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,GAAG;CA8DZ"}
|
package/tsgo-check-plugin.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TsgoCheckPlugin = void 0;
|
|
3
|
+
exports.TsgoCheckPlugin = exports.TSGO_MISSING_CAUSE = exports.TSGO_PACKAGE = void 0;
|
|
4
|
+
exports.resolveTsgoPackageDir = resolveTsgoPackageDir;
|
|
4
5
|
exports.resolveTsgoBin = resolveTsgoBin;
|
|
6
|
+
exports.reportTsgoUnavailable = reportTsgoUnavailable;
|
|
5
7
|
exports.writeShimTsconfig = writeShimTsconfig;
|
|
6
8
|
const tslib_1 = require("tslib");
|
|
7
9
|
/**
|
|
@@ -27,17 +29,191 @@ const tslib_1 = require("tslib");
|
|
|
27
29
|
* `module: preserve` + `emitDecoratorMetadata` otherwise flags TS1272 on decorated signatures —
|
|
28
30
|
* an emit-safety rule that doesn't apply here (this checker never emits; webpack owns emit).
|
|
29
31
|
* Parity was verified per adopting workspace (0-error parity with tsc on all spot-checked apps);
|
|
30
|
-
* tsgo is a PREVIEW compiler, which is why this
|
|
31
|
-
*
|
|
32
|
+
* tsgo is a PREVIEW compiler, which is why this MODE is opt-in.
|
|
33
|
+
*
|
|
34
|
+
* OWNERSHIP: `@lensmcp/cluster` DEPENDS on `@typescript/native-preview` — this is our feature, so we
|
|
35
|
+
* ship the compiler that powers it rather than asking every consumer to remember a devDep. That was
|
|
36
|
+
* the old contract, and it made the feature (and, once this plugin became the `typecheck://`
|
|
37
|
+
* producer, the whole typecheck health signal) die quietly in any workspace that forgot. Resolution
|
|
38
|
+
* still PREFERS a workspace's own copy (a workspace pinning a tsgo version keeps winning), and a
|
|
39
|
+
* missing binary is now an ANOMALY: it degrades gracefully — never crashing the dev server — but it
|
|
40
|
+
* reports itself onto the lens bus instead of only warning into a log nobody reads.
|
|
32
41
|
*/
|
|
33
42
|
const node_child_process_1 = require("node:child_process");
|
|
34
43
|
const fs = tslib_1.__importStar(require("node:fs"));
|
|
35
44
|
const path = tslib_1.__importStar(require("node:path"));
|
|
36
45
|
const typecheck_bus_1 = require("./typecheck-bus");
|
|
37
|
-
/** The
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
/** The npm package that ships the tsgo compiler — a real `dependency` of `@lensmcp/cluster`. */
|
|
47
|
+
exports.TSGO_PACKAGE = '@typescript/native-preview';
|
|
48
|
+
/**
|
|
49
|
+
* The spawnable shim names inside a `node_modules/.bin`.
|
|
50
|
+
*
|
|
51
|
+
* On win32 the only shims `child_process.spawn` can execute directly are `tsgo.cmd` / `tsgo.exe`;
|
|
52
|
+
* the EXTENSIONLESS `tsgo` npm writes beside them is a POSIX shell script for git-bash, and
|
|
53
|
+
* spawning it without a shell fails. So win32 never considers the bare name.
|
|
54
|
+
*/
|
|
55
|
+
function shimNames(platform) {
|
|
56
|
+
return platform === 'win32' ? ['tsgo.cmd', 'tsgo.exe'] : ['tsgo'];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Existence is not enough — a directory, a dangling symlink, or a non-`+x` file all "exist" and
|
|
60
|
+
* then fail at spawn time as an opaque EACCES/ENOENT mid-serve. Probe once, here.
|
|
61
|
+
* `statSync` follows symlinks on purpose (`.bin/tsgo` IS one).
|
|
62
|
+
*/
|
|
63
|
+
function isExecutableFile(candidate, platform) {
|
|
64
|
+
try {
|
|
65
|
+
if (!fs.statSync(candidate).isFile())
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false; // missing, or a dangling symlink
|
|
70
|
+
}
|
|
71
|
+
// X_OK is meaningless on win32 (it reports every readable file executable), so the extension
|
|
72
|
+
// check in `shimNames` is the real gate there.
|
|
73
|
+
if (platform === 'win32')
|
|
74
|
+
return true;
|
|
75
|
+
try {
|
|
76
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Where OUR copy of {@link TSGO_PACKAGE} lives, or null when it isn't installed.
|
|
85
|
+
*
|
|
86
|
+
* `require.resolve` walks up from THIS module, so it finds the copy installed for
|
|
87
|
+
* `@lensmcp/cluster` whether the consumer's installer HOISTED it
|
|
88
|
+
* (`<root>/node_modules/@typescript/native-preview`) or NESTED it
|
|
89
|
+
* (`<root>/node_modules/@lensmcp/cluster/node_modules/@typescript/native-preview`, the shape you
|
|
90
|
+
* get on a version conflict) or used pnpm's virtual store. A hand-written `../../node_modules/…`
|
|
91
|
+
* relative guess gets exactly one of those right.
|
|
92
|
+
*/
|
|
93
|
+
function resolveTsgoPackageDir() {
|
|
94
|
+
try {
|
|
95
|
+
// The package maps `"./package.json"` in its `exports`, which is the cheap, exact answer.
|
|
96
|
+
return path.dirname(require.resolve(`${exports.TSGO_PACKAGE}/package.json`));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
/* an `exports` map without ./package.json — fall through to the entry-point walk */
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
let dir = path.dirname(require.resolve(exports.TSGO_PACKAGE));
|
|
103
|
+
// The entry may sit a few levels below the package root (`<pkg>/lib/version.cjs`), so walk up
|
|
104
|
+
// to the manifest that actually NAMES the package rather than assuming a depth.
|
|
105
|
+
for (let i = 0; i < 6; i++) {
|
|
106
|
+
const manifest = path.join(dir, 'package.json');
|
|
107
|
+
if (fs.existsSync(manifest)) {
|
|
108
|
+
try {
|
|
109
|
+
const { name } = JSON.parse(fs.readFileSync(manifest, 'utf8'));
|
|
110
|
+
if (name === exports.TSGO_PACKAGE)
|
|
111
|
+
return dir;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* unreadable manifest — keep walking */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const parent = path.dirname(dir);
|
|
118
|
+
if (parent === dir)
|
|
119
|
+
break;
|
|
120
|
+
dir = parent;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
/* not installed */
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/** `bin.tsgo` (or a string `bin`) from a package dir, as an absolute path. */
|
|
129
|
+
function packageDeclaredBin(pkgDir) {
|
|
130
|
+
try {
|
|
131
|
+
const { bin } = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf8'));
|
|
132
|
+
const rel = typeof bin === 'string' ? bin : bin?.['tsgo'];
|
|
133
|
+
return rel ? path.join(pkgDir, rel) : null;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Candidate binaries for an installed {@link TSGO_PACKAGE}, best first. */
|
|
140
|
+
function ownBinCandidates(pkgDir, platform) {
|
|
141
|
+
// `<…>/node_modules/@typescript/native-preview` ⇒ `<…>/node_modules/.bin` — two levels up holds
|
|
142
|
+
// for a hoisted install, a nested one, and pnpm's store alike (a scoped package is always
|
|
143
|
+
// `<node_modules>/<scope>/<name>`).
|
|
144
|
+
const binDir = path.resolve(pkgDir, '..', '..', '.bin');
|
|
145
|
+
const shims = shimNames(platform).map((n) => path.join(binDir, n));
|
|
146
|
+
// The package's OWN `bin/tsgo` is present whenever the package is, regardless of whether the
|
|
147
|
+
// installer wrote shims — but it is a `#!/usr/bin/env node` script, so it is directly spawnable
|
|
148
|
+
// on POSIX only. On win32 the `.cmd`/`.exe` shims are the only usable entry.
|
|
149
|
+
const declared = platform === 'win32' ? null : packageDeclaredBin(pkgDir);
|
|
150
|
+
return declared ? [declared, ...shims] : shims;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The tsgo binary to run, or null when none is usable.
|
|
154
|
+
*
|
|
155
|
+
* Precedence — first usable wins:
|
|
156
|
+
* 1. `override` (LENSMCP_TSGO_BIN). Deliberately NOT a fallthrough: an override pointing at a
|
|
157
|
+
* missing/non-executable file is an operator mistake to SURFACE, not to paper over by silently
|
|
158
|
+
* running a different compiler than the one that was asked for.
|
|
159
|
+
* 2. The consuming workspace's `node_modules/.bin/tsgo` — a workspace that pins its own tsgo
|
|
160
|
+
* version still beats the one we ship.
|
|
161
|
+
* 3. OURS, via {@link resolveTsgoPackageDir}. `@lensmcp/cluster` depends on {@link TSGO_PACKAGE},
|
|
162
|
+
* so this is the branch that normally answers, and the reason the feature no longer depends on
|
|
163
|
+
* every consumer independently remembering a devDep.
|
|
164
|
+
*/
|
|
165
|
+
function resolveTsgoBin(workspaceRoot, override, deps = {}) {
|
|
166
|
+
const platform = deps.platform ?? process.platform;
|
|
167
|
+
const isExecutable = deps.isExecutable ?? ((c) => isExecutableFile(c, platform));
|
|
168
|
+
if (override)
|
|
169
|
+
return isExecutable(override) ? override : null;
|
|
170
|
+
for (const name of shimNames(platform)) {
|
|
171
|
+
const candidate = path.join(workspaceRoot, 'node_modules', '.bin', name);
|
|
172
|
+
if (isExecutable(candidate))
|
|
173
|
+
return candidate;
|
|
174
|
+
}
|
|
175
|
+
const pkgDir = (deps.ownPackageDir ?? resolveTsgoPackageDir)();
|
|
176
|
+
if (!pkgDir)
|
|
177
|
+
return null;
|
|
178
|
+
return ownBinCandidates(pkgDir, platform).find(isExecutable) ?? null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Why tsgo could not run, and what to do — the CAUSE half, shared by both sites that can hit it.
|
|
182
|
+
*
|
|
183
|
+
* Phrased for the new ownership: `@lensmcp/cluster` DEPENDS on the compiler, so "not installed" is
|
|
184
|
+
* no longer an expected opt-out state, it is a broken install (or an unsupported platform, or a bad
|
|
185
|
+
* override). Callers append the CONSEQUENCE, which differs by site.
|
|
186
|
+
*/
|
|
187
|
+
exports.TSGO_MISSING_CAUSE = `tsgo binary not found — @lensmcp/cluster depends on ${exports.TSGO_PACKAGE}, so an absent binary means an ` +
|
|
188
|
+
`incomplete install (reinstall node_modules), a platform with no ${exports.TSGO_PACKAGE} build, or a ` +
|
|
189
|
+
`LENSMCP_TSGO_BIN override pointing at a missing or non-executable file`;
|
|
190
|
+
/**
|
|
191
|
+
* Report — onto the LENS BUS, not just the console — that tsgo cannot run.
|
|
192
|
+
*
|
|
193
|
+
* A `console.warn` is invisible to anything reading `typecheck://current`, which is precisely the
|
|
194
|
+
* failure this whole area exists to eliminate: a resource that looks healthy because nobody is
|
|
195
|
+
* watching. Published as a `toolError` run, so the reducer records the producer as reporting with
|
|
196
|
+
* `lastRunStatus: 'error'` — `status` degrades to `unknown` (never a false `clean`) and `detail`
|
|
197
|
+
* names the cause and the fix.
|
|
198
|
+
*
|
|
199
|
+
* Strictly passive: fully swallowed, never blocks or fails the host build.
|
|
200
|
+
*/
|
|
201
|
+
function reportTsgoUnavailable(args) {
|
|
202
|
+
try {
|
|
203
|
+
(0, typecheck_bus_1.publishTypecheckRun)({
|
|
204
|
+
workspaceRoot: args.workspaceRoot,
|
|
205
|
+
project: args.appName,
|
|
206
|
+
tool: 'tsgo',
|
|
207
|
+
tsConfig: args.tsConfig,
|
|
208
|
+
output: '',
|
|
209
|
+
durationMs: 0,
|
|
210
|
+
toolError: args.detail,
|
|
211
|
+
...(args.eventFile ? { eventFile: args.eventFile } : {}),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
/* never let instrumentation disturb a build */
|
|
216
|
+
}
|
|
41
217
|
}
|
|
42
218
|
/**
|
|
43
219
|
* Write the shim tsconfig (absolute `extends`; see the header) and return its path. It lives under
|
|
@@ -120,12 +296,14 @@ class TsgoCheckPlugin {
|
|
|
120
296
|
if (!bin) {
|
|
121
297
|
// The binary vanished after config time (e.g. a reinstall mid-serve) — warn once, go quiet.
|
|
122
298
|
this.disabled = true;
|
|
123
|
-
|
|
299
|
+
// No trailing period: `summariseProducers` renders this INSIDE a sentence it terminates itself.
|
|
300
|
+
const detail = `${exports.TSGO_MISSING_CAUSE}. Type checking is disabled for the rest of this session`;
|
|
301
|
+
console.warn(`[tsgo] ${this.opts.appName}: ${detail}.`);
|
|
124
302
|
// A checker that stopped checking must SAY so: without this the resource
|
|
125
303
|
// would keep serving the last (possibly clean) verdict forever, which is
|
|
126
304
|
// exactly the "looks healthy, nobody is watching" state this work exists
|
|
127
305
|
// to make impossible.
|
|
128
|
-
this.publish({ output: '', durationMs: 0, toolError:
|
|
306
|
+
this.publish({ output: '', durationMs: 0, toolError: detail });
|
|
129
307
|
return;
|
|
130
308
|
}
|
|
131
309
|
if (this.inflight) {
|