@flowgram.ai/cli 0.4.10 → 0.4.12

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,34 +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";
99
+ import ignore from "ignore";
26
100
  var File = class {
27
101
  constructor(filePath, root = "/") {
28
102
  this.root = root;
29
103
  this.path = filePath;
30
- this.relativePath = path2.relative(this.root, this.path);
31
- this.suffix = path2.extname(this.path);
32
- if (!fs.existsSync(this.path)) {
33
- 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`);
34
108
  }
35
109
  try {
36
- this.content = fs.readFileSync(this.path, "utf-8");
110
+ this.content = fs2.readFileSync(this.path, "utf-8");
37
111
  this.isUtf8 = true;
38
112
  } catch (e) {
39
113
  this.isUtf8 = false;
@@ -46,19 +120,220 @@ var File = class {
46
120
  return;
47
121
  }
48
122
  this.content = updater(this.content);
49
- fs.writeFileSync(this.path, this.content, "utf-8");
123
+ fs2.writeFileSync(this.path, this.content, "utf-8");
124
+ }
125
+ write(nextContent) {
126
+ this.content = nextContent;
127
+ fs2.writeFileSync(this.path, this.content, "utf-8");
50
128
  }
51
129
  };
130
+ function* traverseRecursiveFilePaths(folder, ig = ignore().add(".git"), root = folder) {
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"));
134
+ }
135
+ for (const file of files) {
136
+ const filePath = path3.join(folder, file);
137
+ if (ig.ignores(path3.relative(root, filePath))) {
138
+ continue;
139
+ }
140
+ if (fs2.statSync(filePath).isDirectory()) {
141
+ yield* traverseRecursiveFilePaths(filePath, ig, root);
142
+ } else {
143
+ yield filePath;
144
+ }
145
+ }
146
+ }
147
+ function* traverseRecursiveFiles(folder) {
148
+ for (const filePath of traverseRecursiveFilePaths(folder)) {
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;
328
+ }
329
+ }
52
330
 
53
331
  // src/utils/export.ts
54
332
  function extractNamedExports(content) {
55
333
  const valueExports = [];
56
334
  const typeExports = [];
57
335
  const typeDefinitions = /* @__PURE__ */ new Set();
58
- const typePatterns = [
59
- /\b(?:type|interface)\s+(\w+)/g,
60
- /\bexport\s+(?:type|interface)\s+(\w+)/g
61
- ];
336
+ const typePatterns = [/\b(?:type|interface)\s+(\w+)/g, /\bexport\s+(?:type|interface)\s+(\w+)/g];
62
337
  let match;
63
338
  for (const pattern of typePatterns) {
64
339
  while ((match = pattern.exec(content)) !== null) {
@@ -137,84 +412,17 @@ function extractNamedExports(content) {
137
412
  };
138
413
  }
139
414
 
140
- // src/utils/import.ts
141
- function assembleImport(declaration) {
142
- const { namedImports, defaultImport, namespaceImport, source } = declaration;
143
- const importClauses = [];
144
- if (namedImports) {
145
- importClauses.push(
146
- `{ ${namedImports.map(
147
- ({ local, imported, typeOnly }) => `${typeOnly ? "type " : ""}${imported}${local ? ` as ${local}` : ""}`
148
- ).join(", ")} }`
149
- );
150
- }
151
- if (defaultImport) {
152
- importClauses.push(defaultImport);
153
- }
154
- if (namespaceImport) {
155
- importClauses.push(`* as ${namespaceImport}`);
156
- }
157
- return `import ${importClauses.join(", ")} from '${source}';`;
158
- }
159
- function* traverseFileImports(fileContent) {
160
- 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;
161
- let match;
162
- while ((match = importRegex.exec(fileContent)) !== null) {
163
- const [fullMatch, defaultPart, namespacePart, namedPart, defaultPart2, source] = match;
164
- const declaration = {
165
- statement: fullMatch,
166
- source
167
- };
168
- const defaultImport = defaultPart?.trim() || defaultPart2?.trim();
169
- if (defaultImport && !namespacePart && !namedPart) {
170
- declaration.defaultImport = defaultImport;
171
- } else if (defaultImport && (namespacePart || namedPart)) {
172
- declaration.defaultImport = defaultImport;
173
- }
174
- if (namespacePart) {
175
- declaration.namespaceImport = namespacePart.trim();
176
- }
177
- if (namedPart) {
178
- const namedImports = [];
179
- const namedItems = namedPart.split(",").map((item) => item.trim()).filter(Boolean);
180
- for (const item of namedItems) {
181
- const typeOnly = item.startsWith("type ");
182
- const cleanItem = typeOnly ? item.slice(5).trim() : item;
183
- if (cleanItem.includes(" as ")) {
184
- const [imported, local] = cleanItem.split(" as ").map((s) => s.trim());
185
- namedImports.push({
186
- imported,
187
- local,
188
- typeOnly
189
- });
190
- } else {
191
- namedImports.push({
192
- imported: cleanItem,
193
- typeOnly
194
- });
195
- }
196
- }
197
- if (namedImports.length > 0) {
198
- declaration.namedImports = namedImports;
199
- }
200
- }
201
- yield declaration;
202
- }
203
- }
204
-
205
415
  // src/utils/ts-file.ts
206
416
  var TsFile = class extends File {
207
- constructor(filePath) {
208
- super(filePath);
417
+ constructor(filePath, root) {
418
+ super(filePath, root);
209
419
  this.exports = {
210
420
  values: [],
211
421
  types: []
212
422
  };
213
423
  this.imports = [];
214
- this.exports = extractNamedExports(fs2.readFileSync(filePath, "utf-8"));
215
- this.imports = Array.from(
216
- traverseFileImports(fs2.readFileSync(filePath, "utf-8"))
217
- );
424
+ this.exports = extractNamedExports(fs3.readFileSync(filePath, "utf-8"));
425
+ this.imports = Array.from(traverseFileImports(fs3.readFileSync(filePath, "utf-8")));
218
426
  }
219
427
  get allExportNames() {
220
428
  return [...this.exports.values, ...this.exports.types];
@@ -228,9 +436,7 @@ var TsFile = class extends File {
228
436
  return content.replace(
229
437
  lastImportStatement.statement,
230
438
  `${lastImportStatement?.statement}
231
- ${importDeclarations.map(
232
- (item) => item.statement
233
- )}
439
+ ${importDeclarations.map((item) => item.statement)}
234
440
  `
235
441
  );
236
442
  });
@@ -238,14 +444,9 @@ ${importDeclarations.map(
238
444
  }
239
445
  removeImport(importDeclarations) {
240
446
  this.replace(
241
- (content) => importDeclarations.reduce(
242
- (prev, cur) => prev.replace(cur.statement, ""),
243
- content
244
- )
245
- );
246
- this.imports = this.imports.filter(
247
- (item) => !importDeclarations.includes(item)
447
+ (content) => importDeclarations.reduce((prev, cur) => prev.replace(cur.statement, ""), content)
248
448
  );
449
+ this.imports = this.imports.filter((item) => !importDeclarations.includes(item));
249
450
  }
250
451
  replaceImport(oldImports, newImports) {
251
452
  newImports.forEach((importDeclaration) => {
@@ -274,9 +475,7 @@ ${importDeclarations.map(
274
475
  content = content.replace(
275
476
  lastImportStatement,
276
477
  `${lastImportStatement}
277
- ${restNewImports.map(
278
- (item) => item.statement
279
- )}
478
+ ${restNewImports.map((item) => item.statement).join("\n")}
280
479
  `
281
480
  );
282
481
  }
@@ -286,238 +485,160 @@ ${restNewImports.map(
286
485
  }
287
486
  };
288
487
  function* traverseRecursiveTsFiles(folder) {
289
- const files = fs2.readdirSync(folder);
290
- for (const file of files) {
291
- const filePath = path3.join(folder, file);
292
- if (fs2.statSync(filePath).isDirectory()) {
293
- yield* traverseRecursiveTsFiles(filePath);
294
- } else {
295
- if (file.endsWith(".ts") || file.endsWith(".tsx")) {
296
- yield new TsFile(filePath);
297
- }
488
+ for (const filePath of traverseRecursiveFilePaths(folder)) {
489
+ if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) {
490
+ yield new TsFile(filePath, folder);
298
491
  }
299
492
  }
300
493
  }
301
494
  function getIndexTsFile(folder) {
302
- const files = fs2.readdirSync(folder);
495
+ const files = fs3.readdirSync(folder);
303
496
  for (const file of files) {
304
497
  if (file === "index.ts" || file === "index.tsx") {
305
- return new TsFile(path3.join(folder, file));
498
+ return new TsFile(path5.join(folder, file), folder);
306
499
  }
307
500
  }
308
501
  return void 0;
309
502
  }
310
503
 
311
- // src/materials/materials.ts
312
- 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 = [
313
540
  "components",
314
541
  "effects",
315
542
  "plugins",
316
543
  "shared",
317
- "typings",
318
544
  "validate",
319
545
  "form-plugins",
320
546
  "hooks"
321
547
  ];
322
- function listAllMaterials(formMaterialSrc) {
323
- const _materials = [];
324
- for (const _type of _types) {
325
- const materialsPath = path4.join(formMaterialSrc, _type);
326
- _materials.push(
327
- ...fs3.readdirSync(materialsPath).map((_path) => {
328
- if (_path === "index.ts") {
329
- return null;
330
- }
331
- return {
332
- name: _path,
333
- // Assuming the folder name is the material name
334
- type: _type,
335
- path: path4.join(materialsPath, _path)
336
- };
337
- }).filter((material) => material !== null)
338
- );
339
- }
340
- return _materials;
341
- }
342
- var getFormMaterialDependencies = (formMaterialPath) => {
343
- const packageJsonPath = path4.join(formMaterialPath, "package.json");
344
- const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
345
- return packageJson.dependencies;
346
- };
347
- var copyMaterial = (material, project, formMaterialPath) => {
348
- const formMaterialDependencies = getFormMaterialDependencies(formMaterialPath);
349
- const sourceDir = material.path;
350
- const materialRoot = path4.join(
351
- project.projectPath,
352
- "src",
353
- "form-materials",
354
- `${material.type}`
355
- );
356
- const targetDir = path4.join(materialRoot, material.name);
357
- const packagesToInstall = /* @__PURE__ */ new Set();
358
- fs3.cpSync(sourceDir, targetDir, { recursive: true });
359
- for (const file of traverseRecursiveTsFiles(targetDir)) {
360
- for (const importDeclaration of file.imports) {
361
- const { source } = importDeclaration;
362
- if (source.startsWith("@/")) {
363
- console.log(
364
- `Replace Import from ${source} to @flowgram.ai/form-materials`
365
- );
366
- file.replaceImport(
367
- [importDeclaration],
368
- [{ ...importDeclaration, source: "@flowgram.ai/form-materials" }]
369
- );
370
- packagesToInstall.add(
371
- `@flowgram.ai/form-materials@${project.flowgramVersion}`
372
- );
373
- } else if (!source.startsWith(".") && !source.startsWith("react")) {
374
- const [dep, version] = Object.entries(formMaterialDependencies).find(
375
- ([_key]) => source.startsWith(_key)
376
- ) || [];
377
- if (!dep) {
378
- 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
379
571
  }
380
- if (dep.startsWith("@flowgram.ai/")) {
381
- packagesToInstall.add(`${dep}@${project.flowgramVersion}`);
382
- } else {
383
- 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
384
581
  }
385
- }
582
+ ]);
583
+ selectedMaterials = [result.material];
386
584
  }
387
585
  }
388
- return {
389
- packagesToInstall: [...packagesToInstall]
390
- };
586
+ return selectedMaterials;
391
587
  };
392
588
 
393
- // src/utils/npm.ts
394
- import { execSync } from "child_process";
395
- import { existsSync } from "fs";
396
- import path5 from "path";
397
- import download from "download";
398
- async function getLatestVersion(packageName) {
399
- return execSync(`npm view ${packageName} version --tag=latest`).toString().trim();
400
- }
401
- async function loadNpm(packageName) {
402
- const packageLatestVersion = await getLatestVersion(packageName);
403
- const packagePath = path5.join(
404
- __dirname,
405
- `./.download/${packageName}-${packageLatestVersion}`
406
- );
407
- if (existsSync(packagePath)) {
408
- return packagePath;
409
- }
410
- try {
411
- const tarballUrl = execSync(
412
- `npm view ${packageName}@${packageLatestVersion} dist.tarball`
413
- ).toString().trim();
414
- await download(tarballUrl, packagePath, { extract: true, strip: 1 });
415
- return packagePath;
416
- } catch (error) {
417
- console.error(`Error downloading or extracting package: ${error}`);
418
- throw error;
419
- }
420
- }
421
-
422
- // src/materials/index.ts
589
+ // src/materials/refresh-project-import.ts
423
590
  import path7 from "path";
424
-
425
- // src/utils/project.ts
426
- import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
427
- import path6 from "path";
428
- import chalk from "chalk";
429
- var Project = class _Project {
430
- constructor() {
431
- }
432
- async init() {
433
- let projectPath = process.cwd();
434
- while (projectPath !== "/" && !existsSync2(path6.join(projectPath, "package.json"))) {
435
- projectPath = path6.join(projectPath, "..");
436
- }
437
- if (projectPath === "/") {
438
- throw new Error("Please run this command in a valid project");
439
- }
440
- this.projectPath = projectPath;
441
- this.srcPath = path6.join(projectPath, "src");
442
- this.packageJsonPath = path6.join(projectPath, "package.json");
443
- this.packageJson = JSON.parse(readFileSync(this.packageJsonPath, "utf8"));
444
- this.flowgramVersion = this.packageJson.dependencies["@flowgram.ai/fixed-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/free-layout-editor"] || this.packageJson.dependencies["@flowgram.ai/editor"];
445
- }
446
- async addDependency(dependency) {
447
- let name;
448
- let version;
449
- const lastAtIndex = dependency.lastIndexOf("@");
450
- if (lastAtIndex <= 0) {
451
- name = dependency;
452
- version = await getLatestVersion(name);
453
- } else {
454
- name = dependency.substring(0, lastAtIndex);
455
- version = dependency.substring(lastAtIndex + 1);
456
- if (!version.trim()) {
457
- version = await getLatestVersion(name);
458
- }
459
- }
460
- this.packageJson.dependencies[name] = version;
461
- writeFileSync(
462
- this.packageJsonPath,
463
- JSON.stringify(this.packageJson, null, 2)
464
- );
465
- }
466
- async addDependencies(dependencies) {
467
- for (const dependency of dependencies) {
468
- 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;
469
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
+ });
470
605
  }
471
- printInfo() {
472
- console.log(chalk.bold("Project Info:"));
473
- console.log(chalk.black(` - Flowgram Version: ${this.flowgramVersion}`));
474
- console.log(chalk.black(` - Project Path: ${this.projectPath}`));
475
- }
476
- static async getSingleton() {
477
- const info = new _Project();
478
- await info.init();
479
- return info;
480
- }
481
- };
482
-
483
- // src/materials/refresh-project-import.ts
484
- import chalk2 from "chalk";
485
- function executeRefreshProjectImport(project, material) {
486
- const materialFile = getIndexTsFile(material.path);
487
- if (!materialFile) {
488
- console.warn(`Material ${material.name} not found`);
489
- return;
490
- }
491
- const targetDir = `@/form-materials/${material.type}/${material.name}`;
492
- const exportNames = materialFile.allExportNames;
493
- console.log(`\u{1F440} The exports of ${material.name} is ${exportNames.join(",")}`);
494
606
  for (const tsFile of traverseRecursiveTsFiles(project.srcPath)) {
495
607
  for (const importDeclaration of tsFile.imports) {
496
- if (importDeclaration.source === "@flowgram.ai/form-materials") {
497
- const currentMaterialImports = importDeclaration.namedImports?.filter(
498
- (item) => exportNames.includes(item.imported)
499
- );
500
- if (!currentMaterialImports?.length) {
608
+ if (importDeclaration.source.startsWith("@flowgram.ai/form-materials")) {
609
+ const restImports = [];
610
+ const importMap = {};
611
+ if (!importDeclaration.namedImports) {
501
612
  continue;
502
613
  }
503
- const nextImports = [
504
- {
505
- ...importDeclaration,
506
- namedImports: currentMaterialImports,
507
- 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);
508
622
  }
509
- ];
510
- const keepImportNames = importDeclaration.namedImports?.filter(
511
- (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
+ })
512
633
  );
513
- if (keepImportNames?.length) {
634
+ if (restImports?.length) {
514
635
  nextImports.unshift({
515
636
  ...importDeclaration,
516
- namedImports: keepImportNames
637
+ namedImports: restImports
517
638
  });
518
639
  }
519
640
  tsFile.replaceImport([importDeclaration], nextImports);
520
- console.log(chalk2.green(`\u{1F504} Refresh Imports In: ${tsFile.path}`));
641
+ console.log(chalk4.green(`\u{1F504} Refresh Imports In: ${tsFile.path}`));
521
642
  console.log(
522
643
  `From:
523
644
  ${importDeclaration.statement}
@@ -529,86 +650,147 @@ ${nextImports.map((item) => item.statement).join("\n")}`
529
650
  }
530
651
  }
531
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
+
532
693
  // src/materials/index.ts
533
- async function syncMaterial(opts) {
534
- const { materialName, refreshProjectImports } = opts;
535
- 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!"));
536
697
  const project = await Project.getSingleton();
537
698
  project.printInfo();
699
+ const targetFormMaterialRoot = targetMaterialRootDir || path9.join(project.projectPath, "src", "form-materials");
700
+ console.log(chalk5.black(` - Target material root: ${targetFormMaterialRoot}`));
538
701
  if (!project.flowgramVersion) {
539
702
  throw new Error(
540
- chalk3.red(
703
+ chalk5.red(
541
704
  "\u274C Please install @flowgram.ai/fixed-layout-editor or @flowgram.ai/free-layout-editor"
542
705
  )
543
706
  );
544
707
  }
545
- const formMaterialPath = await loadNpm("@flowgram.ai/form-materials");
546
- const formMaterialSrc = path7.join(formMaterialPath, "src");
547
- const materials = listAllMaterials(formMaterialSrc);
548
- let material;
549
- if (materialName) {
550
- const selectedMaterial = materials.find(
551
- (m) => `${m.type}/${m.name}` === materialName
552
- );
553
- if (selectedMaterial) {
554
- material = selectedMaterial;
555
- console.log(chalk3.green(`Using material: ${materialName}`));
556
- } else {
557
- console.log(
558
- chalk3.yellow(
559
- `Material "${materialName}" not found. Please select from the list:`
560
- )
561
- );
562
- }
563
- }
564
- if (!material) {
565
- const result = await inquirer.prompt([
566
- {
567
- type: "list",
568
- name: "material",
569
- message: "Select one material to add:",
570
- choices: [
571
- ...materials.map((_material) => ({
572
- name: `${_material.type}/${_material.name}`,
573
- value: _material
574
- }))
575
- ]
576
- }
577
- ]);
578
- material = result.material;
579
- }
580
- if (!material) {
581
- 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."));
582
712
  process.exit(1);
583
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);
584
725
  if (refreshProjectImports) {
585
- console.log(chalk3.bold("\u{1F680} Refresh imports in your project"));
586
- executeRefreshProjectImport(project, material);
587
- }
588
- console.log(
589
- chalk3.bold("\u{1F680} The following materials will be added to your project")
590
- );
591
- console.log(material);
592
- let { packagesToInstall } = copyMaterial(material, project, formMaterialPath);
726
+ console.log(chalk5.bold("\u{1F680} Refresh imports in your project"));
727
+ executeRefreshProjectImport(context);
728
+ }
593
729
  await project.addDependencies(packagesToInstall);
594
- console.log(
595
- chalk3.bold("\u2705 These npm dependencies is added to your package.json")
596
- );
730
+ console.log(chalk5.bold("\n\u2705 These npm dependencies is added to your package.json"));
597
731
  packagesToInstall.forEach((_package) => {
598
732
  console.log(`- ${_package}`);
599
733
  });
600
- 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(","));
601
784
  }
602
785
 
603
786
  // src/create-app/index.ts
604
- import path8 from "path";
787
+ import path10 from "path";
788
+ import https2 from "https";
605
789
  import { execSync as execSync2 } from "child_process";
790
+ import * as tar2 from "tar";
606
791
  import inquirer2 from "inquirer";
607
- import fs4 from "fs-extra";
608
- import chalk4 from "chalk";
609
- import download2 from "download";
610
- import * as tar from "tar";
611
- var args = process.argv.slice(2);
792
+ import fs5 from "fs-extra";
793
+ import chalk7 from "chalk";
612
794
  var updateFlowGramVersions = (dependencies, latestVersion) => {
613
795
  for (const packageName in dependencies) {
614
796
  if (packageName.startsWith("@flowgram.ai")) {
@@ -616,8 +798,29 @@ var updateFlowGramVersions = (dependencies, latestVersion) => {
616
798
  }
617
799
  }
618
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
+ }
619
822
  var createApp = async (projectName) => {
620
- console.log(chalk4.green("Welcome to @flowgram.ai/create-app CLI!"));
823
+ console.log(chalk7.green("Welcome to @flowgram.ai/create-app CLI!"));
621
824
  const latest = execSync2("npm view @flowgram.ai/demo-fixed-layout version --tag=latest latest").toString().trim();
622
825
  let folderName = "";
623
826
  if (!projectName) {
@@ -639,7 +842,14 @@ var createApp = async (projectName) => {
639
842
  ]);
640
843
  folderName = repo;
641
844
  } else {
642
- 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)) {
643
853
  folderName = `demo-${projectName}`;
644
854
  } else {
645
855
  console.error('Invalid projectName. Please run "npx create-app" to choose demo.');
@@ -647,19 +857,20 @@ var createApp = async (projectName) => {
647
857
  }
648
858
  }
649
859
  try {
650
- const targetDir = path8.join(process.cwd());
860
+ const targetDir = path10.join(process.cwd());
651
861
  const downloadPackage = async () => {
652
862
  try {
653
- const tarballBuffer = await download2(`https://registry.npmjs.org/@flowgram.ai/${folderName}/-/${folderName}-${latest}.tgz`);
654
- fs4.ensureDirSync(targetDir);
655
- const tempTarballPath = path8.join(process.cwd(), `${folderName}.tgz`);
656
- fs4.writeFileSync(tempTarballPath, tarballBuffer);
657
- 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({
658
869
  file: tempTarballPath,
659
870
  C: targetDir
660
871
  });
661
- fs4.renameSync(path8.join(targetDir, "package"), path8.join(targetDir, folderName));
662
- fs4.unlinkSync(tempTarballPath);
872
+ fs5.renameSync(path10.join(targetDir, "package"), path10.join(targetDir, folderName));
873
+ fs5.unlinkSync(tempTarballPath);
663
874
  return true;
664
875
  } catch (error) {
665
876
  console.error(`Error downloading or extracting package: ${error}`);
@@ -667,8 +878,8 @@ var createApp = async (projectName) => {
667
878
  }
668
879
  };
669
880
  const res = await downloadPackage();
670
- const pkgJsonPath = path8.join(targetDir, folderName, "package.json");
671
- const data = fs4.readFileSync(pkgJsonPath, "utf-8");
881
+ const pkgJsonPath = path10.join(targetDir, folderName, "package.json");
882
+ const data = fs5.readFileSync(pkgJsonPath, "utf-8");
672
883
  const packageLatestVersion = execSync2("npm view @flowgram.ai/core version --tag=latest latest").toString().trim();
673
884
  const jsonData = JSON.parse(data);
674
885
  if (jsonData.dependencies) {
@@ -677,15 +888,15 @@ var createApp = async (projectName) => {
677
888
  if (jsonData.devDependencies) {
678
889
  updateFlowGramVersions(jsonData.devDependencies, packageLatestVersion);
679
890
  }
680
- fs4.writeFileSync(pkgJsonPath, JSON.stringify(jsonData, null, 2), "utf-8");
891
+ fs5.writeFileSync(pkgJsonPath, JSON.stringify(jsonData, null, 2), "utf-8");
681
892
  if (res) {
682
- console.log(chalk4.green(`${folderName} Demo project created successfully!`));
683
- console.log(chalk4.yellow("Run the following commands to start:"));
684
- console.log(chalk4.cyan(` cd ${folderName}`));
685
- console.log(chalk4.cyan(" npm install"));
686
- 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"));
687
898
  } else {
688
- console.log(chalk4.red("Download failed"));
899
+ console.log(chalk7.red("Download failed"));
689
900
  }
690
901
  } catch (error) {
691
902
  console.error("Error downloading repo:", error);
@@ -699,7 +910,21 @@ program.name("flowgram-cli").version("1.0.0").description("Flowgram CLI");
699
910
  program.command("create-app").description("Create a new flowgram project").argument("[string]", "Project name").action(async (projectName) => {
700
911
  await createApp(projectName);
701
912
  });
702
- program.command("materials").description("Sync materials to the project").argument("[string]", "Material name").option("--refresh-project-imports", "Refresh project imports to copied materials", false).action(async (materialName, options) => {
703
- await syncMaterial({ materialName, refreshProjectImports: options.refreshProjectImports });
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) => {
917
+ await syncMaterial({
918
+ materialName,
919
+ refreshProjectImports: options.refreshProjectImports,
920
+ targetMaterialRootDir: options.targetMaterialRootDir ? path11.join(process.cwd(), options.targetMaterialRootDir) : void 0,
921
+ selectMultiple: options.selectMultiple
922
+ });
923
+ });
924
+ program.command("find-used-materials").description("Find used materials in the project").action(async () => {
925
+ await findUsedMaterials();
926
+ });
927
+ program.command("update-version").description("Update flowgram version in the project").argument("[string]", "Flowgram version").action(async (version) => {
928
+ await updateFlowgramVersion(version);
704
929
  });
705
930
  program.parse(process.argv);