@webjsdev/cli 0.10.26 → 0.10.27

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/webjs.js CHANGED
@@ -6,6 +6,7 @@ import { resolveBin } from '../lib/resolve-bin.js';
6
6
  import { checkNodeInline, nodeInlineMessage } from '../lib/node-preflight.js';
7
7
  import { loadAppEnv, resolvePort } from '../lib/port.js';
8
8
  import { planDevSupervisor } from '../lib/dev-supervisor.js';
9
+ import { importWebjsdev } from '../lib/import-webjsdev.js';
9
10
 
10
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
11
12
  const [cmd, ...rest] = process.argv.slice(2);
@@ -121,7 +122,7 @@ async function main() {
121
122
  // instead of crashing cryptically later. `help` is exempt so a user on an
122
123
  // old Node can still read usage.
123
124
  if (cmd !== 'help' && cmd !== undefined) {
124
- const { assertNodeVersion } = await import('@webjsdev/server');
125
+ const { assertNodeVersion } = await importWebjsdev('@webjsdev/server');
125
126
  assertNodeVersion({ onFail: 'exit' });
126
127
  }
127
128
  switch (cmd) {
@@ -129,7 +130,7 @@ async function main() {
129
130
  // If we're already inside the reload child (node --watch or bun --hot),
130
131
  // start the server directly.
131
132
  if (process.env.__WEBJS_DEV_CHILD === '1') {
132
- const { startServer } = await import('@webjsdev/server');
133
+ const { startServer } = await importWebjsdev('@webjsdev/server');
133
134
  // Load `.env` BEFORE resolving the port so a `PORT` set there is in
134
135
  // process.env at resolution time (#447). The server loads `.env`
135
136
  // too, but that runs too late to affect the port the CLI computes.
@@ -165,7 +166,7 @@ async function main() {
165
166
  });
166
167
 
167
168
  if (plan.mode === 'inline') {
168
- const { startServer } = await import('@webjsdev/server');
169
+ const { startServer } = await importWebjsdev('@webjsdev/server');
169
170
  loadAppEnv(process.cwd());
170
171
  const port = resolvePort(flag(rest, '--port'));
171
172
  await startServer({ appDir: process.cwd(), port, dev: true });
@@ -182,7 +183,7 @@ async function main() {
182
183
  break;
183
184
  }
184
185
  case 'start': {
185
- const { startServer } = await import('@webjsdev/server');
186
+ const { startServer } = await importWebjsdev('@webjsdev/server');
186
187
  // Load `.env` BEFORE resolving the port so a `PORT` set there wins over
187
188
  // the 8080 default (#447), same as for `dev`.
188
189
  loadAppEnv(process.cwd());
@@ -364,7 +365,7 @@ async function main() {
364
365
  break;
365
366
  }
366
367
  case 'check': {
367
- const { checkConventions, RULES } = await import('@webjsdev/server/check');
368
+ const { checkConventions, RULES } = await importWebjsdev('@webjsdev/server/check');
368
369
 
369
370
  if (rest.includes('--rules')) {
370
371
  console.log('webjs check, correctness rules:');
@@ -390,7 +391,7 @@ async function main() {
390
391
  if (rest.includes('--json')) {
391
392
  // The projector lives in @webjsdev/mcp (the MCP `check` tool's home),
392
393
  // so `check --json` and the MCP tool stay byte-identical (#415).
393
- const { projectCheck } = await import('@webjsdev/mcp/check-report');
394
+ const { projectCheck } = await importWebjsdev('@webjsdev/mcp/check-report');
394
395
  console.log(JSON.stringify(projectCheck(violations)));
395
396
  if (violations.length > 0) process.exit(1);
396
397
  break;
@@ -447,7 +448,7 @@ async function main() {
447
448
  // narrowing the @webjsdev/core `Route` href union + per-route `params`.
448
449
  // Opt-in codegen: the static types in @webjsdev/core work without it
449
450
  // (un-generated apps see `Route = string`).
450
- const { generateRouteTypes } = await import('@webjsdev/server');
451
+ const { generateRouteTypes } = await importWebjsdev('@webjsdev/server');
451
452
  const { mkdir, writeFile } = await import('node:fs/promises');
452
453
  const appDir = process.cwd();
453
454
  const text = await generateRouteTypes(appDir);
@@ -545,7 +546,7 @@ Full docs: https://docs.webjs.com`);
545
546
  const sub = rest[0];
546
547
  const args = rest.slice(1);
547
548
  const appDir = process.cwd();
548
- const { pinAll, unpinPackage, listPinned, auditPinned, findOutdated, updatePinned, readPinFile, ensureVendorCommittable, SUPPORTED_PROVIDERS } = await import('@webjsdev/server');
549
+ const { pinAll, unpinPackage, listPinned, auditPinned, findOutdated, updatePinned, readPinFile, ensureVendorCommittable, SUPPORTED_PROVIDERS } = await importWebjsdev('@webjsdev/server');
549
550
 
550
551
  // Parse `--from <provider>` once at the top so subcommands share it.
551
552
  // Mirrors importmap-rails's `bin/importmap pin foo --from jsdelivr`.
@@ -808,7 +809,7 @@ Full docs: https://docs.webjs.com`);
808
809
  // runnable directly as `npx @webjsdev/mcp`); `webjs mcp` delegates to it
809
810
  // for back-compat. The version advertised in the initialize handshake is
810
811
  // @webjsdev/mcp's own, resolved by its bin, so this passes none.
811
- const { runMcpServer } = await import('@webjsdev/mcp');
812
+ const { runMcpServer } = await importWebjsdev('@webjsdev/mcp');
812
813
  const { createRequire } = await import('node:module');
813
814
  const require = createRequire(import.meta.url);
814
815
  let version = '0.0.0';
@@ -0,0 +1,99 @@
1
+ // Pin `@webjsdev/*` imports under Bun zero-install (#709).
2
+ //
3
+ // Under Bun zero-install a bare `import('@webjsdev/server')` from the cli (run
4
+ // out of the global cache) ENOENTs: Bun's runtime auto-install ignores the cli's
5
+ // declared range and flakily fetches latest, which fails. An INLINE-versioned
6
+ // specifier (`@webjsdev/server@^0.8.0`) resolves reliably. So we read the version
7
+ // the APP declares (the scaffold adds `@webjsdev/*` to its deps) and retry inline
8
+ // only when the bare import fails, so Node and installed apps are unaffected.
9
+
10
+ import { readFileSync } from 'node:fs';
11
+ import { join, dirname } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ // The cli's OWN package.json (one level up from this lib), used as the fallback
15
+ // pin source for `@webjsdev/*` packages the app does not declare (mcp, ui).
16
+ const CLI_PKG = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
17
+
18
+ /**
19
+ * Whether a declared version is a Bun inline-safe specifier: an exact version or
20
+ * a single caret / tilde / comparator range, but NOT a range over a prerelease
21
+ * (`^1.0.0-rc.3`, which Bun ENOENTs on, #703), a wildcard, a multi-token range,
22
+ * or a protocol range (`workspace:` / `file:`).
23
+ * @param {unknown} v
24
+ * @returns {boolean}
25
+ */
26
+ export function inlineSafeVersion(v) {
27
+ if (typeof v !== 'string') return false;
28
+ const m = /^(>=|<=|>|<|=|\^|~)?(\d+(?:\.\d+){0,2})([-+][0-9A-Za-z.-]+)?$/.exec(v);
29
+ return !!m && !(m[1] && m[3]);
30
+ }
31
+
32
+ /**
33
+ * The version a `package.json` at `pkgPath` declares for `name`, when it is
34
+ * inline-safe; else null.
35
+ * @param {string} name
36
+ * @param {string} pkgPath absolute path to a package.json
37
+ * @returns {string | null}
38
+ */
39
+ function declaredIn(name, pkgPath) {
40
+ try {
41
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
42
+ const v = { ...pkg.dependencies, ...pkg.devDependencies }[name];
43
+ if (inlineSafeVersion(v)) return v;
44
+ } catch { /* no readable package.json */ }
45
+ return null;
46
+ }
47
+
48
+ /**
49
+ * The inline-safe version to pin `name` to: the app's `package.json` (in `cwd`)
50
+ * first (it declares `@webjsdev/server` etc.), else the cli's OWN package.json
51
+ * (for `@webjsdev/*` the app does not declare, like `mcp` / `ui`). Null if
52
+ * neither has an inline-safe declaration.
53
+ * @param {string} name
54
+ * @param {string} [cwd]
55
+ * @returns {string | null}
56
+ */
57
+ export function appDeclaredVersion(name, cwd = process.cwd()) {
58
+ return declaredIn(name, join(cwd, 'package.json')) || declaredIn(name, CLI_PKG);
59
+ }
60
+
61
+ /**
62
+ * Whether an import error is a resolution / not-found failure (so a version
63
+ * retry is warranted), vs a genuine load-time throw from the module's own code
64
+ * (which we must NOT retry, to avoid masking it and re-running side effects).
65
+ * @param {unknown} err
66
+ * @returns {boolean}
67
+ */
68
+ function isResolutionError(err) {
69
+ if (err && /** @type {any} */ (err).code === 'ERR_MODULE_NOT_FOUND') return true;
70
+ const msg = err && String(/** @type {any} */ (err).message || err);
71
+ // Narrow on purpose: Node sets the code above; Bun's auto-install miss is
72
+ // `ENOENT while resolving package '...'`. A broad "cannot find" would match
73
+ // ordinary runtime errors ("Cannot find user") and wrongly trigger a retry.
74
+ return !!msg && /ENOENT|resolving package|module not found/i.test(msg);
75
+ }
76
+
77
+ /**
78
+ * Import an `@webjsdev/*` module, pinning the version under Bun zero-install. On
79
+ * Node or an installed app the bare specifier resolves from `node_modules` (no
80
+ * retry). On a RESOLUTION failure (Bun zero-install), retry with the app's (else
81
+ * the cli's own) declared version inline. A real load-time throw is rethrown,
82
+ * not retried. A subpath (`/check`) is preserved across the rewrite.
83
+ * @param {string} spec e.g. `@webjsdev/server` or `@webjsdev/server/check`
84
+ * @param {(s: string) => Promise<any>} [importer] injectable for tests
85
+ * @returns {Promise<any>}
86
+ */
87
+ export async function importWebjsdev(spec, importer = (s) => import(s)) {
88
+ try {
89
+ return await importer(spec);
90
+ } catch (err) {
91
+ if (!isResolutionError(err)) throw err;
92
+ const m = /^(@webjsdev\/[^/]+)(\/.*)?$/.exec(spec);
93
+ const pkg = m && m[1];
94
+ const sub = (m && m[2]) || '';
95
+ const v = pkg && appDeclaredVersion(pkg);
96
+ if (v) return await importer(pkg + '@' + v + sub);
97
+ throw err;
98
+ }
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webjsdev/cli",
3
- "version": "0.10.26",
3
+ "version": "0.10.27",
4
4
  "type": "module",
5
5
  "description": "webjs CLI - dev, start, create, db",
6
6
  "bin": {