@celsian/vura-cli 0.8.0 → 0.8.1
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 -3
- package/dist/commands/build.d.ts +15 -11
- package/dist/commands/build.js +43 -53
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -52,10 +52,8 @@ and 12 GiB profiles.
|
|
|
52
52
|
|
|
53
53
|
## Documentation
|
|
54
54
|
|
|
55
|
-
_vura.io docs site launches with v0.5 — until then, see the repo README and CHANGELOG._
|
|
56
|
-
|
|
57
55
|
- [Quick start — /ladder/0-create/](https://vura.io/ladder/0-create/)
|
|
58
|
-
- [Task routes — /
|
|
56
|
+
- [Task routes — /ladder/5-tasks/](https://vura.io/ladder/5-tasks/)
|
|
59
57
|
- [Self-host — /self-host/](https://vura.io/self-host/)
|
|
60
58
|
|
|
61
59
|
## License
|
package/dist/commands/build.d.ts
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* `vura build` — Build the project for deployment.
|
|
3
3
|
*
|
|
4
4
|
* 1. Scan routes → build manifest
|
|
5
|
-
* 2.
|
|
6
|
-
* 3.
|
|
7
|
-
* 4.
|
|
8
|
-
* 5.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
5
|
+
* 2. Bundle browser entries for client and hybrid pages
|
|
6
|
+
* 3. Render build-time pages (static, client shells, hybrid HTML) → dist/static
|
|
7
|
+
* 4. Copy public/ → dist/public
|
|
8
|
+
* 5. core build(): server entry, function entries, task entries, manifest.json,
|
|
9
|
+
* then adapter.buildEnd()
|
|
10
|
+
* 6. Emit hot deploy templates (Dockerfile, fly.toml, package.json)
|
|
11
|
+
*
|
|
12
|
+
* The adapter runs last, and the two steps that write dist/static and
|
|
13
|
+
* dist/public run before it, because an adapter that serves prerendered pages
|
|
14
|
+
* has to be able to read them.
|
|
13
15
|
*/
|
|
14
16
|
/**
|
|
15
17
|
* Remove hashed page bundles that this build did not emit.
|
|
@@ -19,9 +21,11 @@
|
|
|
19
21
|
* `dist/static/_then/pages` grew with every incremental build and the dead
|
|
20
22
|
* copies shipped to whatever the project deployed to.
|
|
21
23
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
24
|
+
* The sweep itself now lives in core as `pruneStaleOutputs`, because the same
|
|
25
|
+
* accretion happens to `dist/server`, `dist/functions` and each adapter's own
|
|
26
|
+
* output directory, and four copies of a recursive delete is four chances to
|
|
27
|
+
* fix one and not the others. This wrapper is the client-bundle name and the
|
|
28
|
+
* client-bundle reason; the behaviour is shared.
|
|
25
29
|
*/
|
|
26
30
|
export declare function pruneStaleBundles(dir: string, keep: Set<string>, fs: {
|
|
27
31
|
readdir: (p: string, o: {
|
package/dist/commands/build.js
CHANGED
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
* `vura build` — Build the project for deployment.
|
|
3
3
|
*
|
|
4
4
|
* 1. Scan routes → build manifest
|
|
5
|
-
* 2.
|
|
6
|
-
* 3.
|
|
7
|
-
* 4.
|
|
8
|
-
* 5.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
5
|
+
* 2. Bundle browser entries for client and hybrid pages
|
|
6
|
+
* 3. Render build-time pages (static, client shells, hybrid HTML) → dist/static
|
|
7
|
+
* 4. Copy public/ → dist/public
|
|
8
|
+
* 5. core build(): server entry, function entries, task entries, manifest.json,
|
|
9
|
+
* then adapter.buildEnd()
|
|
10
|
+
* 6. Emit hot deploy templates (Dockerfile, fly.toml, package.json)
|
|
11
|
+
*
|
|
12
|
+
* The adapter runs last, and the two steps that write dist/static and
|
|
13
|
+
* dist/public run before it, because an adapter that serves prerendered pages
|
|
14
|
+
* has to be able to read them.
|
|
13
15
|
*/
|
|
14
|
-
import { buildManifest, build, renderStaticPages, generateClientPageEntry, vuraBrowserResolvePlugin, vuraActionsStubPlugin } from '@celsian/vura-core';
|
|
16
|
+
import { buildManifest, build, renderStaticPages, generateClientPageEntry, vuraBrowserResolvePlugin, vuraActionsStubPlugin, pruneStaleOutputs } from '@celsian/vura-core';
|
|
15
17
|
import { createRequire } from 'node:module';
|
|
16
18
|
import { existsSync, readFileSync } from 'node:fs';
|
|
17
19
|
import { join as pathJoin, resolve as pathResolve, relative } from 'node:path';
|
|
@@ -25,40 +27,14 @@ import { loadConfig } from '../config-loader.js';
|
|
|
25
27
|
* `dist/static/_then/pages` grew with every incremental build and the dead
|
|
26
28
|
* copies shipped to whatever the project deployed to.
|
|
27
29
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
30
|
+
* The sweep itself now lives in core as `pruneStaleOutputs`, because the same
|
|
31
|
+
* accretion happens to `dist/server`, `dist/functions` and each adapter's own
|
|
32
|
+
* output directory, and four copies of a recursive delete is four chances to
|
|
33
|
+
* fix one and not the others. This wrapper is the client-bundle name and the
|
|
34
|
+
* client-bundle reason; the behaviour is shared.
|
|
31
35
|
*/
|
|
32
36
|
export async function pruneStaleBundles(dir, keep, fs) {
|
|
33
|
-
|
|
34
|
-
let entries;
|
|
35
|
-
try {
|
|
36
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return 0; // nothing was written here
|
|
40
|
-
}
|
|
41
|
-
for (const entry of entries) {
|
|
42
|
-
const full = pathJoin(dir, entry.name);
|
|
43
|
-
if (entry.isDirectory()) {
|
|
44
|
-
removed += await pruneStaleBundles(full, keep, fs);
|
|
45
|
-
// The page that lived here is gone; do not leave the empty shell.
|
|
46
|
-
try {
|
|
47
|
-
const rest = await fs.readdir(full, { withFileTypes: true });
|
|
48
|
-
if (rest.length === 0)
|
|
49
|
-
await fs.rm(full, { recursive: true, force: true });
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
/* raced or unreadable: leaving it is harmless */
|
|
53
|
-
}
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (keep.has(full))
|
|
57
|
-
continue;
|
|
58
|
-
await fs.rm(full, { force: true });
|
|
59
|
-
removed++;
|
|
60
|
-
}
|
|
61
|
-
return removed;
|
|
37
|
+
return pruneStaleOutputs(dir, keep, fs);
|
|
62
38
|
}
|
|
63
39
|
// ---------------------------------------------------------------------------
|
|
64
40
|
// Deploy template strings — inlined so they survive tsc compilation (tsc does
|
|
@@ -343,7 +319,7 @@ export async function buildCommand(_args) {
|
|
|
343
319
|
//
|
|
344
320
|
// Page and layout bundling now happens once, in core. `serverEsmResolvePlugin`
|
|
345
321
|
// below is still used for the standalone entry.
|
|
346
|
-
//
|
|
322
|
+
// 2. Bundle browser entries for client and hybrid pages.
|
|
347
323
|
// Each bundle is a generated wrapper (generateClientPageEntry) that imports
|
|
348
324
|
// the page module and calls mount() (client) / hydrate() (hybrid) — bundling
|
|
349
325
|
// the raw page module alone leaves the shell at "Loading..." forever because
|
|
@@ -382,6 +358,7 @@ export async function buildCommand(_args) {
|
|
|
382
358
|
outfile: outPath,
|
|
383
359
|
jsx: 'automatic',
|
|
384
360
|
jsxImportSource,
|
|
361
|
+
absWorkingDir: root,
|
|
385
362
|
// Browser-resolve first: a page that imports `@celsian/vura-core` for
|
|
386
363
|
// useLoaderData must get the pure client module here, not the package
|
|
387
364
|
// root, which reaches node:fs and cannot be bundled for a browser.
|
|
@@ -420,15 +397,7 @@ export async function buildCommand(_args) {
|
|
|
420
397
|
console.log(` ✓ removed ${pruned} stale bundle${pruned === 1 ? '' : 's'} from earlier builds`);
|
|
421
398
|
}
|
|
422
399
|
}
|
|
423
|
-
//
|
|
424
|
-
console.log(' Building...');
|
|
425
|
-
const result = await build(manifest, config, root);
|
|
426
|
-
console.log(` Server entry: ${result.serverEntry}`);
|
|
427
|
-
console.log(` Functions: ${result.functions.length} serverless bundles`);
|
|
428
|
-
if (result.taskEntries.length > 0) {
|
|
429
|
-
console.log(` Tasks: ${result.taskEntries.length} task entries`);
|
|
430
|
-
}
|
|
431
|
-
// 5. Render build-time pages (static, client shells, and hybrid prerendered HTML)
|
|
400
|
+
// 3. Render build-time pages (static, client shells, and hybrid prerendered HTML)
|
|
432
401
|
const staticPages = manifest.pages.filter(p => p.mode !== 'server');
|
|
433
402
|
if (staticPages.length > 0) {
|
|
434
403
|
console.log(` Rendering ${staticPages.length} build-time pages...`);
|
|
@@ -468,6 +437,12 @@ export async function buildCommand(_args) {
|
|
|
468
437
|
outfile: tmpFile,
|
|
469
438
|
jsx: 'automatic',
|
|
470
439
|
jsxImportSource,
|
|
440
|
+
// esbuild anchors resolution to the working directory its service
|
|
441
|
+
// captured, which is not necessarily the cwd at call time. Core's
|
|
442
|
+
// bundlers pass this for the same reason; this one did not, and a
|
|
443
|
+
// second build in one process resolved against the first build's
|
|
444
|
+
// directory — which fails outright once that directory is gone.
|
|
445
|
+
absWorkingDir: root,
|
|
471
446
|
plugins: [serverEsmResolvePlugin],
|
|
472
447
|
external: sharedRuntimeExternals,
|
|
473
448
|
});
|
|
@@ -482,7 +457,7 @@ export async function buildCommand(_args) {
|
|
|
482
457
|
const { rm } = await import('node:fs/promises');
|
|
483
458
|
await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
484
459
|
}
|
|
485
|
-
//
|
|
460
|
+
// 4. Copy public assets to dist/public/ for production static serving.
|
|
486
461
|
// Prefer the framework-standard root public/ directory, but support the
|
|
487
462
|
// historical starter/runbook layout src/public/ when root public/ is absent.
|
|
488
463
|
const publicDir = join(root, 'public');
|
|
@@ -499,10 +474,25 @@ export async function buildCommand(_args) {
|
|
|
499
474
|
const label = publicSourceDir === publicDir ? 'public/' : 'src/public/';
|
|
500
475
|
console.log(` Copied ${label} → dist/public/`);
|
|
501
476
|
}
|
|
477
|
+
// 5. Build API routes, task entries and the adapter artifacts.
|
|
478
|
+
//
|
|
479
|
+
// This runs AFTER the page render and the public copy, not before. The
|
|
480
|
+
// adapter's buildEnd is the last step by design — this file's own header has
|
|
481
|
+
// said so since it was written — and an adapter that reads dist/static, which
|
|
482
|
+
// is what serving prerendered pages on Cloudflare and Lambda requires, saw
|
|
483
|
+
// either nothing on a clean build or the PREVIOUS build's HTML on a dirty
|
|
484
|
+
// one. It ran fourth of nine only because the page render was added later.
|
|
485
|
+
console.log(' Building...');
|
|
486
|
+
const result = await build(manifest, config, root);
|
|
487
|
+
console.log(` Server entry: ${result.serverEntry}`);
|
|
488
|
+
console.log(` Functions: ${result.functions.length} serverless bundles`);
|
|
489
|
+
if (result.taskEntries.length > 0) {
|
|
490
|
+
console.log(` Tasks: ${result.taskEntries.length} task entries`);
|
|
491
|
+
}
|
|
502
492
|
if (config.adapter) {
|
|
503
493
|
console.log(` Adapter: ${config.adapter.name}`);
|
|
504
494
|
}
|
|
505
|
-
//
|
|
495
|
+
// 6. Emit hot deploy templates when the project has hot routes
|
|
506
496
|
const hotRoutes = manifest.api.filter(r => r.kind === 'hot');
|
|
507
497
|
const hasWsRoutes = hotRoutes.some(r => r.hasWebsocket === true);
|
|
508
498
|
const distDir = join(root, 'dist');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@celsian/vura-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Vura CLI — build and deploy full-stack apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"!dist/**/*.map"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@celsian/vura-core": "0.8.
|
|
18
|
+
"@celsian/vura-core": "0.8.1",
|
|
19
19
|
"esbuild": "^0.28.2",
|
|
20
|
-
"what-framework": "^0.13.
|
|
20
|
+
"what-framework": "^0.13.6"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@celsian/vura-adapter-vura": "0.8.
|
|
23
|
+
"@celsian/vura-adapter-vura": "0.8.1",
|
|
24
24
|
"ws": "^8.0.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependenciesMeta": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@celsian/vura-adapter-vura": "0.8.
|
|
35
|
+
"@celsian/vura-adapter-vura": "0.8.1",
|
|
36
36
|
"@types/ws": "^8.18.1",
|
|
37
37
|
"ws": "^8.21.3"
|
|
38
38
|
},
|