@flowgram.ai/cli 0.1.8 β 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +397 -91
- package/dist/index.js +398 -88
- package/package.json +3 -2
- package/src/index.ts +21 -4
- package/src/materials/index.ts +23 -9
- package/src/materials/materials.ts +32 -39
- package/src/materials/refresh-project-import.ts +68 -0
- package/src/update-version/index.ts +52 -0
- package/src/utils/export.ts +117 -0
- package/src/utils/file.ts +87 -0
- package/src/utils/import.ts +3 -3
- package/src/utils/project.ts +10 -0
- package/src/utils/ts-file.ts +128 -0
- package/src/utils/traverse-file.ts +0 -60
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowgram.ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"description": "A CLI tool to create demo projects or sync materials",
|
|
5
5
|
"bin": {
|
|
6
6
|
"flowgram-cli": "./bin/index.js"
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"chalk": "^5.3.0",
|
|
18
18
|
"download": "8.0.0",
|
|
19
19
|
"tar": "7.4.3",
|
|
20
|
-
"inquirer": "^9.2.7"
|
|
20
|
+
"inquirer": "^9.2.7",
|
|
21
|
+
"ignore": "~7.0.5"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"@types/download": "8.0.5",
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Command } from "commander";
|
|
|
7
7
|
|
|
8
8
|
import { syncMaterial } from "./materials";
|
|
9
9
|
import { createApp } from "./create-app";
|
|
10
|
+
import { updateFlowgramVersion } from "./update-version";
|
|
10
11
|
|
|
11
12
|
const program = new Command();
|
|
12
13
|
|
|
@@ -15,7 +16,7 @@ program.name("flowgram-cli").version("1.0.0").description("Flowgram CLI");
|
|
|
15
16
|
program
|
|
16
17
|
.command("create-app")
|
|
17
18
|
.description("Create a new flowgram project")
|
|
18
|
-
.argument(
|
|
19
|
+
.argument("[string]", "Project name")
|
|
19
20
|
.action(async (projectName) => {
|
|
20
21
|
await createApp(projectName);
|
|
21
22
|
});
|
|
@@ -23,9 +24,25 @@ program
|
|
|
23
24
|
program
|
|
24
25
|
.command("materials")
|
|
25
26
|
.description("Sync materials to the project")
|
|
26
|
-
.argument(
|
|
27
|
-
.
|
|
28
|
-
|
|
27
|
+
.argument("[string]", "Material name")
|
|
28
|
+
.option(
|
|
29
|
+
"--refresh-project-imports",
|
|
30
|
+
"Refresh project imports to copied materials",
|
|
31
|
+
false,
|
|
32
|
+
)
|
|
33
|
+
.action(async (materialName, options) => {
|
|
34
|
+
await syncMaterial({
|
|
35
|
+
materialName,
|
|
36
|
+
refreshProjectImports: options.refreshProjectImports,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
program
|
|
41
|
+
.command("update-version")
|
|
42
|
+
.description("Update flowgram version in the project")
|
|
43
|
+
.argument("[string]", "Flowgram version")
|
|
44
|
+
.action(async (version) => {
|
|
45
|
+
await updateFlowgramVersion(version);
|
|
29
46
|
});
|
|
30
47
|
|
|
31
48
|
program.parse(process.argv);
|
package/src/materials/index.ts
CHANGED
|
@@ -10,17 +10,25 @@ import { copyMaterial, listAllMaterials, Material } from "./materials";
|
|
|
10
10
|
import { loadNpm } from "../utils/npm";
|
|
11
11
|
import path from "path";
|
|
12
12
|
import { Project } from "../utils/project";
|
|
13
|
+
import { executeRefreshProjectImport } from "./refresh-project-import";
|
|
14
|
+
|
|
15
|
+
export async function syncMaterial(opts: {
|
|
16
|
+
materialName?: string;
|
|
17
|
+
refreshProjectImports?: boolean;
|
|
18
|
+
}) {
|
|
19
|
+
const { materialName, refreshProjectImports } = opts;
|
|
13
20
|
|
|
14
|
-
export async function syncMaterial(materialName?: string) {
|
|
15
21
|
// materialName can be undefined
|
|
16
|
-
console.log(chalk.
|
|
22
|
+
console.log(chalk.bold("π Welcome to @flowgram.ai form-materials!"));
|
|
17
23
|
|
|
18
24
|
const project = await Project.getSingleton();
|
|
19
25
|
project.printInfo();
|
|
20
26
|
|
|
21
27
|
if (!project.flowgramVersion) {
|
|
22
28
|
throw new Error(
|
|
23
|
-
|
|
29
|
+
chalk.red(
|
|
30
|
+
"β Please install @flowgram.ai/fixed-layout-editor or @flowgram.ai/free-layout-editor",
|
|
31
|
+
),
|
|
24
32
|
);
|
|
25
33
|
}
|
|
26
34
|
|
|
@@ -31,7 +39,7 @@ export async function syncMaterial(materialName?: string) {
|
|
|
31
39
|
|
|
32
40
|
let material: Material | undefined; // material can be undefined
|
|
33
41
|
|
|
34
|
-
// Check if materialName is provided and exists in materials
|
|
42
|
+
// 1. Check if materialName is provided and exists in materials
|
|
35
43
|
if (materialName) {
|
|
36
44
|
const selectedMaterial = materials.find(
|
|
37
45
|
(m) => `${m.type}/${m.name}` === materialName,
|
|
@@ -48,7 +56,7 @@ export async function syncMaterial(materialName?: string) {
|
|
|
48
56
|
}
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
// If material not found or materialName not provided, prompt user to select
|
|
59
|
+
// 2. If material not found or materialName not provided, prompt user to select
|
|
52
60
|
if (!material) {
|
|
53
61
|
// User select one component
|
|
54
62
|
const result = await inquirer.prompt<{
|
|
@@ -74,9 +82,15 @@ export async function syncMaterial(materialName?: string) {
|
|
|
74
82
|
process.exit(1);
|
|
75
83
|
}
|
|
76
84
|
|
|
85
|
+
// 3. Refresh project imports
|
|
86
|
+
if (refreshProjectImports) {
|
|
87
|
+
console.log(chalk.bold("π Refresh imports in your project"));
|
|
88
|
+
executeRefreshProjectImport(project, material);
|
|
89
|
+
}
|
|
90
|
+
|
|
77
91
|
// 4. Copy the materials to the project
|
|
78
92
|
console.log(
|
|
79
|
-
chalk.bold("The following materials will be added to your project"),
|
|
93
|
+
chalk.bold("π The following materials will be added to your project"),
|
|
80
94
|
);
|
|
81
95
|
console.log(material);
|
|
82
96
|
let { packagesToInstall } = copyMaterial(material, project, formMaterialPath);
|
|
@@ -84,10 +98,10 @@ export async function syncMaterial(materialName?: string) {
|
|
|
84
98
|
// 4. Install the dependencies
|
|
85
99
|
await project.addDependencies(packagesToInstall);
|
|
86
100
|
console.log(
|
|
87
|
-
chalk.bold("These npm dependencies is added to your package.json"),
|
|
101
|
+
chalk.bold("β
These npm dependencies is added to your package.json"),
|
|
88
102
|
);
|
|
89
103
|
packagesToInstall.forEach((_package) => {
|
|
90
104
|
console.log(`- ${_package}`);
|
|
91
|
-
})
|
|
92
|
-
console.log(chalk.bold("\
|
|
105
|
+
});
|
|
106
|
+
console.log(chalk.bold("\nβ‘οΈ Please run npm install to install dependencies\n"));
|
|
93
107
|
}
|
|
@@ -3,16 +3,11 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { fileURLToPath } from "url";
|
|
7
6
|
import path from "path";
|
|
8
7
|
import fs from "fs";
|
|
9
8
|
|
|
10
|
-
import { traverseRecursiveFiles } from "../utils/traverse-file";
|
|
11
|
-
import { replaceImport, traverseFileImports } from "../utils/import";
|
|
12
9
|
import { Project } from "../utils/project"; // Import ProjectInfo
|
|
13
|
-
|
|
14
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
+
import { traverseRecursiveTsFiles } from "../utils/ts-file";
|
|
16
11
|
|
|
17
12
|
// Added type definitions
|
|
18
13
|
export interface Material {
|
|
@@ -77,7 +72,8 @@ export const copyMaterial = (
|
|
|
77
72
|
): {
|
|
78
73
|
packagesToInstall: string[];
|
|
79
74
|
} => {
|
|
80
|
-
const formMaterialDependencies =
|
|
75
|
+
const formMaterialDependencies =
|
|
76
|
+
getFormMaterialDependencies(formMaterialPath);
|
|
81
77
|
|
|
82
78
|
const sourceDir: string = material.path;
|
|
83
79
|
const materialRoot: string = path.join(
|
|
@@ -91,38 +87,35 @@ export const copyMaterial = (
|
|
|
91
87
|
|
|
92
88
|
fs.cpSync(sourceDir, targetDir, { recursive: true });
|
|
93
89
|
|
|
94
|
-
for (const file of
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
} else {
|
|
124
|
-
packagesToInstall.add(`${dep}@${version}`);
|
|
125
|
-
}
|
|
90
|
+
for (const file of traverseRecursiveTsFiles(targetDir)) {
|
|
91
|
+
for (const importDeclaration of file.imports) {
|
|
92
|
+
const { source } = importDeclaration;
|
|
93
|
+
|
|
94
|
+
if (source.startsWith("@/")) {
|
|
95
|
+
// is inner import
|
|
96
|
+
console.log(
|
|
97
|
+
`Replace Import from ${source} to @flowgram.ai/form-materials`,
|
|
98
|
+
);
|
|
99
|
+
file.replaceImport(
|
|
100
|
+
[importDeclaration],
|
|
101
|
+
[{ ...importDeclaration, source: "@flowgram.ai/form-materials" }],
|
|
102
|
+
);
|
|
103
|
+
packagesToInstall.add(
|
|
104
|
+
`@flowgram.ai/form-materials@${project.flowgramVersion}`,
|
|
105
|
+
);
|
|
106
|
+
} else if (!source.startsWith(".") && !source.startsWith("react")) {
|
|
107
|
+
// check if is in form material dependencies
|
|
108
|
+
const [dep, version] =
|
|
109
|
+
Object.entries(formMaterialDependencies).find(([_key]) =>
|
|
110
|
+
source.startsWith(_key),
|
|
111
|
+
) || [];
|
|
112
|
+
if (!dep) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (dep.startsWith("@flowgram.ai/")) {
|
|
116
|
+
packagesToInstall.add(`${dep}@${project.flowgramVersion}`);
|
|
117
|
+
} else {
|
|
118
|
+
packagesToInstall.add(`${dep}@${version}`);
|
|
126
119
|
}
|
|
127
120
|
}
|
|
128
121
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { ImportDeclaration } from "../utils/import";
|
|
8
|
+
import { Project } from "../utils/project";
|
|
9
|
+
import { getIndexTsFile, traverseRecursiveTsFiles } from "../utils/ts-file";
|
|
10
|
+
import { Material } from "./materials";
|
|
11
|
+
|
|
12
|
+
export function executeRefreshProjectImport(
|
|
13
|
+
project: Project,
|
|
14
|
+
material: Material,
|
|
15
|
+
) {
|
|
16
|
+
const materialFile = getIndexTsFile(material.path);
|
|
17
|
+
|
|
18
|
+
if (!materialFile) {
|
|
19
|
+
console.warn(`Material ${material.name} not found`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const targetDir = `@/form-materials/${material.type}/${material.name}`;
|
|
24
|
+
|
|
25
|
+
const exportNames = materialFile.allExportNames;
|
|
26
|
+
|
|
27
|
+
console.log(`π The exports of ${material.name} is ${exportNames.join(",")}`);
|
|
28
|
+
|
|
29
|
+
for (const tsFile of traverseRecursiveTsFiles(project.srcPath)) {
|
|
30
|
+
for (const importDeclaration of tsFile.imports) {
|
|
31
|
+
if (importDeclaration.source === "@flowgram.ai/form-materials") {
|
|
32
|
+
const currentMaterialImports = importDeclaration.namedImports?.filter(
|
|
33
|
+
(item) => exportNames.includes(item.imported),
|
|
34
|
+
);
|
|
35
|
+
if (!currentMaterialImports?.length) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const nextImports: ImportDeclaration[] = [
|
|
39
|
+
{
|
|
40
|
+
...importDeclaration,
|
|
41
|
+
namedImports: currentMaterialImports,
|
|
42
|
+
source: targetDir,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const keepImportNames = importDeclaration.namedImports?.filter(
|
|
47
|
+
(item) => !exportNames.includes(item.imported),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (keepImportNames?.length) {
|
|
51
|
+
nextImports.unshift({
|
|
52
|
+
...importDeclaration,
|
|
53
|
+
namedImports: keepImportNames,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
tsFile.replaceImport([importDeclaration], nextImports);
|
|
58
|
+
console.log(chalk.green(`π Refresh Imports In: ${tsFile.path}`));
|
|
59
|
+
|
|
60
|
+
console.log(
|
|
61
|
+
`From:\n${importDeclaration.statement}\nTo:\n${nextImports
|
|
62
|
+
.map((item) => item.statement)
|
|
63
|
+
.join("\n")}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getLatestVersion } from "../utils/npm";
|
|
7
|
+
import { traverseRecursiveFiles } from "../utils/file";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
|
|
10
|
+
export async function updateFlowgramVersion(inputVersion?: string) {
|
|
11
|
+
console.log(chalk.bold("π Welcome to @flowgram.ai update-version helper"));
|
|
12
|
+
|
|
13
|
+
// Get latest version
|
|
14
|
+
const latestVersion = await getLatestVersion("@flowgram.ai/editor");
|
|
15
|
+
const currentPath = process.cwd();
|
|
16
|
+
console.log("- Latest flowgram version: ", latestVersion);
|
|
17
|
+
console.log("- Current Path: ", currentPath);
|
|
18
|
+
|
|
19
|
+
// User Input flowgram version, default is latestVersion
|
|
20
|
+
const flowgramVersion: string = inputVersion || latestVersion;
|
|
21
|
+
|
|
22
|
+
for (const file of traverseRecursiveFiles(currentPath)) {
|
|
23
|
+
if (file.path.endsWith("package.json")) {
|
|
24
|
+
console.log("π Find package.json: ", file.path);
|
|
25
|
+
let updated = false;
|
|
26
|
+
const json = JSON.parse(file.content);
|
|
27
|
+
if (json.dependencies) {
|
|
28
|
+
for (const key in json.dependencies) {
|
|
29
|
+
if (key.startsWith("@flowgram.ai/")) {
|
|
30
|
+
updated = true;
|
|
31
|
+
json.dependencies[key] = flowgramVersion;
|
|
32
|
+
console.log(`- Update ${key} to ${flowgramVersion}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (json.devDependencies) {
|
|
37
|
+
for (const key in json.devDependencies) {
|
|
38
|
+
if (key.startsWith("@flowgram.ai/")) {
|
|
39
|
+
updated = true;
|
|
40
|
+
json.devDependencies[key] = flowgramVersion;
|
|
41
|
+
console.log(`- Update ${key} to ${flowgramVersion}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (updated) {
|
|
47
|
+
file.write(JSON.stringify(json, null, 2));
|
|
48
|
+
console.log(`β
${file.path} Updated`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function extractNamedExports(content: string) {
|
|
7
|
+
const valueExports = [];
|
|
8
|
+
const typeExports = [];
|
|
9
|
+
|
|
10
|
+
// Collect all type definition names
|
|
11
|
+
const typeDefinitions = new Set();
|
|
12
|
+
const typePatterns = [
|
|
13
|
+
/\b(?:type|interface)\s+(\w+)/g,
|
|
14
|
+
/\bexport\s+(?:type|interface)\s+(\w+)/g,
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
let match;
|
|
18
|
+
for (const pattern of typePatterns) {
|
|
19
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
20
|
+
typeDefinitions.add(match[1]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Match various export patterns
|
|
25
|
+
const exportPatterns = [
|
|
26
|
+
// export const/var/let/function/class/type/interface
|
|
27
|
+
/\bexport\s+(const|var|let|function|class|type|interface)\s+(\w+)/g,
|
|
28
|
+
// export { name1, name2 }
|
|
29
|
+
/\bexport\s*\{([^}]+)\}/g,
|
|
30
|
+
// export { name as alias }
|
|
31
|
+
/\bexport\s*\{[^}]*\b(\w+)\s+as\s+(\w+)[^}]*\}/g,
|
|
32
|
+
// export default function name()
|
|
33
|
+
/\bexport\s+default\s+(?:function|class)\s+(\w+)/g,
|
|
34
|
+
// export type { Type1, Type2 }
|
|
35
|
+
/\bexport\s+type\s*\{([^}]+)\}/g,
|
|
36
|
+
// export type { Original as Alias }
|
|
37
|
+
/\bexport\s+type\s*\{[^}]*\b(\w+)\s+as\s+(\w+)[^}]*\}/g,
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// Handle first pattern: export const/var/let/function/class/type/interface
|
|
41
|
+
exportPatterns[0].lastIndex = 0;
|
|
42
|
+
while ((match = exportPatterns[0].exec(content)) !== null) {
|
|
43
|
+
const [, kind, name] = match;
|
|
44
|
+
if (kind === "type" || kind === "interface" || typeDefinitions.has(name)) {
|
|
45
|
+
typeExports.push(name);
|
|
46
|
+
} else {
|
|
47
|
+
valueExports.push(name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle second pattern: export { name1, name2 }
|
|
52
|
+
exportPatterns[1].lastIndex = 0;
|
|
53
|
+
while ((match = exportPatterns[1].exec(content)) !== null) {
|
|
54
|
+
const exportsList = match[1]
|
|
55
|
+
.split(",")
|
|
56
|
+
.map((item) => item.trim())
|
|
57
|
+
.filter((item) => item && !item.includes(" as "));
|
|
58
|
+
|
|
59
|
+
for (const name of exportsList) {
|
|
60
|
+
if (name.startsWith("type ")) {
|
|
61
|
+
typeExports.push(name.replace("type ", "").trim());
|
|
62
|
+
} else if (typeDefinitions.has(name)) {
|
|
63
|
+
typeExports.push(name);
|
|
64
|
+
} else {
|
|
65
|
+
valueExports.push(name);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handle third pattern: export { name as alias }
|
|
71
|
+
exportPatterns[2].lastIndex = 0;
|
|
72
|
+
while ((match = exportPatterns[2].exec(content)) !== null) {
|
|
73
|
+
const [, original, alias] = match;
|
|
74
|
+
if (typeDefinitions.has(original)) {
|
|
75
|
+
typeExports.push(alias);
|
|
76
|
+
} else {
|
|
77
|
+
valueExports.push(alias);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Handle fourth pattern: export default function name()
|
|
82
|
+
exportPatterns[3].lastIndex = 0;
|
|
83
|
+
while ((match = exportPatterns[3].exec(content)) !== null) {
|
|
84
|
+
const name = match[1];
|
|
85
|
+
if (typeDefinitions.has(name)) {
|
|
86
|
+
typeExports.push(name);
|
|
87
|
+
} else {
|
|
88
|
+
valueExports.push(name);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Handle fifth pattern: export type { Type1, Type2 }
|
|
93
|
+
exportPatterns[4].lastIndex = 0;
|
|
94
|
+
while ((match = exportPatterns[4].exec(content)) !== null) {
|
|
95
|
+
const exportsList = match[1]
|
|
96
|
+
.split(",")
|
|
97
|
+
.map((item) => item.trim())
|
|
98
|
+
.filter((item) => item && !item.includes(" as "));
|
|
99
|
+
|
|
100
|
+
for (const name of exportsList) {
|
|
101
|
+
typeExports.push(name);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Handle sixth pattern: export type { Original as Alias }
|
|
106
|
+
exportPatterns[5].lastIndex = 0;
|
|
107
|
+
while ((match = exportPatterns[5].exec(content)) !== null) {
|
|
108
|
+
const [, original, alias] = match;
|
|
109
|
+
typeExports.push(alias);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Deduplicate and sort
|
|
113
|
+
return {
|
|
114
|
+
values: [...new Set(valueExports)].sort(),
|
|
115
|
+
types: [...new Set(typeExports)].sort(),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from "path";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import ignore, { Ignore } from "ignore";
|
|
9
|
+
|
|
10
|
+
export class File {
|
|
11
|
+
content: string;
|
|
12
|
+
|
|
13
|
+
isUtf8: boolean;
|
|
14
|
+
|
|
15
|
+
relativePath: string;
|
|
16
|
+
|
|
17
|
+
path: string;
|
|
18
|
+
|
|
19
|
+
suffix: string;
|
|
20
|
+
|
|
21
|
+
constructor(filePath: string, public root: string = "/") {
|
|
22
|
+
this.path = filePath;
|
|
23
|
+
this.relativePath = path.relative(this.root, this.path);
|
|
24
|
+
this.suffix = path.extname(this.path);
|
|
25
|
+
|
|
26
|
+
// Check if exists
|
|
27
|
+
if (!fs.existsSync(this.path)) {
|
|
28
|
+
throw Error(`File ${path} Not Exists`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// If no utf-8, skip
|
|
32
|
+
try {
|
|
33
|
+
this.content = fs.readFileSync(this.path, "utf-8");
|
|
34
|
+
this.isUtf8 = true;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
this.isUtf8 = false;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
replace(updater: (content: string) => string) {
|
|
42
|
+
if (!this.isUtf8) {
|
|
43
|
+
console.warn("Not UTF-8 file skipped: ", this.path);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.content = updater(this.content);
|
|
47
|
+
fs.writeFileSync(this.path, this.content, "utf-8");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
write(nextContent: string) {
|
|
51
|
+
this.content = nextContent;
|
|
52
|
+
fs.writeFileSync(this.path, this.content, "utf-8");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function* traverseRecursiveFilePaths(
|
|
57
|
+
folder: string,
|
|
58
|
+
ig: Ignore = ignore().add(".git"),
|
|
59
|
+
root: string = folder,
|
|
60
|
+
): Generator<string> {
|
|
61
|
+
const files = fs.readdirSync(folder);
|
|
62
|
+
|
|
63
|
+
// add .gitignore to ignore if exists
|
|
64
|
+
if (fs.existsSync(path.join(folder, ".gitignore"))) {
|
|
65
|
+
ig.add(fs.readFileSync(path.join(folder, ".gitignore"), "utf-8"));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const filePath = path.join(folder, file);
|
|
70
|
+
|
|
71
|
+
if (ig.ignores(path.relative(root, filePath))) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (fs.statSync(filePath).isDirectory()) {
|
|
76
|
+
yield* traverseRecursiveFilePaths(filePath, ig, root);
|
|
77
|
+
} else {
|
|
78
|
+
yield filePath;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function* traverseRecursiveFiles(folder: string): Generator<File> {
|
|
84
|
+
for (const filePath of traverseRecursiveFilePaths(folder)) {
|
|
85
|
+
yield new File(filePath);
|
|
86
|
+
}
|
|
87
|
+
}
|
package/src/utils/import.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* import D, { type E, F } from 'module';
|
|
12
12
|
* import A, { B as B1 } from 'module';
|
|
13
13
|
*/
|
|
14
|
-
interface ImportDeclaration {
|
|
14
|
+
export interface ImportDeclaration {
|
|
15
15
|
// origin statement
|
|
16
16
|
statement: string;
|
|
17
17
|
|
|
@@ -50,7 +50,7 @@ export function assembleImport(declaration: ImportDeclaration): string {
|
|
|
50
50
|
if (namespaceImport) {
|
|
51
51
|
importClauses.push(`* as ${namespaceImport}`);
|
|
52
52
|
}
|
|
53
|
-
return `import ${importClauses.join(', ')} from '${source}'
|
|
53
|
+
return `import ${importClauses.join(', ')} from '${source}';`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export function replaceImport(
|
|
@@ -66,7 +66,7 @@ export function replaceImport(
|
|
|
66
66
|
export function* traverseFileImports(fileContent: string): Generator<ImportDeclaration> {
|
|
67
67
|
// εΉι
ζζ import θ―ε₯ηζ£ε葨达εΌ
|
|
68
68
|
const importRegex =
|
|
69
|
-
/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*['"`]([^'"`]+)['"`]
|
|
69
|
+
/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;
|
|
70
70
|
|
|
71
71
|
let match;
|
|
72
72
|
while ((match = importRegex.exec(fileContent)) !== null) {
|
package/src/utils/project.ts
CHANGED
|
@@ -24,6 +24,8 @@ export class Project {
|
|
|
24
24
|
|
|
25
25
|
packageJson: PackageJson;
|
|
26
26
|
|
|
27
|
+
srcPath: string;
|
|
28
|
+
|
|
27
29
|
protected constructor() {}
|
|
28
30
|
|
|
29
31
|
async init() {
|
|
@@ -42,6 +44,7 @@ export class Project {
|
|
|
42
44
|
|
|
43
45
|
this.projectPath = projectPath;
|
|
44
46
|
|
|
47
|
+
this.srcPath = path.join(projectPath, "src");
|
|
45
48
|
this.packageJsonPath = path.join(projectPath, "package.json");
|
|
46
49
|
this.packageJson = JSON.parse(readFileSync(this.packageJsonPath, "utf8"));
|
|
47
50
|
|
|
@@ -86,6 +89,13 @@ export class Project {
|
|
|
86
89
|
}
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
writeToPackageJsonFile() {
|
|
93
|
+
writeFileSync(
|
|
94
|
+
this.packageJsonPath,
|
|
95
|
+
JSON.stringify(this.packageJson, null, 2),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
89
99
|
printInfo() {
|
|
90
100
|
console.log(chalk.bold("Project Info:"));
|
|
91
101
|
console.log(chalk.black(` - Flowgram Version: ${this.flowgramVersion}`));
|