@valancex/cli 0.0.0-stage → 0.12.0
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 +334 -2
- package/bin/vlx.js +4 -0
- package/dist/add.d.ts +20 -0
- package/dist/add.js +143 -0
- package/dist/args.d.ts +20 -0
- package/dist/args.js +78 -0
- package/dist/author.d.ts +4 -0
- package/dist/author.js +30 -0
- package/dist/build.d.ts +14 -0
- package/dist/build.js +75 -0
- package/dist/check.d.ts +8 -0
- package/dist/check.js +37 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +280 -0
- package/dist/compile.d.ts +52 -0
- package/dist/compile.js +217 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.js +142 -0
- package/dist/content.d.ts +12 -0
- package/dist/content.js +194 -0
- package/dist/contract.d.ts +31 -0
- package/dist/contract.js +45 -0
- package/dist/delegate.d.ts +2 -0
- package/dist/delegate.js +23 -0
- package/dist/dev.d.ts +21 -0
- package/dist/dev.js +257 -0
- package/dist/diagnostics.d.ts +16 -0
- package/dist/diagnostics.js +16 -0
- package/dist/emit.d.ts +13 -0
- package/dist/emit.js +107 -0
- package/dist/init.d.ts +69 -0
- package/dist/init.js +185 -0
- package/dist/markdown.d.ts +20 -0
- package/dist/markdown.js +186 -0
- package/dist/node.d.ts +10 -0
- package/dist/node.js +6 -0
- package/dist/outline.d.ts +17 -0
- package/dist/outline.js +88 -0
- package/dist/project.d.ts +8 -0
- package/dist/project.js +20 -0
- package/dist/security.d.ts +4 -0
- package/dist/security.js +35 -0
- package/dist/targets.d.ts +12 -0
- package/dist/targets.js +7 -0
- package/dist/tree.d.ts +47 -0
- package/dist/tree.js +149 -0
- package/dist/tty.d.ts +13 -0
- package/dist/tty.js +4 -0
- package/dist/ui/Spinner.d.ts +3 -0
- package/dist/ui/Spinner.js +16 -0
- package/dist/ui/init.d.ts +22 -0
- package/dist/ui/init.js +137 -0
- package/dist/ui/prompts.d.ts +39 -0
- package/dist/ui/prompts.js +98 -0
- package/dist/ui/report.d.ts +31 -0
- package/dist/ui/report.js +30 -0
- package/dist/ui/run.d.ts +15 -0
- package/dist/ui/run.js +75 -0
- package/dist/vite.d.ts +7 -0
- package/dist/vite.js +49 -0
- package/dist/wiring.d.ts +108 -0
- package/dist/wiring.js +330 -0
- package/package.json +70 -3
- package/schemas/valance.schema.json +32 -0
- package/schemas/valance.web.schema.json +56 -0
- package/templates/web/README.md +14 -0
- package/templates/web/content/404.md +7 -0
- package/templates/web/content/guide/getting-started.md +33 -0
- package/templates/web/content/guide/writing-pages.md +21 -0
- package/templates/web/content/index.md +16 -0
- package/templates/web/gitignore +4 -0
- package/templates/web/public/logo.svg +1 -0
- package/templates/web/src/layout.mprx +27 -0
- package/templates/web/src/main.ts +20 -0
- package/templates/web/src/styles.css +28 -0
- package/templates/web/src/theme-toggle.mprx +1 -0
- package/templates/web/src/theme-toggle.ts +10 -0
- package/templates/web/tsconfig.json +18 -0
- package/views/block.mprx +3 -0
- package/views/blocks.mprx +3 -0
- package/views/heading.mprx +3 -0
- package/views/inline.mprx +3 -0
- package/views/layout.mprx +26 -0
- package/views/list-block.mprx +5 -0
- package/views/list-items.mprx +3 -0
- package/views/runs.mprx +3 -0
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Built {
|
|
2
|
+
readonly dir: string;
|
|
3
|
+
readonly files: ReadonlyArray<string>;
|
|
4
|
+
readonly pages: ReadonlyArray<string>;
|
|
5
|
+
}
|
|
6
|
+
/** The file a pathname is written to in the site: `/` → `index.html`, `/guide/x` → `guide/x/index.html`, relative to the base. */
|
|
7
|
+
export declare const fileOf: (pathname: string, base: string) => string;
|
|
8
|
+
/** The stages of a build, in order, as `onStage` reports them. */
|
|
9
|
+
export type BuildStage = "configuration" | "content" | "templates" | "bundle" | "pages";
|
|
10
|
+
export interface BuildOptions {
|
|
11
|
+
/** Called as each stage begins. */
|
|
12
|
+
readonly onStage?: (stage: BuildStage) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare const build: (root: string, options?: BuildOptions) => Promise<Built>;
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// `vlx build`: the project → a static site in `dist/`. The stages, in order: configuration, content, templates, the browser bundle, then one document per page (and a 404 page, a sitemap
|
|
2
|
+
// when the site has a published address). Everything is generated under `.valance/` first; `dist/` holds only the site.
|
|
3
|
+
import { build as bundle } from "vite";
|
|
4
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { loadAuthor } from "./author.js";
|
|
7
|
+
import { loadConfig } from "./config.js";
|
|
8
|
+
import { headersFile } from "./security.js";
|
|
9
|
+
import { AppError } from "./diagnostics.js";
|
|
10
|
+
import { generateContent, generateEntries, generateProgram } from "./emit.js";
|
|
11
|
+
import { OUTPUT_DIR } from "./project.js";
|
|
12
|
+
import { baseConfig, createLoader, SERVER_ENTRY } from "./vite.js";
|
|
13
|
+
/** The file a pathname is written to in the site: `/` → `index.html`, `/guide/x` → `guide/x/index.html`, relative to the base. */
|
|
14
|
+
export const fileOf = (pathname, base) => {
|
|
15
|
+
const inside = pathname.slice(base.length).replace(/\/+$/, "");
|
|
16
|
+
return inside === "" ? "index.html" : `${inside}/index.html`;
|
|
17
|
+
};
|
|
18
|
+
const sitemap = (content) => `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${content.pages.map((page) => ` <url><loc>${content.site.url}${content.site.base}${page.path.replace(/^\//, "")}</loc></url>`).join("\n")}\n</urlset>\n`;
|
|
19
|
+
export const build = async (root, options = {}) => {
|
|
20
|
+
const stage = options.onStage ?? (() => undefined);
|
|
21
|
+
stage("configuration");
|
|
22
|
+
const config = loadConfig(root);
|
|
23
|
+
generateEntries(root);
|
|
24
|
+
stage("content");
|
|
25
|
+
const content = generateContent(root, config);
|
|
26
|
+
const loader = await createLoader(root, config);
|
|
27
|
+
let rendered;
|
|
28
|
+
try {
|
|
29
|
+
stage("templates");
|
|
30
|
+
const author = await loadAuthor(loader, root);
|
|
31
|
+
await generateProgram(root, author);
|
|
32
|
+
// The browser bundle first: it empties dist/, and the documents are written into it after.
|
|
33
|
+
stage("bundle");
|
|
34
|
+
await bundle({ ...baseConfig(root, config), mode: "production" });
|
|
35
|
+
rendered = (await loader.ssrLoadModule(SERVER_ENTRY));
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
await loader.close();
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const dist = join(root, OUTPUT_DIR);
|
|
43
|
+
const manifest = JSON.parse(readFileSync(join(dist, ".vite", "manifest.json"), "utf8"));
|
|
44
|
+
const entry = Object.values(manifest).find((chunk) => chunk.isEntry === true);
|
|
45
|
+
if (entry === undefined) {
|
|
46
|
+
throw new AppError([{ file: OUTPUT_DIR, line: 0, column: 0, code: "build-entry", message: "the bundle has no entry chunk" }]);
|
|
47
|
+
}
|
|
48
|
+
rmSync(join(dist, ".vite"), { recursive: true, force: true }); // the bundler's bookkeeping is not part of the site
|
|
49
|
+
const built = { script: `${config.web.base}${entry.file}`, styles: (entry.css ?? []).map((file) => `${config.web.base}${file}`) };
|
|
50
|
+
const files = [];
|
|
51
|
+
const write = (relative, text) => {
|
|
52
|
+
mkdirSync(dirname(join(dist, relative)), { recursive: true });
|
|
53
|
+
writeFileSync(join(dist, relative), text);
|
|
54
|
+
files.push(relative);
|
|
55
|
+
};
|
|
56
|
+
const pages = rendered.paths();
|
|
57
|
+
stage("pages");
|
|
58
|
+
for (const pathname of pages) {
|
|
59
|
+
write(fileOf(pathname, config.web.base), (await rendered.render(pathname, built)).html);
|
|
60
|
+
}
|
|
61
|
+
// An address that is not a page: the file a static host serves with a 404.
|
|
62
|
+
write("404.html", (await rendered.render(`${config.web.base}404`, built)).html);
|
|
63
|
+
if (config.web.url !== "") {
|
|
64
|
+
write("sitemap.xml", sitemap(content));
|
|
65
|
+
}
|
|
66
|
+
// The headers to serve it with: the page is only as safe as the host makes it (src/security.ts).
|
|
67
|
+
if (config.web.headers) {
|
|
68
|
+
write("_headers", headersFile(config));
|
|
69
|
+
}
|
|
70
|
+
return { dir: dist, files, pages };
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await loader.close();
|
|
74
|
+
}
|
|
75
|
+
};
|
package/dist/check.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Diagnostic } from "./diagnostics.js";
|
|
2
|
+
export interface Checked {
|
|
3
|
+
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
4
|
+
readonly pages: number;
|
|
5
|
+
readonly components: ReadonlyArray<string>;
|
|
6
|
+
}
|
|
7
|
+
/** Checks the project at `root`. Never throws for a mistake in the project: that is what the result is for. */
|
|
8
|
+
export declare const check: (root: string) => Promise<Checked>;
|
package/dist/check.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AppError } from "./diagnostics.js";
|
|
2
|
+
import { loadAuthor } from "./author.js";
|
|
3
|
+
import { loadConfig } from "./config.js";
|
|
4
|
+
import { generateContent, generateEntries, generateProgram } from "./emit.js";
|
|
5
|
+
import { createLoader } from "./vite.js";
|
|
6
|
+
const collect = async (into, work) => {
|
|
7
|
+
try {
|
|
8
|
+
return await work();
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
if (error instanceof AppError) {
|
|
12
|
+
into.push(...error.diagnostics);
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/** Checks the project at `root`. Never throws for a mistake in the project: that is what the result is for. */
|
|
19
|
+
export const check = async (root) => {
|
|
20
|
+
const problems = [];
|
|
21
|
+
const config = await collect(problems, () => loadConfig(root));
|
|
22
|
+
if (config === undefined) {
|
|
23
|
+
return { diagnostics: problems, pages: 0, components: [] };
|
|
24
|
+
}
|
|
25
|
+
generateEntries(root);
|
|
26
|
+
const content = await collect(problems, () => generateContent(root, config));
|
|
27
|
+
const server = await createLoader(root, config);
|
|
28
|
+
try {
|
|
29
|
+
const author = await collect(problems, () => loadAuthor(server, root));
|
|
30
|
+
const failedAuthor = problems.some((each) => each.file === "src/main.ts");
|
|
31
|
+
const components = failedAuthor ? undefined : await collect(problems, () => generateProgram(root, author));
|
|
32
|
+
return { diagnostics: problems, pages: content?.pages.length ?? 0, components: components ?? [] };
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
await server.close();
|
|
36
|
+
}
|
|
37
|
+
};
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const main: (args: ReadonlyArray<string>, cwd?: string) => Promise<number>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// The `vlx` command.
|
|
2
|
+
//
|
|
3
|
+
// vlx init [name] [--web ...] make a new application (asks what the flags do not say)
|
|
4
|
+
// vlx dev [--port N] [--host H] serve the project and rebuild it as it changes
|
|
5
|
+
// vlx build write the site to dist/
|
|
6
|
+
// vlx check [--json] find mistakes in authored files without building
|
|
7
|
+
// vlx generate page|component <name> add a page, or a component registered in src/main.ts (alias: g)
|
|
8
|
+
// vlx sync bring .valance/ (generated types, manifest, entries) up to date; runs by itself after install and generate
|
|
9
|
+
// vlx help [command] what a command does
|
|
10
|
+
//
|
|
11
|
+
// In a terminal, `init`, `dev`, `build` and `check` draw an interface (Ink, `ui/`). Anywhere else (a pipe, CI, `--json`, `--plain`) they print the plain text they always have.
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { pathToFileURL } from "node:url";
|
|
14
|
+
import { INIT_HELP, parseInit } from "./args.js";
|
|
15
|
+
import { build } from "./build.js";
|
|
16
|
+
import { generateComponent, generatePage, GenerateError, KINDS } from "./add.js";
|
|
17
|
+
import { check } from "./check.js";
|
|
18
|
+
import { findRoot } from "./config.js";
|
|
19
|
+
import { localCli } from "./delegate.js";
|
|
20
|
+
import { dev } from "./dev.js";
|
|
21
|
+
import { AppError, format } from "./diagnostics.js";
|
|
22
|
+
import { detectPackageManager, directoryFor, directoryProblem, execute, nameProblem, runCommand, titleOf } from "./init.js";
|
|
23
|
+
import { TARGETS } from "./targets.js";
|
|
24
|
+
import { canAsk, canDraw } from "./tty.js";
|
|
25
|
+
const HELP = `vlx <command>
|
|
26
|
+
|
|
27
|
+
init [name] [--web] make a new application
|
|
28
|
+
dev [--port N] [--host H] serve it and rebuild as it changes
|
|
29
|
+
build write the site to dist/
|
|
30
|
+
check [--json] find mistakes in your files without building
|
|
31
|
+
generate page|component <name> add a page, or a component (alias: g)
|
|
32
|
+
sync refresh the generated files (this runs by itself; see vlx help sync)
|
|
33
|
+
help [command] say more about one command
|
|
34
|
+
|
|
35
|
+
Run it in a project (a directory with a valance.json), or any directory inside one.
|
|
36
|
+
In a terminal the commands draw an interface; --plain (or a pipe, or CI) prints plain text.
|
|
37
|
+
`;
|
|
38
|
+
const GENERATE_HELP = `vlx generate <page|component> <name> (alias: vlx g)
|
|
39
|
+
|
|
40
|
+
vlx generate page guide/getting-started content/guide/getting-started.md, with its front matter
|
|
41
|
+
(--title "Getting started" to say the title)
|
|
42
|
+
vlx generate component post-card src/post-card.mprx and src/post-card.ts, imported in
|
|
43
|
+
src/main.ts and listed in its \`components\`. Write
|
|
44
|
+
<post-card></post-card> in a template to use it.
|
|
45
|
+
|
|
46
|
+
Nothing is overwritten: a name that is taken is refused.
|
|
47
|
+
`;
|
|
48
|
+
const COMMAND_HELP = {
|
|
49
|
+
init: INIT_HELP,
|
|
50
|
+
generate: GENERATE_HELP,
|
|
51
|
+
g: GENERATE_HELP,
|
|
52
|
+
dev: "vlx dev [--port N] [--host H]\n\nServe the project and rebuild it as files change. A mistake leaves the last good build serving and shows in the terminal and in the page.\n",
|
|
53
|
+
build: "vlx build\n\nWrite the site to dist/. Any host that serves files can serve it.\n",
|
|
54
|
+
sync: "vlx sync\n\nBring .valance/ up to date: the generated types for your templates (so your editor and tsc agree with them), the manifest and the entries. You rarely run it: a new project does it after install (the `prepare` script), `vlx generate` does it after it writes, and dev, build and check do it as they run. It never fails: mistakes in your files are reported by vlx check.\n",
|
|
55
|
+
check: "vlx check [--json]\n\nFind mistakes in your files without building. --json prints them as { file, line, column, code, message } for editors and CI.\n",
|
|
56
|
+
};
|
|
57
|
+
/** Refreshes `.valance/` and says so in a line. Best effort: a mistake in the project is `check`'s to report, and a project with no `valance.json` (the install ran before init finished) is left alone. */
|
|
58
|
+
const sync = async (from) => {
|
|
59
|
+
try {
|
|
60
|
+
const checked = await check(findRoot(from));
|
|
61
|
+
return checked.diagnostics.length === 0
|
|
62
|
+
? "the generated files are up to date"
|
|
63
|
+
: `the generated files are up to date; ${checked.diagnostics.length} problem${checked.diagnostics.length === 1 ? "" : "s"} in your files (vlx check)`;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return `skipped (${error instanceof Error ? error.message.split("\n")[0] : String(error)})`;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const option = (args, name) => {
|
|
70
|
+
const at = args.indexOf(name);
|
|
71
|
+
return at < 0 ? undefined : args[at + 1];
|
|
72
|
+
};
|
|
73
|
+
const version = () => JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
74
|
+
const runInit = async (args, cwd) => {
|
|
75
|
+
if (args.name !== undefined) {
|
|
76
|
+
const problem = nameProblem(args.name) ?? directoryProblem(directoryFor(args.name, cwd));
|
|
77
|
+
if (problem !== undefined) {
|
|
78
|
+
console.error(`vlx init: ${problem}`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Asking needs a terminal to read keys from; with `--yes` nothing is asked, so drawing the progress needs only one to draw on.
|
|
83
|
+
if (!args.plain && (args.yes ? canDraw() : canAsk())) {
|
|
84
|
+
const { drawInit } = await import("./ui/run.js");
|
|
85
|
+
const result = await drawInit(args, cwd, detectPackageManager(), version());
|
|
86
|
+
return result.status === "done" ? 0 : result.status === "cancelled" ? 130 : 1;
|
|
87
|
+
}
|
|
88
|
+
if (args.name === undefined) {
|
|
89
|
+
console.error("vlx init: it cannot ask in this terminal, so it needs a name: vlx init <app-name> --web");
|
|
90
|
+
return 2;
|
|
91
|
+
}
|
|
92
|
+
if (args.targets.length === 0 && !args.yes) {
|
|
93
|
+
console.error(`vlx init: choose a target (${TARGETS.map((each) => `--${each.id}`).join(", ")}), or --yes for all of them`);
|
|
94
|
+
return 2;
|
|
95
|
+
}
|
|
96
|
+
const answers = {
|
|
97
|
+
name: args.name,
|
|
98
|
+
dir: directoryFor(args.name, cwd),
|
|
99
|
+
targets: args.targets.length === 0 ? TARGETS.map((each) => each.id) : args.targets,
|
|
100
|
+
web: { base: args.base ?? "", url: args.url ?? "" },
|
|
101
|
+
packageManager: args.packageManager ?? detectPackageManager(),
|
|
102
|
+
install: args.install ?? true,
|
|
103
|
+
git: args.git ?? true,
|
|
104
|
+
...(args.cli === undefined ? {} : { cli: args.cli }),
|
|
105
|
+
};
|
|
106
|
+
const outcome = await execute(answers, (step, state, detail) => {
|
|
107
|
+
if (state === "done" || state === "failed") {
|
|
108
|
+
console.log(`vlx init: ${step} ${state === "done" ? "done" : "failed"}${detail === undefined ? "" : ` (${detail.split("\n")[0]})`}`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
console.log(`vlx init: created ${titleOf(answers.name)} in ${answers.dir} (${outcome.files.length} files)`);
|
|
112
|
+
outcome.notes.forEach((note) => {
|
|
113
|
+
console.log(`vlx init: ${note}`);
|
|
114
|
+
});
|
|
115
|
+
console.log(`vlx init: next: cd ${answers.dir}${outcome.installed ? "" : ` && ${answers.packageManager} install`} && ${runCommand(answers.packageManager)} dev`);
|
|
116
|
+
return 0;
|
|
117
|
+
};
|
|
118
|
+
export const main = async (args, cwd = process.cwd()) => {
|
|
119
|
+
const [command, ...rest] = args;
|
|
120
|
+
// `vlx dev --help` says what dev does and runs nothing (`init` and `generate` answer for themselves below).
|
|
121
|
+
if (command !== undefined &&
|
|
122
|
+
command !== "init" &&
|
|
123
|
+
command !== "generate" &&
|
|
124
|
+
command !== "g" &&
|
|
125
|
+
COMMAND_HELP[command] !== undefined &&
|
|
126
|
+
(rest.includes("--help") || rest.includes("-h"))) {
|
|
127
|
+
console.log(COMMAND_HELP[command]);
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
// A global `vlx` runs the project's own (see `delegate.ts`).
|
|
131
|
+
if (command === "dev" || command === "build" || command === "check" || command === "sync" || command === "generate" || command === "g") {
|
|
132
|
+
const local = localCli(cwd);
|
|
133
|
+
if (local !== undefined) {
|
|
134
|
+
const theirs = (await import(pathToFileURL(local).href));
|
|
135
|
+
return theirs.main(args, cwd);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const draws = !rest.includes("--plain") && canDraw();
|
|
139
|
+
try {
|
|
140
|
+
switch (command) {
|
|
141
|
+
case "init": {
|
|
142
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
143
|
+
console.log(INIT_HELP);
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
const parsed = parseInit(rest);
|
|
147
|
+
if (typeof parsed === "string") {
|
|
148
|
+
console.error(`vlx init: ${parsed}\n\n${INIT_HELP}`);
|
|
149
|
+
return 2;
|
|
150
|
+
}
|
|
151
|
+
return await runInit(parsed, cwd);
|
|
152
|
+
}
|
|
153
|
+
case "dev": {
|
|
154
|
+
const port = option(rest, "--port");
|
|
155
|
+
const host = option(rest, "--host");
|
|
156
|
+
if (draws) {
|
|
157
|
+
const { drawDev } = await import("./ui/run.js");
|
|
158
|
+
await drawDev(findRoot(cwd), { ...(port === undefined ? {} : { port: Number(port) }), ...(host === undefined ? {} : { host }) });
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
const running = await dev(findRoot(cwd), { ...(port === undefined ? {} : { port: Number(port) }), ...(host === undefined ? {} : { host }) });
|
|
162
|
+
console.log(`vlx dev: ${running.url}`);
|
|
163
|
+
// Until interrupted.
|
|
164
|
+
await new Promise((resolve) => {
|
|
165
|
+
process.once("SIGINT", resolve);
|
|
166
|
+
process.once("SIGTERM", resolve);
|
|
167
|
+
});
|
|
168
|
+
await running.close();
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
case "build": {
|
|
172
|
+
if (draws) {
|
|
173
|
+
const { drawBuild } = await import("./ui/run.js");
|
|
174
|
+
await drawBuild(findRoot(cwd));
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
const started = Date.now();
|
|
178
|
+
const built = await build(findRoot(cwd));
|
|
179
|
+
console.log(`vlx build: ${built.pages.length} pages, ${built.files.length} files → ${built.dir} (${((Date.now() - started) / 1000).toFixed(1)}s)`);
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
case "check": {
|
|
183
|
+
if (draws && !rest.includes("--json")) {
|
|
184
|
+
const { drawCheck } = await import("./ui/run.js");
|
|
185
|
+
return (await drawCheck(findRoot(cwd))).diagnostics.length === 0 ? 0 : 1;
|
|
186
|
+
}
|
|
187
|
+
const result = await check(findRoot(cwd));
|
|
188
|
+
if (rest.includes("--json")) {
|
|
189
|
+
console.log(JSON.stringify(result.diagnostics, null, 2));
|
|
190
|
+
}
|
|
191
|
+
else if (result.diagnostics.length === 0) {
|
|
192
|
+
console.log(`vlx check: ${result.pages} pages, ${result.components.length} components, no problems`);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
result.diagnostics.forEach((each) => {
|
|
196
|
+
console.error(format(each));
|
|
197
|
+
});
|
|
198
|
+
console.error(`vlx check: ${result.diagnostics.length} problem${result.diagnostics.length === 1 ? "" : "s"}`);
|
|
199
|
+
}
|
|
200
|
+
return result.diagnostics.length === 0 ? 0 : 1;
|
|
201
|
+
}
|
|
202
|
+
case "sync": {
|
|
203
|
+
const synced = await sync(cwd);
|
|
204
|
+
console.log(synced);
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
case "generate":
|
|
208
|
+
case "g": {
|
|
209
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
210
|
+
console.log(GENERATE_HELP);
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
const title = option(rest, "--title");
|
|
214
|
+
const positional = rest.filter((each, at) => !each.startsWith("-") && rest[at - 1] !== "--title");
|
|
215
|
+
const kind = positional[0];
|
|
216
|
+
if (kind === undefined || !KINDS.includes(kind) || positional[1] === undefined || positional.length > 2) {
|
|
217
|
+
console.error(`vlx generate: say what and its name: ${KINDS.map((each) => `vlx generate ${each} <name>`).join(", ")}\n\n${GENERATE_HELP}`);
|
|
218
|
+
return 2;
|
|
219
|
+
}
|
|
220
|
+
const root = findRoot(cwd);
|
|
221
|
+
try {
|
|
222
|
+
const made = kind === "page" ? generatePage(root, positional[1], title) : generateComponent(root, positional[1]);
|
|
223
|
+
made.created.forEach((file) => console.log(`vlx generate: created ${file}`));
|
|
224
|
+
made.changed.forEach((file) => console.log(`vlx generate: changed ${file}`));
|
|
225
|
+
console.log(`vlx generate: ${await sync(root)}`);
|
|
226
|
+
made.notes.forEach((note) => console.log(`vlx generate: ${note}`));
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
if (error instanceof GenerateError) {
|
|
231
|
+
console.error(`vlx generate: ${error.message}`);
|
|
232
|
+
return 1;
|
|
233
|
+
}
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
case "--version":
|
|
238
|
+
case "-v":
|
|
239
|
+
console.log(version());
|
|
240
|
+
return 0;
|
|
241
|
+
case "help": {
|
|
242
|
+
const about = rest[0];
|
|
243
|
+
if (about === undefined) {
|
|
244
|
+
console.log(HELP);
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
const text = COMMAND_HELP[about];
|
|
248
|
+
if (text === undefined) {
|
|
249
|
+
console.error(`vlx help: no command "${about}"\n\n${HELP}`);
|
|
250
|
+
return 2;
|
|
251
|
+
}
|
|
252
|
+
console.log(text);
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
case undefined:
|
|
256
|
+
case "--help":
|
|
257
|
+
case "-h":
|
|
258
|
+
console.log(HELP);
|
|
259
|
+
return 0;
|
|
260
|
+
default:
|
|
261
|
+
console.error(`vlx: unknown command "${command}"\n\n${HELP}`);
|
|
262
|
+
return 2;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
if (error instanceof AppError) {
|
|
267
|
+
if (draws) {
|
|
268
|
+
const { drawDiagnostics } = await import("./ui/run.js");
|
|
269
|
+
await drawDiagnostics(error.diagnostics);
|
|
270
|
+
return 1;
|
|
271
|
+
}
|
|
272
|
+
error.diagnostics.forEach((each) => {
|
|
273
|
+
console.error(format(each));
|
|
274
|
+
});
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
console.error(error instanceof Error ? (error.stack ?? error.message) : String(error));
|
|
278
|
+
return 1;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type * as Valance from "@valancex/valance";
|
|
2
|
+
import type { DefinedApp } from "@valancex/common";
|
|
3
|
+
import type { Node } from "./tree.js";
|
|
4
|
+
import type { Vocabulary } from "@valancex/common/internal";
|
|
5
|
+
export declare const SOURCE_DIR = "src";
|
|
6
|
+
/** The component the page is drawn by. */
|
|
7
|
+
export declare const ROOT = "layout";
|
|
8
|
+
export declare const BUILT_IN: string;
|
|
9
|
+
/** The package directory: where its README, which documents the framework's own names, is. */
|
|
10
|
+
export declare const PACKAGE_DIR: string;
|
|
11
|
+
interface Template {
|
|
12
|
+
readonly component: string;
|
|
13
|
+
/** As shown in a diagnostic. */
|
|
14
|
+
readonly path: string;
|
|
15
|
+
readonly source: string;
|
|
16
|
+
readonly builtIn: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface Templates {
|
|
19
|
+
readonly all: ReadonlyArray<Template>;
|
|
20
|
+
/** The declared tree, in `src/main.ts`'s own words. */
|
|
21
|
+
readonly tree: ReadonlyArray<Node>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Built-in templates, then the author's. The author's are exactly the ones `src/main.ts` declares: `layout`, `components` and `override`. A file under `src/` that is not declared is an error, not
|
|
25
|
+
* a guess; a registered component that no template writes is allowed (it is not part of the program until one does). Which components a template uses is read from the template. A name that is a built-in's replaces it only through `override`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const templatesOf: (root: string, author: DefinedApp | undefined, vocabulary?: Vocabulary) => Templates;
|
|
28
|
+
export declare const manifestOf: (author: DefinedApp | undefined, vocabulary?: Vocabulary) => import("@valancex/valance/web/build").Manifest;
|
|
29
|
+
export interface Compiled {
|
|
30
|
+
readonly program: Valance.Program;
|
|
31
|
+
/**
|
|
32
|
+
* What a template may use: every tag, prop, event, scope name and command, **with the contracts the compiler inferred for the author's own components** (a component's props are the
|
|
33
|
+
* arguments its uses pass). This is the program's own model. The manifest derived before compiling declares only the tags and the layout, so an editor checking one file against it
|
|
34
|
+
* would call every component of the author's "unknown". For editors.
|
|
35
|
+
*/
|
|
36
|
+
readonly manifest: unknown;
|
|
37
|
+
/** Each template the program was built from, by source path, for the editor: `src/theme-toggle.mprx` → `theme-toggle`. Built-in templates are not listed (they are not in the project). */
|
|
38
|
+
readonly files: Readonly<Record<string, string>>;
|
|
39
|
+
readonly components: ReadonlyArray<string>;
|
|
40
|
+
/** The component tree as `src/main.ts` declares it. */
|
|
41
|
+
readonly tree: ReadonlyArray<Node>;
|
|
42
|
+
/** Every template the program was built from, with its source text and its file relative to the project (a built-in's is under the package, so it may start with `..`). */
|
|
43
|
+
readonly templates: ReadonlyArray<{
|
|
44
|
+
readonly component: string;
|
|
45
|
+
readonly file: string;
|
|
46
|
+
readonly source: string;
|
|
47
|
+
readonly builtIn: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
/** Compiles the application's templates against a target's vocabulary (the web's by default). Fails with every diagnostic, each pointing into the file it is about. */
|
|
51
|
+
export declare const compileViews: (root: string, author: DefinedApp | undefined, vocabulary?: Vocabulary) => Promise<Compiled>;
|
|
52
|
+
export {};
|