@webjsdev/cli 0.10.27 → 0.10.29
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/README.md +1 -1
- package/bin/webjs.js +20 -20
- package/lib/create.js +48 -158
- package/lib/runtime-rewrite.js +7 -3
- package/lib/saas-template.js +8 -8
- package/package.json +1 -1
- package/templates/AGENTS.md +13 -24
- package/templates/CONVENTIONS.md +5 -4
- package/lib/import-webjsdev.js +0 -99
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ npx @webjsdev/cli create my-app
|
|
|
33
33
|
cd my-app && npm run dev
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
`webjs create`
|
|
36
|
+
Both `webjs create` and `create-webjs-app` auto-install dependencies in the new directory using your detected package manager (npm / pnpm / yarn / bun). Pass `--no-install` to opt out.
|
|
37
37
|
|
|
38
38
|
## Commands
|
|
39
39
|
|
package/bin/webjs.js
CHANGED
|
@@ -6,7 +6,6 @@ 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';
|
|
10
9
|
|
|
11
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
11
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
@@ -51,12 +50,12 @@ const USAGE = `webjs commands:
|
|
|
51
50
|
webjs doctor Verify project health (Node, tsconfig, env, vendor pins, importmap coherence, @webjsdev versions, git hook, page/layout elision)
|
|
52
51
|
webjs types Generate .webjs/routes.d.ts (typed Route union + per-route params)
|
|
53
52
|
webjs typecheck [tsc args...] Type-check the app with the project's tsc --noEmit (non-zero on errors)
|
|
54
|
-
webjs create <name> [--template full-stack|api|saas] [--db sqlite|postgres] [--runtime node|bun] [--
|
|
53
|
+
webjs create <name> [--template full-stack|api|saas] [--db sqlite|postgres] [--runtime node|bun] [--no-install] Scaffold a new webjs app
|
|
55
54
|
(only 3 templates exist. default: full-stack, Drizzle, --db sqlite, --runtime node)
|
|
56
55
|
--runtime bun emits a Bun-flavored app (bun.lock, bun Dockerfile/CI, bun docs);
|
|
57
56
|
also auto-detected when run via "bun create webjs".
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
Auto-runs the detected package manager's install in the new dir
|
|
58
|
+
unless --no-install is passed.
|
|
60
59
|
webjs db generate Generate a SQL migration from the schema (drizzle-kit generate)
|
|
61
60
|
webjs db migrate Apply pending migrations (drizzle-kit migrate)
|
|
62
61
|
webjs db push Push the schema straight to the dev DB (drizzle-kit push)
|
|
@@ -122,7 +121,7 @@ async function main() {
|
|
|
122
121
|
// instead of crashing cryptically later. `help` is exempt so a user on an
|
|
123
122
|
// old Node can still read usage.
|
|
124
123
|
if (cmd !== 'help' && cmd !== undefined) {
|
|
125
|
-
const { assertNodeVersion } = await
|
|
124
|
+
const { assertNodeVersion } = await import('@webjsdev/server');
|
|
126
125
|
assertNodeVersion({ onFail: 'exit' });
|
|
127
126
|
}
|
|
128
127
|
switch (cmd) {
|
|
@@ -130,7 +129,7 @@ async function main() {
|
|
|
130
129
|
// If we're already inside the reload child (node --watch or bun --hot),
|
|
131
130
|
// start the server directly.
|
|
132
131
|
if (process.env.__WEBJS_DEV_CHILD === '1') {
|
|
133
|
-
const { startServer } = await
|
|
132
|
+
const { startServer } = await import('@webjsdev/server');
|
|
134
133
|
// Load `.env` BEFORE resolving the port so a `PORT` set there is in
|
|
135
134
|
// process.env at resolution time (#447). The server loads `.env`
|
|
136
135
|
// too, but that runs too late to affect the port the CLI computes.
|
|
@@ -148,6 +147,12 @@ async function main() {
|
|
|
148
147
|
// down on exit so a watcher cannot outlive the server.
|
|
149
148
|
const { readAppTasks } = await import('../lib/app-tasks.js');
|
|
150
149
|
const devTasks = readAppTasks(process.cwd());
|
|
150
|
+
// Load `.env` BEFORE the before-steps (same as `start`, L188), so a
|
|
151
|
+
// `dev.before` `webjs db migrate` sees DATABASE_URL from `.env`. Without
|
|
152
|
+
// this a Postgres dev migrate runs with no connection string and fails
|
|
153
|
+
// (sqlite survives via its `?? 'db/dev.db'` config fallback). The watch
|
|
154
|
+
// child / inline server load `.env` again later (idempotent).
|
|
155
|
+
loadAppEnv(process.cwd());
|
|
151
156
|
await runPhaseBeforeSteps('dev', devTasks.dev.before, process.cwd());
|
|
152
157
|
const killTasks = await startDevParallelTasks(devTasks.dev.parallel, process.cwd());
|
|
153
158
|
process.on('SIGINT', () => { killTasks(); process.exit(0); });
|
|
@@ -166,7 +171,7 @@ async function main() {
|
|
|
166
171
|
});
|
|
167
172
|
|
|
168
173
|
if (plan.mode === 'inline') {
|
|
169
|
-
const { startServer } = await
|
|
174
|
+
const { startServer } = await import('@webjsdev/server');
|
|
170
175
|
loadAppEnv(process.cwd());
|
|
171
176
|
const port = resolvePort(flag(rest, '--port'));
|
|
172
177
|
await startServer({ appDir: process.cwd(), port, dev: true });
|
|
@@ -183,7 +188,7 @@ async function main() {
|
|
|
183
188
|
break;
|
|
184
189
|
}
|
|
185
190
|
case 'start': {
|
|
186
|
-
const { startServer } = await
|
|
191
|
+
const { startServer } = await import('@webjsdev/server');
|
|
187
192
|
// Load `.env` BEFORE resolving the port so a `PORT` set there wins over
|
|
188
193
|
// the 8080 default (#447), same as for `dev`.
|
|
189
194
|
loadAppEnv(process.cwd());
|
|
@@ -365,7 +370,7 @@ async function main() {
|
|
|
365
370
|
break;
|
|
366
371
|
}
|
|
367
372
|
case 'check': {
|
|
368
|
-
const { checkConventions, RULES } = await
|
|
373
|
+
const { checkConventions, RULES } = await import('@webjsdev/server/check');
|
|
369
374
|
|
|
370
375
|
if (rest.includes('--rules')) {
|
|
371
376
|
console.log('webjs check, correctness rules:');
|
|
@@ -391,7 +396,7 @@ async function main() {
|
|
|
391
396
|
if (rest.includes('--json')) {
|
|
392
397
|
// The projector lives in @webjsdev/mcp (the MCP `check` tool's home),
|
|
393
398
|
// so `check --json` and the MCP tool stay byte-identical (#415).
|
|
394
|
-
const { projectCheck } = await
|
|
399
|
+
const { projectCheck } = await import('@webjsdev/mcp/check-report');
|
|
395
400
|
console.log(JSON.stringify(projectCheck(violations)));
|
|
396
401
|
if (violations.length > 0) process.exit(1);
|
|
397
402
|
break;
|
|
@@ -448,7 +453,7 @@ async function main() {
|
|
|
448
453
|
// narrowing the @webjsdev/core `Route` href union + per-route `params`.
|
|
449
454
|
// Opt-in codegen: the static types in @webjsdev/core work without it
|
|
450
455
|
// (un-generated apps see `Route = string`).
|
|
451
|
-
const { generateRouteTypes } = await
|
|
456
|
+
const { generateRouteTypes } = await import('@webjsdev/server');
|
|
452
457
|
const { mkdir, writeFile } = await import('node:fs/promises');
|
|
453
458
|
const appDir = process.cwd();
|
|
454
459
|
const text = await generateRouteTypes(appDir);
|
|
@@ -522,11 +527,7 @@ files.
|
|
|
522
527
|
Full docs: https://docs.webjs.com`);
|
|
523
528
|
process.exit(1);
|
|
524
529
|
}
|
|
525
|
-
// Install policy (#682). Default per runtime: Node installs (needs
|
|
526
|
-
// node_modules to run); Bun skips (zero-install, `bun run dev` resolves
|
|
527
|
-
// on the fly). `--install` / `--no-install` override either way.
|
|
528
530
|
const noInstall = rest.includes('--no-install');
|
|
529
|
-
const explicitInstall = rest.includes('--install');
|
|
530
531
|
// --db picks the database dialect: sqlite (default) or postgres.
|
|
531
532
|
const db = flag(rest, '--db', 'sqlite');
|
|
532
533
|
// --runtime picks the target runtime: node (default) or bun. Orthogonal
|
|
@@ -537,16 +538,15 @@ Full docs: https://docs.webjs.com`);
|
|
|
537
538
|
console.error(`Error: unknown --runtime '${runtime}'. Only node / bun are supported.`);
|
|
538
539
|
process.exit(1);
|
|
539
540
|
}
|
|
540
|
-
const { scaffoldApp
|
|
541
|
-
|
|
542
|
-
await scaffoldApp(name, process.cwd(), { template, db, runtime, install });
|
|
541
|
+
const { scaffoldApp } = await import('../lib/create.js');
|
|
542
|
+
await scaffoldApp(name, process.cwd(), { template, db, runtime, install: !noInstall });
|
|
543
543
|
break;
|
|
544
544
|
}
|
|
545
545
|
case 'vendor': {
|
|
546
546
|
const sub = rest[0];
|
|
547
547
|
const args = rest.slice(1);
|
|
548
548
|
const appDir = process.cwd();
|
|
549
|
-
const { pinAll, unpinPackage, listPinned, auditPinned, findOutdated, updatePinned, readPinFile, ensureVendorCommittable, SUPPORTED_PROVIDERS } = await
|
|
549
|
+
const { pinAll, unpinPackage, listPinned, auditPinned, findOutdated, updatePinned, readPinFile, ensureVendorCommittable, SUPPORTED_PROVIDERS } = await import('@webjsdev/server');
|
|
550
550
|
|
|
551
551
|
// Parse `--from <provider>` once at the top so subcommands share it.
|
|
552
552
|
// Mirrors importmap-rails's `bin/importmap pin foo --from jsdelivr`.
|
|
@@ -809,7 +809,7 @@ Full docs: https://docs.webjs.com`);
|
|
|
809
809
|
// runnable directly as `npx @webjsdev/mcp`); `webjs mcp` delegates to it
|
|
810
810
|
// for back-compat. The version advertised in the initialize handshake is
|
|
811
811
|
// @webjsdev/mcp's own, resolved by its bin, so this passes none.
|
|
812
|
-
const { runMcpServer } = await
|
|
812
|
+
const { runMcpServer } = await import('@webjsdev/mcp');
|
|
813
813
|
const { createRequire } = await import('node:module');
|
|
814
814
|
const require = createRequire(import.meta.url);
|
|
815
815
|
let version = '0.0.0';
|
package/lib/create.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { mkdir, writeFile, readFile, cp } from 'node:fs/promises';
|
|
15
15
|
import { join, resolve, dirname } from 'node:path';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
|
-
import { existsSync
|
|
17
|
+
import { existsSync } from 'node:fs';
|
|
18
18
|
import { createRequire } from 'node:module';
|
|
19
19
|
import { spawnSync } from 'node:child_process';
|
|
20
20
|
import { bunifyProse, bunifyDockerfile, bunifyCompose, bunifyCi } from './runtime-rewrite.js';
|
|
@@ -35,86 +35,6 @@ function detectPackageManager() {
|
|
|
35
35
|
return 'npm';
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
* Decide whether `webjs create` runs the post-scaffold install, per target
|
|
40
|
-
* runtime (#682). Node installs by default (it needs `node_modules` to run);
|
|
41
|
-
* Bun SKIPS by default (zero-install: `bun run dev` resolves deps on the fly,
|
|
42
|
-
* #675). Under Bun zero-install webjs pins a declared dep to its package.json
|
|
43
|
-
* version via the #685/#698 onLoad rewrite (a caret range now resolves the
|
|
44
|
-
* highest match, not absolute latest), so `bun install` is the path to a frozen
|
|
45
|
-
* lockfile, not a correctness fix. Explicit flags win: `--install` forces it
|
|
46
|
-
* on, `--no-install` forces it off. The CLI entry points call this and pass an
|
|
47
|
-
* explicit boolean to `scaffoldApp`, so the library default (no install unless
|
|
48
|
-
* `install: true`) is unchanged for programmatic callers.
|
|
49
|
-
*
|
|
50
|
-
* @param {{ runtime?: string, explicitInstall?: boolean, noInstall?: boolean }} [o]
|
|
51
|
-
* @returns {boolean}
|
|
52
|
-
*/
|
|
53
|
-
export function resolveCreateInstall({ runtime, explicitInstall, noInstall } = {}) {
|
|
54
|
-
if (explicitInstall) return true;
|
|
55
|
-
if (noInstall) return false;
|
|
56
|
-
const isBun = runtime === 'bun' || (!runtime && detectPackageManager() === 'bun');
|
|
57
|
-
return !isBun;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Read the EXACT version of `@webjsdev/<pkg>` the scaffolding CLI ships with.
|
|
62
|
-
* `webjsdevRange` carets over this for the generated `package.json` (#700), so a
|
|
63
|
-
* fresh app tracks the line the CLI shipped. Walks the `require.resolve` node_modules search paths
|
|
64
|
-
* and fs-reads `<pkg>/package.json` directly: `@webjsdev/server` (and `ui`) hide
|
|
65
|
-
* `./package.json` behind `exports`, so a bare `require('<pkg>/package.json')`
|
|
66
|
-
* fails (same constraint #687 hit). Falls back to `'latest'` when the package is
|
|
67
|
-
* not resolvable (defensive; the CLI's own dependency closure is normally
|
|
68
|
-
* present), which keeps the scaffold working rather than emitting a bad pin.
|
|
69
|
-
* @param {string} pkg e.g. 'cli', 'core', 'server'
|
|
70
|
-
* @returns {string} an exact version, or 'latest'
|
|
71
|
-
*/
|
|
72
|
-
function webjsdevVersion(pkg) {
|
|
73
|
-
const req = createRequire(import.meta.url);
|
|
74
|
-
for (const base of (req.resolve.paths(`@webjsdev/${pkg}`) || [])) {
|
|
75
|
-
const pj = join(base, '@webjsdev', pkg, 'package.json');
|
|
76
|
-
if (existsSync(pj)) {
|
|
77
|
-
try {
|
|
78
|
-
const v = JSON.parse(readFileSync(pj, 'utf8')).version;
|
|
79
|
-
if (v) return v;
|
|
80
|
-
} catch { /* unreadable; keep looking, then fall back */ }
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return 'latest';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* The `@webjsdev/<pkg>` specifier for the generated `package.json` (#700): a
|
|
88
|
-
* caret range over the version the scaffolding CLI ships with, so a fresh app
|
|
89
|
-
* picks up patch updates the way an npm user expects (a `^0.x` caret stays
|
|
90
|
-
* within the minor). Bun zero-install resolves a normal caret range correctly
|
|
91
|
-
* since #698, so this no longer diverges from npm. Falls back to `'latest'` when
|
|
92
|
-
* the version is not resolvable (keeps `^latest` from ever being emitted).
|
|
93
|
-
* @param {string} pkg e.g. 'cli', 'core', 'server'
|
|
94
|
-
* @returns {string}
|
|
95
|
-
*/
|
|
96
|
-
function webjsdevRange(pkg) {
|
|
97
|
-
const v = webjsdevVersion(pkg);
|
|
98
|
-
return v === 'latest' ? 'latest' : '^' + v;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Third-party dep specifiers the scaffold ships (#700). These are template deps
|
|
103
|
-
* the CLI does NOT itself depend on, so they cannot be read from the CLI's
|
|
104
|
-
* closure (unlike `@webjsdev/*`). Since #698, Bun zero-install resolves a normal
|
|
105
|
-
* caret range correctly (highest match, not absolute latest), so `pg` is an
|
|
106
|
-
* idiomatic `^` range. Drizzle is PINNED to an exact RC: its 1.0 line is a
|
|
107
|
-
* PRERELEASE (`1.0.0-rc.3`), and Bun zero-install ENOENTs on a caret-prerelease
|
|
108
|
-
* inline specifier (`drizzle-orm@^1.0.0-rc.3`, verified on Bun 1.3.14) while the
|
|
109
|
-
* exact prerelease resolves, so drizzle must stay exact until the 1.0 stable
|
|
110
|
-
* ships. Refresh on a deliberate bump.
|
|
111
|
-
*/
|
|
112
|
-
const SCAFFOLD_DEP_VERSIONS = {
|
|
113
|
-
'drizzle-orm': '1.0.0-rc.3',
|
|
114
|
-
'drizzle-kit': '1.0.0-rc.3',
|
|
115
|
-
pg: '^8.22.0',
|
|
116
|
-
};
|
|
117
|
-
|
|
118
38
|
/**
|
|
119
39
|
* Run `<pm> install` inside the scaffolded app. Returns true on success.
|
|
120
40
|
* Inherits stdio so the user sees the install progress live. Caller decides
|
|
@@ -353,10 +273,6 @@ export async function scaffoldApp(name, cwd, opts = {}) {
|
|
|
353
273
|
throw new Error(`Unknown --runtime '${runtime}'. Only ${VALID_RUNTIMES.join(' / ')} are supported.`);
|
|
354
274
|
}
|
|
355
275
|
const isBun = runtime === 'bun';
|
|
356
|
-
// Zero-install Bun entry (#675): the app-local `webjs-bun.mjs` bootstrap, run
|
|
357
|
-
// under `bun --bun`, so the server resolves the CLI + deps via Bun auto-install
|
|
358
|
-
// (no `bun install` required). App-local so it is resolvable with no node_modules.
|
|
359
|
-
const bunBoot = 'bun --bun webjs-bun.mjs';
|
|
360
276
|
const appDir = join(cwd, name);
|
|
361
277
|
if (existsSync(appDir)) {
|
|
362
278
|
console.error(`Error: directory '${name}' already exists.`);
|
|
@@ -398,23 +314,22 @@ export async function scaffoldApp(name, cwd, opts = {}) {
|
|
|
398
314
|
},
|
|
399
315
|
scripts: {
|
|
400
316
|
// No `predev` / `prestart` hooks (#550): the `webjs` block below holds
|
|
401
|
-
// the start orchestration (`webjs db migrate`), run INSIDE `webjs
|
|
402
|
-
// so `npm run start` (
|
|
403
|
-
//
|
|
317
|
+
// the dev + start orchestration (`webjs db migrate`), run INSIDE `webjs
|
|
318
|
+
// dev` / `webjs start`, so `npm run dev` / `start` (thin aliases) behave
|
|
319
|
+
// identically. Both apply pending migrations before serving (#725).
|
|
404
320
|
//
|
|
405
|
-
// Bun runtime (#541
|
|
406
|
-
//
|
|
407
|
-
// `webjs` bin's `#!/usr/bin/env node` shebang (without it `bun run dev`
|
|
321
|
+
// Bun runtime (#541): the long-running server scripts (`dev` / `start`)
|
|
322
|
+
// are prefixed `bun --bun` so the app SERVES on Bun. The `--bun` overrides
|
|
323
|
+
// the `webjs` bin's `#!/usr/bin/env node` shebang (without it `bun run dev`
|
|
408
324
|
// would exec webjs under Node, silently running the "bun" app on Node).
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
// with NO `bun install` (install becomes optional, for editor types /
|
|
413
|
-
// offline). The runtime-neutral tooling scripts (test / check / typecheck
|
|
325
|
+
// Baking it into the script body means a plain `bun run dev` (or even
|
|
326
|
+
// `npm run dev`) starts on Bun, so a user never has to remember the flag.
|
|
327
|
+
// The runtime-neutral tooling scripts below (test / db / check / typecheck
|
|
414
328
|
// / doctor) stay plain `webjs ...`: they spawn node tooling (`node --test`,
|
|
415
|
-
// tsc)
|
|
416
|
-
|
|
417
|
-
|
|
329
|
+
// drizzle-kit, tsc) and forcing `--bun` there buys nothing (and `webjs
|
|
330
|
+
// test` shells `node --test`, which a `bun --test` would not be).
|
|
331
|
+
dev: isBun ? 'bun --bun webjs dev' : 'webjs dev',
|
|
332
|
+
start: isBun ? 'bun --bun webjs start' : 'webjs start',
|
|
418
333
|
test: 'webjs test',
|
|
419
334
|
'test:server': 'webjs test --server',
|
|
420
335
|
'test:browser': 'webjs test --browser',
|
|
@@ -425,31 +340,25 @@ export async function scaffoldApp(name, cwd, opts = {}) {
|
|
|
425
340
|
// vendor pins, @webjsdev versions, git hook). Local tool, NOT a CI gate
|
|
426
341
|
// (its env-drift + network pin-freshness checks would make CI flaky).
|
|
427
342
|
doctor: 'webjs doctor',
|
|
428
|
-
'db:generate':
|
|
429
|
-
'db:migrate':
|
|
430
|
-
'db:push':
|
|
431
|
-
'db:studio':
|
|
432
|
-
'db:seed':
|
|
343
|
+
'db:generate': 'webjs db generate',
|
|
344
|
+
'db:migrate': 'webjs db migrate',
|
|
345
|
+
'db:push': 'webjs db push',
|
|
346
|
+
'db:studio': 'webjs db studio',
|
|
347
|
+
'db:seed': 'webjs db seed',
|
|
433
348
|
},
|
|
434
349
|
dependencies: {
|
|
435
350
|
// Drizzle ORM (no codegen, no engine binary). Pinned to the 1.0 line
|
|
436
351
|
// for relations v2. SQLite needs NO driver dependency: the connection
|
|
437
352
|
// uses the built-in node:sqlite (Node) / bun:sqlite (Bun) via Drizzle's
|
|
438
353
|
// node-sqlite / bun-sqlite adapters. Postgres still needs the pg driver.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
'
|
|
444
|
-
...(dialect === 'postgres' ? { pg: SCAFFOLD_DEP_VERSIONS.pg } : {}),
|
|
445
|
-
'@webjsdev/cli': webjsdevRange('cli'),
|
|
446
|
-
'@webjsdev/core': webjsdevRange('core'),
|
|
447
|
-
'@webjsdev/server': webjsdevRange('server'),
|
|
354
|
+
'drizzle-orm': '^1.0.0-rc.3',
|
|
355
|
+
...(dialect === 'postgres' ? { pg: '^8.13.0' } : {}),
|
|
356
|
+
'@webjsdev/cli': 'latest',
|
|
357
|
+
'@webjsdev/core': 'latest',
|
|
358
|
+
'@webjsdev/server': 'latest',
|
|
448
359
|
},
|
|
449
360
|
devDependencies: {
|
|
450
|
-
|
|
451
|
-
// cannot resolve as a caret-prerelease range, so it stays exact (#700).
|
|
452
|
-
'drizzle-kit': SCAFFOLD_DEP_VERSIONS['drizzle-kit'],
|
|
361
|
+
'drizzle-kit': '^1.0.0-rc.3',
|
|
453
362
|
...(dialect === 'postgres' ? { '@types/pg': '^8.11.0' } : {}),
|
|
454
363
|
// The TypeScript compiler, for `npm run typecheck` (webjs typecheck runs
|
|
455
364
|
// tsc --noEmit). Not needed at runtime (Node strips types in place), only
|
|
@@ -477,38 +386,20 @@ export async function scaffoldApp(name, cwd, opts = {}) {
|
|
|
477
386
|
// into components/ui/ (they import @webjsdev/core, not the kit), and the
|
|
478
387
|
// CLI resolves @webjsdev/ui from its own install.
|
|
479
388
|
},
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
// identically.
|
|
483
|
-
//
|
|
389
|
+
// Dev + start task orchestration (#550). `webjs dev` / `webjs start` read
|
|
390
|
+
// `before` and run it in-process, so `npm run dev` / `start` (thin aliases
|
|
391
|
+
// above) behave identically. Both apply pending migrations via `webjs db
|
|
392
|
+
// migrate` (idempotent, a no-op when the db is current), so a freshly
|
|
393
|
+
// generated migration is applied without a manual step (#725). The scaffold
|
|
484
394
|
// uses the Tailwind browser runtime (no CSS build step), so there is no dev
|
|
485
395
|
// `parallel` watcher here; an app that adds the Tailwind CLI puts its
|
|
486
396
|
// `--watch` command under `webjs.dev.parallel`.
|
|
487
397
|
webjs: {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
// On Bun this runs through the same zero-install bootstrap as `start`, so
|
|
491
|
-
// the boot-time migrate needs no `webjs` bin in node_modules (#675).
|
|
492
|
-
start: { before: [isBun ? `${bunBoot} db migrate` : 'webjs db migrate'] },
|
|
398
|
+
dev: { before: ['webjs db migrate'] },
|
|
399
|
+
start: { before: ['webjs db migrate'] },
|
|
493
400
|
},
|
|
494
401
|
}, null, 2) + '\n');
|
|
495
402
|
|
|
496
|
-
// The zero-install Bun entry (#675). `bun run dev` / `start` invoke this via
|
|
497
|
-
// `bun --bun` (see the scripts above). Importing the webjs CLI by bare
|
|
498
|
-
// specifier lets Bun auto-install resolve `@webjsdev/*` and your deps on
|
|
499
|
-
// demand, so a fresh app serves with NO `bun install`. The CLI reads its
|
|
500
|
-
// command (dev / start / db ...) and flags straight from argv. Node apps do
|
|
501
|
-
// not get this file; they run the `webjs` bin directly.
|
|
502
|
-
if (isBun) {
|
|
503
|
-
await writeFile(join(appDir, 'webjs-bun.mjs'),
|
|
504
|
-
'// Zero-install Bun entry (webjs #675). Run via `bun --bun webjs-bun.mjs <cmd>`\n' +
|
|
505
|
-
'// (the dev / start / db npm scripts do this). Importing the CLI by bare\n' +
|
|
506
|
-
'// specifier lets Bun auto-install resolve @webjsdev/* and your deps on\n' +
|
|
507
|
-
'// demand, so the app serves with no `bun install` (install stays optional,\n' +
|
|
508
|
-
'// for editor types and offline runs). Args pass through to the CLI.\n' +
|
|
509
|
-
"await import('@webjsdev/cli/bin/webjs.js');\n");
|
|
510
|
-
}
|
|
511
|
-
|
|
512
403
|
await writeFile(join(appDir, 'tsconfig.json'), JSON.stringify({
|
|
513
404
|
compilerOptions: {
|
|
514
405
|
target: 'ES2022',
|
|
@@ -1490,13 +1381,6 @@ For AI agents, read this before editing scaffolded files:
|
|
|
1490
1381
|
if (!installed) {
|
|
1491
1382
|
console.log(`\n[warn] ${pm} install failed. Run '${pm} install' manually in ${name}/ to finish setup.\n`);
|
|
1492
1383
|
}
|
|
1493
|
-
} else if (isBun) {
|
|
1494
|
-
// Bun zero-install (#675): no install needed; `bun run dev` resolves deps on
|
|
1495
|
-
// the fly. Since #698 they resolve to their package.json versions (a caret
|
|
1496
|
-
// range to its highest match), so point at `bun install` for a frozen
|
|
1497
|
-
// lockfile and editor type intelligence, not a correctness fix.
|
|
1498
|
-
console.log(`Skipped install. Bun resolves dependencies on the fly, so 'bun run dev' and 'bun run start' work as-is (no node_modules).`);
|
|
1499
|
-
console.log(`These resolve to the versions in package.json. Run 'bun install' in ${name}/ to freeze a lockfile and get editor type intelligence.\n`);
|
|
1500
1384
|
}
|
|
1501
1385
|
|
|
1502
1386
|
// Next-steps banner prints LAST so the actionable command is the
|
|
@@ -1504,16 +1388,22 @@ For AI agents, read this before editing scaffolded files:
|
|
|
1504
1388
|
// Single copy-paste line so the user can move from "scaffold done"
|
|
1505
1389
|
// to "dev server up" in one command. The full-stack and saas
|
|
1506
1390
|
// templates ship with @webjsdev/ui already initialised; the api
|
|
1507
|
-
// template has no UI but may add one later.
|
|
1508
|
-
|
|
1509
|
-
//
|
|
1510
|
-
//
|
|
1511
|
-
//
|
|
1512
|
-
//
|
|
1513
|
-
//
|
|
1514
|
-
|
|
1515
|
-
const dbSegment = isSaas ? `${pm} run db:generate &&
|
|
1391
|
+
// template has no UI but may add one later.
|
|
1392
|
+
const installSegment = installed ? '' : `${pm} install && `;
|
|
1393
|
+
// The saas example queries the users table on its first request (auth), so it
|
|
1394
|
+
// needs a migration authored first: `db:generate` writes it and the
|
|
1395
|
+
// `webjs.dev.before` migrate applies it on `run dev` (Drizzle splits Prisma's
|
|
1396
|
+
// `migrate dev` into generate-then-migrate). The full-stack / api examples do
|
|
1397
|
+
// not query the db on first paint, so they boot with just `run dev`; once you
|
|
1398
|
+
// add a db route, `db:generate` then `run dev` is the loop (dev auto-migrates).
|
|
1399
|
+
const dbSegment = isSaas ? `${pm} run db:generate && ` : '';
|
|
1516
1400
|
const runCommand = `cd ${name} && ${installSegment}${dbSegment}${pm} run dev`;
|
|
1401
|
+
// Postgres needs a reachable DATABASE_URL before any migrate (sqlite uses a
|
|
1402
|
+
// local file with no .env). Point it at a running database; `dev` / `start`
|
|
1403
|
+
// then apply pending migrations via webjs.*.before.
|
|
1404
|
+
const pgNote = dialect === 'postgres'
|
|
1405
|
+
? `\nPostgres: copy .env.example to .env and set DATABASE_URL to a running database before \`${pm} run dev\`.\n`
|
|
1406
|
+
: '';
|
|
1517
1407
|
// Use `npx webjsdev ui ...` here, not `npx webjs ui ...`. The bare
|
|
1518
1408
|
// `webjs` npm name is owned by an unrelated package; `npx webjs
|
|
1519
1409
|
// <cmd>` would fetch THAT package instead of ours when run outside
|
|
@@ -1529,7 +1419,7 @@ For AI agents, read this before editing scaffolded files:
|
|
|
1529
1419
|
Next steps:
|
|
1530
1420
|
${runCommand}
|
|
1531
1421
|
# → http://localhost:8080
|
|
1532
|
-
|
|
1422
|
+
${pgNote}
|
|
1533
1423
|
Optional:
|
|
1534
1424
|
${uiNote}
|
|
1535
1425
|
`);
|
package/lib/runtime-rewrite.js
CHANGED
|
@@ -58,10 +58,14 @@ export function bunifyProse(s) {
|
|
|
58
58
|
'Dockerfile pins `node:24-alpine` (the same Node major CI uses), installs\ndeps (no build step, since Drizzle has no codegen), and starts via',
|
|
59
59
|
'Dockerfile is a pure `oven/bun:1` image (no Node, since `webjs db migrate`\nresolves drizzle-kit and runs under Bun with no `npx`, #570), installs deps\nwith `bun install` (no build step, since Drizzle has no codegen), and starts via',
|
|
60
60
|
)
|
|
61
|
-
// The "Running on Bun" section
|
|
62
|
-
//
|
|
63
|
-
//
|
|
61
|
+
// The "Running on Bun" section frames Bun as opt-in ("force it with --bun").
|
|
62
|
+
// In a bun-flavored app the dev/start scripts ALREADY embed --bun, so reframe
|
|
63
|
+
// it as the configured default.
|
|
64
64
|
.replaceAll('### Running on Bun instead of Node', '### Runtime: this app runs on Bun')
|
|
65
|
+
.replaceAll(
|
|
66
|
+
'The same `package.json` scripts work on\neither; to run under Bun, force it with `--bun` so the server executes on Bun\nrather than the `webjs` bin\'s Node shebang:',
|
|
67
|
+
'This app is configured for Bun. Its `dev` / `start` scripts already force\n`--bun` (which overrides the `webjs` bin\'s Node shebang), so a plain `bun run dev`\nserves on Bun. The other scripts (test / db / check) run on Node, the runtime\nthe `webjs` tooling targets:',
|
|
68
|
+
)
|
|
65
69
|
// Invocation styles first, so "npm create webjs@latest" does not get
|
|
66
70
|
// mangled by the generic "npm <x>" rules below.
|
|
67
71
|
.replaceAll('npm create webjs@latest', 'bun create webjs')
|
package/lib/saas-template.js
CHANGED
|
@@ -186,9 +186,10 @@ export async function writeSaasFiles(appDir, opts = {}) {
|
|
|
186
186
|
// ALWAYS once the app modules import: auth() only reads a cookie, no DB
|
|
187
187
|
// query. This is the headline security assertion and it is REAL.
|
|
188
188
|
// The signup, login, and protected-route flow writes + reads a user, so it
|
|
189
|
-
// needs the
|
|
190
|
-
//
|
|
191
|
-
//
|
|
189
|
+
// needs the migrated users table (`npm run db:generate`, then `npm run dev`
|
|
190
|
+
// applies it via webjs.dev.before). Until then those flows error, so the
|
|
191
|
+
// suite probes readiness and skips with a clear message instead of crashing,
|
|
192
|
+
// then runs for real once the db is set up.
|
|
192
193
|
await mkdir(join(appDir, 'test', 'auth'), { recursive: true });
|
|
193
194
|
// The generated comments reference `npm run db:*` setup; bun-ify them so a
|
|
194
195
|
// bun-flavored saas app reads `bun run db:*` (#541; db is Node tooling, so a
|
|
@@ -205,12 +206,11 @@ export async function writeSaasFiles(appDir, opts = {}) {
|
|
|
205
206
|
"const appDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');",
|
|
206
207
|
"",
|
|
207
208
|
"// The auth pages + dashboard middleware query the users table via Drizzle.",
|
|
208
|
-
"// Until `npm run db:generate`
|
|
209
|
-
"//
|
|
209
|
+
"// Until `npm run db:generate` has authored the migration (then `npm run dev`",
|
|
210
|
+
"// applies it via webjs.dev.before, or `npm run db:migrate` directly), a",
|
|
211
|
+
"// request hitting those modules 500s; we detect that at the RESPONSE level",
|
|
210
212
|
"// (a 5xx on the dashboard) and SKIP with a clear message rather than report",
|
|
211
|
-
"// a misleading failure. After
|
|
212
|
-
"// npm install && npm run db:generate && npm run db:migrate",
|
|
213
|
-
"// every assertion below runs for real.",
|
|
213
|
+
"// a misleading failure. After the db is set up every assertion runs for real.",
|
|
214
214
|
"process.env.DATABASE_URL ||= 'file:./dev.db';",
|
|
215
215
|
"process.env.AUTH_SECRET ||= 'test-secret-at-least-32-characters-long!!';",
|
|
216
216
|
"",
|
package/package.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -329,7 +329,7 @@ db/
|
|
|
329
329
|
columns.server.ts column helpers (dialect-specific; the only file to swap for Postgres)
|
|
330
330
|
connection.server.ts opens the driver, exports the \`db\` singleton (import \`db\` from here)
|
|
331
331
|
seed.server.ts optional seed (run via \`webjs db seed\`)
|
|
332
|
-
dev.db SQLite file (gitignored);
|
|
332
|
+
dev.db SQLite file (gitignored); created when migrations apply (\`dev\`/\`start\` run \`webjs db migrate\`)
|
|
333
333
|
migrations/ generated migration SQL (committed)
|
|
334
334
|
drizzle.config.ts drizzle-kit config (root; SQLite by default, --db postgres to switch)
|
|
335
335
|
public/ static assets, served at /public/*
|
|
@@ -387,13 +387,16 @@ lives in the `webjs` block of `package.json` and runs INSIDE
|
|
|
387
387
|
|
|
388
388
|
```jsonc
|
|
389
389
|
"webjs": {
|
|
390
|
+
"dev": { "before": ["webjs db migrate"] },
|
|
390
391
|
"start": { "before": ["webjs db migrate"] }
|
|
391
392
|
}
|
|
392
393
|
```
|
|
393
394
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
395
|
+
Both `dev` and `start` apply pending migrations via `webjs db migrate`
|
|
396
|
+
(idempotent, a no-op when the db is current), so a freshly generated
|
|
397
|
+
migration is applied without a manual step. An app that adds the Tailwind
|
|
398
|
+
CLI puts its `--watch` command under `webjs.dev.parallel` and it runs
|
|
399
|
+
alongside the server, torn down on exit.
|
|
397
400
|
`before` steps run to completion first; a failed `webjs db migrate`
|
|
398
401
|
aborts the boot with a clear message rather than serving a stale schema.
|
|
399
402
|
|
|
@@ -404,29 +407,15 @@ an npm `prestart` hook.
|
|
|
404
407
|
|
|
405
408
|
### Running on Bun instead of Node
|
|
406
409
|
|
|
407
|
-
webjs runs on **Node 24+ or Bun**.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
bootstrap imports the CLI by bare specifier, so Bun auto-install resolves deps on
|
|
411
|
-
demand and **no `bun install` is needed**:
|
|
410
|
+
webjs runs on **Node 24+ or Bun**. The same `package.json` scripts work on
|
|
411
|
+
either; to run under Bun, force it with `--bun` so the server executes on Bun
|
|
412
|
+
rather than the `webjs` bin's Node shebang:
|
|
412
413
|
|
|
413
414
|
```sh
|
|
414
|
-
bun
|
|
415
|
+
bun install
|
|
416
|
+
bun --bun run dev # or: bun --bun run start
|
|
415
417
|
```
|
|
416
418
|
|
|
417
|
-
`bun create` does not run an install on Bun, so a fresh app serves immediately.
|
|
418
|
-
Under zero-install, Bun's runtime auto-install resolves a BARE import to LATEST
|
|
419
|
-
(it ignores `package.json` and `bun.lock`), so webjs rewrites each declared dep's
|
|
420
|
-
specifier to an inline-versioned one via an `onLoad` transform. The version is the
|
|
421
|
-
`bun.lock` exact when present, else the `package.json` value when it is an
|
|
422
|
-
inline-safe semver (an exact pin, or a caret / tilde / comparator range, which
|
|
423
|
-
Bun resolves to the highest match). A protocol range (`workspace:`, `file:`), a
|
|
424
|
-
wildcard (`*`), and a dist-tag (`latest`) stay at latest. Run `bun install` when
|
|
425
|
-
you want versions frozen identically across machines (it materializes
|
|
426
|
-
`node_modules` from the lockfile) or editor type intelligence. (To run a
|
|
427
|
-
Node-flavored app on Bun instead, force `bun --bun run dev`, which still expects
|
|
428
|
-
an install.)
|
|
429
|
-
|
|
430
419
|
On Node the `.ts` type-stripping is the built-in `module.stripTypeScriptTypes`;
|
|
431
420
|
on Bun (which has no built-in) it comes from `amaro` automatically, so the same
|
|
432
421
|
source serves identically. SSR action-result seeding (an internal hydration
|
|
@@ -471,7 +460,7 @@ Scripts (all wrap `drizzle-kit`):
|
|
|
471
460
|
- `npm run db:push`: `webjs db push` (push the schema straight to the dev DB)
|
|
472
461
|
- `npm run db:studio`: `webjs db studio` (visual DB browser)
|
|
473
462
|
- `npm run db:seed`: `webjs db seed` (run `db/seed.server.ts`)
|
|
474
|
-
- `webjs.start.before`
|
|
463
|
+
- `webjs.dev.before` and `webjs.start.before` both run `webjs db migrate` inside `webjs dev` / `webjs start` (idempotent; replaces the old `prestart` hook), so after you `db:generate` a migration it is applied on the next boot with no manual `db:migrate` step.
|
|
475
464
|
|
|
476
465
|
Always import `db` from `db/connection.server.ts` (the globalThis-cached
|
|
477
466
|
singleton avoids opening a new connection on every dev-server reload), and
|
package/templates/CONVENTIONS.md
CHANGED
|
@@ -268,11 +268,12 @@ docs". That is the agent's default behavior in a webjs project.
|
|
|
268
268
|
|
|
269
269
|
Every webjs app uses **Drizzle + SQLite** for persistence by default. The
|
|
270
270
|
scaffold ships the `db/` folder (`schema.server.ts`, `columns.server.ts`,
|
|
271
|
-
`connection.server.ts`), the `webjs.
|
|
272
|
-
`webjs db migrate` inside `webjs start` (#550), and the
|
|
271
|
+
`connection.server.ts`), the `webjs.dev.before` + `webjs.start.before` steps
|
|
272
|
+
that run `webjs db migrate` inside `webjs dev` / `webjs start` (#550), and the
|
|
273
273
|
`npm run db:generate` / `db:migrate` / `db:push` / `db:studio` / `db:seed`
|
|
274
|
-
scripts (which route through `webjs db` to drizzle-kit).
|
|
275
|
-
|
|
274
|
+
scripts (which route through `webjs db` to drizzle-kit). The loop after a
|
|
275
|
+
schema change is `db:generate` (authors the migration) then `webjs dev` (the
|
|
276
|
+
`dev.before` step applies it); `db:generate` is never auto-run on boot.
|
|
276
277
|
|
|
277
278
|
**AI agents: these rules are absolute.**
|
|
278
279
|
|
package/lib/import-webjsdev.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
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
|
-
}
|