Przeglądaj źródła

feat(cli): cli auto update version (#776)

* feat: update version

* fix: blur input ts

* feat: clean dist path
Yiwei Mao 4 miesięcy temu
rodzic
commit
e1d0b0ac01

+ 2 - 1
apps/cli/package.json

@@ -21,7 +21,8 @@
     "chalk": "^5.3.0",
     "download": "8.0.0",
     "tar": "7.4.3",
-    "inquirer": "^9.2.7"
+    "inquirer": "^9.2.7",
+    "ignore": "~7.0.5"
   },
   "devDependencies": {
     "@types/download": "8.0.5",

+ 20 - 4
apps/cli/src/index.ts

@@ -7,6 +7,7 @@ import { Command } from "commander";
 
 import { syncMaterial } from "./materials";
 import { createApp } from "./create-app";
+import { updateFlowgramVersion } from "./update-version";
 
 const program = new Command();
 
@@ -15,7 +16,7 @@ program.name("flowgram-cli").version("1.0.0").description("Flowgram CLI");
 program
   .command("create-app")
   .description("Create a new flowgram project")
-  .argument('[string]', 'Project name')
+  .argument("[string]", "Project name")
   .action(async (projectName) => {
     await createApp(projectName);
   });
@@ -23,10 +24,25 @@ program
 program
   .command("materials")
   .description("Sync materials to the project")
-  .argument('[string]', 'Material name')
-  .option('--refresh-project-imports', 'Refresh project imports to copied materials', false)
+  .argument("[string]", "Material name")
+  .option(
+    "--refresh-project-imports",
+    "Refresh project imports to copied materials",
+    false,
+  )
   .action(async (materialName, options) => {
-    await syncMaterial({ materialName, refreshProjectImports: options.refreshProjectImports });
+    await syncMaterial({
+      materialName,
+      refreshProjectImports: options.refreshProjectImports,
+    });
+  });
+
+program
+  .command("update-version")
+  .description("Update flowgram version in the project")
+  .argument("[string]", "Flowgram version")
+  .action(async (version) => {
+    await updateFlowgramVersion(version);
   });
 
 program.parse(process.argv);

+ 52 - 0
apps/cli/src/update-version/index.ts

@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { getLatestVersion } from "../utils/npm";
+import { traverseRecursiveFiles } from "../utils/file";
+import chalk from "chalk";
+
+export async function updateFlowgramVersion(inputVersion?: string) {
+  console.log(chalk.bold("🚀 Welcome to @flowgram.ai update-version helper"));
+
+  // Get latest version
+  const latestVersion = await getLatestVersion("@flowgram.ai/editor");
+  const currentPath = process.cwd();
+  console.log("- Latest flowgram version: ", latestVersion);
+  console.log("- Current Path: ", currentPath);
+
+  // User Input flowgram version, default is latestVersion
+  const flowgramVersion: string = inputVersion || latestVersion;
+
+  for (const file of traverseRecursiveFiles(currentPath)) {
+    if (file.path.endsWith("package.json")) {
+      console.log("👀 Find package.json: ", file.path);
+      let updated = false;
+      const json = JSON.parse(file.content);
+      if (json.dependencies) {
+        for (const key in json.dependencies) {
+          if (key.startsWith("@flowgram.ai/")) {
+            updated = true;
+            json.dependencies[key] = flowgramVersion;
+            console.log(`- Update ${key} to ${flowgramVersion}`);
+          }
+        }
+      }
+      if (json.devDependencies) {
+        for (const key in json.devDependencies) {
+          if (key.startsWith("@flowgram.ai/")) {
+            updated = true;
+            json.devDependencies[key] = flowgramVersion;
+            console.log(`- Update ${key} to ${flowgramVersion}`);
+          }
+        }
+      }
+
+      if (updated) {
+        file.write(JSON.stringify(json, null, 2));
+        console.log(`✅ ${file.path} Updated`);
+      }
+    }
+  }
+}

+ 36 - 9
apps/cli/src/utils/file.ts

@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: MIT
  */
 
-import path from 'path';
-import fs from 'fs';
+import path from "path";
+import fs from "fs";
+import ignore, { Ignore } from "ignore";
 
 export class File {
   content: string;
@@ -17,7 +18,7 @@ export class File {
 
   suffix: string;
 
-  constructor(filePath: string, public root: string = '/') {
+  constructor(filePath: string, public root: string = "/") {
     this.path = filePath;
     this.relativePath = path.relative(this.root, this.path);
     this.suffix = path.extname(this.path);
@@ -29,7 +30,7 @@ export class File {
 
     // If no utf-8, skip
     try {
-      this.content = fs.readFileSync(this.path, 'utf-8');
+      this.content = fs.readFileSync(this.path, "utf-8");
       this.isUtf8 = true;
     } catch (e) {
       this.isUtf8 = false;
@@ -39,22 +40,48 @@ export class File {
 
   replace(updater: (content: string) => string) {
     if (!this.isUtf8) {
-      console.warn('Not UTF-8 file skipped: ', this.path);
+      console.warn("Not UTF-8 file skipped: ", this.path);
       return;
     }
     this.content = updater(this.content);
-    fs.writeFileSync(this.path, this.content, 'utf-8');
+    fs.writeFileSync(this.path, this.content, "utf-8");
+  }
+
+  write(nextContent: string) {
+    this.content = nextContent;
+    fs.writeFileSync(this.path, this.content, "utf-8");
   }
 }
 
-export function* traverseRecursiveFiles(folder: string): Generator<File> {
+export function* traverseRecursiveFilePaths(
+  folder: string,
+  ig: Ignore = ignore().add(".git"),
+  root: string = folder,
+): Generator<string> {
   const files = fs.readdirSync(folder);
+
+  // add .gitignore to ignore if exists
+  if (fs.existsSync(path.join(folder, ".gitignore"))) {
+    ig.add(fs.readFileSync(path.join(folder, ".gitignore"), "utf-8"));
+  }
+
   for (const file of files) {
     const filePath = path.join(folder, file);
+
+    if (ig.ignores(path.relative(root, filePath))) {
+      continue;
+    }
+
     if (fs.statSync(filePath).isDirectory()) {
-      yield* traverseRecursiveFiles(filePath);
+      yield* traverseRecursiveFilePaths(filePath, ig, root);
     } else {
-      yield new File(filePath);
+      yield filePath;
     }
   }
 }
+
+export function* traverseRecursiveFiles(folder: string): Generator<File> {
+  for (const filePath of traverseRecursiveFilePaths(folder)) {
+    yield new File(filePath);
+  }
+}

+ 7 - 0
apps/cli/src/utils/project.ts

@@ -89,6 +89,13 @@ export class Project {
     }
   }
 
+  writeToPackageJsonFile() {
+    writeFileSync(
+      this.packageJsonPath,
+      JSON.stringify(this.packageJson, null, 2),
+    );
+  }
+
   printInfo() {
     console.log(chalk.bold("Project Info:"));
     console.log(chalk.black(`  - Flowgram Version: ${this.flowgramVersion}`));

+ 4 - 10
apps/cli/src/utils/ts-file.ts

@@ -5,7 +5,7 @@
 
 import path from "path";
 import fs from "fs";
-import { File } from "./file";
+import { File, traverseRecursiveFilePaths } from "./file";
 import { extractNamedExports } from "./export";
 import {
   assembleImport,
@@ -109,15 +109,9 @@ class TsFile extends File {
 }
 
 export function* traverseRecursiveTsFiles(folder: string): Generator<TsFile> {
-  const files = fs.readdirSync(folder);
-  for (const file of files) {
-    const filePath = path.join(folder, file);
-    if (fs.statSync(filePath).isDirectory()) {
-      yield* traverseRecursiveTsFiles(filePath);
-    } else {
-      if (file.endsWith(".ts") || file.endsWith(".tsx")) {
-        yield new TsFile(filePath);
-      }
+  for (const filePath of traverseRecursiveFilePaths(folder)) {
+    if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) {
+      yield new TsFile(filePath);
     }
   }
 }

+ 14 - 3
common/config/rush/pnpm-lock.yaml

@@ -22,6 +22,9 @@ importers:
       fs-extra:
         specifier: ^9.1.0
         version: 9.1.0
+      ignore:
+        specifier: ~7.0.5
+        version: 7.0.5
       inquirer:
         specifier: ^9.2.7
         version: 9.3.7
@@ -2073,6 +2076,9 @@ importers:
       '@types/react-dom':
         specifier: ^18
         version: 18.3.5(@types/react@18.3.16)
+      cross-env:
+        specifier: ~7.0.3
+        version: 7.0.3
       eslint:
         specifier: ^8.54.0
         version: 8.57.1
@@ -2082,9 +2088,6 @@ importers:
       react-dom:
         specifier: ^18
         version: 18.3.1(react@18.3.1)
-      tsup:
-        specifier: ^8.0.1
-        version: 8.3.5(typescript@5.8.3)
       vitest:
         specifier: ^0.34.6
         version: 0.34.6(jsdom@22.1.0)
@@ -2283,6 +2286,9 @@ importers:
       '@types/styled-components':
         specifier: ^5
         version: 5.1.34
+      cross-env:
+        specifier: ~7.0.3
+        version: 7.0.3
       eslint:
         specifier: ^8.54.0
         version: 8.57.1
@@ -14294,6 +14300,11 @@ packages:
     resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
     engines: {node: '>= 4'}
 
+  /ignore@7.0.5:
+    resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+    engines: {node: '>= 4'}
+    dev: false
+
   /image-size@0.5.5:
     resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
     engines: {node: '>=0.10.0'}

+ 4 - 4
packages/materials/coze-editor/package.json

@@ -88,9 +88,9 @@
     "dist"
   ],
   "scripts": {
-    "build": "rslib build",
-    "build:fast": "rslib build",
-    "build:watch": "rslib build",
+    "build": "cross-env NODE_ENV=production rslib build",
+    "build:fast": "cross-env NODE_ENV=development rslib build",
+    "build:watch": "npm run build:fast",
     "clean": "rimraf dist",
     "gen": "node scripts/gen.js",
     "test": "exit 0",
@@ -111,9 +111,9 @@
     "eslint": "^8.54.0",
     "react": "^18",
     "react-dom": "^18",
-    "tsup": "^8.0.1",
     "typescript": "^5.8.3",
     "vitest": "^0.34.6",
+    "cross-env": "~7.0.3",
     "@rslib/core": "~0.12.4"
   },
   "peerDependencies": {

+ 3 - 0
packages/materials/coze-editor/rslib.config.ts

@@ -53,4 +53,7 @@ const formats: Partial<RsbuildConfig>[] = [
 
 export default defineConfig({
   lib: formats,
+  output: {
+    cleanDistPath: process.env.NODE_ENV === 'production',
+  },
 });

+ 4 - 3
packages/materials/form-materials/package.json

@@ -21,9 +21,9 @@
     "src"
   ],
   "scripts": {
-    "build": "rslib build",
-    "build:fast": "rslib build",
-    "build:watch": "rslib build",
+    "build": "cross-env NODE_ENV=production rslib build",
+    "build:fast": "cross-env NODE_ENV=development rslib build",
+    "build:watch": "npm run build:fast",
     "name-export": "node scripts/name-export.js",
     "clean": "rimraf dist",
     "test": "exit 0",
@@ -62,6 +62,7 @@
     "typescript": "^5.8.3",
     "vitest": "^0.34.6",
     "@rslib/core": "~0.12.4",
+    "cross-env": "~7.0.3",
     "@rsbuild/plugin-react": "^1.1.1"
   },
   "peerDependencies": {

+ 1 - 1
packages/materials/form-materials/rslib.config.ts

@@ -56,7 +56,7 @@ export default defineConfig({
   lib: formats,
   output: {
     target: 'web',
-    cleanDistPath: true,
+    cleanDistPath: process.env.NODE_ENV === 'production',
   },
   plugins: [pluginReact({ swcReactOptions: {} })],
 });

+ 9 - 1
packages/materials/form-materials/src/components/blur-input/index.tsx

@@ -3,9 +3,17 @@
  * SPDX-License-Identifier: MIT
  */
 
+/* eslint-disable react/prop-types */
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
 import React, { useEffect, useState } from 'react';
 
-import Input, { InputProps } from '@douyinfe/semi-ui/lib/es/input';
+import { Input } from '@douyinfe/semi-ui';
+
+type InputProps = React.ComponentPropsWithoutRef<typeof Input>;
 
 export function BlurInput(props: InputProps) {
   const [value, setValue] = useState('');