camelmind 0.2.2 → 0.2.4
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 +214 -14
- package/package.json +1 -1
- package/template/_package.json +4 -0
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { fileURLToPath as
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6
|
+
import path3 from "path";
|
|
7
|
+
import fs3 from "fs";
|
|
8
8
|
|
|
9
9
|
// src/commands/init.ts
|
|
10
10
|
import path from "path";
|
|
@@ -135,11 +135,210 @@ async function init(projectName, options) {
|
|
|
135
135
|
`);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
// src/commands/
|
|
138
|
+
// src/commands/update.ts
|
|
139
|
+
import path2 from "path";
|
|
140
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
141
|
+
import { execSync as execSync2 } from "child_process";
|
|
142
|
+
import fs2 from "fs-extra";
|
|
139
143
|
import chalk2 from "chalk";
|
|
140
144
|
import ora2 from "ora";
|
|
145
|
+
import prompts2 from "prompts";
|
|
146
|
+
var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
|
|
147
|
+
var TEMPLATE_DIR2 = path2.join(__dirname2, "../template");
|
|
148
|
+
var FRAMEWORK_DIRS = ["app", "components", "lib", "public", "scripts"];
|
|
149
|
+
var FRAMEWORK_FILES = [
|
|
150
|
+
"next.config.ts",
|
|
151
|
+
"postcss.config.mjs",
|
|
152
|
+
"tsconfig.json",
|
|
153
|
+
"eslint.config.mjs",
|
|
154
|
+
"proxy.ts"
|
|
155
|
+
];
|
|
156
|
+
var DEFAULT_IGNORED_FILES = ["app/home/page.tsx"];
|
|
157
|
+
async function walkFiles(dir) {
|
|
158
|
+
if (!await fs2.pathExists(dir)) return [];
|
|
159
|
+
const entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
160
|
+
const files = [];
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
if (entry.name === "node_modules" || entry.name === ".next" || entry.name === ".git") {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const fullPath = path2.join(dir, entry.name);
|
|
166
|
+
if (entry.isDirectory()) {
|
|
167
|
+
files.push(...await walkFiles(fullPath));
|
|
168
|
+
} else if (entry.isFile()) {
|
|
169
|
+
files.push(fullPath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return files;
|
|
173
|
+
}
|
|
174
|
+
function mergePackageJson(current, template) {
|
|
175
|
+
return {
|
|
176
|
+
...current,
|
|
177
|
+
scripts: {
|
|
178
|
+
...template.scripts,
|
|
179
|
+
...current.scripts
|
|
180
|
+
},
|
|
181
|
+
dependencies: {
|
|
182
|
+
...current.dependencies,
|
|
183
|
+
...template.dependencies
|
|
184
|
+
},
|
|
185
|
+
devDependencies: {
|
|
186
|
+
...current.devDependencies,
|
|
187
|
+
...template.devDependencies
|
|
188
|
+
},
|
|
189
|
+
overrides: {
|
|
190
|
+
...current.overrides,
|
|
191
|
+
...template.overrides
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
async function copyWithBackup(source, target, backupRoot, projectRoot) {
|
|
196
|
+
if (await fs2.pathExists(target)) {
|
|
197
|
+
await backupFile(target, backupRoot, projectRoot);
|
|
198
|
+
}
|
|
199
|
+
await fs2.ensureDir(path2.dirname(target));
|
|
200
|
+
await fs2.copy(source, target, { overwrite: true });
|
|
201
|
+
}
|
|
202
|
+
async function backupFile(file, backupRoot, projectRoot) {
|
|
203
|
+
const rel = path2.relative(projectRoot, file);
|
|
204
|
+
await fs2.ensureDir(path2.dirname(path2.join(backupRoot, rel)));
|
|
205
|
+
await fs2.copy(file, path2.join(backupRoot, rel), { overwrite: true });
|
|
206
|
+
}
|
|
207
|
+
async function updatePackageJson(projectRoot, backupRoot, dryRun) {
|
|
208
|
+
const projectPkgPath = path2.join(projectRoot, "package.json");
|
|
209
|
+
const templatePkgPath = path2.join(TEMPLATE_DIR2, "_package.json");
|
|
210
|
+
const current = await fs2.readJson(projectPkgPath);
|
|
211
|
+
const template = await fs2.readJson(templatePkgPath);
|
|
212
|
+
const merged = mergePackageJson(current, template);
|
|
213
|
+
if (dryRun) return;
|
|
214
|
+
await backupFile(projectPkgPath, backupRoot, projectRoot);
|
|
215
|
+
await fs2.writeJson(projectPkgPath, merged, { spaces: 2 });
|
|
216
|
+
await fs2.appendFile(projectPkgPath, "\n");
|
|
217
|
+
}
|
|
218
|
+
async function plannedTemplateFiles() {
|
|
219
|
+
const files = [];
|
|
220
|
+
for (const dir of FRAMEWORK_DIRS) {
|
|
221
|
+
const dirPath = path2.join(TEMPLATE_DIR2, dir);
|
|
222
|
+
const dirFiles = await walkFiles(dirPath);
|
|
223
|
+
files.push(...dirFiles.map((file) => path2.relative(TEMPLATE_DIR2, file)));
|
|
224
|
+
}
|
|
225
|
+
files.push(...FRAMEWORK_FILES);
|
|
226
|
+
return files.sort();
|
|
227
|
+
}
|
|
228
|
+
function normalizeUpdatePath(file) {
|
|
229
|
+
return file.replace(/\\/g, "/").replace(/^\.?\//, "");
|
|
230
|
+
}
|
|
231
|
+
function ignoredFilesFor(options) {
|
|
232
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
233
|
+
if (!options.overwriteHome) {
|
|
234
|
+
for (const file of DEFAULT_IGNORED_FILES) ignored.add(file);
|
|
235
|
+
}
|
|
236
|
+
for (const file of options.ignoreFile ?? []) {
|
|
237
|
+
ignored.add(normalizeUpdatePath(file));
|
|
238
|
+
}
|
|
239
|
+
return ignored;
|
|
240
|
+
}
|
|
241
|
+
async function update(targetDir, options) {
|
|
242
|
+
const projectRoot = path2.resolve(process.cwd(), targetDir ?? ".");
|
|
243
|
+
const packageJsonPath = path2.join(projectRoot, "package.json");
|
|
244
|
+
const configPath = path2.join(projectRoot, "camelmind.config.ts");
|
|
245
|
+
if (!await fs2.pathExists(TEMPLATE_DIR2)) {
|
|
246
|
+
console.error(chalk2.red("Template directory not found in the installed CamelMind package."));
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
if (!await fs2.pathExists(packageJsonPath) || !await fs2.pathExists(configPath)) {
|
|
250
|
+
console.error(chalk2.red("Run this command from a CamelMind project root, or pass the project directory."));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
const ignoredFiles = ignoredFilesFor(options);
|
|
254
|
+
const allFiles = await plannedTemplateFiles();
|
|
255
|
+
const files = allFiles.filter((file) => !ignoredFiles.has(file));
|
|
256
|
+
const skippedFiles = allFiles.filter((file) => ignoredFiles.has(file));
|
|
257
|
+
const backupRoot = path2.join(
|
|
258
|
+
projectRoot,
|
|
259
|
+
".camelmind-update-backup",
|
|
260
|
+
(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")
|
|
261
|
+
);
|
|
262
|
+
console.log(`
|
|
263
|
+
${chalk2.hex("#D8A15B").bold("CamelMind update")}
|
|
264
|
+
|
|
265
|
+
Project: ${chalk2.cyan(projectRoot)}
|
|
266
|
+
Files: ${chalk2.cyan(String(files.length))} framework files + package.json
|
|
267
|
+
Skipped: ${chalk2.cyan(String(skippedFiles.length))} preserved files
|
|
268
|
+
Backup: ${chalk2.cyan(path2.relative(projectRoot, backupRoot))}
|
|
269
|
+
`);
|
|
270
|
+
if (options.dryRun) {
|
|
271
|
+
console.log(chalk2.bold(" Dry run \u2014 files that would be refreshed:\n"));
|
|
272
|
+
for (const file of files) console.log(` ${file}`);
|
|
273
|
+
console.log(" package.json");
|
|
274
|
+
if (skippedFiles.length) {
|
|
275
|
+
console.log(chalk2.bold("\n Preserved files:\n"));
|
|
276
|
+
for (const file of skippedFiles) console.log(` ${file}`);
|
|
277
|
+
}
|
|
278
|
+
console.log("");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (!options.yes) {
|
|
282
|
+
const { proceed } = await prompts2(
|
|
283
|
+
{
|
|
284
|
+
type: "confirm",
|
|
285
|
+
name: "proceed",
|
|
286
|
+
message: "Refresh CamelMind framework files and merge package updates?",
|
|
287
|
+
initial: true
|
|
288
|
+
},
|
|
289
|
+
{ onCancel: () => process.exit(0) }
|
|
290
|
+
);
|
|
291
|
+
if (!proceed) {
|
|
292
|
+
console.log(chalk2.gray("\n Update cancelled.\n"));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const spinner = ora2("Refreshing CamelMind framework files...").start();
|
|
297
|
+
try {
|
|
298
|
+
for (const rel of files) {
|
|
299
|
+
await copyWithBackup(
|
|
300
|
+
path2.join(TEMPLATE_DIR2, rel),
|
|
301
|
+
path2.join(projectRoot, rel),
|
|
302
|
+
backupRoot,
|
|
303
|
+
projectRoot
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
await updatePackageJson(projectRoot, backupRoot, false);
|
|
307
|
+
spinner.succeed("CamelMind files refreshed");
|
|
308
|
+
} catch (err) {
|
|
309
|
+
spinner.fail("Update failed");
|
|
310
|
+
console.error(chalk2.red(`
|
|
311
|
+
${String(err)}
|
|
312
|
+
`));
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
if (options.install) {
|
|
316
|
+
const installSpinner = ora2("Installing updated dependencies...").start();
|
|
317
|
+
try {
|
|
318
|
+
execSync2("npm install", {
|
|
319
|
+
cwd: projectRoot,
|
|
320
|
+
stdio: "ignore"
|
|
321
|
+
});
|
|
322
|
+
installSpinner.succeed("Dependencies installed");
|
|
323
|
+
} catch {
|
|
324
|
+
installSpinner.warn("Dependency install failed \u2014 run npm install inside your project");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
console.log(`
|
|
328
|
+
${chalk2.green("Done.")} Review the changes, then run:
|
|
329
|
+
|
|
330
|
+
${chalk2.cyan("npm run lint")}
|
|
331
|
+
${chalk2.cyan("npm run build")}
|
|
332
|
+
|
|
333
|
+
Backup saved in ${chalk2.cyan(path2.relative(projectRoot, backupRoot))}
|
|
334
|
+
`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/commands/version.ts
|
|
338
|
+
import chalk3 from "chalk";
|
|
339
|
+
import ora3 from "ora";
|
|
141
340
|
async function checkVersion(currentVersion) {
|
|
142
|
-
const spinner =
|
|
341
|
+
const spinner = ora3("Checking latest version...").start();
|
|
143
342
|
let latestVersion;
|
|
144
343
|
try {
|
|
145
344
|
const res = await fetch("https://registry.npmjs.org/camelmind/latest");
|
|
@@ -150,7 +349,7 @@ async function checkVersion(currentVersion) {
|
|
|
150
349
|
} catch {
|
|
151
350
|
spinner.fail("Could not reach the npm registry");
|
|
152
351
|
console.log(`
|
|
153
|
-
Installed: ${
|
|
352
|
+
Installed: ${chalk3.cyan(currentVersion)}
|
|
154
353
|
`);
|
|
155
354
|
return;
|
|
156
355
|
}
|
|
@@ -158,28 +357,29 @@ async function checkVersion(currentVersion) {
|
|
|
158
357
|
const [latMajor, latMinor, latPatch] = latestVersion.split(".").map(Number);
|
|
159
358
|
const isOutdated = latMajor > curMajor || latMajor === curMajor && latMinor > curMinor || latMajor === curMajor && latMinor === curMinor && latPatch > curPatch;
|
|
160
359
|
console.log(`
|
|
161
|
-
Installed ${
|
|
162
|
-
Latest ${isOutdated ?
|
|
360
|
+
Installed ${chalk3.cyan(currentVersion)}
|
|
361
|
+
Latest ${isOutdated ? chalk3.yellow(latestVersion) : chalk3.green(latestVersion)}
|
|
163
362
|
`);
|
|
164
363
|
if (isOutdated) {
|
|
165
364
|
console.log(
|
|
166
|
-
` ${
|
|
365
|
+
` ${chalk3.yellow("\u26A0")} A new version is available. To update an existing site, run:
|
|
167
366
|
|
|
168
|
-
${
|
|
367
|
+
${chalk3.cyan("npx camelmind@latest update")}
|
|
169
368
|
`
|
|
170
369
|
);
|
|
171
370
|
} else {
|
|
172
|
-
console.log(` ${
|
|
371
|
+
console.log(` ${chalk3.green("\u2713")} You're up to date.
|
|
173
372
|
`);
|
|
174
373
|
}
|
|
175
374
|
}
|
|
176
375
|
|
|
177
376
|
// src/index.ts
|
|
178
|
-
var
|
|
179
|
-
var pkgPath =
|
|
180
|
-
var pkg = JSON.parse(
|
|
377
|
+
var __dirname3 = path3.dirname(fileURLToPath3(import.meta.url));
|
|
378
|
+
var pkgPath = path3.join(__dirname3, "../package.json");
|
|
379
|
+
var pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
181
380
|
var program = new Command();
|
|
182
381
|
program.name("camelmind").description("CamelMind CLI \u2014 scaffold and manage documentation sites").version(pkg.version);
|
|
183
382
|
program.command("init [project-name]").description("Create a new CamelMind documentation site").option("--no-install", "Skip installing npm dependencies").action(init);
|
|
184
383
|
program.command("version").description("Show installed version and check for updates").action(() => checkVersion(pkg.version));
|
|
384
|
+
program.command("update [project-dir]").description("Update an existing CamelMind site to the latest platform files").option("--no-install", "Skip installing npm dependencies").option("--dry-run", "Show what would change without writing files").option("--ignore-file <path...>", "Preserve additional project files during update").option("--overwrite-home", "Overwrite app/home/page.tsx with the latest template").option("-y, --yes", "Skip the confirmation prompt").action(update);
|
|
185
385
|
program.parse();
|
package/package.json
CHANGED
package/template/_package.json
CHANGED