@knotx/cli 0.0.4 → 0.0.6

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.cjs CHANGED
@@ -4,6 +4,7 @@
4
4
  const node_child_process = require('node:child_process');
5
5
  const path = require('node:path');
6
6
  const process = require('node:process');
7
+ const node_util = require('node:util');
7
8
  const chalk = require('chalk');
8
9
  const commander = require('commander');
9
10
  const fs = require('fs-extra');
@@ -58,6 +59,11 @@ var __async = (__this, __arguments, generator) => {
58
59
  step((generator = generator.apply(__this, __arguments)).next());
59
60
  });
60
61
  };
62
+ process__default.on("uncaughtException", (error) => {
63
+ console.error(chalk__default.red("\u274C \u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:"));
64
+ console.error(chalk__default.red(error.stack || error.toString()));
65
+ process__default.exit(1);
66
+ });
61
67
  const templatesDir = path__default.resolve(__dirname, "..", "templates");
62
68
  const program = new commander.Command();
63
69
  program.name("knotx").description("Knotx CLI - \u521B\u5EFA\u548C\u7BA1\u7406Knotx\u63D2\u4EF6").version("0.0.1");
@@ -67,33 +73,43 @@ program.command("create-plugin").description("\u521B\u5EFA\u4E00\u4E2A\u65B0\u76
67
73
  {
68
74
  type: "input",
69
75
  name: "pluginName",
70
- message: "\u8BF7\u8F93\u5165\u63D2\u4EF6\u540D\u79F0 (\u4F8B\u5982: my-plugin):",
76
+ message: "\u8BF7\u8F93\u5165\u63D2\u4EF6\u540D\u79F0 (\u4F8B\u5982: @knotx/plugins-my-plugin):",
71
77
  validate: (input) => {
72
78
  if (!input)
73
79
  return "\u63D2\u4EF6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
74
- if (!/^[a-z0-9-]+$/.test(input))
75
- return "\u63D2\u4EF6\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
80
+ if (!/^(?:@[a-z0-9-]+\/)?[a-z0-9-]+$/.test(input))
81
+ return "\u63D2\u4EF6\u540D\u79F0\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5E94\u8BE5\u662F\u6709\u6548\u7684npm\u5305\u540D";
76
82
  return true;
77
- }
83
+ },
84
+ default: "@knotx/plugins-"
78
85
  },
79
86
  {
80
87
  type: "list",
81
88
  name: "environment",
82
89
  message: "\u8BF7\u9009\u62E9\u63D2\u4EF6\u8FD0\u884C\u73AF\u5883:",
83
- choices: ["jsx", "react"]
90
+ choices: ["react", "jsx"]
84
91
  }
85
92
  ]);
86
93
  const { pluginName, environment } = answers;
87
- const fullPluginName = `plugins-${pluginName}`;
88
- const targetDir = path__default.resolve(process__default.cwd(), fullPluginName);
94
+ const dirName = pluginName.split("/").pop();
95
+ let currentDir;
96
+ try {
97
+ currentDir = process__default.cwd();
98
+ } catch (error) {
99
+ console.log(chalk__default.red(`\u274C \u9519\u8BEF: \u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55: ${error}`));
100
+ console.log(chalk__default.yellow("\u5C1D\u8BD5\u4F7F\u7528\u5907\u7528\u65B9\u6CD5\u83B7\u53D6\u76EE\u5F55..."));
101
+ currentDir = path__default.resolve(__dirname, "..");
102
+ console.log(chalk__default.green(`\u2705 \u4F7F\u7528\u5907\u7528\u76EE\u5F55: ${currentDir}`));
103
+ }
104
+ const targetDir = safeResolvePath(currentDir, dirName);
89
105
  if (fs__default.existsSync(targetDir)) {
90
- console.log(chalk__default.red(`\u274C \u9519\u8BEF: \u76EE\u5F55 ${fullPluginName} \u5DF2\u5B58\u5728`));
106
+ console.log(chalk__default.red(`\u274C \u9519\u8BEF: \u76EE\u5F55 ${dirName} \u5DF2\u5B58\u5728`));
91
107
  return;
92
108
  }
93
109
  let spinner = ora__default("\u{1F4C2} \u521B\u5EFA\u63D2\u4EF6\u76EE\u5F55...").start();
94
110
  try {
95
- yield fs__default.ensureDir(targetDir);
96
- yield fs__default.ensureDir(path__default.join(targetDir, "src"));
111
+ yield safeEnsureDir(targetDir);
112
+ yield safeEnsureDir(safeResolvePath(targetDir, "src"));
97
113
  spinner.succeed("\u{1F4C2} \u63D2\u4EF6\u76EE\u5F55\u521B\u5EFA\u6210\u529F");
98
114
  spinner = ora__default("\u{1F50D} \u83B7\u53D6 @knotx \u5305\u7684\u6700\u65B0\u7248\u672C...").start();
99
115
  const knotxVersions = yield getKnotxPackageVersions();
@@ -122,13 +138,19 @@ program.command("create-plugin").description("\u521B\u5EFA\u4E00\u4E2A\u65B0\u76
122
138
  console.warn(chalk__default.yellow(` \u53EF\u80FD\u7684\u539F\u56E0: ${error.message}`));
123
139
  }
124
140
  console.log(chalk__default.green(`
125
- \u2705 \u63D2\u4EF6 ${chalk__default.bold(fullPluginName)} \u521B\u5EFA\u6210\u529F\uFF01`));
141
+ \u2705 \u63D2\u4EF6 ${chalk__default.bold(pluginName)} \u521B\u5EFA\u6210\u529F\uFF01`));
126
142
  console.log(`
127
143
  \u{1F4C1} \u63D2\u4EF6\u76EE\u5F55: ${chalk__default.cyan(targetDir)}`);
128
144
  console.log(`
129
145
  \u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u4EE5\u5B89\u88C5\u4F9D\u8D56\u5E76\u5F00\u59CB\u5F00\u53D1:
130
146
  `);
131
- console.log(chalk__default.cyan(` cd ${path__default.relative(process__default.cwd(), targetDir)}`));
147
+ let cdCommand;
148
+ try {
149
+ cdCommand = `cd ${path__default.relative(process__default.cwd(), targetDir)}`;
150
+ } catch (e) {
151
+ cdCommand = `cd ${targetDir}`;
152
+ }
153
+ console.log(chalk__default.cyan(` ${cdCommand}`));
132
154
  console.log(chalk__default.cyan(" pnpm install"));
133
155
  console.log(chalk__default.cyan(" pnpm build"));
134
156
  console.log(chalk__default.cyan(" pnpm dev # \u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668\uFF0C\u8FD0\u884C playground"));
@@ -158,15 +180,23 @@ function getKnotxPackageVersions() {
158
180
  "@knotx/plugins-base-render"
159
181
  ];
160
182
  const npmRegistry = "https://registry.npmjs.org";
161
- for (const pkg of packages) {
162
- try {
163
- const version = node_child_process.execSync(`npm view ${pkg} version --registry=${npmRegistry}`, { encoding: "utf8" }).trim();
164
- knotxVersions[pkg] = `^${version}`;
165
- } catch (e) {
166
- console.warn(chalk__default.yellow(`\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6 ${pkg} \u7684\u6700\u65B0\u7248\u672C\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u7248\u672C`));
167
- knotxVersions[pkg] = "latest";
168
- }
169
- }
183
+ const execAsync = node_util.promisify(node_child_process.exec);
184
+ const results = yield Promise.all(
185
+ packages.map((pkg) => __async(this, null, function* () {
186
+ try {
187
+ const cmd = `npm view ${pkg} version --registry=${npmRegistry} --timeout=10000`;
188
+ const { stdout } = yield execAsync(cmd, { timeout: 1e4 });
189
+ const version = stdout.trim();
190
+ return { pkg, version: `^${version}`, success: true };
191
+ } catch (e) {
192
+ console.warn(chalk__default.yellow(`\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6 ${pkg} \u7684\u6700\u65B0\u7248\u672C\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u7248\u672C`));
193
+ return { pkg, version: "latest", success: false };
194
+ }
195
+ }))
196
+ );
197
+ results.forEach(({ pkg, version }) => {
198
+ knotxVersions[pkg] = version;
199
+ });
170
200
  return knotxVersions;
171
201
  } catch (e) {
172
202
  console.warn(chalk__default.yellow("\u26A0\uFE0F \u83B7\u53D6 @knotx \u5305\u7248\u672C\u5931\u8D25\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u7248\u672C"));
@@ -176,7 +206,9 @@ function getKnotxPackageVersions() {
176
206
  "@knotx/jsx": "latest",
177
207
  "@knotx/build-config": "latest",
178
208
  "@knotx/eslint-config": "latest",
179
- "@knotx/typescript-config": "latest"
209
+ "@knotx/typescript-config": "latest",
210
+ "@knotx/react": "latest",
211
+ "@knotx/plugins-base-render": "latest"
180
212
  };
181
213
  }
182
214
  });
@@ -184,10 +216,10 @@ function getKnotxPackageVersions() {
184
216
  function createPackageJson(targetDir, pluginName, environment, knotxVersions) {
185
217
  return __async(this, null, function* () {
186
218
  const packageJson = {
187
- name: `@knotx/plugins-${pluginName}`,
219
+ name: pluginName,
188
220
  type: "module",
189
221
  version: "0.0.1",
190
- description: `${capitalize(pluginName)} Plugin for Knotx`,
222
+ description: `${capitalize(pluginName.split("/").pop() || pluginName)} Plugin for Knotx`,
191
223
  license: "MIT",
192
224
  publishConfig: {
193
225
  access: "public"
@@ -231,13 +263,15 @@ function createPackageJson(targetDir, pluginName, environment, knotxVersions) {
231
263
  "vitest": "^3.0.0"
232
264
  }
233
265
  };
234
- if (environment === "jsx") ; else if (environment === "react") {
235
- packageJson.peerDependencies.react = "^17";
236
- packageJson.peerDependencies["react-dom"] = "^17";
237
- packageJson.devDependencies["@knotx/react"] = knotxVersions["@knotx/react"];
266
+ if (environment === "jsx") {
238
267
  packageJson.devDependencies["@knotx/plugins-base-render"] = knotxVersions["@knotx/plugins-base-render"];
239
- packageJson.devDependencies.react = "^17";
268
+ } else if (environment === "react") {
269
+ packageJson.peerDependencies.react = ">=17";
270
+ packageJson.peerDependencies["react-dom"] = ">=17";
271
+ packageJson.peerDependencies["@knotx/react"] = knotxVersions["@knotx/react"];
272
+ packageJson.devDependencies["@knotx/react"] = knotxVersions["@knotx/react"];
240
273
  packageJson.devDependencies["@vitejs/plugin-react"] = "^4.3.4";
274
+ packageJson.devDependencies.react = "^17";
241
275
  packageJson.devDependencies["react-dom"] = "^17";
242
276
  packageJson.devDependencies["@types/react"] = "^17";
243
277
  packageJson.devDependencies["@types/react-dom"] = "^17";
@@ -300,12 +334,12 @@ function sortObjectAlphabetically(obj) {
300
334
  function createConfigFiles(targetDir) {
301
335
  return __async(this, null, function* () {
302
336
  try {
303
- const tsconfigTemplate = yield fs__default.readFile(path__default.join(templatesDir, "tsconfig.json.template"), "utf-8");
304
- const buildConfigTemplate = yield fs__default.readFile(path__default.join(templatesDir, "build.config.ts.template"), "utf-8");
305
- const eslintConfigTemplate = yield fs__default.readFile(path__default.join(templatesDir, "eslint.config.js.template"), "utf-8");
306
- yield fs__default.writeFile(path__default.join(targetDir, "tsconfig.json"), tsconfigTemplate);
307
- yield fs__default.writeFile(path__default.join(targetDir, "build.config.ts"), buildConfigTemplate);
308
- yield fs__default.writeFile(path__default.join(targetDir, "eslint.config.js"), eslintConfigTemplate);
337
+ const tsconfigTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "tsconfig.json.template"), "utf-8");
338
+ const buildConfigTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "build.config.ts.template"), "utf-8");
339
+ const eslintConfigTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "eslint.config.js.template"), "utf-8");
340
+ yield fs__default.writeFile(safeResolvePath(targetDir, "tsconfig.json"), tsconfigTemplate);
341
+ yield fs__default.writeFile(safeResolvePath(targetDir, "build.config.ts"), buildConfigTemplate);
342
+ yield fs__default.writeFile(safeResolvePath(targetDir, "eslint.config.js"), eslintConfigTemplate);
309
343
  } catch (error) {
310
344
  console.error("\u65E0\u6CD5\u8BFB\u53D6\u6A21\u677F\u6587\u4EF6:", error);
311
345
  const tsconfig = {
@@ -318,15 +352,15 @@ function createConfigFiles(targetDir) {
318
352
  }
319
353
  };
320
354
  yield fs__default.writeFile(
321
- path__default.join(targetDir, "tsconfig.json"),
355
+ safeResolvePath(targetDir, "tsconfig.json"),
322
356
  JSON.stringify(tsconfig, null, 2)
323
357
  );
324
358
  yield fs__default.writeFile(
325
- path__default.join(targetDir, "build.config.ts"),
359
+ safeResolvePath(targetDir, "build.config.ts"),
326
360
  'import { defineBuildConfig } from "@knotx/build-config";\n\nexport default defineBuildConfig();'
327
361
  );
328
362
  yield fs__default.writeFile(
329
- path__default.join(targetDir, "eslint.config.js"),
363
+ safeResolvePath(targetDir, "eslint.config.js"),
330
364
  "export { default } from '@knotx/eslint-config';"
331
365
  );
332
366
  }
@@ -335,46 +369,49 @@ function createConfigFiles(targetDir) {
335
369
  function createSourceFiles(targetDir, pluginName, environment) {
336
370
  return __async(this, null, function* () {
337
371
  try {
338
- const indexTemplate = yield fs__default.readFile(path__default.join(templatesDir, "index.ts.template"), "utf-8");
339
- const indexContent = indexTemplate.replace(/\{\{pluginName\}\}/g, pluginName);
340
- yield fs__default.writeFile(path__default.join(targetDir, "src", "index.ts"), indexContent);
372
+ const pluginNameWithoutScope = pluginName.split("/").pop() || pluginName;
373
+ const camelCaseName = toCamelCase(pluginNameWithoutScope);
374
+ const pascalCaseName = toPascalCase(pluginNameWithoutScope);
375
+ const indexTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "index.ts.template"), "utf-8");
376
+ const indexContent = indexTemplate.replace(/\{\{pluginName\}\}/g, camelCaseName);
377
+ yield fs__default.writeFile(safeResolvePath(targetDir, "src", "index.ts"), indexContent);
341
378
  let templatePath;
342
379
  if (environment === "jsx") {
343
- templatePath = path__default.join(templatesDir, "plugin.jsx.template");
380
+ templatePath = safeResolvePath(templatesDir, "plugin.jsx.template");
344
381
  } else {
345
- templatePath = path__default.join(templatesDir, "plugin.jsx.template");
382
+ templatePath = safeResolvePath(templatesDir, "plugin.jsx.template");
346
383
  }
347
384
  const pluginTemplate = yield fs__default.readFile(templatePath, "utf-8");
348
- const pluginContent = pluginTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName));
349
- const fileName = `${pluginName}.tsx`;
350
- yield fs__default.writeFile(path__default.join(targetDir, "src", fileName), pluginContent);
351
- yield createPlayground(targetDir, pluginName, environment);
385
+ const pluginContent = pluginTemplate.replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName);
386
+ const fileName = `${camelCaseName}.tsx`;
387
+ yield fs__default.writeFile(safeResolvePath(targetDir, "src", fileName), pluginContent);
388
+ yield createPlayground(targetDir, pluginNameWithoutScope, camelCaseName, pascalCaseName, environment);
352
389
  } catch (error) {
353
390
  console.error("\u65E0\u6CD5\u8BFB\u53D6\u6A21\u677F\u6587\u4EF6:", error);
354
391
  }
355
392
  });
356
393
  }
357
- function createPlayground(targetDir, pluginName, environment) {
394
+ function createPlayground(targetDir, pluginName, camelCaseName, pascalCaseName, environment) {
358
395
  return __async(this, null, function* () {
359
- const playgroundDir = path__default.join(targetDir, "playground");
360
- yield fs__default.ensureDir(playgroundDir);
396
+ const playgroundDir = safeResolvePath(targetDir, "playground");
397
+ yield safeEnsureDir(playgroundDir);
361
398
  try {
362
- const viteConfigTemplate = yield fs__default.readFile(path__default.join(templatesDir, "vite.config.ts.template"), "utf-8");
363
- const htmlTemplate = yield fs__default.readFile(path__default.join(templatesDir, "playground/index.html.template"), "utf-8");
364
- const tsConfigTemplate = yield fs__default.readFile(path__default.join(templatesDir, "playground/tsconfig.json.template"), "utf-8");
399
+ const viteConfigTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "vite.config.ts.template"), "utf-8");
400
+ const htmlTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "playground/index.html.template"), "utf-8");
401
+ const tsConfigTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "playground/tsconfig.json.template"), "utf-8");
365
402
  const isReact = environment === "react";
366
403
  const mainTemplate = yield fs__default.readFile(
367
- path__default.join(templatesDir, `playground/main.${isReact ? "react" : "jsx"}.template`),
404
+ safeResolvePath(templatesDir, `playground/main.${isReact ? "react" : "jsx"}.template`),
368
405
  "utf-8"
369
406
  );
370
407
  const fileExtension = isReact ? "tsx" : "ts";
371
408
  const viteConfigContent = viteConfigTemplate.replace(new RegExp("\\{\\{#if isReact\\}\\}(.*?)\\{\\{\\/if\\}\\}", "gs"), isReact ? "$1" : "");
372
- const htmlContent = htmlTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName)).replace(/\{\{fileExtension\}\}/g, fileExtension);
373
- const mainContent = mainTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName));
374
- yield fs__default.writeFile(path__default.join(playgroundDir, "vite.config.ts"), viteConfigContent);
375
- yield fs__default.writeFile(path__default.join(playgroundDir, "index.html"), htmlContent);
376
- yield fs__default.writeFile(path__default.join(playgroundDir, `main.${fileExtension}`), mainContent);
377
- yield fs__default.writeFile(path__default.join(playgroundDir, "tsconfig.json"), tsConfigTemplate);
409
+ const htmlContent = htmlTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{pluginNameWithoutScope\}\}/g, pluginName).replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName).replace(/\{\{fileExtension\}\}/g, fileExtension);
410
+ const mainContent = mainTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName);
411
+ yield fs__default.writeFile(safeResolvePath(playgroundDir, "vite.config.ts"), viteConfigContent);
412
+ yield fs__default.writeFile(safeResolvePath(playgroundDir, "index.html"), htmlContent);
413
+ yield fs__default.writeFile(safeResolvePath(playgroundDir, `main.${fileExtension}`), mainContent);
414
+ yield fs__default.writeFile(safeResolvePath(playgroundDir, "tsconfig.json"), tsConfigTemplate);
378
415
  } catch (error) {
379
416
  console.error("\u8BFB\u53D6 playground \u6A21\u677F\u6587\u4EF6\u5931\u8D25:", error);
380
417
  }
@@ -383,50 +420,84 @@ function createPlayground(targetDir, pluginName, environment) {
383
420
  function createReadme(targetDir, pluginName) {
384
421
  return __async(this, null, function* () {
385
422
  try {
386
- const readmeTemplate = yield fs__default.readFile(path__default.join(templatesDir, "README.md.template"), "utf-8");
387
- const description = `${capitalize(pluginName)} Plugin for Knotx`;
388
- const readmeContent = readmeTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName)).replace(/\{\{description\}\}/g, description);
389
- yield fs__default.writeFile(path__default.join(targetDir, "README.md"), readmeContent);
423
+ const readmeTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, "README.md.template"), "utf-8");
424
+ const pluginNameWithoutScope = pluginName.split("/").pop() || pluginName;
425
+ const camelCaseName = toCamelCase(pluginNameWithoutScope);
426
+ const pascalCaseName = toPascalCase(pluginNameWithoutScope);
427
+ const description = `${pascalCaseName} Plugin for Knotx`;
428
+ const readmeContent = readmeTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{pluginNameWithoutScope\}\}/g, pluginNameWithoutScope).replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName).replace(/\{\{description\}\}/g, description);
429
+ yield fs__default.writeFile(safeResolvePath(targetDir, "README.md"), readmeContent);
390
430
  } catch (error) {
391
431
  console.error("\u65E0\u6CD5\u8BFB\u53D6 README \u6A21\u677F\u6587\u4EF6:", error);
392
- const readmeContent = `# @knotx/plugins-${pluginName}
432
+ const pluginNameWithoutScope = pluginName.split("/").pop() || pluginName;
433
+ const camelCaseName = toCamelCase(pluginNameWithoutScope);
434
+ const pascalCaseName = toPascalCase(pluginNameWithoutScope);
435
+ const readmeContent = `# ${pluginName}
393
436
 
394
- ${capitalize(pluginName)} Plugin for Knotx
437
+ ${pascalCaseName} Plugin for Knotx
395
438
 
396
439
  ## \u5B89\u88C5
397
440
 
398
441
  \`\`\`bash
399
- npm install @knotx/plugins-${pluginName}
442
+ npm install ${pluginName}
400
443
  # \u6216\u8005
401
- yarn add @knotx/plugins-${pluginName}
444
+ yarn add ${pluginName}
402
445
  # \u6216\u8005
403
- pnpm add @knotx/plugins-${pluginName}
446
+ pnpm add ${pluginName}
404
447
  \`\`\`
405
448
 
406
449
  ## \u4F7F\u7528\u65B9\u6CD5
407
450
 
408
451
  \`\`\`typescript
409
- import { ${pluginName} } from '@knotx/plugins-${pluginName}';
452
+ import { ${camelCaseName} } from '${pluginName}';
410
453
 
411
454
  // \u5728\u60A8\u7684Knotx\u5E94\u7528\u4E2D\u4F7F\u7528\u63D2\u4EF6
412
- app.use(${pluginName});
455
+ app.use(${camelCaseName});
413
456
  \`\`\`
414
457
 
415
458
  ## \u8BB8\u53EF\u8BC1
416
459
 
417
460
  MIT`;
418
- yield fs__default.writeFile(path__default.join(targetDir, "README.md"), readmeContent);
461
+ yield fs__default.writeFile(safeResolvePath(targetDir, "README.md"), readmeContent);
419
462
  }
420
463
  });
421
464
  }
422
465
  function capitalize(str) {
423
466
  return str.charAt(0).toUpperCase() + str.slice(1);
424
467
  }
468
+ function toCamelCase(str) {
469
+ return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
470
+ }
471
+ function toPascalCase(str) {
472
+ return capitalize(toCamelCase(str));
473
+ }
474
+ function safeResolvePath(basePath, ...paths) {
475
+ try {
476
+ return path__default.resolve(basePath, ...paths);
477
+ } catch (error) {
478
+ console.warn(chalk__default.yellow(`\u26A0\uFE0F \u8DEF\u5F84\u89E3\u6790\u5931\u8D25: ${error}`));
479
+ return paths.reduce((result, part) => `${result}/${part}`, basePath);
480
+ }
481
+ }
482
+ function safeEnsureDir(dirPath) {
483
+ return __async(this, null, function* () {
484
+ try {
485
+ yield fs__default.ensureDir(dirPath);
486
+ } catch (e) {
487
+ console.warn(chalk__default.yellow(`\u26A0\uFE0F \u521B\u5EFA\u76EE\u5F55 ${dirPath} \u5931\u8D25\uFF0C\u5C1D\u8BD5\u4F7F\u7528\u5907\u7528\u65B9\u6CD5`));
488
+ try {
489
+ fs__default.mkdirSync(dirPath, { recursive: true });
490
+ } catch (syncError) {
491
+ throw new Error(`\u65E0\u6CD5\u521B\u5EFA\u76EE\u5F55 ${dirPath}: ${syncError}`);
492
+ }
493
+ }
494
+ });
495
+ }
425
496
  function createGitignore(targetDir) {
426
497
  return __async(this, null, function* () {
427
498
  try {
428
- const gitignoreTemplate = yield fs__default.readFile(path__default.join(templatesDir, ".gitignore.template"), "utf-8");
429
- yield fs__default.writeFile(path__default.join(targetDir, ".gitignore"), gitignoreTemplate);
499
+ const gitignoreTemplate = yield fs__default.readFile(safeResolvePath(templatesDir, ".gitignore.template"), "utf-8");
500
+ yield fs__default.writeFile(safeResolvePath(targetDir, ".gitignore"), gitignoreTemplate);
430
501
  } catch (error) {
431
502
  console.error("\u65E0\u6CD5\u8BFB\u53D6 .gitignore \u6A21\u677F\u6587\u4EF6:", error);
432
503
  const gitignoreContent = `# \u4F9D\u8D56\u76EE\u5F55
@@ -456,15 +527,16 @@ pnpm-debug.log*
456
527
  # \u6D4B\u8BD5\u8986\u76D6\u7387
457
528
  coverage
458
529
  `;
459
- yield fs__default.writeFile(path__default.join(targetDir, ".gitignore"), gitignoreContent);
530
+ yield fs__default.writeFile(safeResolvePath(targetDir, ".gitignore"), gitignoreContent);
460
531
  }
461
532
  });
462
533
  }
463
534
  function initGitRepo(targetDir) {
464
535
  try {
465
- node_child_process.execSync("git init", { cwd: targetDir, stdio: "ignore" });
466
- node_child_process.execSync("git add .", { cwd: targetDir, stdio: "ignore" });
467
- node_child_process.execSync('git commit -m "Initial commit with Knotx plugin template"', {
536
+ if (!fs__default.existsSync(targetDir)) {
537
+ throw new Error(`\u76EE\u5F55 ${targetDir} \u4E0D\u5B58\u5728`);
538
+ }
539
+ const gitOptions = {
468
540
  cwd: targetDir,
469
541
  stdio: "ignore",
470
542
  env: __spreadProps(__spreadValues({}, process__default.env), {
@@ -473,7 +545,10 @@ function initGitRepo(targetDir) {
473
545
  GIT_COMMITTER_NAME: "Knotx CLI",
474
546
  GIT_COMMITTER_EMAIL: "knotx-cli@noreply.github.com"
475
547
  })
476
- });
548
+ };
549
+ node_child_process.execSync("git init", gitOptions);
550
+ node_child_process.execSync("git add .", gitOptions);
551
+ node_child_process.execSync('git commit -m "Initial commit with Knotx plugin template"', gitOptions);
477
552
  return true;
478
553
  } catch (error) {
479
554
  if (error instanceof Error) {
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'node:child_process';
2
+ import { exec, execSync } from 'node:child_process';
3
3
  import path from 'node:path';
4
4
  import process from 'node:process';
5
+ import { promisify } from 'node:util';
5
6
  import chalk from 'chalk';
6
7
  import { Command } from 'commander';
7
8
  import fs from 'fs-extra';
@@ -47,6 +48,11 @@ var __async = (__this, __arguments, generator) => {
47
48
  step((generator = generator.apply(__this, __arguments)).next());
48
49
  });
49
50
  };
51
+ process.on("uncaughtException", (error) => {
52
+ console.error(chalk.red("\u274C \u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:"));
53
+ console.error(chalk.red(error.stack || error.toString()));
54
+ process.exit(1);
55
+ });
50
56
  const templatesDir = path.resolve(__dirname, "..", "templates");
51
57
  const program = new Command();
52
58
  program.name("knotx").description("Knotx CLI - \u521B\u5EFA\u548C\u7BA1\u7406Knotx\u63D2\u4EF6").version("0.0.1");
@@ -56,33 +62,43 @@ program.command("create-plugin").description("\u521B\u5EFA\u4E00\u4E2A\u65B0\u76
56
62
  {
57
63
  type: "input",
58
64
  name: "pluginName",
59
- message: "\u8BF7\u8F93\u5165\u63D2\u4EF6\u540D\u79F0 (\u4F8B\u5982: my-plugin):",
65
+ message: "\u8BF7\u8F93\u5165\u63D2\u4EF6\u540D\u79F0 (\u4F8B\u5982: @knotx/plugins-my-plugin):",
60
66
  validate: (input) => {
61
67
  if (!input)
62
68
  return "\u63D2\u4EF6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
63
- if (!/^[a-z0-9-]+$/.test(input))
64
- return "\u63D2\u4EF6\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
69
+ if (!/^(?:@[a-z0-9-]+\/)?[a-z0-9-]+$/.test(input))
70
+ return "\u63D2\u4EF6\u540D\u79F0\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5E94\u8BE5\u662F\u6709\u6548\u7684npm\u5305\u540D";
65
71
  return true;
66
- }
72
+ },
73
+ default: "@knotx/plugins-"
67
74
  },
68
75
  {
69
76
  type: "list",
70
77
  name: "environment",
71
78
  message: "\u8BF7\u9009\u62E9\u63D2\u4EF6\u8FD0\u884C\u73AF\u5883:",
72
- choices: ["jsx", "react"]
79
+ choices: ["react", "jsx"]
73
80
  }
74
81
  ]);
75
82
  const { pluginName, environment } = answers;
76
- const fullPluginName = `plugins-${pluginName}`;
77
- const targetDir = path.resolve(process.cwd(), fullPluginName);
83
+ const dirName = pluginName.split("/").pop();
84
+ let currentDir;
85
+ try {
86
+ currentDir = process.cwd();
87
+ } catch (error) {
88
+ console.log(chalk.red(`\u274C \u9519\u8BEF: \u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55: ${error}`));
89
+ console.log(chalk.yellow("\u5C1D\u8BD5\u4F7F\u7528\u5907\u7528\u65B9\u6CD5\u83B7\u53D6\u76EE\u5F55..."));
90
+ currentDir = path.resolve(__dirname, "..");
91
+ console.log(chalk.green(`\u2705 \u4F7F\u7528\u5907\u7528\u76EE\u5F55: ${currentDir}`));
92
+ }
93
+ const targetDir = safeResolvePath(currentDir, dirName);
78
94
  if (fs.existsSync(targetDir)) {
79
- console.log(chalk.red(`\u274C \u9519\u8BEF: \u76EE\u5F55 ${fullPluginName} \u5DF2\u5B58\u5728`));
95
+ console.log(chalk.red(`\u274C \u9519\u8BEF: \u76EE\u5F55 ${dirName} \u5DF2\u5B58\u5728`));
80
96
  return;
81
97
  }
82
98
  let spinner = ora("\u{1F4C2} \u521B\u5EFA\u63D2\u4EF6\u76EE\u5F55...").start();
83
99
  try {
84
- yield fs.ensureDir(targetDir);
85
- yield fs.ensureDir(path.join(targetDir, "src"));
100
+ yield safeEnsureDir(targetDir);
101
+ yield safeEnsureDir(safeResolvePath(targetDir, "src"));
86
102
  spinner.succeed("\u{1F4C2} \u63D2\u4EF6\u76EE\u5F55\u521B\u5EFA\u6210\u529F");
87
103
  spinner = ora("\u{1F50D} \u83B7\u53D6 @knotx \u5305\u7684\u6700\u65B0\u7248\u672C...").start();
88
104
  const knotxVersions = yield getKnotxPackageVersions();
@@ -111,13 +127,19 @@ program.command("create-plugin").description("\u521B\u5EFA\u4E00\u4E2A\u65B0\u76
111
127
  console.warn(chalk.yellow(` \u53EF\u80FD\u7684\u539F\u56E0: ${error.message}`));
112
128
  }
113
129
  console.log(chalk.green(`
114
- \u2705 \u63D2\u4EF6 ${chalk.bold(fullPluginName)} \u521B\u5EFA\u6210\u529F\uFF01`));
130
+ \u2705 \u63D2\u4EF6 ${chalk.bold(pluginName)} \u521B\u5EFA\u6210\u529F\uFF01`));
115
131
  console.log(`
116
132
  \u{1F4C1} \u63D2\u4EF6\u76EE\u5F55: ${chalk.cyan(targetDir)}`);
117
133
  console.log(`
118
134
  \u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u4EE5\u5B89\u88C5\u4F9D\u8D56\u5E76\u5F00\u59CB\u5F00\u53D1:
119
135
  `);
120
- console.log(chalk.cyan(` cd ${path.relative(process.cwd(), targetDir)}`));
136
+ let cdCommand;
137
+ try {
138
+ cdCommand = `cd ${path.relative(process.cwd(), targetDir)}`;
139
+ } catch (e) {
140
+ cdCommand = `cd ${targetDir}`;
141
+ }
142
+ console.log(chalk.cyan(` ${cdCommand}`));
121
143
  console.log(chalk.cyan(" pnpm install"));
122
144
  console.log(chalk.cyan(" pnpm build"));
123
145
  console.log(chalk.cyan(" pnpm dev # \u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668\uFF0C\u8FD0\u884C playground"));
@@ -147,15 +169,23 @@ function getKnotxPackageVersions() {
147
169
  "@knotx/plugins-base-render"
148
170
  ];
149
171
  const npmRegistry = "https://registry.npmjs.org";
150
- for (const pkg of packages) {
151
- try {
152
- const version = execSync(`npm view ${pkg} version --registry=${npmRegistry}`, { encoding: "utf8" }).trim();
153
- knotxVersions[pkg] = `^${version}`;
154
- } catch (e) {
155
- console.warn(chalk.yellow(`\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6 ${pkg} \u7684\u6700\u65B0\u7248\u672C\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u7248\u672C`));
156
- knotxVersions[pkg] = "latest";
157
- }
158
- }
172
+ const execAsync = promisify(exec);
173
+ const results = yield Promise.all(
174
+ packages.map((pkg) => __async(this, null, function* () {
175
+ try {
176
+ const cmd = `npm view ${pkg} version --registry=${npmRegistry} --timeout=10000`;
177
+ const { stdout } = yield execAsync(cmd, { timeout: 1e4 });
178
+ const version = stdout.trim();
179
+ return { pkg, version: `^${version}`, success: true };
180
+ } catch (e) {
181
+ console.warn(chalk.yellow(`\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6 ${pkg} \u7684\u6700\u65B0\u7248\u672C\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u7248\u672C`));
182
+ return { pkg, version: "latest", success: false };
183
+ }
184
+ }))
185
+ );
186
+ results.forEach(({ pkg, version }) => {
187
+ knotxVersions[pkg] = version;
188
+ });
159
189
  return knotxVersions;
160
190
  } catch (e) {
161
191
  console.warn(chalk.yellow("\u26A0\uFE0F \u83B7\u53D6 @knotx \u5305\u7248\u672C\u5931\u8D25\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u7248\u672C"));
@@ -165,7 +195,9 @@ function getKnotxPackageVersions() {
165
195
  "@knotx/jsx": "latest",
166
196
  "@knotx/build-config": "latest",
167
197
  "@knotx/eslint-config": "latest",
168
- "@knotx/typescript-config": "latest"
198
+ "@knotx/typescript-config": "latest",
199
+ "@knotx/react": "latest",
200
+ "@knotx/plugins-base-render": "latest"
169
201
  };
170
202
  }
171
203
  });
@@ -173,10 +205,10 @@ function getKnotxPackageVersions() {
173
205
  function createPackageJson(targetDir, pluginName, environment, knotxVersions) {
174
206
  return __async(this, null, function* () {
175
207
  const packageJson = {
176
- name: `@knotx/plugins-${pluginName}`,
208
+ name: pluginName,
177
209
  type: "module",
178
210
  version: "0.0.1",
179
- description: `${capitalize(pluginName)} Plugin for Knotx`,
211
+ description: `${capitalize(pluginName.split("/").pop() || pluginName)} Plugin for Knotx`,
180
212
  license: "MIT",
181
213
  publishConfig: {
182
214
  access: "public"
@@ -220,13 +252,15 @@ function createPackageJson(targetDir, pluginName, environment, knotxVersions) {
220
252
  "vitest": "^3.0.0"
221
253
  }
222
254
  };
223
- if (environment === "jsx") ; else if (environment === "react") {
224
- packageJson.peerDependencies.react = "^17";
225
- packageJson.peerDependencies["react-dom"] = "^17";
226
- packageJson.devDependencies["@knotx/react"] = knotxVersions["@knotx/react"];
255
+ if (environment === "jsx") {
227
256
  packageJson.devDependencies["@knotx/plugins-base-render"] = knotxVersions["@knotx/plugins-base-render"];
228
- packageJson.devDependencies.react = "^17";
257
+ } else if (environment === "react") {
258
+ packageJson.peerDependencies.react = ">=17";
259
+ packageJson.peerDependencies["react-dom"] = ">=17";
260
+ packageJson.peerDependencies["@knotx/react"] = knotxVersions["@knotx/react"];
261
+ packageJson.devDependencies["@knotx/react"] = knotxVersions["@knotx/react"];
229
262
  packageJson.devDependencies["@vitejs/plugin-react"] = "^4.3.4";
263
+ packageJson.devDependencies.react = "^17";
230
264
  packageJson.devDependencies["react-dom"] = "^17";
231
265
  packageJson.devDependencies["@types/react"] = "^17";
232
266
  packageJson.devDependencies["@types/react-dom"] = "^17";
@@ -289,12 +323,12 @@ function sortObjectAlphabetically(obj) {
289
323
  function createConfigFiles(targetDir) {
290
324
  return __async(this, null, function* () {
291
325
  try {
292
- const tsconfigTemplate = yield fs.readFile(path.join(templatesDir, "tsconfig.json.template"), "utf-8");
293
- const buildConfigTemplate = yield fs.readFile(path.join(templatesDir, "build.config.ts.template"), "utf-8");
294
- const eslintConfigTemplate = yield fs.readFile(path.join(templatesDir, "eslint.config.js.template"), "utf-8");
295
- yield fs.writeFile(path.join(targetDir, "tsconfig.json"), tsconfigTemplate);
296
- yield fs.writeFile(path.join(targetDir, "build.config.ts"), buildConfigTemplate);
297
- yield fs.writeFile(path.join(targetDir, "eslint.config.js"), eslintConfigTemplate);
326
+ const tsconfigTemplate = yield fs.readFile(safeResolvePath(templatesDir, "tsconfig.json.template"), "utf-8");
327
+ const buildConfigTemplate = yield fs.readFile(safeResolvePath(templatesDir, "build.config.ts.template"), "utf-8");
328
+ const eslintConfigTemplate = yield fs.readFile(safeResolvePath(templatesDir, "eslint.config.js.template"), "utf-8");
329
+ yield fs.writeFile(safeResolvePath(targetDir, "tsconfig.json"), tsconfigTemplate);
330
+ yield fs.writeFile(safeResolvePath(targetDir, "build.config.ts"), buildConfigTemplate);
331
+ yield fs.writeFile(safeResolvePath(targetDir, "eslint.config.js"), eslintConfigTemplate);
298
332
  } catch (error) {
299
333
  console.error("\u65E0\u6CD5\u8BFB\u53D6\u6A21\u677F\u6587\u4EF6:", error);
300
334
  const tsconfig = {
@@ -307,15 +341,15 @@ function createConfigFiles(targetDir) {
307
341
  }
308
342
  };
309
343
  yield fs.writeFile(
310
- path.join(targetDir, "tsconfig.json"),
344
+ safeResolvePath(targetDir, "tsconfig.json"),
311
345
  JSON.stringify(tsconfig, null, 2)
312
346
  );
313
347
  yield fs.writeFile(
314
- path.join(targetDir, "build.config.ts"),
348
+ safeResolvePath(targetDir, "build.config.ts"),
315
349
  'import { defineBuildConfig } from "@knotx/build-config";\n\nexport default defineBuildConfig();'
316
350
  );
317
351
  yield fs.writeFile(
318
- path.join(targetDir, "eslint.config.js"),
352
+ safeResolvePath(targetDir, "eslint.config.js"),
319
353
  "export { default } from '@knotx/eslint-config';"
320
354
  );
321
355
  }
@@ -324,46 +358,49 @@ function createConfigFiles(targetDir) {
324
358
  function createSourceFiles(targetDir, pluginName, environment) {
325
359
  return __async(this, null, function* () {
326
360
  try {
327
- const indexTemplate = yield fs.readFile(path.join(templatesDir, "index.ts.template"), "utf-8");
328
- const indexContent = indexTemplate.replace(/\{\{pluginName\}\}/g, pluginName);
329
- yield fs.writeFile(path.join(targetDir, "src", "index.ts"), indexContent);
361
+ const pluginNameWithoutScope = pluginName.split("/").pop() || pluginName;
362
+ const camelCaseName = toCamelCase(pluginNameWithoutScope);
363
+ const pascalCaseName = toPascalCase(pluginNameWithoutScope);
364
+ const indexTemplate = yield fs.readFile(safeResolvePath(templatesDir, "index.ts.template"), "utf-8");
365
+ const indexContent = indexTemplate.replace(/\{\{pluginName\}\}/g, camelCaseName);
366
+ yield fs.writeFile(safeResolvePath(targetDir, "src", "index.ts"), indexContent);
330
367
  let templatePath;
331
368
  if (environment === "jsx") {
332
- templatePath = path.join(templatesDir, "plugin.jsx.template");
369
+ templatePath = safeResolvePath(templatesDir, "plugin.jsx.template");
333
370
  } else {
334
- templatePath = path.join(templatesDir, "plugin.jsx.template");
371
+ templatePath = safeResolvePath(templatesDir, "plugin.jsx.template");
335
372
  }
336
373
  const pluginTemplate = yield fs.readFile(templatePath, "utf-8");
337
- const pluginContent = pluginTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName));
338
- const fileName = `${pluginName}.tsx`;
339
- yield fs.writeFile(path.join(targetDir, "src", fileName), pluginContent);
340
- yield createPlayground(targetDir, pluginName, environment);
374
+ const pluginContent = pluginTemplate.replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName);
375
+ const fileName = `${camelCaseName}.tsx`;
376
+ yield fs.writeFile(safeResolvePath(targetDir, "src", fileName), pluginContent);
377
+ yield createPlayground(targetDir, pluginNameWithoutScope, camelCaseName, pascalCaseName, environment);
341
378
  } catch (error) {
342
379
  console.error("\u65E0\u6CD5\u8BFB\u53D6\u6A21\u677F\u6587\u4EF6:", error);
343
380
  }
344
381
  });
345
382
  }
346
- function createPlayground(targetDir, pluginName, environment) {
383
+ function createPlayground(targetDir, pluginName, camelCaseName, pascalCaseName, environment) {
347
384
  return __async(this, null, function* () {
348
- const playgroundDir = path.join(targetDir, "playground");
349
- yield fs.ensureDir(playgroundDir);
385
+ const playgroundDir = safeResolvePath(targetDir, "playground");
386
+ yield safeEnsureDir(playgroundDir);
350
387
  try {
351
- const viteConfigTemplate = yield fs.readFile(path.join(templatesDir, "vite.config.ts.template"), "utf-8");
352
- const htmlTemplate = yield fs.readFile(path.join(templatesDir, "playground/index.html.template"), "utf-8");
353
- const tsConfigTemplate = yield fs.readFile(path.join(templatesDir, "playground/tsconfig.json.template"), "utf-8");
388
+ const viteConfigTemplate = yield fs.readFile(safeResolvePath(templatesDir, "vite.config.ts.template"), "utf-8");
389
+ const htmlTemplate = yield fs.readFile(safeResolvePath(templatesDir, "playground/index.html.template"), "utf-8");
390
+ const tsConfigTemplate = yield fs.readFile(safeResolvePath(templatesDir, "playground/tsconfig.json.template"), "utf-8");
354
391
  const isReact = environment === "react";
355
392
  const mainTemplate = yield fs.readFile(
356
- path.join(templatesDir, `playground/main.${isReact ? "react" : "jsx"}.template`),
393
+ safeResolvePath(templatesDir, `playground/main.${isReact ? "react" : "jsx"}.template`),
357
394
  "utf-8"
358
395
  );
359
396
  const fileExtension = isReact ? "tsx" : "ts";
360
397
  const viteConfigContent = viteConfigTemplate.replace(new RegExp("\\{\\{#if isReact\\}\\}(.*?)\\{\\{\\/if\\}\\}", "gs"), isReact ? "$1" : "");
361
- const htmlContent = htmlTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName)).replace(/\{\{fileExtension\}\}/g, fileExtension);
362
- const mainContent = mainTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName));
363
- yield fs.writeFile(path.join(playgroundDir, "vite.config.ts"), viteConfigContent);
364
- yield fs.writeFile(path.join(playgroundDir, "index.html"), htmlContent);
365
- yield fs.writeFile(path.join(playgroundDir, `main.${fileExtension}`), mainContent);
366
- yield fs.writeFile(path.join(playgroundDir, "tsconfig.json"), tsConfigTemplate);
398
+ const htmlContent = htmlTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{pluginNameWithoutScope\}\}/g, pluginName).replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName).replace(/\{\{fileExtension\}\}/g, fileExtension);
399
+ const mainContent = mainTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName);
400
+ yield fs.writeFile(safeResolvePath(playgroundDir, "vite.config.ts"), viteConfigContent);
401
+ yield fs.writeFile(safeResolvePath(playgroundDir, "index.html"), htmlContent);
402
+ yield fs.writeFile(safeResolvePath(playgroundDir, `main.${fileExtension}`), mainContent);
403
+ yield fs.writeFile(safeResolvePath(playgroundDir, "tsconfig.json"), tsConfigTemplate);
367
404
  } catch (error) {
368
405
  console.error("\u8BFB\u53D6 playground \u6A21\u677F\u6587\u4EF6\u5931\u8D25:", error);
369
406
  }
@@ -372,50 +409,84 @@ function createPlayground(targetDir, pluginName, environment) {
372
409
  function createReadme(targetDir, pluginName) {
373
410
  return __async(this, null, function* () {
374
411
  try {
375
- const readmeTemplate = yield fs.readFile(path.join(templatesDir, "README.md.template"), "utf-8");
376
- const description = `${capitalize(pluginName)} Plugin for Knotx`;
377
- const readmeContent = readmeTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{capitalizedPluginName\}\}/g, capitalize(pluginName)).replace(/\{\{description\}\}/g, description);
378
- yield fs.writeFile(path.join(targetDir, "README.md"), readmeContent);
412
+ const readmeTemplate = yield fs.readFile(safeResolvePath(templatesDir, "README.md.template"), "utf-8");
413
+ const pluginNameWithoutScope = pluginName.split("/").pop() || pluginName;
414
+ const camelCaseName = toCamelCase(pluginNameWithoutScope);
415
+ const pascalCaseName = toPascalCase(pluginNameWithoutScope);
416
+ const description = `${pascalCaseName} Plugin for Knotx`;
417
+ const readmeContent = readmeTemplate.replace(/\{\{pluginName\}\}/g, pluginName).replace(/\{\{pluginNameWithoutScope\}\}/g, pluginNameWithoutScope).replace(/\{\{camelCaseName\}\}/g, camelCaseName).replace(/\{\{capitalizedPluginName\}\}/g, pascalCaseName).replace(/\{\{description\}\}/g, description);
418
+ yield fs.writeFile(safeResolvePath(targetDir, "README.md"), readmeContent);
379
419
  } catch (error) {
380
420
  console.error("\u65E0\u6CD5\u8BFB\u53D6 README \u6A21\u677F\u6587\u4EF6:", error);
381
- const readmeContent = `# @knotx/plugins-${pluginName}
421
+ const pluginNameWithoutScope = pluginName.split("/").pop() || pluginName;
422
+ const camelCaseName = toCamelCase(pluginNameWithoutScope);
423
+ const pascalCaseName = toPascalCase(pluginNameWithoutScope);
424
+ const readmeContent = `# ${pluginName}
382
425
 
383
- ${capitalize(pluginName)} Plugin for Knotx
426
+ ${pascalCaseName} Plugin for Knotx
384
427
 
385
428
  ## \u5B89\u88C5
386
429
 
387
430
  \`\`\`bash
388
- npm install @knotx/plugins-${pluginName}
431
+ npm install ${pluginName}
389
432
  # \u6216\u8005
390
- yarn add @knotx/plugins-${pluginName}
433
+ yarn add ${pluginName}
391
434
  # \u6216\u8005
392
- pnpm add @knotx/plugins-${pluginName}
435
+ pnpm add ${pluginName}
393
436
  \`\`\`
394
437
 
395
438
  ## \u4F7F\u7528\u65B9\u6CD5
396
439
 
397
440
  \`\`\`typescript
398
- import { ${pluginName} } from '@knotx/plugins-${pluginName}';
441
+ import { ${camelCaseName} } from '${pluginName}';
399
442
 
400
443
  // \u5728\u60A8\u7684Knotx\u5E94\u7528\u4E2D\u4F7F\u7528\u63D2\u4EF6
401
- app.use(${pluginName});
444
+ app.use(${camelCaseName});
402
445
  \`\`\`
403
446
 
404
447
  ## \u8BB8\u53EF\u8BC1
405
448
 
406
449
  MIT`;
407
- yield fs.writeFile(path.join(targetDir, "README.md"), readmeContent);
450
+ yield fs.writeFile(safeResolvePath(targetDir, "README.md"), readmeContent);
408
451
  }
409
452
  });
410
453
  }
411
454
  function capitalize(str) {
412
455
  return str.charAt(0).toUpperCase() + str.slice(1);
413
456
  }
457
+ function toCamelCase(str) {
458
+ return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
459
+ }
460
+ function toPascalCase(str) {
461
+ return capitalize(toCamelCase(str));
462
+ }
463
+ function safeResolvePath(basePath, ...paths) {
464
+ try {
465
+ return path.resolve(basePath, ...paths);
466
+ } catch (error) {
467
+ console.warn(chalk.yellow(`\u26A0\uFE0F \u8DEF\u5F84\u89E3\u6790\u5931\u8D25: ${error}`));
468
+ return paths.reduce((result, part) => `${result}/${part}`, basePath);
469
+ }
470
+ }
471
+ function safeEnsureDir(dirPath) {
472
+ return __async(this, null, function* () {
473
+ try {
474
+ yield fs.ensureDir(dirPath);
475
+ } catch (e) {
476
+ console.warn(chalk.yellow(`\u26A0\uFE0F \u521B\u5EFA\u76EE\u5F55 ${dirPath} \u5931\u8D25\uFF0C\u5C1D\u8BD5\u4F7F\u7528\u5907\u7528\u65B9\u6CD5`));
477
+ try {
478
+ fs.mkdirSync(dirPath, { recursive: true });
479
+ } catch (syncError) {
480
+ throw new Error(`\u65E0\u6CD5\u521B\u5EFA\u76EE\u5F55 ${dirPath}: ${syncError}`);
481
+ }
482
+ }
483
+ });
484
+ }
414
485
  function createGitignore(targetDir) {
415
486
  return __async(this, null, function* () {
416
487
  try {
417
- const gitignoreTemplate = yield fs.readFile(path.join(templatesDir, ".gitignore.template"), "utf-8");
418
- yield fs.writeFile(path.join(targetDir, ".gitignore"), gitignoreTemplate);
488
+ const gitignoreTemplate = yield fs.readFile(safeResolvePath(templatesDir, ".gitignore.template"), "utf-8");
489
+ yield fs.writeFile(safeResolvePath(targetDir, ".gitignore"), gitignoreTemplate);
419
490
  } catch (error) {
420
491
  console.error("\u65E0\u6CD5\u8BFB\u53D6 .gitignore \u6A21\u677F\u6587\u4EF6:", error);
421
492
  const gitignoreContent = `# \u4F9D\u8D56\u76EE\u5F55
@@ -445,15 +516,16 @@ pnpm-debug.log*
445
516
  # \u6D4B\u8BD5\u8986\u76D6\u7387
446
517
  coverage
447
518
  `;
448
- yield fs.writeFile(path.join(targetDir, ".gitignore"), gitignoreContent);
519
+ yield fs.writeFile(safeResolvePath(targetDir, ".gitignore"), gitignoreContent);
449
520
  }
450
521
  });
451
522
  }
452
523
  function initGitRepo(targetDir) {
453
524
  try {
454
- execSync("git init", { cwd: targetDir, stdio: "ignore" });
455
- execSync("git add .", { cwd: targetDir, stdio: "ignore" });
456
- execSync('git commit -m "Initial commit with Knotx plugin template"', {
525
+ if (!fs.existsSync(targetDir)) {
526
+ throw new Error(`\u76EE\u5F55 ${targetDir} \u4E0D\u5B58\u5728`);
527
+ }
528
+ const gitOptions = {
457
529
  cwd: targetDir,
458
530
  stdio: "ignore",
459
531
  env: __spreadProps(__spreadValues({}, process.env), {
@@ -462,7 +534,10 @@ function initGitRepo(targetDir) {
462
534
  GIT_COMMITTER_NAME: "Knotx CLI",
463
535
  GIT_COMMITTER_EMAIL: "knotx-cli@noreply.github.com"
464
536
  })
465
- });
537
+ };
538
+ execSync("git init", gitOptions);
539
+ execSync("git add .", gitOptions);
540
+ execSync('git commit -m "Initial commit with Knotx plugin template"', gitOptions);
466
541
  return true;
467
542
  } catch (error) {
468
543
  if (error instanceof Error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knotx/cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "CLI tool for Knotx",
5
5
  "author": "boenfu",
6
6
  "license": "MIT",
@@ -11,8 +11,7 @@
11
11
  "directory": "packages/cli"
12
12
  },
13
13
  "publishConfig": {
14
- "access": "public",
15
- "registry": "https://registry.npmjs.org/"
14
+ "access": "public"
16
15
  },
17
16
  "exports": {
18
17
  ".": {
@@ -41,9 +40,9 @@
41
40
  "devDependencies": {
42
41
  "@types/fs-extra": "^11.0.4",
43
42
  "@types/inquirer": "^9.0.7",
44
- "@knotx/build-config": "0.0.3",
45
- "@knotx/eslint-config": "0.0.3",
46
- "@knotx/typescript-config": "0.0.3"
43
+ "@knotx/build-config": "0.0.5",
44
+ "@knotx/eslint-config": "0.0.5",
45
+ "@knotx/typescript-config": "0.0.5"
47
46
  },
48
47
  "scripts": {
49
48
  "build": "unbuild --failOnWarn=false",
@@ -1,21 +1,21 @@
1
- # @knotx/plugins-{{pluginName}}
1
+ # {{pluginName}}
2
2
 
3
3
  {{description}}
4
4
 
5
5
  ## 安装
6
6
 
7
7
  ```bash
8
- npm install @knotx/plugins-{{pluginName}}
8
+ npm install {{pluginName}}
9
9
  # 或者
10
- yarn add @knotx/plugins-{{pluginName}}
10
+ yarn add {{pluginName}}
11
11
  # 或者
12
- pnpm add @knotx/plugins-{{pluginName}}
12
+ pnpm add {{pluginName}}
13
13
  ```
14
14
 
15
15
  ## 使用方法
16
16
 
17
17
  ```typescript
18
- import { {{capitalizedPluginName}} } from '@knotx/plugins-{{pluginName}}'
18
+ import { {{capitalizedPluginName}} } from '{{pluginName}}'
19
19
  ```
20
20
 
21
21
  ## 许可证
@@ -6,7 +6,6 @@
6
6
  <title>{{capitalizedPluginName}} Plugin Playground</title>
7
7
  <style>
8
8
  body {
9
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
10
9
  margin: 0;
11
10
  padding: 20px;
12
11
  background-color: #f7f7f7;
@@ -29,7 +28,7 @@
29
28
  <body>
30
29
  <div class="container">
31
30
  <h1>{{capitalizedPluginName}} Plugin Demo</h1>
32
- <p>这是 {{pluginName}} 插件的演示页面。您可以在这里测试插件的功能。</p>
31
+ <p>这是 {{pluginNameWithoutScope}} 插件的演示页面。您可以在这里测试插件的功能。</p>
33
32
 
34
33
  <div id="app">
35
34
  <!-- 插件内容将在这里展示 -->
@@ -1,11 +1,4 @@
1
1
  import { {{capitalizedPluginName}} } from '../src'
2
2
 
3
3
  // 在这里初始化和使用您的插件
4
- const plugin = new {{capitalizedPluginName}}()
5
- plugin.init()
6
-
7
- // 更新演示区域
8
- const appElement = document.getElementById('app')
9
- if (appElement) {
10
- appElement.innerHTML += '<p>插件已初始化,请查看控制台输出</p>'
11
- }
4
+ const plugin = new {{capitalizedPluginName}}();
@@ -1,4 +1,3 @@
1
- import { BaseRender } from '@knotx/plugins-base-render'
2
1
  import { Knotx } from '@knotx/react'
3
2
  import { StrictMode } from 'react'
4
3
  import ReactDOM from 'react-dom'
@@ -31,7 +30,7 @@ function App() {
31
30
  style={{ height: 480, boxShadow: '0 0 2px #333', borderRadius: 4 }}
32
31
  nodes={nodes}
33
32
  edges={edges}
34
- plugins={[BaseRender, {{capitalizedPluginName}}]}
33
+ plugins={[{{capitalizedPluginName}}]}
35
34
  />
36
35
  )
37
36
  }
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "extends": "../tsconfig.json",
3
3
  "compilerOptions": {
4
- "jsx": "react",
5
- "types": ["vite/client"]
4
+ "jsx": "react-jsx",
5
+ "types": ["vite/client"],
6
+ "esModuleInterop": true
6
7
  },
7
8
  "include": ["."]
8
9
  }
@@ -3,14 +3,14 @@ import type { NodeData } from '@knotx/core'
3
3
  import { BasePlugin } from '@knotx/core'
4
4
  import { inject, OnInit } from '@knotx/decorators'
5
5
 
6
- export class {{capitalizedPluginName}} extends BasePlugin<'{{pluginName}}'> {
7
- name = '{{pluginName}}' as const
6
+ export class {{capitalizedPluginName}} extends BasePlugin<'{{camelCaseName}}'> {
7
+ name = '{{camelCaseName}}' as const
8
8
 
9
9
  @inject.nodes()
10
10
  nodes!: NodeData[]
11
11
 
12
12
  @OnInit
13
13
  init() {
14
- console.log('{{capitalizedPluginName}} 插件已加载', this.nodes)
14
+ console.log('{{capitalizedPluginName}} 插件已加载')
15
15
  }
16
16
  }
@@ -3,16 +3,16 @@ import { defineConfig } from 'vite'
3
3
 
4
4
  export default defineConfig({
5
5
  plugins: [{{#if isReact}}react({
6
- babel: {
7
- plugins: [[
8
- '@babel/plugin-proposal-decorators',
9
- {
10
- version: '2023-11',
11
- decoratorsBeforeExport: true,
12
- },
13
- ]],
6
+ babel: {
7
+ plugins: [[
8
+ '@babel/plugin-proposal-decorators',
9
+ {
10
+ version: '2023-11',
11
+ decoratorsBeforeExport: true,
14
12
  },
15
- }){{/if}}],
13
+ ]],
14
+ },
15
+ }){{/if}}],
16
16
  server: {
17
17
  port: 5173,
18
18
  },