svedocs-cli 0.1.0-beta.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/LICENSE +5 -0
- package/README.md +26 -0
- package/dist/chunk-QD6TB2KV.js +751 -0
- package/dist/create-svedocs.d.ts +1 -0
- package/dist/create-svedocs.js +12 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +12 -0
- package/dist/svedocs.d.ts +1 -0
- package/dist/svedocs.js +12 -0
- package/package.json +48 -0
- package/templates/cloudflare/.dev.vars.example +14 -0
- package/templates/cloudflare/README.md +13 -0
- package/templates/cloudflare/content/docs/index.md +20 -0
- package/templates/cloudflare/content/pages/index.md +9 -0
- package/templates/cloudflare/package.json +35 -0
- package/templates/cloudflare/src/app.cloudflare.d.ts +21 -0
- package/templates/cloudflare/src/app.d.ts +35 -0
- package/templates/cloudflare/src/app.html +12 -0
- package/templates/cloudflare/src/lib/server/env.ts +3 -0
- package/templates/cloudflare/src/routes/+layout.svelte +5 -0
- package/templates/cloudflare/src/routes/+layout.ts +3 -0
- package/templates/cloudflare/src/routes/+page.svelte +8 -0
- package/templates/cloudflare/src/routes/+page.ts +12 -0
- package/templates/cloudflare/src/routes/[...path]/+page.svelte +8 -0
- package/templates/cloudflare/src/routes/[...path]/+page.ts +22 -0
- package/templates/cloudflare/src/routes/api/ask/+server.ts +15 -0
- package/templates/cloudflare/src/routes/api/search/+server.ts +13 -0
- package/templates/cloudflare/src/routes/og/[...path]/+server.ts +26 -0
- package/templates/cloudflare/src/routes/robots.txt/+server.ts +12 -0
- package/templates/cloudflare/src/routes/sitemap.xml/+server.ts +13 -0
- package/templates/cloudflare/static/favicon.svg +6 -0
- package/templates/cloudflare/svedocs.config.ts +26 -0
- package/templates/cloudflare/svelte.config.js +18 -0
- package/templates/cloudflare/tsconfig.json +7 -0
- package/templates/cloudflare/vite.config.ts +9 -0
- package/templates/cloudflare/wrangler.toml +7 -0
- package/templates/docs/README.md +12 -0
- package/templates/docs/content/docs/index.md +14 -0
- package/templates/docs/content/pages/index.md +9 -0
- package/templates/docs/package.json +32 -0
- package/templates/docs/src/app.d.ts +35 -0
- package/templates/docs/src/app.html +12 -0
- package/templates/docs/src/lib/server/env.ts +3 -0
- package/templates/docs/src/routes/+layout.svelte +5 -0
- package/templates/docs/src/routes/+layout.ts +3 -0
- package/templates/docs/src/routes/+page.svelte +8 -0
- package/templates/docs/src/routes/+page.ts +12 -0
- package/templates/docs/src/routes/[...path]/+page.svelte +8 -0
- package/templates/docs/src/routes/[...path]/+page.ts +22 -0
- package/templates/docs/src/routes/api/ask/+server.ts +16 -0
- package/templates/docs/src/routes/api/search/+server.ts +13 -0
- package/templates/docs/src/routes/og/[...path]/+server.ts +26 -0
- package/templates/docs/src/routes/robots.txt/+server.ts +12 -0
- package/templates/docs/src/routes/sitemap.xml/+server.ts +13 -0
- package/templates/docs/static/favicon.svg +6 -0
- package/templates/docs/svedocs.config.ts +12 -0
- package/templates/docs/svelte.config.js +18 -0
- package/templates/docs/tsconfig.json +7 -0
- package/templates/docs/vite.config.ts +9 -0
- package/templates/minimal/README.md +12 -0
- package/templates/minimal/content/docs/index.md +8 -0
- package/templates/minimal/content/pages/index.md +9 -0
- package/templates/minimal/package.json +32 -0
- package/templates/minimal/src/app.d.ts +35 -0
- package/templates/minimal/src/app.html +12 -0
- package/templates/minimal/src/routes/+layout.svelte +5 -0
- package/templates/minimal/src/routes/+layout.ts +3 -0
- package/templates/minimal/src/routes/+page.svelte +8 -0
- package/templates/minimal/src/routes/+page.ts +12 -0
- package/templates/minimal/src/routes/[...path]/+page.svelte +8 -0
- package/templates/minimal/src/routes/[...path]/+page.ts +22 -0
- package/templates/minimal/static/favicon.svg +6 -0
- package/templates/minimal/svedocs.config.ts +10 -0
- package/templates/minimal/svelte.config.js +18 -0
- package/templates/minimal/tsconfig.json +7 -0
- package/templates/minimal/vite.config.ts +9 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
3
|
+
import path6 from "path";
|
|
4
|
+
import { loadSvedocsConfig } from "svedocs/config";
|
|
5
|
+
import { checkPackagePublication } from "svedocs/core";
|
|
6
|
+
import { createOgImage, createOgImageInput } from "svedocs/og";
|
|
7
|
+
import { syncCloudflareAiSearchIndex } from "svedocs/search";
|
|
8
|
+
|
|
9
|
+
// src/commands/create.ts
|
|
10
|
+
import { access, cp, mkdir, readFile as readFile3, readdir, writeFile } from "fs/promises";
|
|
11
|
+
import path3 from "path";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
import { Command } from "commander";
|
|
14
|
+
|
|
15
|
+
// src/package-manager.ts
|
|
16
|
+
import { spawn } from "child_process";
|
|
17
|
+
import { readFile } from "fs/promises";
|
|
18
|
+
import path from "path";
|
|
19
|
+
var packageManagers = ["pnpm", "npm", "yarn", "bun"];
|
|
20
|
+
var fallbackOrder = ["pnpm", "npm", "yarn", "bun"];
|
|
21
|
+
async function resolvePackageManager(options = {}) {
|
|
22
|
+
const requested = normalizePackageManagerName(options.requested);
|
|
23
|
+
if (options.requested && !requested && options.requested !== "auto") {
|
|
24
|
+
throw new Error(`Invalid package manager "${options.requested}". Use auto, pnpm, npm, yarn, or bun.`);
|
|
25
|
+
}
|
|
26
|
+
const readVersion = options.readVersion ?? readPackageManagerVersion;
|
|
27
|
+
if (requested) {
|
|
28
|
+
const version = await readVersion(requested);
|
|
29
|
+
if (!version) throw new Error(`${requested} was requested but was not found on this machine.`);
|
|
30
|
+
return { name: requested, version, source: "explicit" };
|
|
31
|
+
}
|
|
32
|
+
const env = options.env ?? process.env;
|
|
33
|
+
const envManager = detectPackageManagerFromEnv(env);
|
|
34
|
+
const projectManager = await detectPackageManagerFromProject(options.cwd ?? process.cwd());
|
|
35
|
+
for (const candidate of [
|
|
36
|
+
envManager ? { name: envManager, source: "environment" } : void 0,
|
|
37
|
+
projectManager ? { name: projectManager, source: "project" } : void 0
|
|
38
|
+
]) {
|
|
39
|
+
if (!candidate) continue;
|
|
40
|
+
const version = await readVersion(candidate.name);
|
|
41
|
+
if (version) return { ...candidate, version, detected: candidate.name };
|
|
42
|
+
}
|
|
43
|
+
for (const [index, name] of fallbackOrder.entries()) {
|
|
44
|
+
const version = await readVersion(name);
|
|
45
|
+
if (version) {
|
|
46
|
+
const detected = envManager ?? projectManager;
|
|
47
|
+
return {
|
|
48
|
+
name,
|
|
49
|
+
version,
|
|
50
|
+
source: index === 0 ? "default" : "fallback",
|
|
51
|
+
...detected ? { detected } : {}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw new Error("No supported package manager was found. Install pnpm, npm, yarn, or bun and try again.");
|
|
56
|
+
}
|
|
57
|
+
function detectPackageManagerFromEnv(env = process.env) {
|
|
58
|
+
const userAgent = env.npm_config_user_agent;
|
|
59
|
+
const userAgentManager = userAgent ? normalizePackageManagerName(userAgent.split(/[ /]/)[0]) : void 0;
|
|
60
|
+
if (userAgentManager) return userAgentManager;
|
|
61
|
+
const execPath = `${env.npm_execpath ?? ""} ${env.npm_node_execpath ?? ""}`.toLowerCase();
|
|
62
|
+
if (execPath.includes("pnpm")) return "pnpm";
|
|
63
|
+
if (execPath.includes("yarn")) return "yarn";
|
|
64
|
+
if (execPath.includes("bun")) return "bun";
|
|
65
|
+
if (execPath.includes("npm")) return "npm";
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
async function detectPackageManagerFromProject(cwd) {
|
|
69
|
+
for (const directory of ancestors(cwd)) {
|
|
70
|
+
const packageJsonManager = await readPackageManagerField(path.join(directory, "package.json"));
|
|
71
|
+
if (packageJsonManager) return packageJsonManager;
|
|
72
|
+
if (await fileExists(path.join(directory, "pnpm-lock.yaml"))) return "pnpm";
|
|
73
|
+
if (await fileExists(path.join(directory, "package-lock.json")) || await fileExists(path.join(directory, "npm-shrinkwrap.json"))) return "npm";
|
|
74
|
+
if (await fileExists(path.join(directory, "yarn.lock"))) return "yarn";
|
|
75
|
+
if (await fileExists(path.join(directory, "bun.lock")) || await fileExists(path.join(directory, "bun.lockb"))) return "bun";
|
|
76
|
+
}
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
function createPackageManagerField(choice) {
|
|
80
|
+
return choice.version ? `${choice.name}@${choice.version}` : void 0;
|
|
81
|
+
}
|
|
82
|
+
function createInstallCommand(name) {
|
|
83
|
+
return name === "yarn" ? ["yarn", "install"] : [name, "install"];
|
|
84
|
+
}
|
|
85
|
+
function createRunCommand(name, script) {
|
|
86
|
+
if (name === "npm") return ["npm", "run", script];
|
|
87
|
+
if (name === "bun") return ["bun", "run", script];
|
|
88
|
+
return [name, script];
|
|
89
|
+
}
|
|
90
|
+
function formatCommand(parts) {
|
|
91
|
+
return parts.map(shellQuote).join(" ");
|
|
92
|
+
}
|
|
93
|
+
function describePackageManagerSource(choice) {
|
|
94
|
+
const version = choice.version ? ` ${choice.version}` : "";
|
|
95
|
+
if (choice.source === "explicit") return `${choice.name}${version} selected by --package-manager`;
|
|
96
|
+
if (choice.source === "environment") return `${choice.name}${version} detected from the invoking package manager`;
|
|
97
|
+
if (choice.source === "project") return `${choice.name}${version} detected from the current project`;
|
|
98
|
+
if (choice.source === "fallback") {
|
|
99
|
+
const detected = choice.detected ? ` after ${choice.detected} was unavailable` : "";
|
|
100
|
+
return `${choice.name}${version} selected as fallback${detected}`;
|
|
101
|
+
}
|
|
102
|
+
return `${choice.name}${version} selected as the default package manager`;
|
|
103
|
+
}
|
|
104
|
+
function normalizePackageManagerName(value) {
|
|
105
|
+
if (!value || value === "auto") return void 0;
|
|
106
|
+
const normalized = value.toLowerCase();
|
|
107
|
+
return packageManagers.includes(normalized) ? normalized : void 0;
|
|
108
|
+
}
|
|
109
|
+
async function readPackageManagerVersion(name) {
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
const child = spawn(name, ["--version"], {
|
|
112
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
113
|
+
shell: process.platform === "win32"
|
|
114
|
+
});
|
|
115
|
+
let output = "";
|
|
116
|
+
child.stdout.on("data", (chunk) => {
|
|
117
|
+
output += String(chunk);
|
|
118
|
+
});
|
|
119
|
+
child.stderr.on("data", (chunk) => {
|
|
120
|
+
output += String(chunk);
|
|
121
|
+
});
|
|
122
|
+
child.on("close", (code) => {
|
|
123
|
+
resolve(code === 0 ? output.trim().split(/\s+/)[0] : void 0);
|
|
124
|
+
});
|
|
125
|
+
child.on("error", () => {
|
|
126
|
+
resolve(void 0);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async function readPackageManagerField(packageJsonPath) {
|
|
131
|
+
try {
|
|
132
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
133
|
+
return normalizePackageManagerName(packageJson.packageManager?.split("@")[0]);
|
|
134
|
+
} catch {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function fileExists(file) {
|
|
139
|
+
try {
|
|
140
|
+
await readFile(file);
|
|
141
|
+
return true;
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function ancestors(cwd) {
|
|
147
|
+
const directories = [];
|
|
148
|
+
let current = path.resolve(cwd);
|
|
149
|
+
while (true) {
|
|
150
|
+
directories.push(current);
|
|
151
|
+
const parent = path.dirname(current);
|
|
152
|
+
if (parent === current) return directories;
|
|
153
|
+
current = parent;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function shellQuote(value) {
|
|
157
|
+
return /^[a-zA-Z0-9_./:@-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/result.ts
|
|
161
|
+
function ok(command, args, message) {
|
|
162
|
+
return { command, args, ok: true, message };
|
|
163
|
+
}
|
|
164
|
+
function fail(command, args, message) {
|
|
165
|
+
return { command, args, ok: false, message };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/utils.ts
|
|
169
|
+
import { spawn as spawn2 } from "child_process";
|
|
170
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
171
|
+
import path2 from "path";
|
|
172
|
+
async function spawnCommand(command, args, env = {}, options = {}) {
|
|
173
|
+
return new Promise((resolve) => {
|
|
174
|
+
const child = spawn2(command, args, {
|
|
175
|
+
stdio: "inherit",
|
|
176
|
+
shell: process.platform === "win32",
|
|
177
|
+
cwd: options.cwd,
|
|
178
|
+
env: { ...process.env, ...env }
|
|
179
|
+
});
|
|
180
|
+
child.on("close", (code) => {
|
|
181
|
+
resolve({
|
|
182
|
+
ok: code === 0,
|
|
183
|
+
message: `${command} ${args.join(" ")} exited with code ${code ?? 0}.`
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
child.on("error", (error) => {
|
|
187
|
+
resolve({ ok: false, message: error.message });
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function readOption(args, name) {
|
|
192
|
+
const inline = args.find((arg) => arg.startsWith(`${name}=`));
|
|
193
|
+
if (inline) return inline.slice(name.length + 1);
|
|
194
|
+
const index = args.indexOf(name);
|
|
195
|
+
return index >= 0 ? args[index + 1] : void 0;
|
|
196
|
+
}
|
|
197
|
+
function readOptions(args, name) {
|
|
198
|
+
const values = [];
|
|
199
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
200
|
+
const arg = args[index];
|
|
201
|
+
if (arg?.startsWith(`${name}=`)) {
|
|
202
|
+
values.push(arg.slice(name.length + 1));
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const next = args[index + 1];
|
|
206
|
+
if (arg === name && next) {
|
|
207
|
+
values.push(next);
|
|
208
|
+
index += 1;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return values;
|
|
212
|
+
}
|
|
213
|
+
function readCsvOptions(args, name) {
|
|
214
|
+
return readOptions(args, name).flatMap((value) => value.split(",").map((item) => item.trim()).filter(Boolean));
|
|
215
|
+
}
|
|
216
|
+
function readPositiveIntegerOption(args, name) {
|
|
217
|
+
const value = Number(readOption(args, name));
|
|
218
|
+
return Number.isInteger(value) && value > 0 ? value : void 0;
|
|
219
|
+
}
|
|
220
|
+
function readNonNegativeIntegerOption(args, name) {
|
|
221
|
+
const value = Number(readOption(args, name));
|
|
222
|
+
return Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
223
|
+
}
|
|
224
|
+
async function readOgFonts(args) {
|
|
225
|
+
const fontPaths = readOptions(args, "--font");
|
|
226
|
+
return Promise.all(
|
|
227
|
+
fontPaths.map(async (fontPath, index) => ({
|
|
228
|
+
name: index === 0 ? "Inter" : `SvedocsFont${index + 1}`,
|
|
229
|
+
data: await readFile2(path2.resolve(process.cwd(), fontPath)),
|
|
230
|
+
weight: 400,
|
|
231
|
+
style: "normal"
|
|
232
|
+
}))
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/commands/create.ts
|
|
237
|
+
function renderCreateSvedocsHelp() {
|
|
238
|
+
return [
|
|
239
|
+
"create-svedocs",
|
|
240
|
+
"",
|
|
241
|
+
"Usage:",
|
|
242
|
+
" create-svedocs [dir]",
|
|
243
|
+
"",
|
|
244
|
+
"Options:",
|
|
245
|
+
" --template <name> minimal, docs, or cloudflare",
|
|
246
|
+
" --package-manager <name>",
|
|
247
|
+
" auto, pnpm, npm, yarn, or bun",
|
|
248
|
+
" --pm <name> Alias for --package-manager",
|
|
249
|
+
" --install Install dependencies after creating the project",
|
|
250
|
+
" --force Allow writing into an existing directory"
|
|
251
|
+
].join("\n");
|
|
252
|
+
}
|
|
253
|
+
async function runCreateSvedocsCli(args, runtime = {}) {
|
|
254
|
+
if (args.includes("--help") || args.includes("-h")) return ok("help", args, renderCreateSvedocsHelp());
|
|
255
|
+
const program = new Command("create-svedocs").argument("[dir]", "target directory", "svedocs-app").option("--template <name>", "template name", "docs").option("--package-manager <name>", "package manager to use: auto, pnpm, npm, yarn, or bun", "auto").option("--pm <name>", "alias for --package-manager").option("--install", "install dependencies after creating the project", false).option("--force", "allow writing into an existing directory").exitOverride();
|
|
256
|
+
try {
|
|
257
|
+
program.parse(args, { from: "user" });
|
|
258
|
+
} catch (error) {
|
|
259
|
+
return fail("create", args, error instanceof Error ? error.message : String(error));
|
|
260
|
+
}
|
|
261
|
+
const target = program.args[0] ?? "svedocs-app";
|
|
262
|
+
const options = program.opts();
|
|
263
|
+
const template = options.template;
|
|
264
|
+
if (!["minimal", "docs", "cloudflare"].includes(template)) {
|
|
265
|
+
return fail("create", args, `Unknown template "${template}". Use minimal, docs, or cloudflare.`);
|
|
266
|
+
}
|
|
267
|
+
const source = await resolveTemplateSource(template);
|
|
268
|
+
const destination = path3.resolve(process.cwd(), target);
|
|
269
|
+
let packageManager;
|
|
270
|
+
try {
|
|
271
|
+
packageManager = await resolvePackageManager({
|
|
272
|
+
requested: options.pm ?? options.packageManager,
|
|
273
|
+
cwd: process.cwd(),
|
|
274
|
+
...runtime.env ? { env: runtime.env } : {},
|
|
275
|
+
...runtime.readPackageManagerVersion ? { readVersion: runtime.readPackageManagerVersion } : {}
|
|
276
|
+
});
|
|
277
|
+
} catch (error) {
|
|
278
|
+
return fail("create", args, error instanceof Error ? error.message : String(error));
|
|
279
|
+
}
|
|
280
|
+
if (!options.force && !await isEmptyOrMissingDirectory(destination)) {
|
|
281
|
+
return fail("create", args, `${destination} already exists and is not empty. Re-run with --force to merge template files.`);
|
|
282
|
+
}
|
|
283
|
+
await mkdir(destination, { recursive: true });
|
|
284
|
+
await cp(source, destination, { recursive: true, force: Boolean(options.force), errorOnExist: false });
|
|
285
|
+
await rewriteTemplatePackageJson(destination, target, packageManager);
|
|
286
|
+
let installMessage;
|
|
287
|
+
if (options.install) {
|
|
288
|
+
const installCommand = createInstallCommand(packageManager.name);
|
|
289
|
+
const result = await spawnCommand(installCommand[0], installCommand.slice(1), {}, { cwd: destination });
|
|
290
|
+
if (!result.ok) return fail("create", args, result.message);
|
|
291
|
+
installMessage = `Installed dependencies with ${packageManager.name}.`;
|
|
292
|
+
}
|
|
293
|
+
return ok("create", args, renderCreateSuccess({
|
|
294
|
+
destination,
|
|
295
|
+
packageManager,
|
|
296
|
+
template,
|
|
297
|
+
installed: Boolean(options.install),
|
|
298
|
+
...installMessage ? { installMessage } : {}
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
async function isEmptyOrMissingDirectory(directory) {
|
|
302
|
+
try {
|
|
303
|
+
return (await readdir(directory)).length === 0;
|
|
304
|
+
} catch {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function rewriteTemplatePackageJson(directory, target, packageManager) {
|
|
309
|
+
const packageJsonPath = path3.join(directory, "package.json");
|
|
310
|
+
const pkg = JSON.parse(await readFile3(packageJsonPath, "utf8"));
|
|
311
|
+
pkg.name = createPackageName(target);
|
|
312
|
+
const packageManagerField = createPackageManagerField(packageManager);
|
|
313
|
+
if (packageManagerField) pkg.packageManager = packageManagerField;
|
|
314
|
+
await writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}
|
|
315
|
+
`, "utf8");
|
|
316
|
+
}
|
|
317
|
+
function createPackageName(target) {
|
|
318
|
+
const name = path3.basename(target).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
319
|
+
return name || "svedocs-app";
|
|
320
|
+
}
|
|
321
|
+
async function resolveTemplateSource(template) {
|
|
322
|
+
const candidates = [
|
|
323
|
+
new URL(`../../templates/${template}`, import.meta.url),
|
|
324
|
+
new URL(`../templates/${template}`, import.meta.url)
|
|
325
|
+
].map((url) => fileURLToPath(url));
|
|
326
|
+
for (const candidate of candidates) {
|
|
327
|
+
try {
|
|
328
|
+
await access(candidate);
|
|
329
|
+
return candidate;
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
throw new Error(`Template "${template}" was not found. Checked: ${candidates.join(", ")}`);
|
|
334
|
+
}
|
|
335
|
+
function renderCreateSuccess(input) {
|
|
336
|
+
const relativeDestination = path3.relative(process.cwd(), input.destination) || ".";
|
|
337
|
+
const cdCommand = ["cd", relativeDestination.startsWith("..") ? input.destination : relativeDestination];
|
|
338
|
+
const installCommand = createInstallCommand(input.packageManager.name);
|
|
339
|
+
const devCommand = createRunCommand(input.packageManager.name, "dev");
|
|
340
|
+
const buildCommand = createRunCommand(input.packageManager.name, "build");
|
|
341
|
+
const ssgCommand = createRunCommand(input.packageManager.name, "build:ssg");
|
|
342
|
+
return [
|
|
343
|
+
`Created ${input.template} svedocs project at ${input.destination}`,
|
|
344
|
+
`Package manager: ${describePackageManagerSource(input.packageManager)}.`,
|
|
345
|
+
...input.installMessage ? [input.installMessage] : [],
|
|
346
|
+
"",
|
|
347
|
+
"Next steps:",
|
|
348
|
+
` ${formatCommand(cdCommand)}`,
|
|
349
|
+
...input.installed ? [] : [` ${formatCommand(installCommand)}`],
|
|
350
|
+
` ${formatCommand(devCommand)}`,
|
|
351
|
+
"",
|
|
352
|
+
"Build commands:",
|
|
353
|
+
` ${formatCommand(buildCommand)}`,
|
|
354
|
+
` ${formatCommand(ssgCommand)}`
|
|
355
|
+
].join("\n");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/commands/deploy.ts
|
|
359
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
360
|
+
import path5 from "path";
|
|
361
|
+
import { createCloudflareEnvDts, createWranglerJsonc, createWranglerToml } from "svedocs/cloudflare";
|
|
362
|
+
|
|
363
|
+
// src/project.ts
|
|
364
|
+
import { access as access2 } from "fs/promises";
|
|
365
|
+
import { createRequire } from "module";
|
|
366
|
+
import path4 from "path";
|
|
367
|
+
import { pathToFileURL } from "url";
|
|
368
|
+
import { loadSvedocsContent } from "svedocs/core";
|
|
369
|
+
var defaultConfigFiles = [
|
|
370
|
+
"svedocs.config.ts",
|
|
371
|
+
"svedocs.config.mts",
|
|
372
|
+
"svedocs.config.js",
|
|
373
|
+
"svedocs.config.mjs"
|
|
374
|
+
];
|
|
375
|
+
async function loadProjectManifest(options = {}) {
|
|
376
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
377
|
+
const config = await loadProjectConfig(projectRoot, options.configFile);
|
|
378
|
+
const merged = mergeSvedocsConfig(config, options.configOverrides);
|
|
379
|
+
return loadSvedocsContent(merged ? { projectRoot, config: merged } : { projectRoot });
|
|
380
|
+
}
|
|
381
|
+
async function loadProjectConfig(projectRoot = process.cwd(), configFile) {
|
|
382
|
+
const resolvedConfigFile = configFile ? path4.resolve(projectRoot, configFile) : await findConfigFile(projectRoot);
|
|
383
|
+
if (!resolvedConfigFile) return void 0;
|
|
384
|
+
try {
|
|
385
|
+
return await loadConfigWithVite(projectRoot, resolvedConfigFile);
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (!/\.[cm]?js$/.test(resolvedConfigFile)) {
|
|
388
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
389
|
+
throw new Error(`Failed to load ${path4.relative(projectRoot, resolvedConfigFile)}. Install project dependencies first so Vite can load TypeScript config files. ${message}`);
|
|
390
|
+
}
|
|
391
|
+
const module = await import(pathToFileURL(resolvedConfigFile).href);
|
|
392
|
+
return module.default ?? module.config;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async function findConfigFile(projectRoot) {
|
|
396
|
+
for (const file of defaultConfigFiles) {
|
|
397
|
+
const candidate = path4.join(projectRoot, file);
|
|
398
|
+
if (await fileExists2(candidate)) return candidate;
|
|
399
|
+
}
|
|
400
|
+
return void 0;
|
|
401
|
+
}
|
|
402
|
+
async function loadConfigWithVite(projectRoot, configFile) {
|
|
403
|
+
const require2 = createRequire(path4.join(projectRoot, "package.json"));
|
|
404
|
+
const vitePath = require2.resolve("vite");
|
|
405
|
+
const vite = await import(pathToFileURL(vitePath).href);
|
|
406
|
+
const loaded = await vite.loadConfigFromFile(
|
|
407
|
+
{
|
|
408
|
+
command: "serve",
|
|
409
|
+
mode: process.env.NODE_ENV ?? "production"
|
|
410
|
+
},
|
|
411
|
+
configFile,
|
|
412
|
+
projectRoot,
|
|
413
|
+
"silent"
|
|
414
|
+
);
|
|
415
|
+
const config = loaded?.config;
|
|
416
|
+
if (!config || typeof config !== "object") {
|
|
417
|
+
throw new Error(`No config object was exported from ${configFile}.`);
|
|
418
|
+
}
|
|
419
|
+
return config;
|
|
420
|
+
}
|
|
421
|
+
function mergeSvedocsConfig(base, overrides) {
|
|
422
|
+
if (!base) return overrides;
|
|
423
|
+
if (!overrides) return base;
|
|
424
|
+
const site = mergeObject(base.site, overrides.site);
|
|
425
|
+
const content = mergeObject(base.content, overrides.content);
|
|
426
|
+
const build = mergeObject(base.build, overrides.build);
|
|
427
|
+
const theme = mergeObject(base.theme, overrides.theme);
|
|
428
|
+
const palette = mergeObject(base.theme?.palette, overrides.theme?.palette);
|
|
429
|
+
const fonts = mergeObject(base.theme?.fonts, overrides.theme?.fonts);
|
|
430
|
+
const codeTheme = mergeObject(
|
|
431
|
+
typeof base.theme?.codeTheme === "object" ? base.theme.codeTheme : void 0,
|
|
432
|
+
typeof overrides.theme?.codeTheme === "object" ? overrides.theme.codeTheme : void 0
|
|
433
|
+
);
|
|
434
|
+
const markdown = mergeObject(base.markdown, overrides.markdown);
|
|
435
|
+
const seo = mergeObject(base.seo, overrides.seo);
|
|
436
|
+
const source = mergeObject(base.source, overrides.source);
|
|
437
|
+
const checks = mergeObject(base.checks, overrides.checks);
|
|
438
|
+
const cloudflare = mergeObject(base.cloudflare, overrides.cloudflare);
|
|
439
|
+
const aiSearch = mergeObject(base.cloudflare?.aiSearch, overrides.cloudflare?.aiSearch);
|
|
440
|
+
const search = overrides.search ?? base.search;
|
|
441
|
+
const ai = overrides.ai ?? base.ai;
|
|
442
|
+
const i18n = overrides.i18n ?? base.i18n;
|
|
443
|
+
return {
|
|
444
|
+
...base,
|
|
445
|
+
...overrides,
|
|
446
|
+
...site ? { site } : {},
|
|
447
|
+
...content ? { content } : {},
|
|
448
|
+
...build ? { build } : {},
|
|
449
|
+
...theme ? {
|
|
450
|
+
theme: {
|
|
451
|
+
...theme,
|
|
452
|
+
...palette ? { palette } : {},
|
|
453
|
+
...fonts ? { fonts } : {},
|
|
454
|
+
...codeTheme ? { codeTheme } : {}
|
|
455
|
+
}
|
|
456
|
+
} : {},
|
|
457
|
+
...markdown ? { markdown } : {},
|
|
458
|
+
...seo ? { seo } : {},
|
|
459
|
+
...source ? { source } : {},
|
|
460
|
+
...checks ? { checks } : {},
|
|
461
|
+
...cloudflare ? { cloudflare: { ...cloudflare, ...aiSearch ? { aiSearch } : {} } } : {},
|
|
462
|
+
...search !== void 0 ? { search } : {},
|
|
463
|
+
...ai !== void 0 ? { ai } : {},
|
|
464
|
+
...i18n !== void 0 ? { i18n } : {}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function mergeObject(base, overrides) {
|
|
468
|
+
if (!base && !overrides) return void 0;
|
|
469
|
+
return {
|
|
470
|
+
...base ?? {},
|
|
471
|
+
...overrides ?? {}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
async function fileExists2(filePath) {
|
|
475
|
+
try {
|
|
476
|
+
await access2(filePath);
|
|
477
|
+
return true;
|
|
478
|
+
} catch {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/commands/deploy.ts
|
|
484
|
+
async function runDeployCommand(args) {
|
|
485
|
+
const target = args[0] ?? "cloudflare";
|
|
486
|
+
if (target !== "cloudflare") {
|
|
487
|
+
return fail("deploy", args, `Unknown deploy target "${target}". Use cloudflare.`);
|
|
488
|
+
}
|
|
489
|
+
const write = args.includes("--write");
|
|
490
|
+
const format = readOption(args, "--format") ?? "toml";
|
|
491
|
+
if (!["toml", "jsonc"].includes(format)) {
|
|
492
|
+
return fail("deploy", args, "Invalid Cloudflare config format. Use toml or jsonc.");
|
|
493
|
+
}
|
|
494
|
+
const manifest = await loadProjectManifest({ configFile: readOption(args, "--config") });
|
|
495
|
+
const wrangler = format === "jsonc" ? createWranglerJsonc(manifest.config) : createWranglerToml(manifest.config);
|
|
496
|
+
const wranglerFile = format === "jsonc" ? "wrangler.jsonc" : "wrangler.toml";
|
|
497
|
+
const envDts = createCloudflareEnvDts(manifest.config);
|
|
498
|
+
if (write) {
|
|
499
|
+
await mkdir2(path5.resolve(process.cwd(), "src"), { recursive: true });
|
|
500
|
+
await writeFile2(path5.resolve(process.cwd(), wranglerFile), wrangler, "utf8");
|
|
501
|
+
await writeFile2(path5.resolve(process.cwd(), "src/app.cloudflare.d.ts"), envDts, "utf8");
|
|
502
|
+
return ok("deploy", args, `Wrote ${wranglerFile} and src/app.cloudflare.d.ts for Cloudflare Pages.`);
|
|
503
|
+
}
|
|
504
|
+
return ok("deploy", args, [
|
|
505
|
+
"Cloudflare deploy dry-run passed.",
|
|
506
|
+
"",
|
|
507
|
+
`${wranglerFile}:`,
|
|
508
|
+
wrangler.trim(),
|
|
509
|
+
"",
|
|
510
|
+
`Run with --write to create ${wranglerFile} and Cloudflare platform types.`
|
|
511
|
+
].join("\n"));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/index.ts
|
|
515
|
+
function renderSvedocsHelp() {
|
|
516
|
+
return [
|
|
517
|
+
"svedocs",
|
|
518
|
+
"",
|
|
519
|
+
"Commands:",
|
|
520
|
+
" create Create a new svedocs project",
|
|
521
|
+
" dev Start the development server",
|
|
522
|
+
" build Build the documentation site",
|
|
523
|
+
" ssg Build a static SSG site",
|
|
524
|
+
" preview Preview the built site",
|
|
525
|
+
" check Check project configuration and content",
|
|
526
|
+
" index Print or write search index records",
|
|
527
|
+
" og Generate Open Graph assets",
|
|
528
|
+
" deploy Deploy helpers and provider checks",
|
|
529
|
+
"",
|
|
530
|
+
"Global options:",
|
|
531
|
+
" --config <path> Use a specific svedocs config file",
|
|
532
|
+
"",
|
|
533
|
+
"Build options:",
|
|
534
|
+
" --mode <mode> Build mode: edge, static, or spa",
|
|
535
|
+
" --no-og Skip automatic static OG asset generation",
|
|
536
|
+
"",
|
|
537
|
+
"Check options:",
|
|
538
|
+
" --strict Treat warnings as failures",
|
|
539
|
+
" --external-links Verify http(s) links with HEAD requests",
|
|
540
|
+
" --no-assets Skip local asset existence checks",
|
|
541
|
+
" --translations Warn when configured locales are missing doc pages",
|
|
542
|
+
" --package Validate package files and exports",
|
|
543
|
+
"",
|
|
544
|
+
"Use --help on a command for more details."
|
|
545
|
+
].join("\n");
|
|
546
|
+
}
|
|
547
|
+
async function runSvedocsCli(args) {
|
|
548
|
+
const command = args[0] ?? "help";
|
|
549
|
+
if (command === "help" || command === "--help" || command === "-h") return ok("help", args, renderSvedocsHelp());
|
|
550
|
+
if (command === "create") return runCreateSvedocsCli(args.slice(1));
|
|
551
|
+
if (command === "dev") return runViteCommand("dev", args.slice(1));
|
|
552
|
+
if (command === "preview") return runViteCommand("preview", args.slice(1));
|
|
553
|
+
if (command === "build") return runBuildCommand(args.slice(1));
|
|
554
|
+
if (command === "ssg") return runBuildCommand(["--mode", "static", ...args.slice(1)]);
|
|
555
|
+
if (command === "check") return runCheckCommand(args.slice(1));
|
|
556
|
+
if (command === "index") return runIndexCommand(args.slice(1));
|
|
557
|
+
if (command === "og") return runOgCommand(args.slice(1));
|
|
558
|
+
if (command === "deploy") return runDeployCommand(args.slice(1));
|
|
559
|
+
return fail(command, args, `Unknown command "${command}".
|
|
560
|
+
|
|
561
|
+
${renderSvedocsHelp()}`);
|
|
562
|
+
}
|
|
563
|
+
async function runBuildCommand(args) {
|
|
564
|
+
const configFile = readOption(args, "--config");
|
|
565
|
+
const config = loadSvedocsConfig(await loadProjectConfig(process.cwd(), configFile) ?? {});
|
|
566
|
+
const mode = readOption(args, "--mode") ?? process.env.SVEDOCS_BUILD_MODE ?? config.build.mode;
|
|
567
|
+
if (!mode || !["edge", "static", "spa"].includes(mode)) {
|
|
568
|
+
return fail("build", args, "Invalid build mode. Use edge, static, or spa.");
|
|
569
|
+
}
|
|
570
|
+
const warning = mode === "spa" ? "SPA mode prerenders known docs pages and writes a static fallback; hosted Search, Ask AI, and other server-only features need an edge runtime.\n" : "";
|
|
571
|
+
const result = await spawnCommand("vite", ["build", ...createViteArgs(args)], {
|
|
572
|
+
SVEDOCS_BUILD_MODE: mode,
|
|
573
|
+
...createConfigEnv(configFile)
|
|
574
|
+
});
|
|
575
|
+
if (!result.ok) return fail("build", args, `${warning}${result.message}`);
|
|
576
|
+
const ogResult = await runConfiguredOgGeneration(configFile, args, config.seo.ogImage);
|
|
577
|
+
const message = ogResult ? `${result.message}
|
|
578
|
+
${ogResult.message}` : result.message;
|
|
579
|
+
return ok("build", args, `${warning}${message}`);
|
|
580
|
+
}
|
|
581
|
+
async function runViteCommand(command, args) {
|
|
582
|
+
const configFile = readOption(args, "--config");
|
|
583
|
+
const result = await spawnCommand("vite", [command, ...createViteArgs(args)], createConfigEnv(configFile));
|
|
584
|
+
return result.ok ? ok(command, args, result.message) : fail(command, args, result.message);
|
|
585
|
+
}
|
|
586
|
+
async function runCheckCommand(args) {
|
|
587
|
+
const manifest = await loadProjectManifest({
|
|
588
|
+
configFile: readOption(args, "--config"),
|
|
589
|
+
configOverrides: createCheckConfigOverrides(args)
|
|
590
|
+
});
|
|
591
|
+
const strict = args.includes("--strict");
|
|
592
|
+
const packageIssues = args.includes("--package") ? await checkPackagePublication(process.cwd()) : [];
|
|
593
|
+
const allIssues = [...manifest.issues, ...packageIssues];
|
|
594
|
+
const allErrors = allIssues.filter((issue) => issue.severity === "error");
|
|
595
|
+
const allWarnings = allIssues.filter((issue) => issue.severity === "warning");
|
|
596
|
+
const summary = `svedocs check: ${manifest.pages.length} pages, ${manifest.search.length} search records, ${allErrors.length} errors, ${allWarnings.length} warnings.`;
|
|
597
|
+
const details = allIssues.map((issue) => `${issue.severity.toUpperCase()} ${issue.code}: ${issue.message}`);
|
|
598
|
+
const message = [summary, ...details].join("\n");
|
|
599
|
+
if (allErrors.length > 0 || strict && allWarnings.length > 0) {
|
|
600
|
+
return fail("check", args, message);
|
|
601
|
+
}
|
|
602
|
+
return ok("check", args, message);
|
|
603
|
+
}
|
|
604
|
+
function createCheckConfigOverrides(args) {
|
|
605
|
+
const checks = {};
|
|
606
|
+
if (args.includes("--external-links")) checks.externalLinks = true;
|
|
607
|
+
if (args.includes("--no-assets")) checks.assets = false;
|
|
608
|
+
if (args.includes("--translations")) checks.translations = true;
|
|
609
|
+
return Object.keys(checks).length ? { checks } : void 0;
|
|
610
|
+
}
|
|
611
|
+
function createViteArgs(args) {
|
|
612
|
+
const output = [];
|
|
613
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
614
|
+
const arg = args[index];
|
|
615
|
+
if (!arg) continue;
|
|
616
|
+
if (arg === "--") {
|
|
617
|
+
output.push(...args.slice(index + 1));
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
if (arg === "--no-og") continue;
|
|
621
|
+
if (arg === "--mode" || arg === "--config") {
|
|
622
|
+
index += 1;
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
if (arg.startsWith("--mode=") || arg.startsWith("--config=")) continue;
|
|
626
|
+
output.push(arg);
|
|
627
|
+
}
|
|
628
|
+
return output;
|
|
629
|
+
}
|
|
630
|
+
function createConfigEnv(configFile) {
|
|
631
|
+
return configFile ? { SVEDOCS_CONFIG_FILE: configFile } : {};
|
|
632
|
+
}
|
|
633
|
+
async function runIndexCommand(args) {
|
|
634
|
+
const manifest = await loadProjectManifest({ configFile: readOption(args, "--config") });
|
|
635
|
+
const out = readOption(args, "--out");
|
|
636
|
+
const format = readOption(args, "--format") ?? "json";
|
|
637
|
+
const provider = readOption(args, "--provider") ?? manifest.config.search.provider;
|
|
638
|
+
if (!["json", "jsonl"].includes(format)) {
|
|
639
|
+
return fail("index", args, "Invalid index format. Use json or jsonl.");
|
|
640
|
+
}
|
|
641
|
+
const payload = format === "jsonl" ? manifest.search.map((record) => JSON.stringify(record)).join("\n") : JSON.stringify(manifest.search, null, 2);
|
|
642
|
+
if (provider === "cloudflare-ai-search" && !out) {
|
|
643
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
644
|
+
const apiToken = process.env.CLOUDFLARE_API_TOKEN;
|
|
645
|
+
const namespace = readOption(args, "--namespace");
|
|
646
|
+
const strategy = readOption(args, "--strategy") ?? "append";
|
|
647
|
+
if (!["append", "replace"].includes(strategy)) {
|
|
648
|
+
return fail("index", args, "Invalid Cloudflare AI Search strategy. Use append or replace.");
|
|
649
|
+
}
|
|
650
|
+
const existingIds = readCsvOptions(args, "--existing");
|
|
651
|
+
const deleteIds = readOptions(args, "--delete");
|
|
652
|
+
const result = await syncCloudflareAiSearchIndex({
|
|
653
|
+
records: manifest.search,
|
|
654
|
+
instanceName: readOption(args, "--instance") ?? manifest.config.cloudflare.aiSearch.instanceName,
|
|
655
|
+
...accountId ? { accountId } : {},
|
|
656
|
+
...apiToken ? { apiToken } : {},
|
|
657
|
+
...namespace ? { namespace } : {},
|
|
658
|
+
...args.includes("--dry-run") ? { dryRun: true } : {},
|
|
659
|
+
...args.includes("--wait") ? { waitForCompletion: true } : {},
|
|
660
|
+
strategy,
|
|
661
|
+
batchSize: readPositiveIntegerOption(args, "--batch-size") ?? 10,
|
|
662
|
+
maxRetries: readNonNegativeIntegerOption(args, "--retries") ?? 2,
|
|
663
|
+
...existingIds.length ? { existingIds } : {},
|
|
664
|
+
...deleteIds.length ? { deleteIds } : {}
|
|
665
|
+
});
|
|
666
|
+
const details = result.errors.map((error) => `ERROR ${error.id}: ${error.message}`);
|
|
667
|
+
const message = [
|
|
668
|
+
result.dryRun ? `Cloudflare AI Search dry-run: ${result.indexed} uploads and ${result.deleted} deletes planned for ${result.instanceName}.` : `Cloudflare AI Search indexed ${result.indexed} records and deleted ${result.deleted} items for ${result.instanceName}; ${result.failed} failed.`,
|
|
669
|
+
`Strategy: ${strategy}`,
|
|
670
|
+
`Endpoint: ${result.endpoint}`,
|
|
671
|
+
...result.dryRun ? ["Set CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN to upload records."] : [],
|
|
672
|
+
...details
|
|
673
|
+
].join("\n");
|
|
674
|
+
return result.failed > 0 ? fail("index", args, message) : ok("index", args, message);
|
|
675
|
+
}
|
|
676
|
+
if (out) {
|
|
677
|
+
const destination = path6.resolve(process.cwd(), out);
|
|
678
|
+
await mkdir3(path6.dirname(destination), { recursive: true });
|
|
679
|
+
await writeFile3(destination, `${payload}
|
|
680
|
+
`, "utf8");
|
|
681
|
+
return ok("index", args, `Indexed ${manifest.search.length} records for ${provider} at ${destination}`);
|
|
682
|
+
}
|
|
683
|
+
return ok("index", args, payload);
|
|
684
|
+
}
|
|
685
|
+
async function runOgCommand(args) {
|
|
686
|
+
const manifest = await loadProjectManifest({ configFile: readOption(args, "--config") });
|
|
687
|
+
const out = path6.resolve(process.cwd(), readOption(args, "--out") ?? (manifest.config.seo.ogImage === false ? "static/og" : manifest.config.seo.ogImage.outDir));
|
|
688
|
+
const format = readOption(args, "--format") ?? (manifest.config.seo.ogImage === false ? "svg" : manifest.config.seo.ogImage.format);
|
|
689
|
+
const renderer = readOption(args, "--renderer") ?? (manifest.config.seo.ogImage === false ? "svg" : manifest.config.seo.ogImage.renderer);
|
|
690
|
+
if (!["svg", "png"].includes(format)) {
|
|
691
|
+
return fail("og", args, "Invalid OG format. Use svg or png.");
|
|
692
|
+
}
|
|
693
|
+
if (!["svg", "satori"].includes(renderer)) {
|
|
694
|
+
return fail("og", args, "Invalid OG renderer. Use svg or satori.");
|
|
695
|
+
}
|
|
696
|
+
const fonts = await readOgFonts(args);
|
|
697
|
+
if (renderer === "satori" && fonts.length === 0) {
|
|
698
|
+
return fail("og", args, "Satori OG rendering requires at least one --font path.");
|
|
699
|
+
}
|
|
700
|
+
const configFile = readOption(args, "--config");
|
|
701
|
+
return generateOgAssets({
|
|
702
|
+
args,
|
|
703
|
+
out,
|
|
704
|
+
format,
|
|
705
|
+
renderer,
|
|
706
|
+
fonts,
|
|
707
|
+
...configFile ? { configFile } : {}
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
async function runConfiguredOgGeneration(configFile, buildArgs, ogConfig) {
|
|
711
|
+
if (buildArgs.includes("--no-og")) return void 0;
|
|
712
|
+
if (ogConfig === false) return void 0;
|
|
713
|
+
if (ogConfig.renderer === "satori") return void 0;
|
|
714
|
+
const manifest = await loadProjectManifest({ configFile });
|
|
715
|
+
return generateOgAssets({
|
|
716
|
+
args: ["og", "--auto"],
|
|
717
|
+
out: path6.resolve(process.cwd(), ogConfig.outDir),
|
|
718
|
+
format: ogConfig.format,
|
|
719
|
+
renderer: ogConfig.renderer,
|
|
720
|
+
fonts: [],
|
|
721
|
+
...configFile ? { configFile } : {},
|
|
722
|
+
manifest
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
async function generateOgAssets(input) {
|
|
726
|
+
const manifest = input.manifest ?? await loadProjectManifest({ configFile: input.configFile });
|
|
727
|
+
const { out, format, renderer, fonts, args } = input;
|
|
728
|
+
await mkdir3(out, { recursive: true });
|
|
729
|
+
const written = [];
|
|
730
|
+
for (const page of manifest.pages) {
|
|
731
|
+
if (page.hidden) continue;
|
|
732
|
+
const fileName = `${page.routePath === "/" ? "index" : page.routePath.replace(/^\/+/, "").replace(/[^a-zA-Z0-9]+/g, "-")}.${format}`;
|
|
733
|
+
const destination = path6.join(out, fileName);
|
|
734
|
+
const asset = await createOgImage(createOgImageInput(manifest.config, page), {
|
|
735
|
+
format,
|
|
736
|
+
renderer,
|
|
737
|
+
...manifest.config.seo.ogImage !== false && typeof manifest.config.seo.ogImage.template === "function" ? { template: manifest.config.seo.ogImage.template } : {},
|
|
738
|
+
...fonts.length ? { fonts } : {}
|
|
739
|
+
});
|
|
740
|
+
await writeFile3(destination, asset);
|
|
741
|
+
written.push(destination);
|
|
742
|
+
}
|
|
743
|
+
return ok("og", args, `Generated ${written.length} OG ${format.toUpperCase()} files with ${renderer} renderer in ${out}`);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export {
|
|
747
|
+
renderCreateSvedocsHelp,
|
|
748
|
+
runCreateSvedocsCli,
|
|
749
|
+
renderSvedocsHelp,
|
|
750
|
+
runSvedocsCli
|
|
751
|
+
};
|