@contentrail/cli 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +81 -2
- package/bin/contentrail.js +5 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +204 -0
- package/dist/cli.js.map +1 -0
- package/dist/collections/load.d.ts +6 -0
- package/dist/collections/load.d.ts.map +1 -0
- package/dist/collections/load.js +57 -0
- package/dist/collections/load.js.map +1 -0
- package/dist/commands/build.d.ts +5 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +46 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +136 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +129 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +67 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/validate.d.ts +9 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +84 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/config/load.d.ts +11 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +33 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/schema.d.ts +3 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +34 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/types.d.ts +14 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +8 -0
- package/dist/config/types.js.map +1 -0
- package/dist/detect/framework.d.ts +13 -0
- package/dist/detect/framework.d.ts.map +1 -0
- package/dist/detect/framework.js +52 -0
- package/dist/detect/framework.js.map +1 -0
- package/dist/editor/validate-config.d.ts +10 -0
- package/dist/editor/validate-config.d.ts.map +1 -0
- package/dist/editor/validate-config.js +135 -0
- package/dist/editor/validate-config.js.map +1 -0
- package/dist/exit-codes.d.ts +9 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +8 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/util/fs.d.ts +7 -0
- package/dist/util/fs.d.ts.map +1 -0
- package/dist/util/fs.js +27 -0
- package/dist/util/fs.js.map +1 -0
- package/dist/util/paths.d.ts +7 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +23 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/print.d.ts +7 -0
- package/dist/util/print.d.ts.map +1 -0
- package/dist/util/print.js +14 -0
- package/dist/util/print.js.map +1 -0
- package/dist/util/redact.d.ts +10 -0
- package/dist/util/redact.d.ts.map +1 -0
- package/dist/util/redact.js +19 -0
- package/dist/util/redact.js.map +1 -0
- package/dist/util/spawn.d.ts +11 -0
- package/dist/util/spawn.d.ts.map +1 -0
- package/dist/util/spawn.js +32 -0
- package/dist/util/spawn.js.map +1 -0
- package/package.json +52 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContentRail contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,82 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@contentrail/cli`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Node.js CLI for ContentRail local development (ADR 020).
|
|
4
|
+
|
|
5
|
+
Requires **Node.js ≥ 22**. Supported frameworks: **Astro** and **Vite**
|
|
6
|
+
(verified with Astro **7.3.8** and Vite **8.3.5**; `@contentrail/astro` needs
|
|
7
|
+
Node ≥ **22.12**).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
### Monorepo
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm install
|
|
15
|
+
pnpm --filter @contentrail/cli build
|
|
16
|
+
pnpm exec contentrail --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### From npm (upcoming `0.2.0` after authorized publish)
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
pnpm add -D @contentrail/cli@0.2.0
|
|
23
|
+
pnpm exec contentrail --help
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
CLI version tracks `@contentrail/cli` (`contentrail --version`). Collection
|
|
27
|
+
schemas use Zod **`^4.6.7`** via `@contentrail/core`.
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
| Command | Purpose |
|
|
32
|
+
| ---------------------- | ----------------------------------------------- |
|
|
33
|
+
| `contentrail init` | Scaffold config, sample collection, sample JSON |
|
|
34
|
+
| `contentrail validate` | Validate config + local JSON content |
|
|
35
|
+
| `contentrail build` | Validate, then run `package.json` build script |
|
|
36
|
+
| `contentrail doctor` | Diagnose setup (no secret values printed) |
|
|
37
|
+
| `contentrail status` | Summarize local readiness |
|
|
38
|
+
|
|
39
|
+
`publish` / `rollback` are **deferred** — use the Worker API or editor.
|
|
40
|
+
|
|
41
|
+
### Exit codes
|
|
42
|
+
|
|
43
|
+
| Code | Meaning |
|
|
44
|
+
| ---- | --------------------------------- |
|
|
45
|
+
| 0 | Success |
|
|
46
|
+
| 1 | Failure (validation/build/doctor) |
|
|
47
|
+
| 2 | Usage error |
|
|
48
|
+
| 3 | Configuration error |
|
|
49
|
+
|
|
50
|
+
## Configuration (`contentrail.config.json`)
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"contentRoot": "content",
|
|
55
|
+
"framework": "auto",
|
|
56
|
+
"collections": "content/config.ts",
|
|
57
|
+
"buildScript": "build",
|
|
58
|
+
"editorConfig": "contentrail.editor.json",
|
|
59
|
+
"publishApiUrl": "https://your-worker.example"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Paths are relative to the project root and cannot escape it.
|
|
64
|
+
- Do not put tokens or credentials in this file.
|
|
65
|
+
- `publishApiUrl` is optional and only checked structurally by doctor/status.
|
|
66
|
+
|
|
67
|
+
## Typical workflow
|
|
68
|
+
|
|
69
|
+
1. Create an Astro or Vite app (or use a monorepo `examples/*` app).
|
|
70
|
+
2. Add `@contentrail/core` + `zod`, plus `@contentrail/astro` or `@contentrail/vite`
|
|
71
|
+
(workspace links today; `pnpm add` from npm only after public release).
|
|
72
|
+
3. `pnpm exec contentrail init --framework vite` (or `astro`)
|
|
73
|
+
4. Edit collections / JSON under `content/`
|
|
74
|
+
5. `pnpm exec contentrail validate`
|
|
75
|
+
6. Framework `dev` / `contentrail build`
|
|
76
|
+
7. `contentrail doctor` when something fails
|
|
77
|
+
|
|
78
|
+
## Limitations
|
|
79
|
+
|
|
80
|
+
- No live Access/Worker publish from the CLI yet (ADR 020).
|
|
81
|
+
- Collection TypeScript modules are loaded via **jiti**.
|
|
82
|
+
- Lint/format tooling remains a separate monorepo gap.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AA4E3C,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,CAAC,EAAE;IACR,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAChC,GACA,OAAO,CAAC,QAAQ,CAAC,CAsFnB"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { runBuild } from "./commands/build.js";
|
|
4
|
+
import { runDoctor } from "./commands/doctor.js";
|
|
5
|
+
import { runInit } from "./commands/init.js";
|
|
6
|
+
import { runStatus } from "./commands/status.js";
|
|
7
|
+
import { runValidate } from "./commands/validate.js";
|
|
8
|
+
import { loadProject } from "./config/load.js";
|
|
9
|
+
import { ExitCode } from "./exit-codes.js";
|
|
10
|
+
import { createLogger } from "./util/print.js";
|
|
11
|
+
import { findProjectRoot } from "./util/paths.js";
|
|
12
|
+
const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
13
|
+
const HELP = `ContentRail CLI — local developer tooling
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
contentrail <command> [options]
|
|
17
|
+
|
|
18
|
+
Commands:
|
|
19
|
+
init Initialize ContentRail files in an Astro or Vite project
|
|
20
|
+
validate Validate configuration and local JSON content
|
|
21
|
+
build Validate then run the project build script
|
|
22
|
+
doctor Diagnose common setup problems
|
|
23
|
+
status Summarize local project state
|
|
24
|
+
|
|
25
|
+
Global options:
|
|
26
|
+
-h, --help Show help
|
|
27
|
+
-V, --version Show version
|
|
28
|
+
-C, --cwd <dir> Project directory (default: current working directory)
|
|
29
|
+
|
|
30
|
+
init options:
|
|
31
|
+
--framework <astro|vite> Force framework (default: auto-detect)
|
|
32
|
+
--force Overwrite existing generated files
|
|
33
|
+
--with-editor Also write contentrail.editor.json
|
|
34
|
+
-y, --yes Non-interactive; accept defaults
|
|
35
|
+
|
|
36
|
+
Exit codes:
|
|
37
|
+
0 success
|
|
38
|
+
1 failure (validation/build/doctor)
|
|
39
|
+
2 usage error
|
|
40
|
+
3 configuration error
|
|
41
|
+
|
|
42
|
+
Notes:
|
|
43
|
+
publish / rollback are not implemented in the CLI (ADR 020).
|
|
44
|
+
Use the authenticated Worker API or editor for remote publishing.
|
|
45
|
+
`;
|
|
46
|
+
function printCommandHelp(command) {
|
|
47
|
+
switch (command) {
|
|
48
|
+
case "init":
|
|
49
|
+
return `Usage: contentrail init [--framework astro|vite] [--force] [--with-editor] [-y]
|
|
50
|
+
|
|
51
|
+
Creates contentrail.config.json, a sample collection module, and sample JSON.
|
|
52
|
+
Never overwrites existing files unless --force is set.
|
|
53
|
+
Does not require publishing credentials.`;
|
|
54
|
+
case "validate":
|
|
55
|
+
return `Usage: contentrail validate
|
|
56
|
+
|
|
57
|
+
Loads collections, validates local JSON via @contentrail/reader, checks
|
|
58
|
+
duplicate slugs when present, and validates optional editor metadata.`;
|
|
59
|
+
case "build":
|
|
60
|
+
return `Usage: contentrail build
|
|
61
|
+
|
|
62
|
+
Runs validate, then the package.json script named by buildScript (default: build).`;
|
|
63
|
+
case "doctor":
|
|
64
|
+
return `Usage: contentrail doctor
|
|
65
|
+
|
|
66
|
+
Checks Node version, framework deps, config, content, validation, and whether
|
|
67
|
+
remote env var *names* are set (values never printed).`;
|
|
68
|
+
case "status":
|
|
69
|
+
return `Usage: contentrail status
|
|
70
|
+
|
|
71
|
+
Reports framework, collections, validation, build readiness, and whether a
|
|
72
|
+
publishApiUrl is configured (does not probe live deployment health).`;
|
|
73
|
+
default:
|
|
74
|
+
return HELP;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export async function runCli(argv, options) {
|
|
78
|
+
const log = createLogger(options?.stdout, options?.stderr);
|
|
79
|
+
const baseCwd = options?.cwd ?? process.cwd();
|
|
80
|
+
try {
|
|
81
|
+
const { command, flags, cwd } = parseArgv(argv, baseCwd);
|
|
82
|
+
if (flags.version) {
|
|
83
|
+
log.info(VERSION);
|
|
84
|
+
return ExitCode.Success;
|
|
85
|
+
}
|
|
86
|
+
if (command === undefined || command === "help" || flags.help) {
|
|
87
|
+
log.info(command !== undefined && command !== "help"
|
|
88
|
+
? printCommandHelp(command)
|
|
89
|
+
: HELP);
|
|
90
|
+
return ExitCode.Success;
|
|
91
|
+
}
|
|
92
|
+
const projectRoot = findProjectRoot(cwd);
|
|
93
|
+
switch (command) {
|
|
94
|
+
case "init": {
|
|
95
|
+
const framework = flags.framework;
|
|
96
|
+
if (framework !== undefined &&
|
|
97
|
+
framework !== "astro" &&
|
|
98
|
+
framework !== "vite") {
|
|
99
|
+
log.error('--framework must be "astro" or "vite".');
|
|
100
|
+
return ExitCode.Usage;
|
|
101
|
+
}
|
|
102
|
+
return runInit({
|
|
103
|
+
projectRoot,
|
|
104
|
+
...(framework !== undefined ? { framework } : {}),
|
|
105
|
+
force: flags.force,
|
|
106
|
+
withEditor: flags.withEditor,
|
|
107
|
+
yes: flags.yes,
|
|
108
|
+
}, log);
|
|
109
|
+
}
|
|
110
|
+
case "validate": {
|
|
111
|
+
const project = await loadProject(projectRoot);
|
|
112
|
+
return (await runValidate(project, log)).code;
|
|
113
|
+
}
|
|
114
|
+
case "build": {
|
|
115
|
+
const project = await loadProject(projectRoot);
|
|
116
|
+
return runBuild(project, log);
|
|
117
|
+
}
|
|
118
|
+
case "doctor":
|
|
119
|
+
return runDoctor(projectRoot, log);
|
|
120
|
+
case "status":
|
|
121
|
+
return runStatus(projectRoot, log);
|
|
122
|
+
case "publish":
|
|
123
|
+
case "rollback":
|
|
124
|
+
log.error(`Command "${command}" is deferred (ADR 020). Use the Worker API or editor for authenticated publishing.`);
|
|
125
|
+
return ExitCode.Usage;
|
|
126
|
+
default:
|
|
127
|
+
log.error(`Unknown command "${command}". Run contentrail --help.`);
|
|
128
|
+
return ExitCode.Usage;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
133
|
+
log.error(message);
|
|
134
|
+
if (/Unknown option|Unexpected argument|requires a directory|--framework requires/i.test(message)) {
|
|
135
|
+
return ExitCode.Usage;
|
|
136
|
+
}
|
|
137
|
+
if (/contentrail\.config\.json|Invalid contentrail|collections module|escapes the project/i.test(message)) {
|
|
138
|
+
return ExitCode.Config;
|
|
139
|
+
}
|
|
140
|
+
return ExitCode.Failure;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function parseArgv(argv, baseCwd) {
|
|
144
|
+
let command;
|
|
145
|
+
let cwd = baseCwd;
|
|
146
|
+
const flags = {
|
|
147
|
+
help: false,
|
|
148
|
+
version: false,
|
|
149
|
+
force: false,
|
|
150
|
+
withEditor: false,
|
|
151
|
+
yes: false,
|
|
152
|
+
};
|
|
153
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
154
|
+
const arg = argv[i];
|
|
155
|
+
if (arg === "--help" || arg === "-h") {
|
|
156
|
+
flags.help = true;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (arg === "--version" || arg === "-V") {
|
|
160
|
+
flags.version = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (arg === "--force") {
|
|
164
|
+
flags.force = true;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (arg === "--with-editor") {
|
|
168
|
+
flags.withEditor = true;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (arg === "--yes" || arg === "-y") {
|
|
172
|
+
flags.yes = true;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (arg === "--framework") {
|
|
176
|
+
const value = argv[i + 1];
|
|
177
|
+
if (value === undefined || value.startsWith("-")) {
|
|
178
|
+
throw new Error("--framework requires astro or vite.");
|
|
179
|
+
}
|
|
180
|
+
flags.framework = value;
|
|
181
|
+
i += 1;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (arg === "--cwd" || arg === "-C") {
|
|
185
|
+
const value = argv[i + 1];
|
|
186
|
+
if (value === undefined || value.startsWith("-")) {
|
|
187
|
+
throw new Error(`${arg} requires a directory path.`);
|
|
188
|
+
}
|
|
189
|
+
cwd = path.resolve(baseCwd, value);
|
|
190
|
+
i += 1;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (arg.startsWith("-")) {
|
|
194
|
+
throw new Error(`Unknown option ${arg}`);
|
|
195
|
+
}
|
|
196
|
+
if (command === undefined) {
|
|
197
|
+
command = arg;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
throw new Error(`Unexpected argument "${arg}"`);
|
|
201
|
+
}
|
|
202
|
+
return { command, cwd, flags };
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ContentCollection } from "@contentrail/core";
|
|
2
|
+
/**
|
|
3
|
+
* Dynamically load named ContentCollection exports from a JS/TS module.
|
|
4
|
+
*/
|
|
5
|
+
export declare function loadCollectionsModule(absoluteModulePath: string): Promise<ContentCollection[]>;
|
|
6
|
+
//# sourceMappingURL=load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/collections/load.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAe3D;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,kBAAkB,EAAE,MAAM,GACzB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA0D9B"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createJiti } from "jiti";
|
|
2
|
+
import { pathExists } from "../util/fs.js";
|
|
3
|
+
function isContentCollection(value) {
|
|
4
|
+
return (value !== null &&
|
|
5
|
+
typeof value === "object" &&
|
|
6
|
+
"name" in value &&
|
|
7
|
+
"schema" in value &&
|
|
8
|
+
typeof value.name === "string" &&
|
|
9
|
+
typeof value.schema?.safeParse ===
|
|
10
|
+
"function");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Dynamically load named ContentCollection exports from a JS/TS module.
|
|
14
|
+
*/
|
|
15
|
+
export async function loadCollectionsModule(absoluteModulePath) {
|
|
16
|
+
if (!(await pathExists(absoluteModulePath))) {
|
|
17
|
+
throw new Error(`Collections module not found: ${absoluteModulePath}`);
|
|
18
|
+
}
|
|
19
|
+
const jiti = createJiti(import.meta.url, {
|
|
20
|
+
interopDefault: true,
|
|
21
|
+
});
|
|
22
|
+
const mod = (await jiti.import(absoluteModulePath));
|
|
23
|
+
const collections = [];
|
|
24
|
+
for (const [exportName, value] of Object.entries(mod)) {
|
|
25
|
+
if (exportName === "default" &&
|
|
26
|
+
value !== null &&
|
|
27
|
+
typeof value === "object") {
|
|
28
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
29
|
+
if (isContentCollection(nested)) {
|
|
30
|
+
collections.push(nested);
|
|
31
|
+
}
|
|
32
|
+
else if (nested !== null &&
|
|
33
|
+
typeof nested === "object" &&
|
|
34
|
+
"articles" in nested) {
|
|
35
|
+
// ignore unrelated default bags
|
|
36
|
+
void key;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (isContentCollection(value)) {
|
|
42
|
+
collections.push(value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (collections.length === 0) {
|
|
46
|
+
throw new Error(`No ContentRail collections exported from ${absoluteModulePath}. Export defineCollection(...) results as named exports.`);
|
|
47
|
+
}
|
|
48
|
+
const names = new Set();
|
|
49
|
+
for (const collection of collections) {
|
|
50
|
+
if (names.has(collection.name)) {
|
|
51
|
+
throw new Error(`Duplicate collection name "${collection.name}" in ${absoluteModulePath}.`);
|
|
52
|
+
}
|
|
53
|
+
names.add(collection.name);
|
|
54
|
+
}
|
|
55
|
+
return collections;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/collections/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,SAAS,mBAAmB,CAAC,KAAc;IACzC,OAAO,CACL,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,MAAM,IAAI,KAAK;QACf,QAAQ,IAAI,KAAK;QACjB,OAAQ,KAA2B,CAAC,IAAI,KAAK,QAAQ;QACrD,OAAQ,KAA6C,CAAC,MAAM,EAAE,SAAS;YACrE,UAAU,CACb,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,kBAA0B;IAE1B,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,iCAAiC,kBAAkB,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACvC,cAAc,EAAE,IAAI;KACrB,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAGjD,CAAC;IACF,MAAM,WAAW,GAAwB,EAAE,CAAC;IAE5C,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,IACE,UAAU,KAAK,SAAS;YACxB,KAAK,KAAK,IAAI;YACd,OAAO,KAAK,KAAK,QAAQ,EACzB,CAAC;YACD,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CACxC,KAAgC,CACjC,EAAE,CAAC;gBACF,IAAI,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC;oBAChC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC3B,CAAC;qBAAM,IACL,MAAM,KAAK,IAAI;oBACf,OAAO,MAAM,KAAK,QAAQ;oBAC1B,UAAU,IAAK,MAAiB,EAChC,CAAC;oBACD,gCAAgC;oBAChC,KAAK,GAAG,CAAC;gBACX,CAAC;YACH,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,4CAA4C,kBAAkB,0DAA0D,CACzH,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACb,8BAA8B,UAAU,CAAC,IAAI,QAAQ,kBAAkB,GAAG,CAC3E,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LoadedProject } from "../config/load.js";
|
|
2
|
+
import { ExitCode } from "../exit-codes.js";
|
|
3
|
+
import type { Logger } from "../util/print.js";
|
|
4
|
+
export declare function runBuild(project: LoadedProject, log: Logger): Promise<ExitCode>;
|
|
5
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,QAAQ,CAAC,CAkDnB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { detectPackageManager, readPackageJson } from "../detect/framework.js";
|
|
2
|
+
import { ExitCode } from "../exit-codes.js";
|
|
3
|
+
import { runCommand } from "../util/spawn.js";
|
|
4
|
+
import { runValidate } from "./validate.js";
|
|
5
|
+
export async function runBuild(project, log) {
|
|
6
|
+
const validation = await runValidate(project, log);
|
|
7
|
+
if (validation.code !== ExitCode.Success) {
|
|
8
|
+
log.error("Build aborted because validation failed.");
|
|
9
|
+
return validation.code;
|
|
10
|
+
}
|
|
11
|
+
const pkg = await readPackageJson(project.projectRoot);
|
|
12
|
+
const script = project.config.buildScript;
|
|
13
|
+
if (pkg?.scripts?.[script] === undefined) {
|
|
14
|
+
log.error(`package.json has no "${script}" script. Set buildScript in contentrail.config.json or add the script.`);
|
|
15
|
+
return ExitCode.Config;
|
|
16
|
+
}
|
|
17
|
+
const pm = await detectPackageManager(project.projectRoot);
|
|
18
|
+
const command = pm === "pnpm" ? "pnpm" : pm === "yarn" ? "yarn" : "npm";
|
|
19
|
+
const args = pm === "pnpm" || pm === "yarn" ? ["run", script] : ["run", script];
|
|
20
|
+
log.info(`Running ${command} ${args.join(" ")} …`);
|
|
21
|
+
let result;
|
|
22
|
+
try {
|
|
23
|
+
result = await runCommand(command, args, { cwd: project.projectRoot });
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
log.error(`Failed to start build command: ${error instanceof Error ? error.message : String(error)}`);
|
|
27
|
+
return ExitCode.Failure;
|
|
28
|
+
}
|
|
29
|
+
if (result.stdout.trim() !== "") {
|
|
30
|
+
log.info(result.stdout.trimEnd());
|
|
31
|
+
}
|
|
32
|
+
if (result.stderr.trim() !== "") {
|
|
33
|
+
log.warn(result.stderr.trimEnd());
|
|
34
|
+
}
|
|
35
|
+
if (result.signal !== null) {
|
|
36
|
+
log.error(`Build interrupted (${result.signal}).`);
|
|
37
|
+
return ExitCode.Failure;
|
|
38
|
+
}
|
|
39
|
+
if (result.code !== 0) {
|
|
40
|
+
log.error(`Build failed with exit code ${result.code ?? "unknown"}.`);
|
|
41
|
+
return ExitCode.Failure;
|
|
42
|
+
}
|
|
43
|
+
log.info("Build completed successfully.");
|
|
44
|
+
return ExitCode.Success;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAW/C,wBAAsB,SAAS,CAC7B,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,QAAQ,CAAC,CAwInB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadProject } from "../config/load.js";
|
|
4
|
+
import { detectFrameworkFromPackage, detectPackageManager, readPackageJson, resolveFramework, } from "../detect/framework.js";
|
|
5
|
+
import { ExitCode } from "../exit-codes.js";
|
|
6
|
+
import { pathExists } from "../util/fs.js";
|
|
7
|
+
import { formatEnvPresence } from "../util/redact.js";
|
|
8
|
+
import { runValidate } from "./validate.js";
|
|
9
|
+
const REMOTE_ENV_HINTS = [
|
|
10
|
+
"CONTENTRAIL_ACCESS_TEAM_DOMAIN",
|
|
11
|
+
"CONTENTRAIL_ACCESS_AUD",
|
|
12
|
+
"GITHUB_TOKEN",
|
|
13
|
+
"R2_ACCESS_KEY_ID",
|
|
14
|
+
"R2_SECRET_ACCESS_KEY",
|
|
15
|
+
];
|
|
16
|
+
export async function runDoctor(projectRoot, log) {
|
|
17
|
+
let failed = false;
|
|
18
|
+
const recommendations = [];
|
|
19
|
+
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
20
|
+
if (Number.isNaN(nodeMajor) || nodeMajor < 22) {
|
|
21
|
+
log.error(`FAIL Node.js >= 22 required (found ${process.versions.node}).`);
|
|
22
|
+
failed = true;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
log.info(`OK Node.js ${process.versions.node}`);
|
|
26
|
+
}
|
|
27
|
+
const pm = await detectPackageManager(projectRoot);
|
|
28
|
+
if (pm === "unknown") {
|
|
29
|
+
recommendations.push("No lockfile detected (pnpm-lock.yaml / package-lock.json / yarn.lock).");
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
log.info(`OK package manager hint: ${pm}`);
|
|
33
|
+
}
|
|
34
|
+
const pkg = await readPackageJson(projectRoot);
|
|
35
|
+
if (pkg === null) {
|
|
36
|
+
log.error("FAIL package.json missing.");
|
|
37
|
+
return ExitCode.Failure;
|
|
38
|
+
}
|
|
39
|
+
log.info("OK package.json present");
|
|
40
|
+
const detected = detectFrameworkFromPackage(pkg);
|
|
41
|
+
if (detected === "unknown") {
|
|
42
|
+
log.error("FAIL neither Astro nor Vite dependency detected.");
|
|
43
|
+
failed = true;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
log.info(`OK framework dependency: ${detected}`);
|
|
47
|
+
}
|
|
48
|
+
let project;
|
|
49
|
+
try {
|
|
50
|
+
project = await loadProject(projectRoot);
|
|
51
|
+
log.info(`OK ${path.basename(project.configPath)}`);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
log.error(`FAIL config: ${error instanceof Error ? error.message : String(error)}`);
|
|
55
|
+
return ExitCode.Config;
|
|
56
|
+
}
|
|
57
|
+
const framework = resolveFramework(project.config.framework, detected);
|
|
58
|
+
log.info(`OK resolved framework: ${framework}`);
|
|
59
|
+
if (!(await pathExists(project.contentRootAbs))) {
|
|
60
|
+
log.error(`FAIL content root missing: ${project.config.contentRoot}`);
|
|
61
|
+
failed = true;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const stat = await fs.stat(project.contentRootAbs);
|
|
65
|
+
if (!stat.isDirectory()) {
|
|
66
|
+
log.error(`FAIL content root is not a directory: ${project.config.contentRoot}`);
|
|
67
|
+
failed = true;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
log.info(`OK content root: ${project.config.contentRoot}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!(await pathExists(project.collectionsAbs))) {
|
|
74
|
+
log.error(`FAIL collections module missing: ${project.config.collections}`);
|
|
75
|
+
failed = true;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
log.info(`OK collections module: ${project.config.collections}`);
|
|
79
|
+
}
|
|
80
|
+
const validation = await runValidate(project, {
|
|
81
|
+
info: (message) => log.info(` ${message}`),
|
|
82
|
+
warn: log.warn,
|
|
83
|
+
error: (message) => {
|
|
84
|
+
failed = true;
|
|
85
|
+
log.error(`FAIL validation: ${message}`);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
if (validation.code === ExitCode.Success) {
|
|
89
|
+
log.info("OK content validation");
|
|
90
|
+
}
|
|
91
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
92
|
+
if (framework === "astro" && deps["@contentrail/astro"] === undefined) {
|
|
93
|
+
recommendations.push("Add @contentrail/astro for Astro integration.");
|
|
94
|
+
}
|
|
95
|
+
if (framework === "vite" && deps["@contentrail/vite"] === undefined) {
|
|
96
|
+
recommendations.push("Add @contentrail/vite for Vite integration.");
|
|
97
|
+
}
|
|
98
|
+
if (deps["@contentrail/core"] === undefined) {
|
|
99
|
+
recommendations.push("Add @contentrail/core for collection schemas.");
|
|
100
|
+
}
|
|
101
|
+
if (project.config.publishApiUrl !== undefined) {
|
|
102
|
+
try {
|
|
103
|
+
const url = new URL(project.config.publishApiUrl);
|
|
104
|
+
if (url.protocol !== "https:" && url.hostname !== "localhost") {
|
|
105
|
+
recommendations.push("publishApiUrl should use https in non-local environments.");
|
|
106
|
+
}
|
|
107
|
+
log.info(`OK publishApiUrl structure: ${url.origin}`);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
log.error("FAIL publishApiUrl is not a valid URL.");
|
|
111
|
+
failed = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
recommendations.push("publishApiUrl is unset — fine for local-only projects; set it when a Worker API exists.");
|
|
116
|
+
}
|
|
117
|
+
log.info("Remote env presence (names only, values never printed):");
|
|
118
|
+
for (const name of REMOTE_ENV_HINTS) {
|
|
119
|
+
const present = process.env[name] !== undefined && process.env[name] !== "";
|
|
120
|
+
log.info(` ${formatEnvPresence(name, present)}`);
|
|
121
|
+
}
|
|
122
|
+
recommendations.push("Missing remote publishing env vars is OK for local validate/build.");
|
|
123
|
+
if (recommendations.length > 0) {
|
|
124
|
+
log.info("Recommendations:");
|
|
125
|
+
for (const item of recommendations) {
|
|
126
|
+
log.info(` - ${item}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (failed) {
|
|
130
|
+
log.error("Doctor found failures.");
|
|
131
|
+
return ExitCode.Failure;
|
|
132
|
+
}
|
|
133
|
+
log.info("Doctor completed with no failures.");
|
|
134
|
+
return ExitCode.Success;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ExitCode } from "../exit-codes.js";
|
|
2
|
+
import type { Logger } from "../util/print.js";
|
|
3
|
+
export interface InitOptions {
|
|
4
|
+
readonly projectRoot: string;
|
|
5
|
+
readonly framework?: "astro" | "vite" | "auto";
|
|
6
|
+
readonly force?: boolean;
|
|
7
|
+
readonly withEditor?: boolean;
|
|
8
|
+
readonly yes?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function runInit(options: InitOptions, log: Logger): Promise<ExitCode>;
|
|
11
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAsB,OAAO,CAC3B,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,QAAQ,CAAC,CAoKnB"}
|