@flowgram.ai/cli 0.1.8

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 ADDED
@@ -0,0 +1,472 @@
1
+ // ../../common/temp/node_modules/.pnpm/tsup@8.3.5_typescript@5.8.3/node_modules/tsup/assets/esm_shims.js
2
+ import { fileURLToPath } from "url";
3
+ import path from "path";
4
+ var getFilename = () => fileURLToPath(import.meta.url);
5
+ var getDirname = () => path.dirname(getFilename());
6
+ var __dirname = /* @__PURE__ */ getDirname();
7
+
8
+ // src/index.ts
9
+ import { Command } from "commander";
10
+
11
+ // src/materials/index.ts
12
+ import inquirer from "inquirer";
13
+ import chalk2 from "chalk";
14
+
15
+ // src/materials/materials.ts
16
+ import { fileURLToPath as fileURLToPath2 } from "url";
17
+ import path3 from "path";
18
+ import fs2 from "fs";
19
+
20
+ // src/utils/traverse-file.ts
21
+ import path2 from "path";
22
+ import fs from "fs";
23
+ var File = class {
24
+ constructor(filePath, root = "/") {
25
+ this.root = root;
26
+ this.path = filePath;
27
+ this.relativePath = path2.relative(this.root, this.path);
28
+ this.suffix = path2.extname(this.path);
29
+ if (!fs.existsSync(this.path)) {
30
+ throw Error(`File ${path2} Not Exists`);
31
+ }
32
+ try {
33
+ this.content = fs.readFileSync(this.path, "utf-8");
34
+ this.isUtf8 = true;
35
+ } catch (e) {
36
+ this.isUtf8 = false;
37
+ return;
38
+ }
39
+ }
40
+ replace(updater) {
41
+ if (!this.isUtf8) {
42
+ console.warn("Not UTF-8 file skipped: ", this.path);
43
+ return;
44
+ }
45
+ this.content = updater(this.content);
46
+ fs.writeFileSync(this.path, this.content, "utf-8");
47
+ }
48
+ };
49
+ function* traverseRecursiveFiles(folder) {
50
+ const files = fs.readdirSync(folder);
51
+ for (const file of files) {
52
+ const filePath = path2.join(folder, file);
53
+ if (fs.statSync(filePath).isDirectory()) {
54
+ yield* traverseRecursiveFiles(filePath);
55
+ } else {
56
+ yield new File(filePath);
57
+ }
58
+ }
59
+ }
60
+
61
+ // src/utils/import.ts
62
+ function assembleImport(declaration) {
63
+ const { namedImports, defaultImport, namespaceImport, source } = declaration;
64
+ const importClauses = [];
65
+ if (namedImports) {
66
+ importClauses.push(
67
+ `{ ${namedImports.map(
68
+ ({ local, imported, typeOnly }) => `${typeOnly ? "type " : ""}${imported}${local ? ` as ${local}` : ""}`
69
+ ).join(", ")} }`
70
+ );
71
+ }
72
+ if (defaultImport) {
73
+ importClauses.push(defaultImport);
74
+ }
75
+ if (namespaceImport) {
76
+ importClauses.push(`* as ${namespaceImport}`);
77
+ }
78
+ return `import ${importClauses.join(", ")} from '${source}'`;
79
+ }
80
+ function replaceImport(fileContent, origin, replaceTo) {
81
+ const replaceImportStatements = replaceTo.map(assembleImport);
82
+ return fileContent.replace(origin.statement, replaceImportStatements.join("\n"));
83
+ }
84
+ function* traverseFileImports(fileContent) {
85
+ const importRegex = /import\s+([^{}*,]*?)?(?:\s*\*\s*as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,?)?(?:\s*\{([^}]*)\}\s*,?)?(?:\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,?)?\s*from\s*['"`]([^'"`]+)['"`]/g;
86
+ let match;
87
+ while ((match = importRegex.exec(fileContent)) !== null) {
88
+ const [fullMatch, defaultPart, namespacePart, namedPart, defaultPart2, source] = match;
89
+ const declaration = {
90
+ statement: fullMatch,
91
+ source
92
+ };
93
+ const defaultImport = defaultPart?.trim() || defaultPart2?.trim();
94
+ if (defaultImport && !namespacePart && !namedPart) {
95
+ declaration.defaultImport = defaultImport;
96
+ } else if (defaultImport && (namespacePart || namedPart)) {
97
+ declaration.defaultImport = defaultImport;
98
+ }
99
+ if (namespacePart) {
100
+ declaration.namespaceImport = namespacePart.trim();
101
+ }
102
+ if (namedPart) {
103
+ const namedImports = [];
104
+ const namedItems = namedPart.split(",").map((item) => item.trim()).filter(Boolean);
105
+ for (const item of namedItems) {
106
+ const typeOnly = item.startsWith("type ");
107
+ const cleanItem = typeOnly ? item.slice(5).trim() : item;
108
+ if (cleanItem.includes(" as ")) {
109
+ const [imported, local] = cleanItem.split(" as ").map((s) => s.trim());
110
+ namedImports.push({
111
+ imported,
112
+ local,
113
+ typeOnly
114
+ });
115
+ } else {
116
+ namedImports.push({
117
+ imported: cleanItem,
118
+ typeOnly
119
+ });
120
+ }
121
+ }
122
+ if (namedImports.length > 0) {
123
+ declaration.namedImports = namedImports;
124
+ }
125
+ }
126
+ yield declaration;
127
+ }
128
+ }
129
+
130
+ // src/materials/materials.ts
131
+ var __filename2 = fileURLToPath2(import.meta.url);
132
+ var __dirname2 = path3.dirname(__filename2);
133
+ var _types = [
134
+ "components",
135
+ "effects",
136
+ "plugins",
137
+ "shared",
138
+ "typings",
139
+ "validate",
140
+ "form-plugins",
141
+ "hooks"
142
+ ];
143
+ function listAllMaterials(formMaterialSrc) {
144
+ const _materials = [];
145
+ for (const _type of _types) {
146
+ const materialsPath = path3.join(formMaterialSrc, _type);
147
+ _materials.push(
148
+ ...fs2.readdirSync(materialsPath).map((_path) => {
149
+ if (_path === "index.ts") {
150
+ return null;
151
+ }
152
+ return {
153
+ name: _path,
154
+ // Assuming the folder name is the material name
155
+ type: _type,
156
+ path: path3.join(materialsPath, _path)
157
+ };
158
+ }).filter((material) => material !== null)
159
+ );
160
+ }
161
+ return _materials;
162
+ }
163
+ var getFormMaterialDependencies = (formMaterialPath) => {
164
+ const packageJsonPath = path3.join(formMaterialPath, "package.json");
165
+ const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
166
+ return packageJson.dependencies;
167
+ };
168
+ var copyMaterial = (material, project, formMaterialPath) => {
169
+ const formMaterialDependencies = getFormMaterialDependencies(formMaterialPath);
170
+ const sourceDir = material.path;
171
+ const materialRoot = path3.join(
172
+ project.projectPath,
173
+ "src",
174
+ "form-materials",
175
+ `${material.type}`
176
+ );
177
+ const targetDir = path3.join(materialRoot, material.name);
178
+ const packagesToInstall = /* @__PURE__ */ new Set();
179
+ fs2.cpSync(sourceDir, targetDir, { recursive: true });
180
+ for (const file of traverseRecursiveFiles(targetDir)) {
181
+ if ([".ts", ".tsx"].includes(file.suffix)) {
182
+ for (const importDeclaration of traverseFileImports(file.content)) {
183
+ const { source } = importDeclaration;
184
+ if (source.startsWith("@/")) {
185
+ console.log(
186
+ `Replace Import from ${source} to @flowgram.ai/form-materials`
187
+ );
188
+ file.replace(
189
+ (content) => replaceImport(content, importDeclaration, [
190
+ { ...importDeclaration, source: "@flowgram.ai/form-materials" }
191
+ ])
192
+ );
193
+ packagesToInstall.add(
194
+ `@flowgram.ai/form-materials@${project.flowgramVersion}`
195
+ );
196
+ } else if (!source.startsWith(".") && !source.startsWith("react")) {
197
+ const [dep, version] = Object.entries(formMaterialDependencies).find(
198
+ ([_key]) => source.startsWith(_key)
199
+ ) || [];
200
+ if (!dep) {
201
+ continue;
202
+ }
203
+ if (dep.startsWith("@flowgram.ai/")) {
204
+ packagesToInstall.add(`${dep}@${project.flowgramVersion}`);
205
+ } else {
206
+ packagesToInstall.add(`${dep}@${version}`);
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ return {
213
+ packagesToInstall: [...packagesToInstall]
214
+ };
215
+ };
216
+
217
+ // src/utils/npm.ts
218
+ import { execSync } from "child_process";
219
+ import { existsSync } from "fs";
220
+ import path4 from "path";
221
+ import download from "download";
222
+ async function getLatestVersion(packageName) {
223
+ return execSync(`npm view ${packageName} version --tag=latest`).toString().trim();
224
+ }
225
+ async function loadNpm(packageName) {
226
+ const packageLatestVersion = await getLatestVersion(packageName);
227
+ const packagePath = path4.join(
228
+ __dirname,
229
+ `./.download/${packageName}-${packageLatestVersion}`
230
+ );
231
+ if (existsSync(packagePath)) {
232
+ return packagePath;
233
+ }
234
+ try {
235
+ const tarballUrl = execSync(
236
+ `npm view ${packageName}@${packageLatestVersion} dist.tarball`
237
+ ).toString().trim();
238
+ await download(tarballUrl, packagePath, { extract: true, strip: 1 });
239
+ return packagePath;
240
+ } catch (error) {
241
+ console.error(`Error downloading or extracting package: ${error}`);
242
+ throw error;
243
+ }
244
+ }
245
+
246
+ // src/materials/index.ts
247
+ import path6 from "path";
248
+
249
+ // src/utils/project.ts
250
+ import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
251
+ import path5 from "path";
252
+ import chalk from "chalk";
253
+ var Project = class _Project {
254
+ constructor() {
255
+ }
256
+ async init() {
257
+ let projectPath = process.cwd();
258
+ while (projectPath !== "/" && !existsSync2(path5.join(projectPath, "package.json"))) {
259
+ projectPath = path5.join(projectPath, "..");
260
+ }
261
+ if (projectPath === "/") {
262
+ throw new Error("Please run this command in a valid project");
263
+ }
264
+ this.projectPath = projectPath;
265
+ this.packageJsonPath = path5.join(projectPath, "package.json");
266
+ this.packageJson = JSON.parse(readFileSync(this.packageJsonPath, "utf8"));
267
+ this.flowgramVersion = this.packageJson.dependencies["@flowgram.ai/fixed-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/free-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/editor"];
268
+ }
269
+ async addDependency(dependency) {
270
+ let name;
271
+ let version;
272
+ const lastAtIndex = dependency.lastIndexOf("@");
273
+ if (lastAtIndex <= 0) {
274
+ name = dependency;
275
+ version = await getLatestVersion(name);
276
+ } else {
277
+ name = dependency.substring(0, lastAtIndex);
278
+ version = dependency.substring(lastAtIndex + 1);
279
+ if (!version.trim()) {
280
+ version = await getLatestVersion(name);
281
+ }
282
+ }
283
+ this.packageJson.dependencies[name] = version;
284
+ writeFileSync(
285
+ this.packageJsonPath,
286
+ JSON.stringify(this.packageJson, null, 2)
287
+ );
288
+ }
289
+ async addDependencies(dependencies) {
290
+ for (const dependency of dependencies) {
291
+ await this.addDependency(dependency);
292
+ }
293
+ }
294
+ printInfo() {
295
+ console.log(chalk.bold("Project Info:"));
296
+ console.log(chalk.black(` - Flowgram Version: ${this.flowgramVersion}`));
297
+ console.log(chalk.black(` - Project Path: ${this.projectPath}`));
298
+ }
299
+ static async getSingleton() {
300
+ const info = new _Project();
301
+ await info.init();
302
+ return info;
303
+ }
304
+ };
305
+
306
+ // src/materials/index.ts
307
+ async function syncMaterial(materialName) {
308
+ console.log(chalk2.bgGreenBright("Welcome to @flowgram.ai form-materials!"));
309
+ const project = await Project.getSingleton();
310
+ project.printInfo();
311
+ if (!project.flowgramVersion) {
312
+ throw new Error(
313
+ "Please install @flowgram.ai/fixed-layout-editor or @flowgram.ai/free-layout-editor"
314
+ );
315
+ }
316
+ const formMaterialPath = await loadNpm("@flowgram.ai/form-materials");
317
+ const formMaterialSrc = path6.join(formMaterialPath, "src");
318
+ const materials = listAllMaterials(formMaterialSrc);
319
+ let material;
320
+ if (materialName) {
321
+ const selectedMaterial = materials.find(
322
+ (m) => `${m.type}/${m.name}` === materialName
323
+ );
324
+ if (selectedMaterial) {
325
+ material = selectedMaterial;
326
+ console.log(chalk2.green(`Using material: ${materialName}`));
327
+ } else {
328
+ console.log(
329
+ chalk2.yellow(
330
+ `Material "${materialName}" not found. Please select from the list:`
331
+ )
332
+ );
333
+ }
334
+ }
335
+ if (!material) {
336
+ const result = await inquirer.prompt([
337
+ {
338
+ type: "list",
339
+ name: "material",
340
+ message: "Select one material to add:",
341
+ choices: [
342
+ ...materials.map((_material) => ({
343
+ name: `${_material.type}/${_material.name}`,
344
+ value: _material
345
+ }))
346
+ ]
347
+ }
348
+ ]);
349
+ material = result.material;
350
+ }
351
+ if (!material) {
352
+ console.error(chalk2.red("No material selected. Exiting."));
353
+ process.exit(1);
354
+ }
355
+ console.log(
356
+ chalk2.bold("The following materials will be added to your project")
357
+ );
358
+ console.log(material);
359
+ let { packagesToInstall } = copyMaterial(material, project, formMaterialPath);
360
+ await project.addDependencies(packagesToInstall);
361
+ console.log(
362
+ chalk2.bold("These npm dependencies is added to your package.json")
363
+ );
364
+ packagesToInstall.forEach((_package) => {
365
+ console.log(`- ${_package}`);
366
+ });
367
+ console.log(chalk2.bold("\nPlease run npm install to install dependencies"));
368
+ }
369
+
370
+ // src/create-app/index.ts
371
+ import path7 from "path";
372
+ import { execSync as execSync2 } from "child_process";
373
+ import inquirer2 from "inquirer";
374
+ import fs3 from "fs-extra";
375
+ import chalk3 from "chalk";
376
+ import download2 from "download";
377
+ import * as tar from "tar";
378
+ var args = process.argv.slice(2);
379
+ var updateFlowGramVersions = (dependencies, latestVersion) => {
380
+ for (const packageName in dependencies) {
381
+ if (packageName.startsWith("@flowgram.ai")) {
382
+ dependencies[packageName] = latestVersion;
383
+ }
384
+ }
385
+ };
386
+ var createApp = async (projectName) => {
387
+ console.log(chalk3.green("Welcome to @flowgram.ai/create-app CLI!"));
388
+ const latest = execSync2("npm view @flowgram.ai/demo-fixed-layout version --tag=latest latest").toString().trim();
389
+ let folderName = "";
390
+ if (!projectName) {
391
+ const { repo } = await inquirer2.prompt([
392
+ {
393
+ type: "list",
394
+ name: "repo",
395
+ message: "Select a demo to create:",
396
+ choices: [
397
+ { name: "Fixed Layout Demo", value: "demo-fixed-layout" },
398
+ { name: "Free Layout Demo", value: "demo-free-layout" },
399
+ { name: "Fixed Layout Demo Simple", value: "demo-fixed-layout-simple" },
400
+ { name: "Free Layout Demo Simple", value: "demo-free-layout-simple" },
401
+ { name: "Free Layout Nextjs Demo", value: "demo-nextjs" },
402
+ { name: "Free Layout Vite Demo Simple", value: "demo-vite" },
403
+ { name: "Demo Playground for infinite canvas", value: "demo-playground" }
404
+ ]
405
+ }
406
+ ]);
407
+ folderName = repo;
408
+ } else {
409
+ if (["fixed-layout", "free-layout", "fixed-layout-simple", "free-layout-simple", "playground", "nextjs"].includes(projectName)) {
410
+ folderName = `demo-${projectName}`;
411
+ } else {
412
+ console.error('Invalid projectName. Please run "npx create-app" to choose demo.');
413
+ return;
414
+ }
415
+ }
416
+ try {
417
+ const targetDir = path7.join(process.cwd());
418
+ const downloadPackage = async () => {
419
+ try {
420
+ const tarballBuffer = await download2(`https://registry.npmjs.org/@flowgram.ai/${folderName}/-/${folderName}-${latest}.tgz`);
421
+ fs3.ensureDirSync(targetDir);
422
+ const tempTarballPath = path7.join(process.cwd(), `${folderName}.tgz`);
423
+ fs3.writeFileSync(tempTarballPath, tarballBuffer);
424
+ await tar.x({
425
+ file: tempTarballPath,
426
+ C: targetDir
427
+ });
428
+ fs3.renameSync(path7.join(targetDir, "package"), path7.join(targetDir, folderName));
429
+ fs3.unlinkSync(tempTarballPath);
430
+ return true;
431
+ } catch (error) {
432
+ console.error(`Error downloading or extracting package: ${error}`);
433
+ return false;
434
+ }
435
+ };
436
+ const res = await downloadPackage();
437
+ const pkgJsonPath = path7.join(targetDir, folderName, "package.json");
438
+ const data = fs3.readFileSync(pkgJsonPath, "utf-8");
439
+ const packageLatestVersion = execSync2("npm view @flowgram.ai/core version --tag=latest latest").toString().trim();
440
+ const jsonData = JSON.parse(data);
441
+ if (jsonData.dependencies) {
442
+ updateFlowGramVersions(jsonData.dependencies, packageLatestVersion);
443
+ }
444
+ if (jsonData.devDependencies) {
445
+ updateFlowGramVersions(jsonData.devDependencies, packageLatestVersion);
446
+ }
447
+ fs3.writeFileSync(pkgJsonPath, JSON.stringify(jsonData, null, 2), "utf-8");
448
+ if (res) {
449
+ console.log(chalk3.green(`${folderName} Demo project created successfully!`));
450
+ console.log(chalk3.yellow("Run the following commands to start:"));
451
+ console.log(chalk3.cyan(` cd ${folderName}`));
452
+ console.log(chalk3.cyan(" npm install"));
453
+ console.log(chalk3.cyan(" npm start"));
454
+ } else {
455
+ console.log(chalk3.red("Download failed"));
456
+ }
457
+ } catch (error) {
458
+ console.error("Error downloading repo:", error);
459
+ return;
460
+ }
461
+ };
462
+
463
+ // src/index.ts
464
+ var program = new Command();
465
+ program.name("flowgram-cli").version("1.0.0").description("Flowgram CLI");
466
+ program.command("create-app").description("Create a new flowgram project").argument("[string]", "Project name").action(async (projectName) => {
467
+ await createApp(projectName);
468
+ });
469
+ program.command("materials").description("Sync materials to the project").argument("[string]", "Material name").action(async (materialName) => {
470
+ await syncMaterial(materialName);
471
+ });
472
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@flowgram.ai/cli",
3
+ "version": "0.1.8",
4
+ "description": "A CLI tool to create demo projects or sync materials",
5
+ "bin": {
6
+ "flowgram-cli": "./bin/index.js"
7
+ },
8
+ "type": "module",
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "dist"
13
+ ],
14
+ "dependencies": {
15
+ "fs-extra": "^9.1.0",
16
+ "commander": "^11.0.0",
17
+ "chalk": "^5.3.0",
18
+ "download": "8.0.0",
19
+ "tar": "7.4.3",
20
+ "inquirer": "^9.2.7"
21
+ },
22
+ "devDependencies": {
23
+ "@types/download": "8.0.5",
24
+ "@types/fs-extra": "11.0.4",
25
+ "@types/node": "^18",
26
+ "@types/inquirer": "9.0.7",
27
+ "tsup": "^8.0.1",
28
+ "eslint": "^8.54.0",
29
+ "@typescript-eslint/parser": "^6.10.0",
30
+ "typescript": "^5.8.3"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public",
34
+ "registry": "https://registry.npmjs.org/"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup src/index.ts --format esm,cjs --dts --out-dir dist",
38
+ "start": "node bin/index.js"
39
+ }
40
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import path from 'path';
7
+
8
+ import { execSync } from 'child_process';
9
+ import inquirer from 'inquirer';
10
+ import fs from 'fs-extra';
11
+ import chalk from 'chalk';
12
+ import download from 'download';
13
+ import * as tar from 'tar';
14
+
15
+ const args = process.argv.slice(2);
16
+
17
+ const updateFlowGramVersions = (dependencies: any[], latestVersion: string) => {
18
+ for(const packageName in dependencies) {
19
+ if (packageName.startsWith('@flowgram.ai')) {
20
+ dependencies[packageName] = latestVersion
21
+ }
22
+ }
23
+ }
24
+
25
+ export const createApp = async (projectName?: string) => {
26
+ console.log(chalk.green('Welcome to @flowgram.ai/create-app CLI!'));
27
+ const latest = execSync('npm view @flowgram.ai/demo-fixed-layout version --tag=latest latest').toString().trim();
28
+
29
+ let folderName = ''
30
+
31
+ if (!projectName) {
32
+ // 询问用户选择 demo 项目
33
+ const { repo } = await inquirer.prompt([
34
+ {
35
+ type: 'list',
36
+ name: 'repo',
37
+ message: 'Select a demo to create:',
38
+ choices: [
39
+ { name: 'Fixed Layout Demo', value: 'demo-fixed-layout' },
40
+ { name: 'Free Layout Demo', value: 'demo-free-layout' },
41
+ { name: 'Fixed Layout Demo Simple', value: 'demo-fixed-layout-simple' },
42
+ { name: 'Free Layout Demo Simple', value: 'demo-free-layout-simple' },
43
+ { name: 'Free Layout Nextjs Demo', value: 'demo-nextjs' },
44
+ { name: 'Free Layout Vite Demo Simple', value: 'demo-vite' },
45
+ { name: 'Demo Playground for infinite canvas', value: 'demo-playground' }
46
+ ],
47
+ },
48
+ ]);
49
+
50
+ folderName = repo;
51
+ } else {
52
+ if (['fixed-layout', 'free-layout', 'fixed-layout-simple', 'free-layout-simple', 'playground', 'nextjs'].includes(projectName)) {
53
+ folderName = `demo-${projectName}`;
54
+ } else {
55
+ console.error('Invalid projectName. Please run "npx create-app" to choose demo.');
56
+ return;
57
+ }
58
+ }
59
+
60
+ try {
61
+ const targetDir = path.join(process.cwd());
62
+ // 下载 npm 包的 tarball
63
+ const downloadPackage = async () => {
64
+ try {
65
+ // 从 npm registry 下载 tarball 文件
66
+ const tarballBuffer = await download(`https://registry.npmjs.org/@flowgram.ai/${folderName}/-/${folderName}-${latest}.tgz`);
67
+
68
+ // 确保目标文件夹存在
69
+ fs.ensureDirSync(targetDir);
70
+
71
+ // 创建一个临时文件名来保存 tarball 数据
72
+ const tempTarballPath = path.join(process.cwd(), `${folderName}.tgz`);
73
+
74
+ // 将下载的 tarball 写入临时文件
75
+ fs.writeFileSync(tempTarballPath, tarballBuffer);
76
+
77
+ // 解压 tarball 文件到目标文件夹
78
+ await tar.x({
79
+ file: tempTarballPath,
80
+ C: targetDir,
81
+ });
82
+
83
+ fs.renameSync(path.join(targetDir, 'package'), path.join(targetDir, folderName))
84
+
85
+ // 删除下载的 tarball 文件
86
+ fs.unlinkSync(tempTarballPath);
87
+ return true;
88
+
89
+ } catch (error) {
90
+ console.error(`Error downloading or extracting package: ${error}`);
91
+ return false;
92
+ }
93
+ };
94
+ const res = await downloadPackage();
95
+
96
+ // 下载完成后,执行操作,替换 package.json 文件内部的所有 @flowgram.ai 包版本为 latest
97
+ const pkgJsonPath = path.join(targetDir, folderName, 'package.json');
98
+ const data = fs.readFileSync(pkgJsonPath, 'utf-8');
99
+
100
+ const packageLatestVersion = execSync('npm view @flowgram.ai/core version --tag=latest latest').toString().trim();
101
+
102
+ const jsonData = JSON.parse(data);
103
+ if (jsonData.dependencies) {
104
+ updateFlowGramVersions(jsonData.dependencies, packageLatestVersion);
105
+ }
106
+
107
+ if (jsonData.devDependencies) {
108
+ updateFlowGramVersions(jsonData.devDependencies, packageLatestVersion);
109
+ }
110
+
111
+ // 修改完成后写入
112
+ fs.writeFileSync(pkgJsonPath, JSON.stringify(jsonData, null, 2), 'utf-8');
113
+
114
+ if (res) {
115
+ // 克隆项目
116
+ console.log(chalk.green(`${folderName} Demo project created successfully!`));
117
+
118
+ console.log(chalk.yellow('Run the following commands to start:'));
119
+ console.log(chalk.cyan(` cd ${folderName}`));
120
+ console.log(chalk.cyan(' npm install'));
121
+ console.log(chalk.cyan(' npm start'));
122
+ } else {
123
+ console.log(chalk.red('Download failed'))
124
+ }
125
+
126
+ } catch (error) {
127
+ console.error('Error downloading repo:', error);
128
+ return;
129
+ }
130
+ }
package/src/index.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { Command } from "commander";
7
+
8
+ import { syncMaterial } from "./materials";
9
+ import { createApp } from "./create-app";
10
+
11
+ const program = new Command();
12
+
13
+ program.name("flowgram-cli").version("1.0.0").description("Flowgram CLI");
14
+
15
+ program
16
+ .command("create-app")
17
+ .description("Create a new flowgram project")
18
+ .argument('[string]', 'Project name')
19
+ .action(async (projectName) => {
20
+ await createApp(projectName);
21
+ });
22
+
23
+ program
24
+ .command("materials")
25
+ .description("Sync materials to the project")
26
+ .argument('[string]', 'Material name')
27
+ .action(async (materialName) => {
28
+ await syncMaterial(materialName);
29
+ });
30
+
31
+ program.parse(process.argv);