@flowgram.ai/cli 0.4.11 → 0.4.13

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
@@ -6,35 +6,108 @@ var getDirname = () => path.dirname(getFilename());
6
6
  var __dirname = /* @__PURE__ */ getDirname();
7
7
 
8
8
  // src/index.ts
9
+ import path11 from "path";
9
10
  import { Command } from "commander";
10
11
 
11
- // src/materials/index.ts
12
- import inquirer from "inquirer";
13
- import chalk3 from "chalk";
12
+ // src/update-version/index.ts
13
+ import chalk from "chalk";
14
14
 
15
- // src/materials/materials.ts
16
- import path4 from "path";
17
- import fs3 from "fs";
15
+ // src/utils/npm.ts
16
+ import path2 from "path";
17
+ import https from "https";
18
+ import { existsSync, readFileSync } from "fs";
19
+ import { execSync } from "child_process";
20
+ import * as tar from "tar";
21
+ import fs from "fs-extra";
22
+ var LoadedNpmPkg = class {
23
+ constructor(name, version, path12) {
24
+ this.name = name;
25
+ this.version = version;
26
+ this.path = path12;
27
+ }
28
+ get srcPath() {
29
+ return path2.join(this.path, "src");
30
+ }
31
+ get distPath() {
32
+ return path2.join(this.path, "dist");
33
+ }
34
+ get packageJson() {
35
+ if (!this._packageJson) {
36
+ this._packageJson = JSON.parse(readFileSync(path2.join(this.path, "package.json"), "utf8"));
37
+ }
38
+ return this._packageJson;
39
+ }
40
+ get dependencies() {
41
+ return this.packageJson.dependencies;
42
+ }
43
+ };
44
+ function downloadFile(url, dest) {
45
+ return new Promise((resolve, reject) => {
46
+ const file = fs.createWriteStream(dest);
47
+ https.get(url, (response) => {
48
+ if (response.statusCode !== 200) {
49
+ reject(new Error(`Download failed: ${response.statusCode}`));
50
+ return;
51
+ }
52
+ response.pipe(file);
53
+ file.on("finish", () => {
54
+ file.close();
55
+ resolve();
56
+ });
57
+ }).on("error", (err) => {
58
+ fs.unlink(dest, () => reject(err));
59
+ });
60
+ file.on("error", (err) => {
61
+ fs.unlink(dest, () => reject(err));
62
+ });
63
+ });
64
+ }
65
+ async function getLatestVersion(packageName) {
66
+ return execSync(`npm view ${packageName} version --tag=latest`).toString().trim();
67
+ }
68
+ async function loadNpm(packageName) {
69
+ const packageLatestVersion = await getLatestVersion(packageName);
70
+ const packagePath = path2.join(__dirname, `./.download/${packageName}-${packageLatestVersion}`);
71
+ if (existsSync(packagePath)) {
72
+ return new LoadedNpmPkg(packageName, packageLatestVersion, packagePath);
73
+ }
74
+ try {
75
+ const tarballUrl = execSync(`npm view ${packageName}@${packageLatestVersion} dist.tarball`).toString().trim();
76
+ const tempTarballPath = path2.join(
77
+ __dirname,
78
+ `./.download/${packageName}-${packageLatestVersion}.tgz`
79
+ );
80
+ fs.ensureDirSync(path2.dirname(tempTarballPath));
81
+ await downloadFile(tarballUrl, tempTarballPath);
82
+ fs.ensureDirSync(packagePath);
83
+ await tar.x({
84
+ file: tempTarballPath,
85
+ cwd: packagePath,
86
+ strip: 1
87
+ });
88
+ fs.unlinkSync(tempTarballPath);
89
+ return new LoadedNpmPkg(packageName, packageLatestVersion, packagePath);
90
+ } catch (error) {
91
+ console.error(`Error downloading or extracting package: ${error}`);
92
+ throw error;
93
+ }
94
+ }
18
95
 
19
- // src/utils/ts-file.ts
96
+ // src/utils/file.ts
20
97
  import path3 from "path";
21
98
  import fs2 from "fs";
22
-
23
- // src/utils/file.ts
24
- import path2 from "path";
25
- import fs from "fs";
26
99
  import ignore from "ignore";
27
100
  var File = class {
28
101
  constructor(filePath, root = "/") {
29
102
  this.root = root;
30
103
  this.path = filePath;
31
- this.relativePath = path2.relative(this.root, this.path);
32
- this.suffix = path2.extname(this.path);
33
- if (!fs.existsSync(this.path)) {
34
- throw Error(`File ${path2} Not Exists`);
104
+ this.relativePath = path3.relative(this.root, this.path);
105
+ this.suffix = path3.extname(this.path);
106
+ if (!fs2.existsSync(this.path)) {
107
+ throw Error(`File ${path3} Not Exists`);
35
108
  }
36
109
  try {
37
- this.content = fs.readFileSync(this.path, "utf-8");
110
+ this.content = fs2.readFileSync(this.path, "utf-8");
38
111
  this.isUtf8 = true;
39
112
  } catch (e) {
40
113
  this.isUtf8 = false;
@@ -47,24 +120,24 @@ var File = class {
47
120
  return;
48
121
  }
49
122
  this.content = updater(this.content);
50
- fs.writeFileSync(this.path, this.content, "utf-8");
123
+ fs2.writeFileSync(this.path, this.content, "utf-8");
51
124
  }
52
125
  write(nextContent) {
53
126
  this.content = nextContent;
54
- fs.writeFileSync(this.path, this.content, "utf-8");
127
+ fs2.writeFileSync(this.path, this.content, "utf-8");
55
128
  }
56
129
  };
57
130
  function* traverseRecursiveFilePaths(folder, ig = ignore().add(".git"), root = folder) {
58
- const files = fs.readdirSync(folder);
59
- if (fs.existsSync(path2.join(folder, ".gitignore"))) {
60
- ig.add(fs.readFileSync(path2.join(folder, ".gitignore"), "utf-8"));
131
+ const files = fs2.readdirSync(folder);
132
+ if (fs2.existsSync(path3.join(folder, ".gitignore"))) {
133
+ ig.add(fs2.readFileSync(path3.join(folder, ".gitignore"), "utf-8"));
61
134
  }
62
135
  for (const file of files) {
63
- const filePath = path2.join(folder, file);
64
- if (ig.ignores(path2.relative(root, filePath))) {
136
+ const filePath = path3.join(folder, file);
137
+ if (ig.ignores(path3.relative(root, filePath))) {
65
138
  continue;
66
139
  }
67
- if (fs.statSync(filePath).isDirectory()) {
140
+ if (fs2.statSync(filePath).isDirectory()) {
68
141
  yield* traverseRecursiveFilePaths(filePath, ig, root);
69
142
  } else {
70
143
  yield filePath;
@@ -73,7 +146,185 @@ function* traverseRecursiveFilePaths(folder, ig = ignore().add(".git"), root = f
73
146
  }
74
147
  function* traverseRecursiveFiles(folder) {
75
148
  for (const filePath of traverseRecursiveFilePaths(folder)) {
76
- yield new File(filePath);
149
+ yield new File(filePath, folder);
150
+ }
151
+ }
152
+
153
+ // src/update-version/index.ts
154
+ async function updateFlowgramVersion(inputVersion) {
155
+ console.log(chalk.bold("\u{1F680} Welcome to @flowgram.ai update-version helper"));
156
+ const latestVersion = await getLatestVersion("@flowgram.ai/editor");
157
+ const currentPath = process.cwd();
158
+ console.log("- Latest flowgram version: ", latestVersion);
159
+ console.log("- Current Path: ", currentPath);
160
+ const flowgramVersion = inputVersion || latestVersion;
161
+ for (const file of traverseRecursiveFiles(currentPath)) {
162
+ if (file.path.endsWith("package.json")) {
163
+ console.log("\u{1F440} Find package.json: ", file.path);
164
+ let updated = false;
165
+ const json = JSON.parse(file.content);
166
+ if (json.dependencies) {
167
+ for (const key in json.dependencies) {
168
+ if (key.startsWith("@flowgram.ai/")) {
169
+ updated = true;
170
+ json.dependencies[key] = flowgramVersion;
171
+ console.log(`- Update ${key} to ${flowgramVersion}`);
172
+ }
173
+ }
174
+ }
175
+ if (json.devDependencies) {
176
+ for (const key in json.devDependencies) {
177
+ if (key.startsWith("@flowgram.ai/")) {
178
+ updated = true;
179
+ json.devDependencies[key] = flowgramVersion;
180
+ console.log(`- Update ${key} to ${flowgramVersion}`);
181
+ }
182
+ }
183
+ }
184
+ if (updated) {
185
+ file.write(JSON.stringify(json, null, 2));
186
+ console.log(`\u2705 ${file.path} Updated`);
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ // src/materials/index.ts
193
+ import path9 from "path";
194
+ import chalk5 from "chalk";
195
+
196
+ // src/utils/project.ts
197
+ import path4 from "path";
198
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
199
+ import chalk2 from "chalk";
200
+ var Project = class _Project {
201
+ constructor() {
202
+ }
203
+ async init() {
204
+ let projectPath = process.cwd();
205
+ while (projectPath !== "/" && !existsSync2(path4.join(projectPath, "package.json"))) {
206
+ projectPath = path4.join(projectPath, "..");
207
+ }
208
+ if (projectPath === "/") {
209
+ throw new Error("Please run this command in a valid project");
210
+ }
211
+ this.projectPath = projectPath;
212
+ this.srcPath = path4.join(projectPath, "src");
213
+ this.packageJsonPath = path4.join(projectPath, "package.json");
214
+ this.packageJson = JSON.parse(readFileSync2(this.packageJsonPath, "utf8"));
215
+ this.flowgramVersion = this.packageJson.dependencies["@flowgram.ai/fixed-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/free-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/editor"];
216
+ }
217
+ async addDependency(dependency) {
218
+ let name;
219
+ let version;
220
+ const lastAtIndex = dependency.lastIndexOf("@");
221
+ if (lastAtIndex <= 0) {
222
+ name = dependency;
223
+ version = await getLatestVersion(name);
224
+ } else {
225
+ name = dependency.substring(0, lastAtIndex);
226
+ version = dependency.substring(lastAtIndex + 1);
227
+ if (!version.trim()) {
228
+ version = await getLatestVersion(name);
229
+ }
230
+ }
231
+ this.packageJson.dependencies[name] = version;
232
+ writeFileSync(this.packageJsonPath, JSON.stringify(this.packageJson, null, 2));
233
+ }
234
+ async addDependencies(dependencies) {
235
+ for (const dependency of dependencies) {
236
+ await this.addDependency(dependency);
237
+ }
238
+ }
239
+ writeToPackageJsonFile() {
240
+ writeFileSync(this.packageJsonPath, JSON.stringify(this.packageJson, null, 2));
241
+ }
242
+ printInfo() {
243
+ console.log(chalk2.bold("Project Info:"));
244
+ console.log(chalk2.black(` - Flowgram Version: ${this.flowgramVersion}`));
245
+ console.log(chalk2.black(` - Project Path: ${this.projectPath}`));
246
+ }
247
+ static async getSingleton() {
248
+ const info = new _Project();
249
+ await info.init();
250
+ return info;
251
+ }
252
+ };
253
+
254
+ // src/materials/select.ts
255
+ import inquirer from "inquirer";
256
+ import chalk3 from "chalk";
257
+
258
+ // src/materials/material.ts
259
+ import path6 from "path";
260
+ import { readdirSync } from "fs";
261
+
262
+ // src/utils/ts-file.ts
263
+ import path5 from "path";
264
+ import fs3 from "fs";
265
+
266
+ // src/utils/import.ts
267
+ function assembleImport(declaration) {
268
+ const { namedImports, defaultImport, namespaceImport, source } = declaration;
269
+ const importClauses = [];
270
+ if (namedImports) {
271
+ importClauses.push(
272
+ `{ ${namedImports.map(
273
+ ({ local, imported, typeOnly }) => `${typeOnly ? "type " : ""}${imported}${local ? ` as ${local}` : ""}`
274
+ ).join(", ")} }`
275
+ );
276
+ }
277
+ if (defaultImport) {
278
+ importClauses.push(defaultImport);
279
+ }
280
+ if (namespaceImport) {
281
+ importClauses.push(`* as ${namespaceImport}`);
282
+ }
283
+ return `import ${importClauses.join(", ")} from '${source}';`;
284
+ }
285
+ function* traverseFileImports(fileContent) {
286
+ 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;
287
+ let match;
288
+ while ((match = importRegex.exec(fileContent)) !== null) {
289
+ const [fullMatch, defaultPart, namespacePart, namedPart, defaultPart2, source] = match;
290
+ const declaration = {
291
+ statement: fullMatch,
292
+ source
293
+ };
294
+ const defaultImport = defaultPart?.trim() || defaultPart2?.trim();
295
+ if (defaultImport && !namespacePart && !namedPart) {
296
+ declaration.defaultImport = defaultImport;
297
+ } else if (defaultImport && (namespacePart || namedPart)) {
298
+ declaration.defaultImport = defaultImport;
299
+ }
300
+ if (namespacePart) {
301
+ declaration.namespaceImport = namespacePart.trim();
302
+ }
303
+ if (namedPart) {
304
+ const namedImports = [];
305
+ const namedItems = namedPart.split(",").map((item) => item.trim()).filter(Boolean);
306
+ for (const item of namedItems) {
307
+ const typeOnly = item.startsWith("type ");
308
+ const cleanItem = typeOnly ? item.slice(5).trim() : item;
309
+ if (cleanItem.includes(" as ")) {
310
+ const [imported, local] = cleanItem.split(" as ").map((s) => s.trim());
311
+ namedImports.push({
312
+ imported,
313
+ local,
314
+ typeOnly
315
+ });
316
+ } else {
317
+ namedImports.push({
318
+ imported: cleanItem,
319
+ typeOnly
320
+ });
321
+ }
322
+ }
323
+ if (namedImports.length > 0) {
324
+ declaration.namedImports = namedImports;
325
+ }
326
+ }
327
+ yield declaration;
77
328
  }
78
329
  }
79
330
 
@@ -82,10 +333,7 @@ function extractNamedExports(content) {
82
333
  const valueExports = [];
83
334
  const typeExports = [];
84
335
  const typeDefinitions = /* @__PURE__ */ new Set();
85
- const typePatterns = [
86
- /\b(?:type|interface)\s+(\w+)/g,
87
- /\bexport\s+(?:type|interface)\s+(\w+)/g
88
- ];
336
+ const typePatterns = [/\b(?:type|interface)\s+(\w+)/g, /\bexport\s+(?:type|interface)\s+(\w+)/g];
89
337
  let match;
90
338
  for (const pattern of typePatterns) {
91
339
  while ((match = pattern.exec(content)) !== null) {
@@ -164,84 +412,17 @@ function extractNamedExports(content) {
164
412
  };
165
413
  }
166
414
 
167
- // src/utils/import.ts
168
- function assembleImport(declaration) {
169
- const { namedImports, defaultImport, namespaceImport, source } = declaration;
170
- const importClauses = [];
171
- if (namedImports) {
172
- importClauses.push(
173
- `{ ${namedImports.map(
174
- ({ local, imported, typeOnly }) => `${typeOnly ? "type " : ""}${imported}${local ? ` as ${local}` : ""}`
175
- ).join(", ")} }`
176
- );
177
- }
178
- if (defaultImport) {
179
- importClauses.push(defaultImport);
180
- }
181
- if (namespaceImport) {
182
- importClauses.push(`* as ${namespaceImport}`);
183
- }
184
- return `import ${importClauses.join(", ")} from '${source}';`;
185
- }
186
- function* traverseFileImports(fileContent) {
187
- 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;
188
- let match;
189
- while ((match = importRegex.exec(fileContent)) !== null) {
190
- const [fullMatch, defaultPart, namespacePart, namedPart, defaultPart2, source] = match;
191
- const declaration = {
192
- statement: fullMatch,
193
- source
194
- };
195
- const defaultImport = defaultPart?.trim() || defaultPart2?.trim();
196
- if (defaultImport && !namespacePart && !namedPart) {
197
- declaration.defaultImport = defaultImport;
198
- } else if (defaultImport && (namespacePart || namedPart)) {
199
- declaration.defaultImport = defaultImport;
200
- }
201
- if (namespacePart) {
202
- declaration.namespaceImport = namespacePart.trim();
203
- }
204
- if (namedPart) {
205
- const namedImports = [];
206
- const namedItems = namedPart.split(",").map((item) => item.trim()).filter(Boolean);
207
- for (const item of namedItems) {
208
- const typeOnly = item.startsWith("type ");
209
- const cleanItem = typeOnly ? item.slice(5).trim() : item;
210
- if (cleanItem.includes(" as ")) {
211
- const [imported, local] = cleanItem.split(" as ").map((s) => s.trim());
212
- namedImports.push({
213
- imported,
214
- local,
215
- typeOnly
216
- });
217
- } else {
218
- namedImports.push({
219
- imported: cleanItem,
220
- typeOnly
221
- });
222
- }
223
- }
224
- if (namedImports.length > 0) {
225
- declaration.namedImports = namedImports;
226
- }
227
- }
228
- yield declaration;
229
- }
230
- }
231
-
232
415
  // src/utils/ts-file.ts
233
416
  var TsFile = class extends File {
234
- constructor(filePath) {
235
- super(filePath);
417
+ constructor(filePath, root) {
418
+ super(filePath, root);
236
419
  this.exports = {
237
420
  values: [],
238
421
  types: []
239
422
  };
240
423
  this.imports = [];
241
- this.exports = extractNamedExports(fs2.readFileSync(filePath, "utf-8"));
242
- this.imports = Array.from(
243
- traverseFileImports(fs2.readFileSync(filePath, "utf-8"))
244
- );
424
+ this.exports = extractNamedExports(fs3.readFileSync(filePath, "utf-8"));
425
+ this.imports = Array.from(traverseFileImports(fs3.readFileSync(filePath, "utf-8")));
245
426
  }
246
427
  get allExportNames() {
247
428
  return [...this.exports.values, ...this.exports.types];
@@ -255,9 +436,7 @@ var TsFile = class extends File {
255
436
  return content.replace(
256
437
  lastImportStatement.statement,
257
438
  `${lastImportStatement?.statement}
258
- ${importDeclarations.map(
259
- (item) => item.statement
260
- )}
439
+ ${importDeclarations.map((item) => item.statement)}
261
440
  `
262
441
  );
263
442
  });
@@ -265,14 +444,9 @@ ${importDeclarations.map(
265
444
  }
266
445
  removeImport(importDeclarations) {
267
446
  this.replace(
268
- (content) => importDeclarations.reduce(
269
- (prev, cur) => prev.replace(cur.statement, ""),
270
- content
271
- )
272
- );
273
- this.imports = this.imports.filter(
274
- (item) => !importDeclarations.includes(item)
447
+ (content) => importDeclarations.reduce((prev, cur) => prev.replace(cur.statement, ""), content)
275
448
  );
449
+ this.imports = this.imports.filter((item) => !importDeclarations.includes(item));
276
450
  }
277
451
  replaceImport(oldImports, newImports) {
278
452
  newImports.forEach((importDeclaration) => {
@@ -301,9 +475,7 @@ ${importDeclarations.map(
301
475
  content = content.replace(
302
476
  lastImportStatement,
303
477
  `${lastImportStatement}
304
- ${restNewImports.map(
305
- (item) => item.statement
306
- )}
478
+ ${restNewImports.map((item) => item.statement).join("\n")}
307
479
  `
308
480
  );
309
481
  }
@@ -315,236 +487,158 @@ ${restNewImports.map(
315
487
  function* traverseRecursiveTsFiles(folder) {
316
488
  for (const filePath of traverseRecursiveFilePaths(folder)) {
317
489
  if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) {
318
- yield new TsFile(filePath);
490
+ yield new TsFile(filePath, folder);
319
491
  }
320
492
  }
321
493
  }
322
494
  function getIndexTsFile(folder) {
323
- const files = fs2.readdirSync(folder);
495
+ const files = fs3.readdirSync(folder);
324
496
  for (const file of files) {
325
497
  if (file === "index.ts" || file === "index.tsx") {
326
- return new TsFile(path3.join(folder, file));
498
+ return new TsFile(path5.join(folder, file), folder);
327
499
  }
328
500
  }
329
501
  return void 0;
330
502
  }
331
503
 
332
- // src/materials/materials.ts
333
- var _types = [
504
+ // src/materials/material.ts
505
+ var _Material = class _Material {
506
+ constructor(type, name, formMaterialPkg) {
507
+ this.type = type;
508
+ this.name = name;
509
+ this.formMaterialPkg = formMaterialPkg;
510
+ }
511
+ get fullName() {
512
+ return `${this.type}/${this.name}`;
513
+ }
514
+ get sourceDir() {
515
+ return path6.join(this.formMaterialPkg.srcPath, this.type, this.name);
516
+ }
517
+ get indexFile() {
518
+ return getIndexTsFile(this.sourceDir);
519
+ }
520
+ get allExportNames() {
521
+ return this.indexFile?.allExportNames || [];
522
+ }
523
+ static listAll(formMaterialPkg) {
524
+ if (!this._all_materials_cache.length) {
525
+ this._all_materials_cache = _Material.ALL_TYPES.map((type) => {
526
+ const materialsPath = path6.join(formMaterialPkg.srcPath, type);
527
+ return readdirSync(materialsPath).map((_path) => {
528
+ if (_path === "index.ts") {
529
+ return null;
530
+ }
531
+ return new _Material(type, _path, formMaterialPkg);
532
+ }).filter((material) => material !== null);
533
+ }).flat();
534
+ }
535
+ return this._all_materials_cache;
536
+ }
537
+ };
538
+ _Material._all_materials_cache = [];
539
+ _Material.ALL_TYPES = [
334
540
  "components",
335
541
  "effects",
336
542
  "plugins",
337
543
  "shared",
338
- "typings",
339
544
  "validate",
340
545
  "form-plugins",
341
546
  "hooks"
342
547
  ];
343
- function listAllMaterials(formMaterialSrc) {
344
- const _materials = [];
345
- for (const _type of _types) {
346
- const materialsPath = path4.join(formMaterialSrc, _type);
347
- _materials.push(
348
- ...fs3.readdirSync(materialsPath).map((_path) => {
349
- if (_path === "index.ts") {
350
- return null;
351
- }
352
- return {
353
- name: _path,
354
- // Assuming the folder name is the material name
355
- type: _type,
356
- path: path4.join(materialsPath, _path)
357
- };
358
- }).filter((material) => material !== null)
359
- );
360
- }
361
- return _materials;
362
- }
363
- var getFormMaterialDependencies = (formMaterialPath) => {
364
- const packageJsonPath = path4.join(formMaterialPath, "package.json");
365
- const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
366
- return packageJson.dependencies;
367
- };
368
- var copyMaterial = (material, project, formMaterialPath) => {
369
- const formMaterialDependencies = getFormMaterialDependencies(formMaterialPath);
370
- const sourceDir = material.path;
371
- const materialRoot = path4.join(
372
- project.projectPath,
373
- "src",
374
- "form-materials",
375
- `${material.type}`
376
- );
377
- const targetDir = path4.join(materialRoot, material.name);
378
- const packagesToInstall = /* @__PURE__ */ new Set();
379
- fs3.cpSync(sourceDir, targetDir, { recursive: true });
380
- for (const file of traverseRecursiveTsFiles(targetDir)) {
381
- for (const importDeclaration of file.imports) {
382
- const { source } = importDeclaration;
383
- if (source.startsWith("@/")) {
384
- console.log(
385
- `Replace Import from ${source} to @flowgram.ai/form-materials`
386
- );
387
- file.replaceImport(
388
- [importDeclaration],
389
- [{ ...importDeclaration, source: "@flowgram.ai/form-materials" }]
390
- );
391
- packagesToInstall.add(
392
- `@flowgram.ai/form-materials@${project.flowgramVersion}`
393
- );
394
- } else if (!source.startsWith(".") && !source.startsWith("react")) {
395
- const [dep, version] = Object.entries(formMaterialDependencies).find(
396
- ([_key]) => source.startsWith(_key)
397
- ) || [];
398
- if (!dep) {
399
- continue;
548
+ var Material = _Material;
549
+
550
+ // src/materials/select.ts
551
+ var getSelectedMaterials = async (cliOpts, formMaterialPkg) => {
552
+ const { materialName, selectMultiple } = cliOpts;
553
+ const materials = Material.listAll(formMaterialPkg);
554
+ let selectedMaterials = [];
555
+ if (materialName) {
556
+ selectedMaterials = materialName.split(",").map((_name) => materials.find((_m) => _m.fullName === _name.trim())).filter(Boolean);
557
+ }
558
+ if (!selectedMaterials.length) {
559
+ console.log(chalk3.yellow(`Material "${materialName}" not found. Please select from the list:`));
560
+ const choices = materials.map((_material) => ({
561
+ name: _material.fullName,
562
+ value: _material
563
+ }));
564
+ if (selectMultiple) {
565
+ const result = await inquirer.prompt([
566
+ {
567
+ type: "checkbox",
568
+ name: "material",
569
+ message: "Select multiple materials to add:",
570
+ choices
400
571
  }
401
- if (dep.startsWith("@flowgram.ai/")) {
402
- packagesToInstall.add(`${dep}@${project.flowgramVersion}`);
403
- } else {
404
- packagesToInstall.add(`${dep}@${version}`);
572
+ ]);
573
+ selectedMaterials = result.material;
574
+ } else {
575
+ const result = await inquirer.prompt([
576
+ {
577
+ type: "list",
578
+ name: "material",
579
+ message: "Select one material to add:",
580
+ choices
405
581
  }
406
- }
582
+ ]);
583
+ selectedMaterials = [result.material];
407
584
  }
408
585
  }
409
- return {
410
- packagesToInstall: [...packagesToInstall]
411
- };
586
+ return selectedMaterials;
412
587
  };
413
588
 
414
- // src/utils/npm.ts
415
- import { execSync } from "child_process";
416
- import { existsSync } from "fs";
417
- import path5 from "path";
418
- import download from "download";
419
- async function getLatestVersion(packageName) {
420
- return execSync(`npm view ${packageName} version --tag=latest`).toString().trim();
421
- }
422
- async function loadNpm(packageName) {
423
- const packageLatestVersion = await getLatestVersion(packageName);
424
- const packagePath = path5.join(
425
- __dirname,
426
- `./.download/${packageName}-${packageLatestVersion}`
427
- );
428
- if (existsSync(packagePath)) {
429
- return packagePath;
430
- }
431
- try {
432
- const tarballUrl = execSync(
433
- `npm view ${packageName}@${packageLatestVersion} dist.tarball`
434
- ).toString().trim();
435
- await download(tarballUrl, packagePath, { extract: true, strip: 1 });
436
- return packagePath;
437
- } catch (error) {
438
- console.error(`Error downloading or extracting package: ${error}`);
439
- throw error;
440
- }
441
- }
442
-
443
- // src/materials/index.ts
589
+ // src/materials/refresh-project-import.ts
444
590
  import path7 from "path";
445
-
446
- // src/utils/project.ts
447
- import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
448
- import path6 from "path";
449
- import chalk from "chalk";
450
- var Project = class _Project {
451
- constructor() {
452
- }
453
- async init() {
454
- let projectPath = process.cwd();
455
- while (projectPath !== "/" && !existsSync2(path6.join(projectPath, "package.json"))) {
456
- projectPath = path6.join(projectPath, "..");
457
- }
458
- if (projectPath === "/") {
459
- throw new Error("Please run this command in a valid project");
460
- }
461
- this.projectPath = projectPath;
462
- this.srcPath = path6.join(projectPath, "src");
463
- this.packageJsonPath = path6.join(projectPath, "package.json");
464
- this.packageJson = JSON.parse(readFileSync(this.packageJsonPath, "utf8"));
465
- this.flowgramVersion = this.packageJson.dependencies["@flowgram.ai/fixed-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/free-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/editor"];
466
- }
467
- async addDependency(dependency) {
468
- let name;
469
- let version;
470
- const lastAtIndex = dependency.lastIndexOf("@");
471
- if (lastAtIndex <= 0) {
472
- name = dependency;
473
- version = await getLatestVersion(name);
474
- } else {
475
- name = dependency.substring(0, lastAtIndex);
476
- version = dependency.substring(lastAtIndex + 1);
477
- if (!version.trim()) {
478
- version = await getLatestVersion(name);
479
- }
480
- }
481
- this.packageJson.dependencies[name] = version;
482
- writeFileSync(
483
- this.packageJsonPath,
484
- JSON.stringify(this.packageJson, null, 2)
485
- );
486
- }
487
- async addDependencies(dependencies) {
488
- for (const dependency of dependencies) {
489
- await this.addDependency(dependency);
591
+ import chalk4 from "chalk";
592
+ function executeRefreshProjectImport(context) {
593
+ const { selectedMaterials, project, targetFormMaterialRoot } = context;
594
+ const exportName2Material = /* @__PURE__ */ new Map();
595
+ const targetModule = `@/${path7.relative(project.srcPath, targetFormMaterialRoot)}`;
596
+ for (const material of selectedMaterials) {
597
+ if (!material.indexFile) {
598
+ console.warn(`Material ${material.name} not found`);
599
+ return;
490
600
  }
601
+ console.log(`\u{1F440} The exports of ${material.name} is ${material.allExportNames.join(",")}`);
602
+ material.allExportNames.forEach((exportName) => {
603
+ exportName2Material.set(exportName, material);
604
+ });
491
605
  }
492
- writeToPackageJsonFile() {
493
- writeFileSync(
494
- this.packageJsonPath,
495
- JSON.stringify(this.packageJson, null, 2)
496
- );
497
- }
498
- printInfo() {
499
- console.log(chalk.bold("Project Info:"));
500
- console.log(chalk.black(` - Flowgram Version: ${this.flowgramVersion}`));
501
- console.log(chalk.black(` - Project Path: ${this.projectPath}`));
502
- }
503
- static async getSingleton() {
504
- const info = new _Project();
505
- await info.init();
506
- return info;
507
- }
508
- };
509
-
510
- // src/materials/refresh-project-import.ts
511
- import chalk2 from "chalk";
512
- function executeRefreshProjectImport(project, material) {
513
- const materialFile = getIndexTsFile(material.path);
514
- if (!materialFile) {
515
- console.warn(`Material ${material.name} not found`);
516
- return;
517
- }
518
- const targetDir = `@/form-materials/${material.type}/${material.name}`;
519
- const exportNames = materialFile.allExportNames;
520
- console.log(`\u{1F440} The exports of ${material.name} is ${exportNames.join(",")}`);
521
606
  for (const tsFile of traverseRecursiveTsFiles(project.srcPath)) {
522
607
  for (const importDeclaration of tsFile.imports) {
523
- if (importDeclaration.source === "@flowgram.ai/form-materials") {
524
- const currentMaterialImports = importDeclaration.namedImports?.filter(
525
- (item) => exportNames.includes(item.imported)
526
- );
527
- if (!currentMaterialImports?.length) {
608
+ if (importDeclaration.source.startsWith("@flowgram.ai/form-materials")) {
609
+ const restImports = [];
610
+ const importMap = {};
611
+ if (!importDeclaration.namedImports) {
528
612
  continue;
529
613
  }
530
- const nextImports = [
531
- {
532
- ...importDeclaration,
533
- namedImports: currentMaterialImports,
534
- source: targetDir
614
+ for (const nameImport of importDeclaration.namedImports) {
615
+ const material = exportName2Material.get(nameImport.imported);
616
+ if (material) {
617
+ const importModule = `${targetModule}/${material.fullName}`;
618
+ importMap[importModule] = importMap[importModule] || [];
619
+ importMap[importModule].push(nameImport);
620
+ } else {
621
+ restImports.push(nameImport);
535
622
  }
536
- ];
537
- const keepImportNames = importDeclaration.namedImports?.filter(
538
- (item) => !exportNames.includes(item.imported)
623
+ }
624
+ if (Object.keys(importMap).length === 0) {
625
+ continue;
626
+ }
627
+ const nextImports = Object.entries(importMap).map(
628
+ ([importModule, namedImports]) => ({
629
+ ...importDeclaration,
630
+ namedImports,
631
+ source: importModule
632
+ })
539
633
  );
540
- if (keepImportNames?.length) {
634
+ if (restImports?.length) {
541
635
  nextImports.unshift({
542
636
  ...importDeclaration,
543
- namedImports: keepImportNames
637
+ namedImports: restImports
544
638
  });
545
639
  }
546
640
  tsFile.replaceImport([importDeclaration], nextImports);
547
- console.log(chalk2.green(`\u{1F504} Refresh Imports In: ${tsFile.path}`));
641
+ console.log(chalk4.green(`\u{1F504} Refresh Imports In: ${tsFile.path}`));
548
642
  console.log(
549
643
  `From:
550
644
  ${importDeclaration.statement}
@@ -556,86 +650,147 @@ ${nextImports.map((item) => item.statement).join("\n")}`
556
650
  }
557
651
  }
558
652
 
653
+ // src/materials/copy.ts
654
+ import path8 from "path";
655
+ import fs4 from "fs";
656
+ var copyMaterials = (ctx) => {
657
+ const { selectedMaterials, project, formMaterialPkg, targetFormMaterialRoot } = ctx;
658
+ const formMaterialDependencies = formMaterialPkg.dependencies;
659
+ const packagesToInstall = /* @__PURE__ */ new Set();
660
+ for (const material of selectedMaterials) {
661
+ const sourceDir = material.sourceDir;
662
+ const targetDir = path8.join(targetFormMaterialRoot, material.type, material.name);
663
+ fs4.cpSync(sourceDir, targetDir, { recursive: true });
664
+ for (const file of traverseRecursiveTsFiles(targetDir)) {
665
+ for (const importDeclaration of file.imports) {
666
+ const { source } = importDeclaration;
667
+ if (source.startsWith("@/")) {
668
+ console.log(`Replace Import from ${source} to @flowgram.ai/form-materials`);
669
+ file.replaceImport(
670
+ [importDeclaration],
671
+ [{ ...importDeclaration, source: "@flowgram.ai/form-materials" }]
672
+ );
673
+ packagesToInstall.add(`@flowgram.ai/form-materials@${project.flowgramVersion}`);
674
+ } else if (!source.startsWith(".") && !source.startsWith("react")) {
675
+ const [dep, version] = Object.entries(formMaterialDependencies).find(([_key]) => source.startsWith(_key)) || [];
676
+ if (!dep) {
677
+ continue;
678
+ }
679
+ if (dep.startsWith("@flowgram.ai/")) {
680
+ packagesToInstall.add(`${dep}@${project.flowgramVersion}`);
681
+ } else {
682
+ packagesToInstall.add(`${dep}@${version}`);
683
+ }
684
+ }
685
+ }
686
+ }
687
+ }
688
+ return {
689
+ packagesToInstall: [...packagesToInstall]
690
+ };
691
+ };
692
+
559
693
  // src/materials/index.ts
560
- async function syncMaterial(opts) {
561
- const { materialName, refreshProjectImports } = opts;
562
- console.log(chalk3.bold("\u{1F680} Welcome to @flowgram.ai form-materials!"));
694
+ async function syncMaterial(cliOpts) {
695
+ const { refreshProjectImports, targetMaterialRootDir } = cliOpts;
696
+ console.log(chalk5.bold("\u{1F680} Welcome to @flowgram.ai form-materials CLI!"));
563
697
  const project = await Project.getSingleton();
564
698
  project.printInfo();
699
+ const targetFormMaterialRoot = targetMaterialRootDir || path9.join(project.projectPath, "src", "form-materials");
700
+ console.log(chalk5.black(` - Target material root: ${targetFormMaterialRoot}`));
565
701
  if (!project.flowgramVersion) {
566
702
  throw new Error(
567
- chalk3.red(
703
+ chalk5.red(
568
704
  "\u274C Please install @flowgram.ai/fixed-layout-editor or @flowgram.ai/free-layout-editor"
569
705
  )
570
706
  );
571
707
  }
572
- const formMaterialPath = await loadNpm("@flowgram.ai/form-materials");
573
- const formMaterialSrc = path7.join(formMaterialPath, "src");
574
- const materials = listAllMaterials(formMaterialSrc);
575
- let material;
576
- if (materialName) {
577
- const selectedMaterial = materials.find(
578
- (m) => `${m.type}/${m.name}` === materialName
579
- );
580
- if (selectedMaterial) {
581
- material = selectedMaterial;
582
- console.log(chalk3.green(`Using material: ${materialName}`));
583
- } else {
584
- console.log(
585
- chalk3.yellow(
586
- `Material "${materialName}" not found. Please select from the list:`
587
- )
588
- );
589
- }
590
- }
591
- if (!material) {
592
- const result = await inquirer.prompt([
593
- {
594
- type: "list",
595
- name: "material",
596
- message: "Select one material to add:",
597
- choices: [
598
- ...materials.map((_material) => ({
599
- name: `${_material.type}/${_material.name}`,
600
- value: _material
601
- }))
602
- ]
603
- }
604
- ]);
605
- material = result.material;
606
- }
607
- if (!material) {
608
- console.error(chalk3.red("No material selected. Exiting."));
708
+ const formMaterialPkg = await loadNpm("@flowgram.ai/form-materials");
709
+ let selectedMaterials = await getSelectedMaterials(cliOpts, formMaterialPkg);
710
+ if (!selectedMaterials.length) {
711
+ console.error(chalk5.red("No material selected. Exiting."));
609
712
  process.exit(1);
610
713
  }
714
+ const context = {
715
+ selectedMaterials,
716
+ project,
717
+ formMaterialPkg,
718
+ cliOpts,
719
+ targetFormMaterialRoot
720
+ };
721
+ console.log(chalk5.bold("\u{1F680} The following materials will be added to your project"));
722
+ console.log(selectedMaterials.map((material) => `\u{1F4E6} ${material.fullName}`).join("\n"));
723
+ console.log("\n");
724
+ let { packagesToInstall } = copyMaterials(context);
611
725
  if (refreshProjectImports) {
612
- console.log(chalk3.bold("\u{1F680} Refresh imports in your project"));
613
- executeRefreshProjectImport(project, material);
614
- }
615
- console.log(
616
- chalk3.bold("\u{1F680} The following materials will be added to your project")
617
- );
618
- console.log(material);
619
- let { packagesToInstall } = copyMaterial(material, project, formMaterialPath);
726
+ console.log(chalk5.bold("\u{1F680} Refresh imports in your project"));
727
+ executeRefreshProjectImport(context);
728
+ }
620
729
  await project.addDependencies(packagesToInstall);
621
- console.log(
622
- chalk3.bold("\u2705 These npm dependencies is added to your package.json")
623
- );
730
+ console.log(chalk5.bold("\n\u2705 These npm dependencies is added to your package.json"));
624
731
  packagesToInstall.forEach((_package) => {
625
732
  console.log(`- ${_package}`);
626
733
  });
627
- console.log(chalk3.bold("\n\u27A1\uFE0F Please run npm install to install dependencies\n"));
734
+ console.log(chalk5.bold(chalk5.bold("\n\u27A1\uFE0F Please run npm install to install dependencies\n")));
735
+ }
736
+
737
+ // src/find-materials/index.ts
738
+ import chalk6 from "chalk";
739
+ async function findUsedMaterials() {
740
+ console.log(chalk6.bold("\u{1F680} Welcome to @flowgram.ai form-materials CLI!"));
741
+ const project = await Project.getSingleton();
742
+ project.printInfo();
743
+ const formMaterialPkg = await loadNpm("@flowgram.ai/form-materials");
744
+ const materials = Material.listAll(formMaterialPkg);
745
+ const allUsedMaterials = /* @__PURE__ */ new Set();
746
+ const exportName2Material = /* @__PURE__ */ new Map();
747
+ for (const material of materials) {
748
+ if (!material.indexFile) {
749
+ console.warn(`Material ${material.name} not found`);
750
+ return;
751
+ }
752
+ console.log(`\u{1F440} The exports of ${material.name} is ${material.allExportNames.join(",")}`);
753
+ material.allExportNames.forEach((exportName) => {
754
+ exportName2Material.set(exportName, material);
755
+ });
756
+ }
757
+ for (const tsFile of traverseRecursiveTsFiles(project.srcPath)) {
758
+ const fileMaterials = /* @__PURE__ */ new Set();
759
+ let fileImportPrinted = false;
760
+ for (const importDeclaration of tsFile.imports) {
761
+ if (!importDeclaration.source.startsWith("@flowgram.ai/form-materials") || !importDeclaration.namedImports?.length) {
762
+ continue;
763
+ }
764
+ if (!fileImportPrinted) {
765
+ fileImportPrinted = true;
766
+ console.log(chalk6.bold(`
767
+ \u{1F440} Searching ${tsFile.path}`));
768
+ }
769
+ console.log(`\u{1F50D} ${importDeclaration.statement}`);
770
+ if (importDeclaration.namedImports) {
771
+ importDeclaration.namedImports.forEach((namedImport) => {
772
+ const material = exportName2Material.get(namedImport.imported);
773
+ if (material) {
774
+ fileMaterials.add(material);
775
+ allUsedMaterials.add(material);
776
+ console.log(`import ${chalk6.bold(material.fullName)} by ${namedImport.imported}`);
777
+ }
778
+ });
779
+ }
780
+ }
781
+ }
782
+ console.log(chalk6.bold("\n\u{1F4E6} All used materials:"));
783
+ console.log([...allUsedMaterials].map((_material) => _material.fullName).join(","));
628
784
  }
629
785
 
630
786
  // src/create-app/index.ts
631
- import path8 from "path";
787
+ import path10 from "path";
788
+ import https2 from "https";
632
789
  import { execSync as execSync2 } from "child_process";
790
+ import * as tar2 from "tar";
633
791
  import inquirer2 from "inquirer";
634
- import fs4 from "fs-extra";
635
- import chalk4 from "chalk";
636
- import download2 from "download";
637
- import * as tar from "tar";
638
- var args = process.argv.slice(2);
792
+ import fs5 from "fs-extra";
793
+ import chalk7 from "chalk";
639
794
  var updateFlowGramVersions = (dependencies, latestVersion) => {
640
795
  for (const packageName in dependencies) {
641
796
  if (packageName.startsWith("@flowgram.ai")) {
@@ -643,8 +798,29 @@ var updateFlowGramVersions = (dependencies, latestVersion) => {
643
798
  }
644
799
  }
645
800
  };
801
+ function downloadFile2(url, dest) {
802
+ return new Promise((resolve, reject) => {
803
+ const file = fs5.createWriteStream(dest);
804
+ https2.get(url, (response) => {
805
+ if (response.statusCode !== 200) {
806
+ reject(new Error(`Download failed: ${response.statusCode}`));
807
+ return;
808
+ }
809
+ response.pipe(file);
810
+ file.on("finish", () => {
811
+ file.close();
812
+ resolve();
813
+ });
814
+ }).on("error", (err) => {
815
+ fs5.unlink(dest, () => reject(err));
816
+ });
817
+ file.on("error", (err) => {
818
+ fs5.unlink(dest, () => reject(err));
819
+ });
820
+ });
821
+ }
646
822
  var createApp = async (projectName) => {
647
- console.log(chalk4.green("Welcome to @flowgram.ai/create-app CLI!"));
823
+ console.log(chalk7.green("Welcome to @flowgram.ai/create-app CLI!"));
648
824
  const latest = execSync2("npm view @flowgram.ai/demo-fixed-layout version --tag=latest latest").toString().trim();
649
825
  let folderName = "";
650
826
  if (!projectName) {
@@ -666,7 +842,14 @@ var createApp = async (projectName) => {
666
842
  ]);
667
843
  folderName = repo;
668
844
  } else {
669
- if (["fixed-layout", "free-layout", "fixed-layout-simple", "free-layout-simple", "playground", "nextjs"].includes(projectName)) {
845
+ if ([
846
+ "fixed-layout",
847
+ "free-layout",
848
+ "fixed-layout-simple",
849
+ "free-layout-simple",
850
+ "playground",
851
+ "nextjs"
852
+ ].includes(projectName)) {
670
853
  folderName = `demo-${projectName}`;
671
854
  } else {
672
855
  console.error('Invalid projectName. Please run "npx create-app" to choose demo.');
@@ -674,19 +857,20 @@ var createApp = async (projectName) => {
674
857
  }
675
858
  }
676
859
  try {
677
- const targetDir = path8.join(process.cwd());
860
+ const targetDir = path10.join(process.cwd());
678
861
  const downloadPackage = async () => {
679
862
  try {
680
- const tarballBuffer = await download2(`https://registry.npmjs.org/@flowgram.ai/${folderName}/-/${folderName}-${latest}.tgz`);
681
- fs4.ensureDirSync(targetDir);
682
- const tempTarballPath = path8.join(process.cwd(), `${folderName}.tgz`);
683
- fs4.writeFileSync(tempTarballPath, tarballBuffer);
684
- await tar.x({
863
+ const url = `https://registry.npmjs.org/@flowgram.ai/${folderName}/-/${folderName}-${latest}.tgz`;
864
+ const tempTarballPath = path10.join(process.cwd(), `${folderName}.tgz`);
865
+ console.log(chalk7.blue(`Downloading ${url} ...`));
866
+ await downloadFile2(url, tempTarballPath);
867
+ fs5.ensureDirSync(targetDir);
868
+ await tar2.x({
685
869
  file: tempTarballPath,
686
870
  C: targetDir
687
871
  });
688
- fs4.renameSync(path8.join(targetDir, "package"), path8.join(targetDir, folderName));
689
- fs4.unlinkSync(tempTarballPath);
872
+ fs5.renameSync(path10.join(targetDir, "package"), path10.join(targetDir, folderName));
873
+ fs5.unlinkSync(tempTarballPath);
690
874
  return true;
691
875
  } catch (error) {
692
876
  console.error(`Error downloading or extracting package: ${error}`);
@@ -694,8 +878,8 @@ var createApp = async (projectName) => {
694
878
  }
695
879
  };
696
880
  const res = await downloadPackage();
697
- const pkgJsonPath = path8.join(targetDir, folderName, "package.json");
698
- const data = fs4.readFileSync(pkgJsonPath, "utf-8");
881
+ const pkgJsonPath = path10.join(targetDir, folderName, "package.json");
882
+ const data = fs5.readFileSync(pkgJsonPath, "utf-8");
699
883
  const packageLatestVersion = execSync2("npm view @flowgram.ai/core version --tag=latest latest").toString().trim();
700
884
  const jsonData = JSON.parse(data);
701
885
  if (jsonData.dependencies) {
@@ -704,15 +888,15 @@ var createApp = async (projectName) => {
704
888
  if (jsonData.devDependencies) {
705
889
  updateFlowGramVersions(jsonData.devDependencies, packageLatestVersion);
706
890
  }
707
- fs4.writeFileSync(pkgJsonPath, JSON.stringify(jsonData, null, 2), "utf-8");
891
+ fs5.writeFileSync(pkgJsonPath, JSON.stringify(jsonData, null, 2), "utf-8");
708
892
  if (res) {
709
- console.log(chalk4.green(`${folderName} Demo project created successfully!`));
710
- console.log(chalk4.yellow("Run the following commands to start:"));
711
- console.log(chalk4.cyan(` cd ${folderName}`));
712
- console.log(chalk4.cyan(" npm install"));
713
- console.log(chalk4.cyan(" npm start"));
893
+ console.log(chalk7.green(`${folderName} Demo project created successfully!`));
894
+ console.log(chalk7.yellow("Run the following commands to start:"));
895
+ console.log(chalk7.cyan(` cd ${folderName}`));
896
+ console.log(chalk7.cyan(" npm install"));
897
+ console.log(chalk7.cyan(" npm start"));
714
898
  } else {
715
- console.log(chalk4.red("Download failed"));
899
+ console.log(chalk7.red("Download failed"));
716
900
  }
717
901
  } catch (error) {
718
902
  console.error("Error downloading repo:", error);
@@ -720,62 +904,26 @@ var createApp = async (projectName) => {
720
904
  }
721
905
  };
722
906
 
723
- // src/update-version/index.ts
724
- import chalk5 from "chalk";
725
- async function updateFlowgramVersion(inputVersion) {
726
- console.log(chalk5.bold("\u{1F680} Welcome to @flowgram.ai update-version helper"));
727
- const latestVersion = await getLatestVersion("@flowgram.ai/editor");
728
- const currentPath = process.cwd();
729
- console.log("- Latest flowgram version: ", latestVersion);
730
- console.log("- Current Path: ", currentPath);
731
- const flowgramVersion = inputVersion || latestVersion;
732
- for (const file of traverseRecursiveFiles(currentPath)) {
733
- if (file.path.endsWith("package.json")) {
734
- console.log("\u{1F440} Find package.json: ", file.path);
735
- let updated = false;
736
- const json = JSON.parse(file.content);
737
- if (json.dependencies) {
738
- for (const key in json.dependencies) {
739
- if (key.startsWith("@flowgram.ai/")) {
740
- updated = true;
741
- json.dependencies[key] = flowgramVersion;
742
- console.log(`- Update ${key} to ${flowgramVersion}`);
743
- }
744
- }
745
- }
746
- if (json.devDependencies) {
747
- for (const key in json.devDependencies) {
748
- if (key.startsWith("@flowgram.ai/")) {
749
- updated = true;
750
- json.devDependencies[key] = flowgramVersion;
751
- console.log(`- Update ${key} to ${flowgramVersion}`);
752
- }
753
- }
754
- }
755
- if (updated) {
756
- file.write(JSON.stringify(json, null, 2));
757
- console.log(`\u2705 ${file.path} Updated`);
758
- }
759
- }
760
- }
761
- }
762
-
763
907
  // src/index.ts
764
908
  var program = new Command();
765
909
  program.name("flowgram-cli").version("1.0.0").description("Flowgram CLI");
766
910
  program.command("create-app").description("Create a new flowgram project").argument("[string]", "Project name").action(async (projectName) => {
767
911
  await createApp(projectName);
768
912
  });
769
- program.command("materials").description("Sync materials to the project").argument("[string]", "Material name").option(
770
- "--refresh-project-imports",
771
- "Refresh project imports to copied materials",
772
- false
773
- ).action(async (materialName, options) => {
913
+ program.command("materials").description("Sync materials to the project").argument(
914
+ "[string]",
915
+ "Material name or names\nExample 1: components/variable-selector \nExample2: components/variable-selector,effect/provideJsonSchemaOutputs"
916
+ ).option("--refresh-project-imports", "Refresh project imports to copied materials", false).option("--target-material-root-dir <string>", "Target directory to copy materials").option("--select-multiple", "Select multiple materials", false).action(async (materialName, options) => {
774
917
  await syncMaterial({
775
918
  materialName,
776
- refreshProjectImports: options.refreshProjectImports
919
+ refreshProjectImports: options.refreshProjectImports,
920
+ targetMaterialRootDir: options.targetMaterialRootDir ? path11.join(process.cwd(), options.targetMaterialRootDir) : void 0,
921
+ selectMultiple: options.selectMultiple
777
922
  });
778
923
  });
924
+ program.command("find-used-materials").description("Find used materials in the project").action(async () => {
925
+ await findUsedMaterials();
926
+ });
779
927
  program.command("update-version").description("Update flowgram version in the project").argument("[string]", "Flowgram version").action(async (version) => {
780
928
  await updateFlowgramVersion(version);
781
929
  });