create-ardo 3.1.0 → 3.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/dist/index.js
CHANGED
|
@@ -1,329 +1,311 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import path2 from "path";
|
|
2
|
+
import { blue, cyan, dim, green, red, reset, yellow } from "kolorist";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
6
5
|
import prompts from "prompts";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import path from "path";
|
|
12
|
-
import { fileURLToPath } from "url";
|
|
13
|
-
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
var templatesRoot = path.resolve(__dirname, "..", "templates");
|
|
15
|
-
var packageJsonPath = path.resolve(__dirname, "..", "package.json");
|
|
6
|
+
//#region src/scaffold.ts
|
|
7
|
+
const __dirname = import.meta.dirname;
|
|
8
|
+
const templatesRoot = path.resolve(__dirname, "..", "templates");
|
|
9
|
+
const packageJsonPath = path.resolve(__dirname, "..", "package.json");
|
|
16
10
|
function getCliVersion() {
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
const pkg = readJsonObject(packageJsonPath);
|
|
12
|
+
return typeof pkg?.version === "string" ? pkg.version : "0.0.0";
|
|
19
13
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
];
|
|
14
|
+
const templates = [{
|
|
15
|
+
name: "minimal",
|
|
16
|
+
display: "Minimal",
|
|
17
|
+
description: "Basic setup with essential files only"
|
|
18
|
+
}];
|
|
27
19
|
function createProjectStructure(root, template, options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
TYPEDOC_NAV: options.typedoc ? "{ text: 'API', link: '/api-reference' }," : "",
|
|
39
|
-
TYPEDOC_SIDEBAR: options.typedoc ? "{ text: 'API Reference', link: '/api-reference' }," : ""
|
|
40
|
-
};
|
|
41
|
-
copyDir(templateDir, root, vars);
|
|
20
|
+
copyDir(path.join(templatesRoot, template), root, {
|
|
21
|
+
SITE_TITLE: options.siteTitle,
|
|
22
|
+
PROJECT_NAME: options.projectName,
|
|
23
|
+
ARDO_VERSION: getCliVersion(),
|
|
24
|
+
TYPEDOC_CONFIG: options.typedoc ? "typedoc: true," : "// typedoc: true, // Uncomment to enable API docs",
|
|
25
|
+
GITHUB_PAGES_CONFIG: options.githubPages ? "// GitHub Pages: base path auto-detected from git remote" : "githubPages: false, // Disabled for non-GitHub Pages deployment",
|
|
26
|
+
GITHUB_PAGES_BASENAME_IMPORT: options.githubPages ? "import { detectGitHubBasename } from \"ardo/vite\"" : "",
|
|
27
|
+
GITHUB_PAGES_BASENAME: options.githubPages ? "basename: detectGitHubBasename()," : "// basename: detectGitHubBasename(), // Uncomment for GitHub Pages",
|
|
28
|
+
DESCRIPTION: options.description
|
|
29
|
+
});
|
|
42
30
|
}
|
|
43
31
|
function copyDir(src, dest, vars) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
fs.writeFileSync(destPath, content);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
32
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
33
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
34
|
+
const srcPath = path.join(src, entry.name);
|
|
35
|
+
const destName = entry.name === "_gitignore" ? ".gitignore" : entry.name;
|
|
36
|
+
const destPath = path.join(dest, destName);
|
|
37
|
+
if (entry.isDirectory()) copyDir(srcPath, destPath, vars);
|
|
38
|
+
else {
|
|
39
|
+
let content = fs.readFileSync(srcPath, "utf8");
|
|
40
|
+
for (const [key, value] of Object.entries(vars)) content = content.replaceAll(`{{${key}}}`, value);
|
|
41
|
+
fs.writeFileSync(destPath, content);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
59
44
|
}
|
|
60
45
|
function formatTargetDir(targetDir) {
|
|
61
|
-
|
|
46
|
+
if (targetDir === void 0) return;
|
|
47
|
+
let normalized = targetDir.trim();
|
|
48
|
+
while (normalized.endsWith("/")) normalized = normalized.slice(0, -1);
|
|
49
|
+
return normalized;
|
|
62
50
|
}
|
|
63
51
|
function isEmpty(dirPath) {
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
const files = fs.readdirSync(dirPath);
|
|
53
|
+
return files.length === 0 || files.length === 1 && files[0] === ".git";
|
|
66
54
|
}
|
|
67
55
|
function emptyDir(dir) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
56
|
+
if (!fs.existsSync(dir)) return;
|
|
57
|
+
for (const file of fs.readdirSync(dir)) {
|
|
58
|
+
if (file === ".git") continue;
|
|
59
|
+
fs.rmSync(path.join(dir, file), {
|
|
60
|
+
recursive: true,
|
|
61
|
+
force: true
|
|
62
|
+
});
|
|
63
|
+
}
|
|
77
64
|
}
|
|
78
65
|
function isValidTemplate(template) {
|
|
79
|
-
|
|
66
|
+
return templates.some((t) => t.name === template);
|
|
80
67
|
}
|
|
81
68
|
function detectProjectDescription(targetDir) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return pkg.description;
|
|
88
|
-
}
|
|
89
|
-
} catch {
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return void 0;
|
|
69
|
+
for (const dir of [path.dirname(targetDir), targetDir]) {
|
|
70
|
+
const pkg = readJsonObject(path.join(dir, "package.json"));
|
|
71
|
+
if (pkg === void 0) continue;
|
|
72
|
+
if (typeof pkg.description === "string" && pkg.description !== "") return pkg.description;
|
|
73
|
+
}
|
|
93
74
|
}
|
|
94
75
|
function isArdoProject(dir) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
76
|
+
const pkg = readJsonObject(path.join(dir, "package.json"));
|
|
77
|
+
if (pkg === void 0) return false;
|
|
78
|
+
return toStringRecord(pkg.dependencies)?.ardo !== void 0;
|
|
79
|
+
}
|
|
80
|
+
function copySkeletonFiles(root, templateDir, result) {
|
|
81
|
+
for (const file of [
|
|
82
|
+
"app/entry.client.tsx",
|
|
83
|
+
"app/entry.server.tsx",
|
|
84
|
+
"app/root.tsx",
|
|
85
|
+
"tsconfig.json"
|
|
86
|
+
]) {
|
|
87
|
+
const src = path.join(templateDir, file);
|
|
88
|
+
const dest = path.join(root, file);
|
|
89
|
+
if (fs.existsSync(src)) {
|
|
90
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
91
|
+
fs.copyFileSync(src, dest);
|
|
92
|
+
result.updated.push(file);
|
|
93
|
+
} else result.skipped.push(file);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function mergePackageJson(root, templateDir, cliVersion) {
|
|
97
|
+
const userPkgPath = path.join(root, "package.json");
|
|
98
|
+
const userPkg = readJsonObject(userPkgPath);
|
|
99
|
+
const templatePkg = readJsonObject(path.join(templateDir, "package.json"));
|
|
100
|
+
if (userPkg === void 0 || templatePkg === void 0) throw new Error("Could not parse package.json while upgrading project");
|
|
101
|
+
const userDeps = ensureStringRecord(userPkg, "dependencies");
|
|
102
|
+
const userDevDeps = ensureStringRecord(userPkg, "devDependencies");
|
|
103
|
+
const templateDeps = toStringRecord(templatePkg.dependencies) ?? {};
|
|
104
|
+
const templateDevDeps = toStringRecord(templatePkg.devDependencies) ?? {};
|
|
105
|
+
userDeps.ardo = `^${cliVersion}`;
|
|
106
|
+
for (const [dep, version] of Object.entries(templateDeps)) if (dep !== "ardo" && !(dep in userDeps)) userDeps[dep] = version;
|
|
107
|
+
for (const [dep, version] of Object.entries(templateDevDeps)) userDevDeps[dep] = version;
|
|
108
|
+
fs.writeFileSync(userPkgPath, `${JSON.stringify(userPkg, null, 2)}\n`);
|
|
109
|
+
}
|
|
110
|
+
function deleteObsoleteFiles(root, result) {
|
|
111
|
+
for (const file of ["app/vite-env.d.ts"]) {
|
|
112
|
+
const filePath = path.join(root, file);
|
|
113
|
+
if (fs.existsSync(filePath)) {
|
|
114
|
+
fs.unlinkSync(filePath);
|
|
115
|
+
result.deleted.push(file);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
102
118
|
}
|
|
103
119
|
function upgradeProject(root) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
userPkg.dependencies[dep] = version;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
for (const [dep, version] of Object.entries(
|
|
141
|
-
templatePkg.devDependencies || {}
|
|
142
|
-
)) {
|
|
143
|
-
userPkg.devDependencies = userPkg.devDependencies || {};
|
|
144
|
-
userPkg.devDependencies[dep] = version;
|
|
145
|
-
}
|
|
146
|
-
fs.writeFileSync(userPkgPath, JSON.stringify(userPkg, null, 2) + "\n");
|
|
147
|
-
result.updated.push("package.json");
|
|
148
|
-
const obsoleteFiles = ["app/vite-env.d.ts"];
|
|
149
|
-
for (const file of obsoleteFiles) {
|
|
150
|
-
const filePath = path.join(root, file);
|
|
151
|
-
if (fs.existsSync(filePath)) {
|
|
152
|
-
fs.unlinkSync(filePath);
|
|
153
|
-
result.deleted.push(file);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return result;
|
|
120
|
+
const templateDir = path.join(templatesRoot, "minimal");
|
|
121
|
+
const result = {
|
|
122
|
+
updated: [],
|
|
123
|
+
deleted: [],
|
|
124
|
+
skipped: []
|
|
125
|
+
};
|
|
126
|
+
copySkeletonFiles(root, templateDir, result);
|
|
127
|
+
mergePackageJson(root, templateDir, getCliVersion());
|
|
128
|
+
result.updated.push("package.json");
|
|
129
|
+
deleteObsoleteFiles(root, result);
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
function ensureStringRecord(object, key) {
|
|
133
|
+
const existing = toStringRecord(object[key]);
|
|
134
|
+
if (existing !== void 0) {
|
|
135
|
+
object[key] = existing;
|
|
136
|
+
return existing;
|
|
137
|
+
}
|
|
138
|
+
const created = {};
|
|
139
|
+
object[key] = created;
|
|
140
|
+
return created;
|
|
141
|
+
}
|
|
142
|
+
function readJsonObject(filePath) {
|
|
143
|
+
try {
|
|
144
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
145
|
+
const parsed = JSON.parse(raw);
|
|
146
|
+
return isJsonObject(parsed) ? parsed : void 0;
|
|
147
|
+
} catch {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function isJsonObject(value) {
|
|
152
|
+
return typeof value === "object" && value !== null;
|
|
157
153
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
154
|
+
function toStringRecord(value) {
|
|
155
|
+
if (!isJsonObject(value)) return;
|
|
156
|
+
const entries = Object.entries(value);
|
|
157
|
+
const result = {};
|
|
158
|
+
for (const [key, entryValue] of entries) if (typeof entryValue === "string") result[key] = entryValue;
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/index.ts
|
|
163
|
+
const defaultTargetDir = "my-docs";
|
|
164
|
+
const onCancel = () => {
|
|
165
|
+
throw new Error(`${red("✖")} Operation cancelled`);
|
|
163
166
|
};
|
|
167
|
+
async function promptProjectName() {
|
|
168
|
+
const projectName = (await prompts({
|
|
169
|
+
type: "text",
|
|
170
|
+
name: "projectName",
|
|
171
|
+
message: reset("Project name:"),
|
|
172
|
+
initial: defaultTargetDir,
|
|
173
|
+
validate: (value) => {
|
|
174
|
+
const name = value.trim();
|
|
175
|
+
if (!name) return "Project name is required";
|
|
176
|
+
if (/^[.-]/.test(name)) return "Project name cannot start with a dot or hyphen";
|
|
177
|
+
if (!/^[\da-z-]+$/.test(name)) return "Project name may only contain lowercase letters, digits, and hyphens";
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
}, { onCancel })).projectName;
|
|
181
|
+
return formatTargetDir(typeof projectName === "string" ? projectName : defaultTargetDir) ?? defaultTargetDir;
|
|
182
|
+
}
|
|
183
|
+
async function runUpgradeFlow(root) {
|
|
184
|
+
if ((await prompts({
|
|
185
|
+
type: "select",
|
|
186
|
+
name: "action",
|
|
187
|
+
message: `Existing Ardo project detected. Upgrade to v${getCliVersion()}?`,
|
|
188
|
+
choices: [{
|
|
189
|
+
title: "Upgrade framework files",
|
|
190
|
+
value: "upgrade"
|
|
191
|
+
}, {
|
|
192
|
+
title: "Cancel",
|
|
193
|
+
value: "cancel"
|
|
194
|
+
}]
|
|
195
|
+
}, { onCancel })).action === "cancel") throw new Error(`${red("✖")} Operation cancelled`);
|
|
196
|
+
console.log(`\n ${cyan("Upgrading project in")} ${root}...\n`);
|
|
197
|
+
const result = upgradeProject(root);
|
|
198
|
+
for (const file of result.updated) console.log(` ${green("●")} ${file}`);
|
|
199
|
+
for (const file of result.deleted) console.log(` ${yellow("●")} ${file} ${dim("(removed)")}`);
|
|
200
|
+
for (const file of result.skipped) console.log(` ${dim("○")} ${file} ${dim("(not found, skipped)")}`);
|
|
201
|
+
console.log(`\n ${green("Done!")} Now run:\n`);
|
|
202
|
+
console.log(` ${blue("pnpm install")}\n`);
|
|
203
|
+
}
|
|
204
|
+
function getNewProjectPrompts(targetDir, root, argTemplate) {
|
|
205
|
+
return [
|
|
206
|
+
{
|
|
207
|
+
type: () => !fs.existsSync(root) || isEmpty(root) ? null : "select",
|
|
208
|
+
name: "overwrite",
|
|
209
|
+
message: () => `${targetDir === "." ? "Current directory" : `Target directory "${targetDir}"`} is not empty. How would you like to proceed?`,
|
|
210
|
+
choices: [
|
|
211
|
+
{
|
|
212
|
+
title: "Remove existing files and continue",
|
|
213
|
+
value: "yes"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
title: "Cancel operation",
|
|
217
|
+
value: "no"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
title: "Ignore files and continue",
|
|
221
|
+
value: "ignore"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
type: (_, { overwrite }) => {
|
|
227
|
+
if (overwrite === "no") throw new Error(`${red("✖")} Operation cancelled`);
|
|
228
|
+
return null;
|
|
229
|
+
},
|
|
230
|
+
name: "overwriteChecker"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: argTemplate !== void 0 && isValidTemplate(argTemplate) ? null : "select",
|
|
234
|
+
name: "template",
|
|
235
|
+
message: reset("Select a template:"),
|
|
236
|
+
choices: templates.map((t) => ({
|
|
237
|
+
title: `${t.display} ${yellow(`- ${t.description}`)}`,
|
|
238
|
+
value: t.name
|
|
239
|
+
}))
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
name: "siteTitle",
|
|
244
|
+
message: reset("Site title:"),
|
|
245
|
+
initial: "My Documentation"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: "select",
|
|
249
|
+
name: "docType",
|
|
250
|
+
message: reset("What are you documenting?"),
|
|
251
|
+
choices: [{
|
|
252
|
+
title: `Code library ${dim("- includes TypeDoc API generation")}`,
|
|
253
|
+
value: "library"
|
|
254
|
+
}, {
|
|
255
|
+
title: `General documentation ${dim("- guides, manuals, etc.")}`,
|
|
256
|
+
value: "general"
|
|
257
|
+
}]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
type: "select",
|
|
261
|
+
name: "githubPages",
|
|
262
|
+
message: reset("Deploy to GitHub Pages?"),
|
|
263
|
+
choices: [{
|
|
264
|
+
title: `Yes ${dim("- auto-detects base path from git remote")}`,
|
|
265
|
+
value: true
|
|
266
|
+
}, {
|
|
267
|
+
title: `No ${dim("- deploy to other platforms (Netlify, Vercel, etc.)")}`,
|
|
268
|
+
value: false
|
|
269
|
+
}]
|
|
270
|
+
}
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
async function runNewProjectFlow(targetDir, root, argTemplate) {
|
|
274
|
+
const response = await prompts(getNewProjectPrompts(targetDir, root, argTemplate), { onCancel });
|
|
275
|
+
const template = response.template ?? argTemplate ?? "minimal";
|
|
276
|
+
if (response.overwrite === "yes") emptyDir(root);
|
|
277
|
+
else if (!fs.existsSync(root)) fs.mkdirSync(root, { recursive: true });
|
|
278
|
+
console.log(`\n ${cyan("Scaffolding project in")} ${root}...\n`);
|
|
279
|
+
createProjectStructure(root, template, {
|
|
280
|
+
siteTitle: response.siteTitle,
|
|
281
|
+
projectName: targetDir,
|
|
282
|
+
typedoc: response.docType === "library",
|
|
283
|
+
githubPages: response.githubPages ?? true,
|
|
284
|
+
description: detectProjectDescription(root) ?? "Built with Ardo"
|
|
285
|
+
});
|
|
286
|
+
console.log(` ${green("Done!")} Now run:\n`);
|
|
287
|
+
if (root !== process.cwd()) console.log(` ${blue("cd")} ${targetDir}`);
|
|
288
|
+
console.log(` ${blue("pnpm install")}`);
|
|
289
|
+
console.log(` ${blue("pnpm dev")}\n`);
|
|
290
|
+
}
|
|
164
291
|
async function main() {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (!/^[a-z0-9-]+$/.test(name))
|
|
183
|
-
return "Project name may only contain lowercase letters, digits, and hyphens";
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
{ onCancel }
|
|
188
|
-
);
|
|
189
|
-
targetDir = formatTargetDir(projectName) || defaultTargetDir;
|
|
190
|
-
}
|
|
191
|
-
const root = path2.join(process.cwd(), targetDir);
|
|
192
|
-
if (fs2.existsSync(root) && !isEmpty(root) && isArdoProject(root)) {
|
|
193
|
-
const cliVersion = getCliVersion();
|
|
194
|
-
const { action } = await prompts(
|
|
195
|
-
{
|
|
196
|
-
type: "select",
|
|
197
|
-
name: "action",
|
|
198
|
-
message: `Existing Ardo project detected. Upgrade to v${cliVersion}?`,
|
|
199
|
-
choices: [
|
|
200
|
-
{ title: "Upgrade framework files", value: "upgrade" },
|
|
201
|
-
{ title: "Cancel", value: "cancel" }
|
|
202
|
-
]
|
|
203
|
-
},
|
|
204
|
-
{ onCancel }
|
|
205
|
-
);
|
|
206
|
-
if (action === "cancel") {
|
|
207
|
-
throw new Error(red("\u2716") + " Operation cancelled");
|
|
208
|
-
}
|
|
209
|
-
console.log();
|
|
210
|
-
console.log(` ${cyan("Upgrading project in")} ${root}...`);
|
|
211
|
-
console.log();
|
|
212
|
-
const result = upgradeProject(root);
|
|
213
|
-
for (const file of result.updated) {
|
|
214
|
-
console.log(` ${green("\u25CF")} ${file}`);
|
|
215
|
-
}
|
|
216
|
-
for (const file of result.deleted) {
|
|
217
|
-
console.log(` ${yellow("\u25CF")} ${file} ${dim("(removed)")}`);
|
|
218
|
-
}
|
|
219
|
-
for (const file of result.skipped) {
|
|
220
|
-
console.log(` ${dim("\u25CB")} ${file} ${dim("(not found, skipped)")}`);
|
|
221
|
-
}
|
|
222
|
-
console.log();
|
|
223
|
-
console.log(` ${green("Done!")} Now run:`);
|
|
224
|
-
console.log();
|
|
225
|
-
console.log(` ${blue("pnpm install")}`);
|
|
226
|
-
console.log();
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
let template = argTemplate;
|
|
230
|
-
const response = await prompts(
|
|
231
|
-
[
|
|
232
|
-
{
|
|
233
|
-
type: () => !fs2.existsSync(root) || isEmpty(root) ? null : "select",
|
|
234
|
-
name: "overwrite",
|
|
235
|
-
message: () => `${targetDir === "." ? "Current directory" : `Target directory "${targetDir}"`} is not empty. How would you like to proceed?`,
|
|
236
|
-
choices: [
|
|
237
|
-
{ title: "Remove existing files and continue", value: "yes" },
|
|
238
|
-
{ title: "Cancel operation", value: "no" },
|
|
239
|
-
{ title: "Ignore files and continue", value: "ignore" }
|
|
240
|
-
]
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
type: (_, { overwrite: overwrite2 }) => {
|
|
244
|
-
if (overwrite2 === "no") {
|
|
245
|
-
throw new Error(red("\u2716") + " Operation cancelled");
|
|
246
|
-
}
|
|
247
|
-
return null;
|
|
248
|
-
},
|
|
249
|
-
name: "overwriteChecker"
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
type: argTemplate && isValidTemplate(argTemplate) ? null : "select",
|
|
253
|
-
name: "template",
|
|
254
|
-
message: reset("Select a template:"),
|
|
255
|
-
choices: templates.map((t) => ({
|
|
256
|
-
title: `${t.display} ${yellow(`- ${t.description}`)}`,
|
|
257
|
-
value: t.name
|
|
258
|
-
}))
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
type: "text",
|
|
262
|
-
name: "siteTitle",
|
|
263
|
-
message: reset("Site title:"),
|
|
264
|
-
initial: "My Documentation"
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
type: "select",
|
|
268
|
-
name: "docType",
|
|
269
|
-
message: reset("What are you documenting?"),
|
|
270
|
-
choices: [
|
|
271
|
-
{
|
|
272
|
-
title: `Code library ${dim("- includes TypeDoc API generation")}`,
|
|
273
|
-
value: "library"
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
title: `General documentation ${dim("- guides, manuals, etc.")}`,
|
|
277
|
-
value: "general"
|
|
278
|
-
}
|
|
279
|
-
]
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
type: "select",
|
|
283
|
-
name: "githubPages",
|
|
284
|
-
message: reset("Deploy to GitHub Pages?"),
|
|
285
|
-
choices: [
|
|
286
|
-
{
|
|
287
|
-
title: `Yes ${dim("- auto-detects base path from git remote")}`,
|
|
288
|
-
value: true
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
title: `No ${dim("- deploy to other platforms (Netlify, Vercel, etc.)")}`,
|
|
292
|
-
value: false
|
|
293
|
-
}
|
|
294
|
-
]
|
|
295
|
-
}
|
|
296
|
-
],
|
|
297
|
-
{ onCancel }
|
|
298
|
-
);
|
|
299
|
-
const { overwrite, template: templateChoice, siteTitle, docType, githubPages } = response;
|
|
300
|
-
template = templateChoice || template || "minimal";
|
|
301
|
-
if (overwrite === "yes") {
|
|
302
|
-
emptyDir(root);
|
|
303
|
-
} else if (!fs2.existsSync(root)) {
|
|
304
|
-
fs2.mkdirSync(root, { recursive: true });
|
|
305
|
-
}
|
|
306
|
-
console.log();
|
|
307
|
-
console.log(` ${cyan("Scaffolding project in")} ${root}...`);
|
|
308
|
-
console.log();
|
|
309
|
-
const description = detectProjectDescription(root) || "Built with Ardo";
|
|
310
|
-
createProjectStructure(root, template, {
|
|
311
|
-
siteTitle,
|
|
312
|
-
projectName: targetDir,
|
|
313
|
-
typedoc: docType === "library",
|
|
314
|
-
githubPages: githubPages ?? true,
|
|
315
|
-
description
|
|
316
|
-
});
|
|
317
|
-
console.log(` ${green("Done!")} Now run:`);
|
|
318
|
-
console.log();
|
|
319
|
-
if (root !== process.cwd()) {
|
|
320
|
-
console.log(` ${blue("cd")} ${targetDir}`);
|
|
321
|
-
}
|
|
322
|
-
console.log(` ${blue("pnpm install")}`);
|
|
323
|
-
console.log(` ${blue("pnpm dev")}`);
|
|
324
|
-
console.log();
|
|
292
|
+
console.log(`\n ${cyan("◆")} ${green("create-ardo")}\n`);
|
|
293
|
+
const argTargetDir = process.argv.length > 2 ? process.argv[2] : void 0;
|
|
294
|
+
const argTemplate = process.argv.length > 3 ? process.argv[3] : void 0;
|
|
295
|
+
const targetDir = argTargetDir ?? await promptProjectName();
|
|
296
|
+
const root = path.join(process.cwd(), targetDir);
|
|
297
|
+
if (fs.existsSync(root) && !isEmpty(root) && isArdoProject(root)) {
|
|
298
|
+
await runUpgradeFlow(root);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
await runNewProjectFlow(targetDir, root, argTemplate);
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
await main();
|
|
305
|
+
} catch (error) {
|
|
306
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
307
|
+
console.error(errorMessage);
|
|
308
|
+
process.exit(1);
|
|
325
309
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
process.exit(1);
|
|
329
|
-
});
|
|
310
|
+
//#endregion
|
|
311
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ardo",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Scaffolding tool for Ardo documentation projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,17 +33,17 @@
|
|
|
33
33
|
"kolorist": "^1.8.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@types/node": "^25.
|
|
36
|
+
"@types/node": "^25.5.0",
|
|
37
37
|
"@types/prompts": "^2.4.9",
|
|
38
|
-
"
|
|
39
|
-
"typescript": "^
|
|
38
|
+
"tsdown": "^0.21.7",
|
|
39
|
+
"typescript": "^6.0.2"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=18.0.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
-
"build": "
|
|
46
|
-
"dev": "
|
|
45
|
+
"build": "tsdown",
|
|
46
|
+
"dev": "tsdown --watch",
|
|
47
47
|
"typecheck": "tsc --noEmit"
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ArdoRootLayout, ArdoRoot } from "ardo/ui"
|
|
2
2
|
import config from "virtual:ardo/config"
|
|
3
3
|
import sidebar from "virtual:ardo/sidebar"
|
|
4
4
|
import type { MetaFunction } from "react-router"
|
|
5
|
+
import "./app.css"
|
|
5
6
|
import "ardo/ui/styles.css"
|
|
6
7
|
|
|
7
8
|
export const meta: MetaFunction = () => [{ title: config.title }]
|
|
8
9
|
|
|
9
10
|
export function Layout({ children }: { children: React.ReactNode }) {
|
|
10
|
-
return <
|
|
11
|
+
return <ArdoRootLayout>{children}</ArdoRootLayout>
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export default function Root() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ArdoHero, ArdoFeatures, ArdoFeatureCard } from "ardo/ui"
|
|
2
2
|
import { Zap, Sparkles, Palette, ArrowRight, Github } from "ardo/icons"
|
|
3
3
|
import type { MetaFunction } from "react-router"
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ export const meta: MetaFunction = () => [
|
|
|
9
9
|
export default function HomePage() {
|
|
10
10
|
return (
|
|
11
11
|
<>
|
|
12
|
-
<
|
|
12
|
+
<ArdoHero
|
|
13
13
|
name="{{SITE_TITLE}}"
|
|
14
14
|
text="Documentation Made Simple"
|
|
15
15
|
tagline="Focus on your content, not configuration"
|
|
@@ -28,25 +28,17 @@ export default function HomePage() {
|
|
|
28
28
|
},
|
|
29
29
|
]}
|
|
30
30
|
/>
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
title: "Flexible",
|
|
45
|
-
icon: <Palette size={28} strokeWidth={1.5} />,
|
|
46
|
-
details: "Fully customizable theme",
|
|
47
|
-
},
|
|
48
|
-
]}
|
|
49
|
-
/>
|
|
31
|
+
<ArdoFeatures>
|
|
32
|
+
<ArdoFeatureCard title="Fast" icon={<Zap size={28} strokeWidth={1.5} />}>
|
|
33
|
+
Lightning fast builds with Vite
|
|
34
|
+
</ArdoFeatureCard>
|
|
35
|
+
<ArdoFeatureCard title="Simple" icon={<Sparkles size={28} strokeWidth={1.5} />}>
|
|
36
|
+
Easy to set up and use
|
|
37
|
+
</ArdoFeatureCard>
|
|
38
|
+
<ArdoFeatureCard title="Flexible" icon={<Palette size={28} strokeWidth={1.5} />}>
|
|
39
|
+
Fully customizable theme
|
|
40
|
+
</ArdoFeatureCard>
|
|
41
|
+
</ArdoFeatures>
|
|
50
42
|
</>
|
|
51
43
|
)
|
|
52
44
|
}
|
|
@@ -10,17 +10,19 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"ardo": "^{{ARDO_VERSION}}",
|
|
13
|
-
"isbot": "
|
|
14
|
-
"lucide-react": "
|
|
15
|
-
"react": "
|
|
16
|
-
"react-dom": "
|
|
17
|
-
"react-router": "
|
|
13
|
+
"isbot": "^5.1.36",
|
|
14
|
+
"lucide-react": "^0.577.0",
|
|
15
|
+
"react": "^19.2.4",
|
|
16
|
+
"react-dom": "^19.2.4",
|
|
17
|
+
"react-router": "^7.13.2"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@react-router/dev": "
|
|
20
|
+
"@react-router/dev": "^7.13.2",
|
|
21
|
+
"@tailwindcss/vite": "^4.2.2",
|
|
21
22
|
"@types/react": "^19.2.14",
|
|
22
23
|
"@types/react-dom": "^19.2.3",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
24
|
+
"tailwindcss": "^4.2.2",
|
|
25
|
+
"typescript": "^6.0.2",
|
|
26
|
+
"vite": "^8.0.3"
|
|
25
27
|
}
|
|
26
28
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { defineConfig } from 'vite'
|
|
2
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
2
3
|
import { ardo } from 'ardo/vite'
|
|
3
4
|
|
|
4
5
|
export default defineConfig({
|
|
5
6
|
plugins: [
|
|
7
|
+
tailwindcss(),
|
|
6
8
|
ardo({
|
|
7
9
|
title: '{{SITE_TITLE}}',
|
|
8
10
|
description: '{{DESCRIPTION}}',
|
|
@@ -10,31 +12,6 @@ export default defineConfig({
|
|
|
10
12
|
{{TYPEDOC_CONFIG}}
|
|
11
13
|
|
|
12
14
|
{{GITHUB_PAGES_CONFIG}}
|
|
13
|
-
|
|
14
|
-
themeConfig: {
|
|
15
|
-
siteTitle: '{{SITE_TITLE}}',
|
|
16
|
-
|
|
17
|
-
nav: [
|
|
18
|
-
{ text: 'Guide', link: '/guide/getting-started' },
|
|
19
|
-
{{TYPEDOC_NAV}}
|
|
20
|
-
],
|
|
21
|
-
|
|
22
|
-
sidebar: [
|
|
23
|
-
{
|
|
24
|
-
text: 'Guide',
|
|
25
|
-
items: [{ text: 'Getting Started', link: '/guide/getting-started' }],
|
|
26
|
-
},
|
|
27
|
-
{{TYPEDOC_SIDEBAR}}
|
|
28
|
-
],
|
|
29
|
-
|
|
30
|
-
footer: {
|
|
31
|
-
message: 'Released under the MIT License.',
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
search: {
|
|
35
|
-
enabled: true,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
15
|
}),
|
|
39
16
|
],
|
|
40
17
|
})
|