@wervt/cli 0.2.17 → 0.2.18
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 +4 -0
- package/dist/index.mjs +216 -8
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -15,6 +15,10 @@ vp exec wervt access my-app --public # or --add friend@example.com
|
|
|
15
15
|
vp exec wervt logs my-app
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
Nitro apps without Nuxt use `@wervt/nitro`. A static site sets `"build": "… && wervt build --static dist"`
|
|
19
|
+
in package.json (any `export default { fetch }` module works too, without `--static`); see
|
|
20
|
+
[Platform contract](https://wervt.app/api/platform-contract#wervt-build).
|
|
21
|
+
|
|
18
22
|
Without [Vite+](https://viteplus.dev), use `npx @wervt/cli` and `pnpm exec wervt`. `wervt --help`
|
|
19
23
|
lists every command: `apps`, `releases`, `audit`, `rollback`, `activate`, `env`, `tokens`, `token`, `members`,
|
|
20
24
|
`tasks`, `stats`, `delete`, `whoami`, … Docs: [wervt.app](https://wervt.app/getting-started/quickstart).
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,160 @@
|
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { appendFile, cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
3
4
|
import { homedir, hostname } from "node:os";
|
|
4
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
5
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
6
|
import { styleText } from "node:util";
|
|
6
7
|
import * as p from "@clack/prompts";
|
|
7
8
|
import { defineCommand, runMain } from "citty";
|
|
8
9
|
import { detectPackageManager } from "nypm";
|
|
9
10
|
import { x } from "tinyexec";
|
|
10
11
|
import { packTar } from "modern-tar";
|
|
12
|
+
// ../core/src/manifest.ts
|
|
13
|
+
/** The bundle's `wervt-bundle.json`, next to `server/` and `public/`. */
|
|
14
|
+
const BUNDLE_MANIFEST = "wervt-bundle.json";
|
|
15
|
+
// src/static-server.ts
|
|
16
|
+
/** The source of `server/index.mjs`. */
|
|
17
|
+
function staticServer({ pages, fallback }) {
|
|
18
|
+
return `const pages = ${JSON.stringify(pages)}
|
|
19
|
+
const fallback = ${JSON.stringify(fallback ?? null)}
|
|
20
|
+
const html = (body, status = 200) =>
|
|
21
|
+
new Response(body, { status, headers: { 'content-type': 'text/html; charset=utf-8' } })
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
fetch(request) {
|
|
25
|
+
let path
|
|
26
|
+
try {
|
|
27
|
+
path = decodeURIComponent(new URL(request.url).pathname).replace(/^\\/+|\\/+$/g, '')
|
|
28
|
+
} catch {
|
|
29
|
+
return html(pages['404.html'] ?? 'Not found', 404)
|
|
30
|
+
}
|
|
31
|
+
for (const page of path ? [path + '/index.html', path + '.html'] : ['index.html'])
|
|
32
|
+
if (Object.hasOwn(pages, page)) return html(pages[page])
|
|
33
|
+
const wantsHtml = (request.headers.get('accept') ?? '').includes('text/html')
|
|
34
|
+
if (wantsHtml && fallback && Object.hasOwn(pages, fallback)) return html(pages[fallback])
|
|
35
|
+
return html(pages['404.html'] ?? 'Not found', 404)
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
// src/build.ts
|
|
41
|
+
const ENTRIES = [
|
|
42
|
+
"server.ts",
|
|
43
|
+
"src/server.ts",
|
|
44
|
+
"src/index.ts",
|
|
45
|
+
"server.js",
|
|
46
|
+
"src/index.js"
|
|
47
|
+
];
|
|
48
|
+
const exists$1 = (path) => stat(path).then(() => true, () => false);
|
|
49
|
+
async function walk$1(dir) {
|
|
50
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
51
|
+
return (await Promise.all(entries.map(async (e) => e.isDirectory() ? walk$1(join(dir, e.name)) : [join(dir, e.name)]))).flat();
|
|
52
|
+
}
|
|
53
|
+
/** The app's `wervt` settings from its package.json. */
|
|
54
|
+
async function readBuildConfig(appDir) {
|
|
55
|
+
return JSON.parse(await readFile(join(appDir, "package.json"), "utf8").catch(() => "{}")).wervt ?? {};
|
|
56
|
+
}
|
|
57
|
+
async function build$1(appDir, options = {}) {
|
|
58
|
+
const config = {
|
|
59
|
+
...await readBuildConfig(appDir),
|
|
60
|
+
...definedOnly(options)
|
|
61
|
+
};
|
|
62
|
+
const out = join(appDir, options.out ?? ".output");
|
|
63
|
+
await rm(out, {
|
|
64
|
+
recursive: true,
|
|
65
|
+
force: true
|
|
66
|
+
});
|
|
67
|
+
await mkdir(join(out, "server"), { recursive: true });
|
|
68
|
+
let kind;
|
|
69
|
+
if (config.static) {
|
|
70
|
+
kind = "static";
|
|
71
|
+
await buildStatic(join(appDir, config.static), out, config.fallback);
|
|
72
|
+
} else {
|
|
73
|
+
kind = "server";
|
|
74
|
+
const entry = config.entry ?? await findEntry(appDir);
|
|
75
|
+
if (!entry) throw new Error(`No server entry. Add one of ${ENTRIES.join(", ")}, or set "wervt": { "entry" } in package.json (or "static" for a static site).`);
|
|
76
|
+
await bundleServer(join(appDir, entry), join(out, "server/index.mjs"));
|
|
77
|
+
const publicDir = join(appDir, config.public ?? "public");
|
|
78
|
+
if (await exists$1(publicDir)) await cp(publicDir, join(out, "public"), { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
const manifest = bundleManifest(config, kind);
|
|
81
|
+
await writeFile(join(out, BUNDLE_MANIFEST), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
82
|
+
return {
|
|
83
|
+
kind,
|
|
84
|
+
out,
|
|
85
|
+
manifest
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function findEntry(appDir) {
|
|
89
|
+
for (const entry of ENTRIES) if (await exists$1(join(appDir, entry))) return entry;
|
|
90
|
+
}
|
|
91
|
+
function bundleManifest(config, kind) {
|
|
92
|
+
const manifest = {
|
|
93
|
+
version: 2,
|
|
94
|
+
framework: config.framework ?? (kind === "static" ? "static" : "fetch")
|
|
95
|
+
};
|
|
96
|
+
if (config.database !== void 0) manifest.database = config.database;
|
|
97
|
+
if (config.immutable || config.noCache) manifest.public = {
|
|
98
|
+
immutable: config.immutable,
|
|
99
|
+
noCache: config.noCache
|
|
100
|
+
};
|
|
101
|
+
if (config.tasks || config.schedules) manifest.tasks = {
|
|
102
|
+
tasks: config.tasks ?? [],
|
|
103
|
+
schedules: config.schedules ?? [],
|
|
104
|
+
...config.timezone ? { timezone: config.timezone } : {}
|
|
105
|
+
};
|
|
106
|
+
if (config.shapes) manifest.shapes = config.shapes;
|
|
107
|
+
if (config.preview) manifest.preview = config.preview;
|
|
108
|
+
if (config.brand) manifest.brand = config.brand;
|
|
109
|
+
return manifest;
|
|
110
|
+
}
|
|
111
|
+
async function buildStatic(siteDir, out, fallback = "index.html") {
|
|
112
|
+
if (!await exists$1(siteDir)) throw new Error(`No ${siteDir} to deploy. Build the site first.`);
|
|
113
|
+
await cp(siteDir, join(out, "public"), { recursive: true });
|
|
114
|
+
const pages = {};
|
|
115
|
+
for (const file of await walk$1(siteDir)) if (file.endsWith(".html")) pages[relative(siteDir, file).split(sep).join("/")] = await readFile(file, "utf8");
|
|
116
|
+
await writeFile(join(out, "server/index.mjs"), staticServer({
|
|
117
|
+
pages,
|
|
118
|
+
fallback: fallback || void 0
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
const builtins = new Set(builtinModules);
|
|
122
|
+
/**
|
|
123
|
+
* One ES module for Supabase Edge Runtime (Deno): every dependency inlined, Node built-ins
|
|
124
|
+
* imported with `node:` (Deno rejects bare `crypto`), and the Node globals Deno lacks injected.
|
|
125
|
+
*/
|
|
126
|
+
async function bundleServer(entry, file) {
|
|
127
|
+
const { build: rolldown } = await import("rolldown");
|
|
128
|
+
await rolldown({
|
|
129
|
+
input: entry,
|
|
130
|
+
platform: "node",
|
|
131
|
+
logLevel: "warn",
|
|
132
|
+
plugins: [{
|
|
133
|
+
name: "wervt:node-prefix",
|
|
134
|
+
resolveId(id) {
|
|
135
|
+
const bare = id.startsWith("node:") ? id.slice(5) : id;
|
|
136
|
+
if (builtins.has(bare) || builtins.has(bare.split("/")[0])) return {
|
|
137
|
+
id: `node:${bare}`,
|
|
138
|
+
external: true
|
|
139
|
+
};
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}],
|
|
143
|
+
transform: { inject: {
|
|
144
|
+
Buffer: ["node:buffer", "Buffer"],
|
|
145
|
+
setImmediate: ["node:timers", "setImmediate"],
|
|
146
|
+
clearImmediate: ["node:timers", "clearImmediate"]
|
|
147
|
+
} },
|
|
148
|
+
output: {
|
|
149
|
+
file,
|
|
150
|
+
format: "esm",
|
|
151
|
+
codeSplitting: false
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function definedOnly(value) {
|
|
156
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
|
157
|
+
}
|
|
11
158
|
// src/bundle.ts
|
|
12
159
|
/** Files under `dir`. Follows symlinks: Nitro can leave traced dependencies in
|
|
13
160
|
* server/node_modules as links into the local pnpm store, which won't exist on the server. */
|
|
@@ -23,14 +170,18 @@ async function gzip(data) {
|
|
|
23
170
|
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
24
171
|
}
|
|
25
172
|
const exists = (path) => stat(path).then(() => true, () => false);
|
|
26
|
-
/**
|
|
27
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Packs the build output's `server/`, `public/` and `wervt-bundle.json` (in `out`, `.output` by
|
|
175
|
+
* default) and the app's `drizzle/` into the bundle the control plane expects.
|
|
176
|
+
*/
|
|
177
|
+
async function createBundle(appDir, out = ".output") {
|
|
178
|
+
const outDir = join(appDir, out);
|
|
28
179
|
const roots = [
|
|
29
|
-
[join(
|
|
30
|
-
[join(
|
|
180
|
+
[join(outDir, "server"), "server"],
|
|
181
|
+
[join(outDir, "public"), "public"],
|
|
31
182
|
[join(appDir, "drizzle"), "drizzle"]
|
|
32
183
|
];
|
|
33
|
-
if (!await exists(roots[0][0])) throw new Error(
|
|
184
|
+
if (!await exists(roots[0][0])) throw new Error(`No ${join(out, "server")}. Build the app first.`);
|
|
34
185
|
const files = [];
|
|
35
186
|
for (const [dir, prefix] of roots) {
|
|
36
187
|
if (!await exists(dir)) continue;
|
|
@@ -46,6 +197,17 @@ async function createBundle(appDir) {
|
|
|
46
197
|
});
|
|
47
198
|
}
|
|
48
199
|
}
|
|
200
|
+
const manifest = join(outDir, BUNDLE_MANIFEST);
|
|
201
|
+
if (await exists(manifest)) {
|
|
202
|
+
const data = await readFile(manifest);
|
|
203
|
+
files.push({
|
|
204
|
+
header: {
|
|
205
|
+
name: BUNDLE_MANIFEST,
|
|
206
|
+
size: data.byteLength
|
|
207
|
+
},
|
|
208
|
+
body: data
|
|
209
|
+
});
|
|
210
|
+
}
|
|
49
211
|
return {
|
|
50
212
|
data: await gzip(await packTar(files)),
|
|
51
213
|
files: files.length
|
|
@@ -287,6 +449,10 @@ const deploy = defineCommand({
|
|
|
287
449
|
default: true,
|
|
288
450
|
description: "Build before deploying"
|
|
289
451
|
},
|
|
452
|
+
out: {
|
|
453
|
+
type: "string",
|
|
454
|
+
description: "Build output to pack (default: .output)"
|
|
455
|
+
},
|
|
290
456
|
check: {
|
|
291
457
|
type: "string",
|
|
292
458
|
description: "Pages to request once the release started, a 5xx fails (default: /). Shape routes are checked too; --check= turns all checks off"
|
|
@@ -319,7 +485,7 @@ const deploy = defineCommand({
|
|
|
319
485
|
}
|
|
320
486
|
const spinner = progress();
|
|
321
487
|
spinner.start("Packing");
|
|
322
|
-
const bundle = await createBundle(dir);
|
|
488
|
+
const bundle = await createBundle(dir, args.out);
|
|
323
489
|
spinner.stop(`Packed ${bundle.files} files (${(bundle.data.byteLength / 1024).toFixed(0)} kB)`);
|
|
324
490
|
spinner.start("Uploading");
|
|
325
491
|
const source = await deploySource(dir);
|
|
@@ -1669,6 +1835,47 @@ const login = defineCommand({
|
|
|
1669
1835
|
outro(`Signed in to ${url} (saved to ${file})`);
|
|
1670
1836
|
}
|
|
1671
1837
|
});
|
|
1838
|
+
const build = defineCommand({
|
|
1839
|
+
meta: {
|
|
1840
|
+
name: "build",
|
|
1841
|
+
description: "Build a fetch handler (Hono, plain `export default { fetch }`) or a static site into a bundle"
|
|
1842
|
+
},
|
|
1843
|
+
args: {
|
|
1844
|
+
dir: {
|
|
1845
|
+
type: "positional",
|
|
1846
|
+
required: false,
|
|
1847
|
+
description: "App directory (default: .)"
|
|
1848
|
+
},
|
|
1849
|
+
entry: {
|
|
1850
|
+
type: "string",
|
|
1851
|
+
description: "Server module (default: server.ts, src/server.ts, src/index.ts)"
|
|
1852
|
+
},
|
|
1853
|
+
static: {
|
|
1854
|
+
type: "string",
|
|
1855
|
+
description: "Folder of a static site to deploy instead of a server, e.g. dist"
|
|
1856
|
+
},
|
|
1857
|
+
fallback: {
|
|
1858
|
+
type: "string",
|
|
1859
|
+
description: "Static site: page for unknown paths (default: index.html, for SPAs); --fallback= for none"
|
|
1860
|
+
},
|
|
1861
|
+
out: {
|
|
1862
|
+
type: "string",
|
|
1863
|
+
description: "Output directory (default: .output)"
|
|
1864
|
+
}
|
|
1865
|
+
},
|
|
1866
|
+
async run({ args }) {
|
|
1867
|
+
const dir = resolveAppDir(args.dir);
|
|
1868
|
+
const started = performance.now();
|
|
1869
|
+
const result = await build$1(dir, {
|
|
1870
|
+
entry: args.entry,
|
|
1871
|
+
static: args.static,
|
|
1872
|
+
fallback: args.fallback === "" ? false : args.fallback,
|
|
1873
|
+
out: args.out
|
|
1874
|
+
});
|
|
1875
|
+
const ms = Math.round(performance.now() - started);
|
|
1876
|
+
console.log(`Built ${result.kind === "static" ? "a static site" : "the server"} into ${result.out} in ${ms} ms (${result.manifest.framework})`);
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1672
1879
|
runMain(defineCommand({
|
|
1673
1880
|
meta: {
|
|
1674
1881
|
name: "wervt",
|
|
@@ -1676,6 +1883,7 @@ runMain(defineCommand({
|
|
|
1676
1883
|
},
|
|
1677
1884
|
subCommands: {
|
|
1678
1885
|
deploy,
|
|
1886
|
+
build,
|
|
1679
1887
|
apps,
|
|
1680
1888
|
releases,
|
|
1681
1889
|
audit,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wervt/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.18",
|
|
4
4
|
"description": "Deploy and manage apps on wervt.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -32,8 +32,12 @@
|
|
|
32
32
|
"citty": "^0.2.2",
|
|
33
33
|
"modern-tar": "^0.8.6",
|
|
34
34
|
"nypm": "^0.6.12",
|
|
35
|
+
"rolldown": "^1.2.16",
|
|
35
36
|
"tinyexec": "^1.3.1"
|
|
36
37
|
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@wervt/core": "0.0.2"
|
|
40
|
+
},
|
|
37
41
|
"scripts": {
|
|
38
42
|
"build": "vp pack"
|
|
39
43
|
}
|