@geekmidas/cli 10.0.0-alpha.47 → 10.0.0-alpha.48
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/bin/gkm.mjs +9 -1
- package/bin/owning-tsconfig-jsx-register.mjs +18 -0
- package/bin/owning-tsconfig-jsx.mjs +108 -0
- package/dist/index.cjs +65 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +65 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -14
package/bin/gkm.mjs
CHANGED
|
@@ -19,6 +19,10 @@ if (
|
|
|
19
19
|
const { createRequire } = await import('node:module');
|
|
20
20
|
const require = createRequire(import.meta.url);
|
|
21
21
|
const tsxPath = pathToFileURL(require.resolve('tsx')).href;
|
|
22
|
+
// Before tsx, so tsx reads each `.tsx` through it and compiles its JSX the
|
|
23
|
+
// way the file's own tsconfig says. See `owning-tsconfig-jsx.mjs`.
|
|
24
|
+
const jsxPath = new URL('./owning-tsconfig-jsx-register.mjs', import.meta.url)
|
|
25
|
+
.href;
|
|
22
26
|
|
|
23
27
|
const { execFileSync } = await import('node:child_process');
|
|
24
28
|
try {
|
|
@@ -26,7 +30,11 @@ if (
|
|
|
26
30
|
stdio: 'inherit',
|
|
27
31
|
env: {
|
|
28
32
|
...process.env,
|
|
29
|
-
|
|
33
|
+
// `--import=<url>`, never `--import <url>`: Next.js re-parses
|
|
34
|
+
// NODE_OPTIONS for its workers, and with two space-separated imports
|
|
35
|
+
// its parser glued them into one specifier `next build` could not find.
|
|
36
|
+
NODE_OPTIONS:
|
|
37
|
+
`${nodeOptions} --import=${jsxPath} --import=${tsxPath}`.trim(),
|
|
30
38
|
},
|
|
31
39
|
});
|
|
32
40
|
} catch (e) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `--import` that installs `owning-tsconfig-jsx.mjs` — it must come before
|
|
3
|
+
* tsx's own `--import`, so that tsx reads the source through it.
|
|
4
|
+
*
|
|
5
|
+
* Registered with `module.register()` whatever tsx chose: tsx's in-thread hooks
|
|
6
|
+
* (`registerHooks`, Node 22.22.3+ / 24.11.1+) hand off to the off-thread chain
|
|
7
|
+
* when they load a file, and on older Node tsx registers off-thread too, after
|
|
8
|
+
* this — so either way this hook is asked after tsx, for the file's source.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { register } from 'node:module';
|
|
12
|
+
import { isInternalThread } from 'node:worker_threads';
|
|
13
|
+
|
|
14
|
+
// As tsx guards its own: where Node runs preloads in the hooks thread too,
|
|
15
|
+
// registering from there would hook the hooks.
|
|
16
|
+
if (!isInternalThread) {
|
|
17
|
+
register('./owning-tsconfig-jsx.mjs', import.meta.url);
|
|
18
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile a `.tsx` file's JSX the way the tsconfig that owns it says to.
|
|
3
|
+
*
|
|
4
|
+
* tsx applies one tsconfig to the whole process — the one in the directory the
|
|
5
|
+
* command ran from — and only to the files that tsconfig's `include` covers.
|
|
6
|
+
* `gkm dev` runs each app from its own directory, so a construct's template in
|
|
7
|
+
* the workspace (`constructs/templates/MagicLink.tsx`) was outside the app's
|
|
8
|
+
* `include` and compiled with esbuild's defaults: the classic runtime, which
|
|
9
|
+
* calls `React.createElement` in a file that never imported React, because the
|
|
10
|
+
* root tsconfig's `"jsx": "react-jsx"` said it need not. It threw
|
|
11
|
+
* `React is not defined` at render under `gkm dev` and rendered under
|
|
12
|
+
* `gkm test`, where Vitest compiles each file with its nearest tsconfig.
|
|
13
|
+
*
|
|
14
|
+
* Pointing tsx at the root tsconfig instead would only move the problem: an
|
|
15
|
+
* app may set its own `jsx` or `jsxImportSource` (a Hono app's `hono/jsx`), and
|
|
16
|
+
* one tsconfig per process is the same mistake `adjacent-tsconfig.mjs` exists
|
|
17
|
+
* to undo for path aliases. tsx has no per-file tsconfig, but esbuild — which
|
|
18
|
+
* tsx compiles with — honours JSX pragmas in a file over its options. So this
|
|
19
|
+
* hook, registered before tsx so that it sits between tsx and the file on disk,
|
|
20
|
+
* appends the owning tsconfig's JSX settings to the source as pragmas.
|
|
21
|
+
*
|
|
22
|
+
* The owning tsconfig is the nearest one above the file, as Vitest and
|
|
23
|
+
* `adjacent-tsconfig.mjs` choose it. A file that already carries its own JSX
|
|
24
|
+
* pragma, or whose tsconfig asks for nothing a pragma can say (`preserve`, or
|
|
25
|
+
* no `jsx` at all), is left exactly as it is. The pragma goes at the end, so no
|
|
26
|
+
* line of the file moves and its stack traces still point at the right place.
|
|
27
|
+
*
|
|
28
|
+
* Only ESM sources pass through here: for a CommonJS module tsx reads the file
|
|
29
|
+
* itself, and those keep the process tsconfig's behaviour.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { dirname } from 'node:path';
|
|
33
|
+
import { fileURLToPath } from 'node:url';
|
|
34
|
+
import { getTsconfig } from 'get-tsconfig';
|
|
35
|
+
|
|
36
|
+
const JSX_SOURCE = /\.[jt]sx$/;
|
|
37
|
+
/** A pragma the file chose for itself, which this hook never overrides. */
|
|
38
|
+
const OWN_PRAGMA = /@jsx(?:Runtime|ImportSource|Frag)?\s/;
|
|
39
|
+
|
|
40
|
+
/** Directory → the pragma its owning tsconfig implies, or `null`. */
|
|
41
|
+
const pragmas = new Map();
|
|
42
|
+
/** Shared with `get-tsconfig`, so a config `extends` is read once. */
|
|
43
|
+
const tsconfigCache = new Map();
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The pragma comment that tells esbuild what these compiler options tell
|
|
47
|
+
* TypeScript, or `null` when there is nothing to say.
|
|
48
|
+
*/
|
|
49
|
+
export function jsxPragma(compilerOptions = {}) {
|
|
50
|
+
const jsx = compilerOptions.jsx?.toLowerCase();
|
|
51
|
+
|
|
52
|
+
if (jsx === 'react-jsx' || jsx === 'react-jsxdev') {
|
|
53
|
+
const source = compilerOptions.jsxImportSource ?? 'react';
|
|
54
|
+
return `/* @jsxRuntime automatic @jsxImportSource ${source} */`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (jsx === 'react') {
|
|
58
|
+
const parts = ['@jsxRuntime classic'];
|
|
59
|
+
if (compilerOptions.jsxFactory) {
|
|
60
|
+
parts.push(`@jsx ${compilerOptions.jsxFactory}`);
|
|
61
|
+
}
|
|
62
|
+
if (compilerOptions.jsxFragmentFactory) {
|
|
63
|
+
parts.push(`@jsxFrag ${compilerOptions.jsxFragmentFactory}`);
|
|
64
|
+
}
|
|
65
|
+
return `/* ${parts.join(' ')} */`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// `preserve` and `react-native` leave JSX in the output, which no pragma
|
|
69
|
+
// can ask for; without `jsx` TypeScript compiles no `.tsx` at all.
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function pragmaFor(directory) {
|
|
74
|
+
if (!pragmas.has(directory)) {
|
|
75
|
+
const tsconfig = getTsconfig(directory, 'tsconfig.json', tsconfigCache);
|
|
76
|
+
pragmas.set(directory, jsxPragma(tsconfig?.config.compilerOptions));
|
|
77
|
+
}
|
|
78
|
+
return pragmas.get(directory);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The JSX file at this URL, when it is one of the project's own. */
|
|
82
|
+
function jsxFilePath(url) {
|
|
83
|
+
if (!url.startsWith('file:')) return null;
|
|
84
|
+
|
|
85
|
+
const path = fileURLToPath(url.split(/[?#]/)[0]);
|
|
86
|
+
if (!JSX_SOURCE.test(path) || path.includes('/node_modules/')) return null;
|
|
87
|
+
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** For `module.register()`, so it runs behind tsx however tsx registered. */
|
|
92
|
+
export async function load(url, context, nextLoad) {
|
|
93
|
+
const loaded = await nextLoad(url, context);
|
|
94
|
+
|
|
95
|
+
const path = jsxFilePath(url);
|
|
96
|
+
if (!path || loaded.source == null) return loaded;
|
|
97
|
+
|
|
98
|
+
const pragma = pragmaFor(dirname(path));
|
|
99
|
+
if (!pragma) return loaded;
|
|
100
|
+
|
|
101
|
+
const source =
|
|
102
|
+
typeof loaded.source === 'string'
|
|
103
|
+
? loaded.source
|
|
104
|
+
: new TextDecoder().decode(loaded.source);
|
|
105
|
+
if (OWN_PRAGMA.test(source)) return loaded;
|
|
106
|
+
|
|
107
|
+
return { ...loaded, source: `${source}\n${pragma}\n` };
|
|
108
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -48,7 +48,7 @@ let _geekmidas_constructs_queue = require("@geekmidas/constructs/queue");
|
|
|
48
48
|
let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
|
|
49
49
|
let node_dns_promises = require("node:dns/promises");
|
|
50
50
|
//#region package.json
|
|
51
|
-
var version = "10.0.0-alpha.
|
|
51
|
+
var version = "10.0.0-alpha.48";
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/auth/credentials.ts
|
|
54
54
|
/**
|
|
@@ -344,6 +344,45 @@ function normalizeRoutes(routes) {
|
|
|
344
344
|
return Array.isArray(routes) ? routes : [routes];
|
|
345
345
|
}
|
|
346
346
|
//#endregion
|
|
347
|
+
//#region src/owningTsconfigJsx.ts
|
|
348
|
+
const REGISTER = "owning-tsconfig-jsx-register.mjs";
|
|
349
|
+
/** The CLI's `bin/` was not found above the module looking for it. */
|
|
350
|
+
var OwningTsconfigJsxHookMissing = class extends Error {
|
|
351
|
+
from;
|
|
352
|
+
constructor(from) {
|
|
353
|
+
super(`Could not find bin/${REGISTER} in any directory above '${from}'. It ships with @geekmidas/cli beside bin/gkm.mjs; reinstall the package if it is missing.`);
|
|
354
|
+
this.from = from;
|
|
355
|
+
this.name = "OwningTsconfigJsxHookMissing";
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* The CLI's `bin/owning-tsconfig-jsx-register.mjs`, as a file URL. Searched
|
|
360
|
+
* upward because this module runs from `src/` in the repo and from a chunk in
|
|
361
|
+
* `dist/` once built, and `bin/` sits beside both.
|
|
362
|
+
*/
|
|
363
|
+
function registerUrl() {
|
|
364
|
+
const from = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
365
|
+
for (let dir = from;; dir = (0, node_path.dirname)(dir)) {
|
|
366
|
+
const candidate = (0, node_path.join)(dir, "bin", REGISTER);
|
|
367
|
+
if ((0, node_fs.existsSync)(candidate)) return (0, node_url.pathToFileURL)(candidate).href;
|
|
368
|
+
if ((0, node_path.dirname)(dir) === dir) throw new OwningTsconfigJsxHookMissing(from);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* `NODE_OPTIONS` for a process that runs app code through tsx, with the hook
|
|
373
|
+
* that compiles each `.tsx` with its own tsconfig's JSX settings (see
|
|
374
|
+
* `bin/owning-tsconfig-jsx.mjs`) put in front of every other `--import`, so it
|
|
375
|
+
* is registered before tsx and reads the source on tsx's behalf.
|
|
376
|
+
*
|
|
377
|
+
* `bin/gkm.mjs` already starts the CLI this way, and its children inherit it;
|
|
378
|
+
* this is for those that set `NODE_OPTIONS` themselves, or whose CLI was
|
|
379
|
+
* started with tsx already imported.
|
|
380
|
+
*/
|
|
381
|
+
function withOwningTsconfigJsx(nodeOptions = "") {
|
|
382
|
+
if (nodeOptions.includes(REGISTER)) return nodeOptions;
|
|
383
|
+
return [`--import=${registerUrl()}`, nodeOptions].filter(Boolean).join(" ");
|
|
384
|
+
}
|
|
385
|
+
//#endregion
|
|
347
386
|
//#region src/trust/index.ts
|
|
348
387
|
/**
|
|
349
388
|
* Installing the local edge's certificate authority into the OS trust store.
|
|
@@ -1696,7 +1735,8 @@ var EntryRunner = class {
|
|
|
1696
1735
|
async runProcess() {
|
|
1697
1736
|
const env = {
|
|
1698
1737
|
...process.env,
|
|
1699
|
-
PORT: String(this.port)
|
|
1738
|
+
PORT: String(this.port),
|
|
1739
|
+
NODE_OPTIONS: withOwningTsconfigJsx(process.env.NODE_OPTIONS)
|
|
1700
1740
|
};
|
|
1701
1741
|
this.childProcess = (0, node_child_process.spawn)(...tsxCommand((0, node_path.dirname)(this.wrapperPath), [this.wrapperPath]), {
|
|
1702
1742
|
stdio: "inherit",
|
|
@@ -1946,7 +1986,8 @@ var DevServer = class {
|
|
|
1946
1986
|
env: {
|
|
1947
1987
|
...process.env,
|
|
1948
1988
|
NODE_ENV: "development",
|
|
1949
|
-
GKM_DEV_PID: String(process.pid)
|
|
1989
|
+
GKM_DEV_PID: String(process.pid),
|
|
1990
|
+
NODE_OPTIONS: withOwningTsconfigJsx(process.env.NODE_OPTIONS)
|
|
1950
1991
|
},
|
|
1951
1992
|
detached: true
|
|
1952
1993
|
});
|
|
@@ -6778,7 +6819,7 @@ async function sniffEntryFile(entryPath, appPath, workspacePath) {
|
|
|
6778
6819
|
],
|
|
6779
6820
|
env: {
|
|
6780
6821
|
...process.env,
|
|
6781
|
-
NODE_OPTIONS: "--import=tsx"
|
|
6822
|
+
NODE_OPTIONS: withOwningTsconfigJsx("--import=tsx")
|
|
6782
6823
|
}
|
|
6783
6824
|
});
|
|
6784
6825
|
let stdout = "";
|
|
@@ -9094,26 +9135,26 @@ function loadPackageJson() {
|
|
|
9094
9135
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
9095
9136
|
*/
|
|
9096
9137
|
const GEEKMIDAS_VERSIONS = {
|
|
9097
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
9098
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
9099
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
9100
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
9101
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
9102
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
9103
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
9104
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
9105
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
9106
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
9107
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
9108
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
9109
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
9110
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
9111
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
9112
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
9113
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
9114
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
9115
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
9116
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
9138
|
+
"@geekmidas/audit": "~10.0.0-alpha.48",
|
|
9139
|
+
"@geekmidas/auth": "~10.0.0-alpha.48",
|
|
9140
|
+
"@geekmidas/cache": "~10.0.0-alpha.48",
|
|
9141
|
+
"@geekmidas/client": "~10.0.0-alpha.48",
|
|
9142
|
+
"@geekmidas/cloud": "~10.0.0-alpha.48",
|
|
9143
|
+
"@geekmidas/constructs": "~10.0.0-alpha.48",
|
|
9144
|
+
"@geekmidas/db": "~10.0.0-alpha.48",
|
|
9145
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.48",
|
|
9146
|
+
"@geekmidas/envkit": "~10.0.0-alpha.48",
|
|
9147
|
+
"@geekmidas/errors": "~10.0.0-alpha.48",
|
|
9148
|
+
"@geekmidas/events": "~10.0.0-alpha.48",
|
|
9149
|
+
"@geekmidas/logger": "~10.0.0-alpha.48",
|
|
9150
|
+
"@geekmidas/manifest": "~10.0.0-alpha.48",
|
|
9151
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.48",
|
|
9152
|
+
"@geekmidas/schema": "~10.0.0-alpha.48",
|
|
9153
|
+
"@geekmidas/services": "~10.0.0-alpha.48",
|
|
9154
|
+
"@geekmidas/storage": "~10.0.0-alpha.48",
|
|
9155
|
+
"@geekmidas/studio": "~10.0.0-alpha.48",
|
|
9156
|
+
"@geekmidas/telescope": "~10.0.0-alpha.48",
|
|
9157
|
+
"@geekmidas/testkit": "~10.0.0-alpha.48",
|
|
9117
9158
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
9118
9159
|
};
|
|
9119
9160
|
//#endregion
|