@junejs/cli 0.0.50 → 0.0.51-dev.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/dist/cli.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ //#region src/cli.d.ts
2
+ type Parsed = {
3
+ verb: string;
4
+ positional: string[];
5
+ flags: Record<string, string | boolean>;
6
+ };
7
+ declare function parse(argv: string[]): Parsed;
8
+ declare function run(argv: string[]): Promise<number | undefined>;
9
+ //#endregion
10
+ export { Parsed, parse, run };
package/dist/cli.js ADDED
@@ -0,0 +1,170 @@
1
+ import { existsSync } from "node:fs";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
4
+ //#region src/cli.ts
5
+ function parse(argv) {
6
+ const [verb = "", ...rest] = argv;
7
+ const positional = [];
8
+ const flags = {};
9
+ for (let i = 0; i < rest.length; i++) {
10
+ const a = rest[i];
11
+ if (a.startsWith("--")) {
12
+ const key = a.slice(2);
13
+ const next = rest[i + 1];
14
+ if (next !== void 0 && !next.startsWith("--")) {
15
+ flags[key] = next;
16
+ i++;
17
+ } else flags[key] = true;
18
+ } else positional.push(a);
19
+ }
20
+ return {
21
+ verb,
22
+ positional,
23
+ flags
24
+ };
25
+ }
26
+ const HELP = `june — the agent-native React framework
27
+
28
+ Usage: june <command> [dir] [options]
29
+
30
+ Commands:
31
+ dev Start the dev server --port <n> --no-watch
32
+ build Build a workerd-ready bundle --out <dir>
33
+ deploy Build + deploy (workers/vercel) --dry-run --prod --skip-migrate --allow-destructive
34
+ gen Freeze content/schema --check
35
+ db Database tasks (db migrate, db types) --allow-destructive
36
+ info Show routes + the agent surface
37
+ help Show this help
38
+
39
+ The app directory defaults to the current directory.
40
+ `;
41
+ function appRoot(positional) {
42
+ return positional[0] ? resolve(positional[0]) : process.cwd();
43
+ }
44
+ async function info(root) {
45
+ const { createApp, loadJuneConfig } = await import("@junejs/server");
46
+ const { resolveAgent } = await import("@junejs/core/config");
47
+ const { ACTION_REGISTRY } = await import("@junejs/core/agent");
48
+ const config = await loadJuneConfig(root);
49
+ const app = createApp({
50
+ appDir: join(root, "app"),
51
+ config
52
+ });
53
+ await app.warmup();
54
+ const routes = await app.routePaths();
55
+ const agent = resolveAgent(config.agent);
56
+ const tools = [...ACTION_REGISTRY.values()].filter((a) => a.description).map((a) => a.id);
57
+ console.log(`June app: ${config.site?.name ?? "(unnamed)"}`);
58
+ console.log(`\nRoutes (${routes.length}):`);
59
+ for (const r of routes) console.log(` ${r}`);
60
+ if (agent.enabled) {
61
+ console.log(`\nAgent surface:`);
62
+ if (agent.discovery) console.log(` discovery /llms.txt · /sitemap.xml · /.well-known/api-catalog`);
63
+ if (agent.mcp) console.log(` mcp /mcp (tools: ${tools.length ? tools.join(", ") : "none"})`);
64
+ } else console.log(`\nAgent surface: disabled`);
65
+ return 0;
66
+ }
67
+ async function run(argv) {
68
+ const { verb, positional, flags } = parse(argv);
69
+ const root = appRoot(positional);
70
+ switch (verb) {
71
+ case "dev": {
72
+ if (!existsSync(join(root, "app"))) {
73
+ console.error(`june dev: ${root} doesn't look like a June app (no app/ directory).`);
74
+ if (/^\d+$/.test(positional[0] ?? "")) console.error(` to choose a port, use --port: june dev --port ${positional[0]} · npm run dev -- --port ${positional[0]}`);
75
+ return 1;
76
+ }
77
+ if (!process.env.JUNE_DEV_CHILD && !flags["no-watch"]) {
78
+ const { superviseDev } = await import("./watch-CeyzNVKv.js");
79
+ return superviseDev(root);
80
+ }
81
+ const { startDevServer } = await import("@junejs/server");
82
+ await startDevServer({
83
+ appDir: join(root, "app"),
84
+ port: flags.port ? Number(flags.port) : 3e3
85
+ });
86
+ return;
87
+ }
88
+ case "build": {
89
+ const { juneBuild } = await import("@junejs/server");
90
+ const r = await juneBuild(root, typeof flags.out === "string" ? { outDir: resolve(flags.out) } : {});
91
+ console.log(`built ${r.outFile} (${r.routes.length} routes, ${r.dynamicRoutes.length} dynamic, ${r.prerendered.length} prerendered)`);
92
+ return 0;
93
+ }
94
+ case "deploy": {
95
+ const { juneDeploy } = await import("@junejs/server");
96
+ const r = await juneDeploy(root, {
97
+ dryRun: !!flags["dry-run"],
98
+ skipMigrate: !!flags["skip-migrate"],
99
+ allowDestructive: !!flags["allow-destructive"],
100
+ prod: !!flags["prod"]
101
+ });
102
+ console.log(r.url ? `deployed → ${r.url}` : r.dryRun ? "dry-run ok" : "deployed");
103
+ return 0;
104
+ }
105
+ case "gen": {
106
+ const { generateContent } = await import("@junejs/server");
107
+ const cols = await generateContent(root);
108
+ console.log(cols.length ? `generated content: ${cols.join(", ")}` : "no content/ collections");
109
+ const { generateMessages } = await import("./messages-DfAlYpfN.js");
110
+ const locales = await generateMessages(root);
111
+ if (locales) console.log(`generated messages: ${locales.join(", ")}`);
112
+ return 0;
113
+ }
114
+ case "db": {
115
+ const sub = positional[0];
116
+ const dbRoot = positional[1] ? resolve(positional[1]) : process.cwd();
117
+ if (sub === "migrate") {
118
+ const { loadJuneConfig, migrateApp, blockedMessage } = await import("@junejs/server");
119
+ const config = await loadJuneConfig(dbRoot);
120
+ if (!config.resources?.db) {
121
+ console.error("june db migrate: no `db` resource declared in june.config.ts.");
122
+ return 1;
123
+ }
124
+ const r = await migrateApp(dbRoot, config, { allowDestructive: !!flags["allow-destructive"] });
125
+ if (r?.applied.length) console.log(`migrated: ${r.applied.join(", ")}`);
126
+ else if (r && !r.blocked) console.log("migrations up to date");
127
+ if (r?.blocked) {
128
+ console.error(blockedMessage(r.blocked));
129
+ return 1;
130
+ }
131
+ return 0;
132
+ }
133
+ if (sub === "types") {
134
+ const { loadJuneConfig, typesApp } = await import("@junejs/server");
135
+ const config = await loadJuneConfig(dbRoot);
136
+ if (!config.resources?.db) {
137
+ console.error("june db types: no `db` resource declared in june.config.ts.");
138
+ return 1;
139
+ }
140
+ if (!config.dataLayer?.emitTypes) {
141
+ console.error("june db types: the configured dataLayer doesn't generate types (declare `dataLayer: junoDataLayer()` in june.config.ts).");
142
+ return 1;
143
+ }
144
+ const text = await typesApp(dbRoot, config);
145
+ if (text == null) {
146
+ console.log("nothing to generate");
147
+ return 0;
148
+ }
149
+ await writeFile(join(dbRoot, "db", "schema.d.ts"), text);
150
+ console.log("wrote db/schema.d.ts");
151
+ return 0;
152
+ }
153
+ console.error(`june db: unknown subcommand "${sub ?? ""}" (try: june db migrate | june db types)`);
154
+ return 1;
155
+ }
156
+ case "info": return info(root);
157
+ case "":
158
+ case "help":
159
+ case "--help":
160
+ case "-h":
161
+ console.log(HELP);
162
+ return 0;
163
+ default:
164
+ console.error(`june: unknown command "${verb}"\n`);
165
+ console.log(HELP);
166
+ return 1;
167
+ }
168
+ }
169
+ //#endregion
170
+ export { parse, run };
@@ -0,0 +1,26 @@
1
+ import { existsSync } from "node:fs";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
4
+ import { createRequire } from "node:module";
5
+ import { pathToFileURL } from "node:url";
6
+ //#region src/messages.ts
7
+ /** Generate `app/_messages.ts` from `messages/*.json`. Returns the locales, or
8
+ * null when there's no `messages/` dir or @junejs/i18n isn't installed. */
9
+ async function generateMessages(root) {
10
+ const messagesDir = join(root, "messages");
11
+ if (!existsSync(messagesDir)) return null;
12
+ let codegen;
13
+ try {
14
+ codegen = await import(pathToFileURL(createRequire(resolve(root, "package.json")).resolve("@junejs/i18n/codegen")).href);
15
+ } catch {
16
+ console.warn("[june] messages/ found but @junejs/i18n is not installed — skipping (run: bun add @junejs/i18n)");
17
+ return null;
18
+ }
19
+ const { loadJuneConfig } = await import("@junejs/server");
20
+ const defaultLocale = (await loadJuneConfig(root)).i18n?.defaultLocale ?? "en";
21
+ const { code, locales } = codegen.generateMessagesModule(messagesDir, { defaultLocale });
22
+ await writeFile(join(root, "app", "_messages.ts"), code);
23
+ return locales;
24
+ }
25
+ //#endregion
26
+ export { generateMessages };
@@ -0,0 +1,97 @@
1
+ import { existsSync, watch } from "node:fs";
2
+ import { join, relative, resolve, sep } from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ //#region src/watch.ts
5
+ const IGNORED_DIRS = /* @__PURE__ */ new Set([
6
+ "node_modules",
7
+ ".git",
8
+ "dist",
9
+ ".june"
10
+ ]);
11
+ function ignoredPath(file) {
12
+ const parts = file.split(sep);
13
+ if (parts.some((p) => IGNORED_DIRS.has(p))) return true;
14
+ if (file.endsWith(".css")) return true;
15
+ return parts.length === 2 && parts[0] === "app" && (/^_content\.[^/]+$/.test(parts[1]) || parts[1].endsWith(".gen.ts"));
16
+ }
17
+ function superviseDev(root) {
18
+ let child;
19
+ let restarting = false;
20
+ const start = () => {
21
+ child = spawn(process.execPath, process.argv.slice(1), {
22
+ stdio: "inherit",
23
+ env: {
24
+ ...process.env,
25
+ JUNE_DEV_CHILD: "1"
26
+ }
27
+ });
28
+ child.on("exit", (code) => {
29
+ if (restarting) return;
30
+ console.error(`[june] dev server exited (${code ?? "?"}) — waiting for changes`);
31
+ child = void 0;
32
+ });
33
+ };
34
+ const restart = async (file, contentChanged) => {
35
+ if (restarting) return;
36
+ restarting = true;
37
+ if (contentChanged) try {
38
+ const { generateContent } = await import("@junejs/server");
39
+ await generateContent(root);
40
+ } catch (err) {
41
+ console.error("[june] content regen failed:", err);
42
+ }
43
+ if (file.startsWith(`messages${sep}`)) try {
44
+ const { generateMessages } = await import("./messages-DfAlYpfN.js");
45
+ await generateMessages(root);
46
+ } catch (err) {
47
+ console.error("[june] messages regen failed:", err);
48
+ }
49
+ console.log(`[june] ${file} changed — restarting`);
50
+ const c = child;
51
+ if (!c || c.exitCode !== null || c.signalCode !== null) {
52
+ restarting = false;
53
+ start();
54
+ return;
55
+ }
56
+ c.once("exit", () => {
57
+ restarting = false;
58
+ start();
59
+ });
60
+ c.kill();
61
+ };
62
+ let pending;
63
+ let timer;
64
+ const schedule = (file, content) => {
65
+ pending = {
66
+ file,
67
+ content: content || (pending?.content ?? false)
68
+ };
69
+ clearTimeout(timer);
70
+ timer = setTimeout(() => void restart(pending.file, pending.content), 80);
71
+ };
72
+ watch(root, { recursive: true }, (_event, file) => {
73
+ if (!file || ignoredPath(file)) return;
74
+ schedule(file, file.startsWith(`content${sep}`));
75
+ });
76
+ (async () => {
77
+ try {
78
+ const { loadJuneConfig } = await import("@junejs/server");
79
+ const config = await loadJuneConfig(root);
80
+ for (const s of config.content?.sources ?? []) {
81
+ const dir = resolve(root, s.dir);
82
+ if (!existsSync(dir)) continue;
83
+ watch(dir, { recursive: true }, (_event, file) => {
84
+ if (!file || ignoredPath(file)) return;
85
+ schedule(relative(root, join(dir, file)), true);
86
+ });
87
+ }
88
+ } catch {}
89
+ })();
90
+ for (const sig of ["SIGINT", "SIGTERM"]) process.on(sig, () => {
91
+ child?.kill(sig);
92
+ process.exit(0);
93
+ });
94
+ start();
95
+ }
96
+ //#endregion
97
+ export { superviseDev };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junejs/cli",
3
- "version": "0.0.50",
3
+ "version": "0.0.51-dev.1",
4
4
  "description": "The `june` CLI — dev / build / deploy / gen / info. A thin bin over @junejs/server.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -9,19 +9,26 @@
9
9
  "june": "./bin.mjs"
10
10
  },
11
11
  "exports": {
12
- ".": "./src/cli.ts"
12
+ ".": {
13
+ "source": "./src/cli.ts",
14
+ "bun": "./src/cli.ts",
15
+ "types": "./dist/cli.d.ts",
16
+ "default": "./dist/cli.js"
17
+ }
13
18
  },
14
19
  "files": [
15
20
  "src",
16
- "bin.mjs"
21
+ "bin.mjs",
22
+ "dist"
17
23
  ],
18
24
  "scripts": {
19
25
  "test": "bun test",
20
- "typecheck": "tsc --noEmit"
26
+ "typecheck": "tsc --noEmit",
27
+ "build": "tsdown"
21
28
  },
22
29
  "dependencies": {
23
- "@junejs/core": ">=0.0.48",
24
- "@junejs/server": ">=0.0.51"
30
+ "@junejs/core": ">=0.1.0-dev.4",
31
+ "@junejs/server": ">=0.1.0-dev.5"
25
32
  },
26
33
  "repository": {
27
34
  "type": "git",