@flowgram.ai/form-materials 0.3.4 → 0.3.5
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/bin/index.ts +5 -19
- package/bin/materials.ts +31 -89
- package/bin/project.ts +4 -0
- package/bin/utils/import.ts +127 -0
- package/bin/utils/traverse-file.ts +60 -0
- package/dist/esm/index.js +383 -488
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +64 -6
- package/dist/index.d.ts +64 -6
- package/dist/index.js +625 -730
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/components/assign-row/index.tsx +4 -4
- package/src/components/assign-rows/index.tsx +1 -1
- package/src/components/batch-outputs/index.tsx +4 -3
- package/src/components/batch-outputs/types.ts +1 -1
- package/src/components/batch-variable-selector/index.tsx +1 -1
- package/src/components/code-editor-mini/index.tsx +1 -1
- package/src/components/condition-row/hooks/useRule.ts +2 -1
- package/src/components/condition-row/index.tsx +5 -4
- package/src/components/condition-row/types.ts +1 -1
- package/src/components/constant-input/index.tsx +5 -2
- package/src/components/constant-input/types.ts +1 -1
- package/src/components/display-flow-value/index.tsx +2 -2
- package/src/components/display-inputs-values/index.tsx +3 -2
- package/src/components/display-outputs/index.tsx +2 -1
- package/src/components/display-schema-tag/index.tsx +3 -2
- package/src/components/display-schema-tree/index.tsx +2 -1
- package/src/components/dynamic-value-input/hooks.ts +1 -1
- package/src/components/dynamic-value-input/index.tsx +13 -9
- package/src/components/inputs-values/index.tsx +7 -5
- package/src/components/inputs-values/types.ts +3 -3
- package/src/components/json-editor-with-variables/extensions/variable-tree.tsx +1 -1
- package/src/components/json-editor-with-variables/index.tsx +2 -1
- package/src/components/json-schema-editor/default-value.tsx +11 -105
- package/src/components/json-schema-editor/hooks.tsx +53 -94
- package/src/components/json-schema-editor/index.tsx +11 -8
- package/src/components/json-schema-editor/styles.tsx +0 -29
- package/src/components/prompt-editor/types.tsx +1 -1
- package/src/components/prompt-editor-with-inputs/extensions/inputs-tree.tsx +2 -1
- package/src/components/prompt-editor-with-inputs/index.tsx +3 -2
- package/src/components/prompt-editor-with-inputs/inputs-picker.tsx +1 -1
- package/src/components/prompt-editor-with-variables/extensions/variable-tree.tsx +1 -1
- package/src/components/prompt-editor-with-variables/index.tsx +2 -1
- package/src/components/type-selector/index.tsx +7 -3
- package/src/components/variable-selector/index.tsx +7 -4
- package/src/components/variable-selector/use-variable-tree.tsx +1 -1
- package/src/effects/auto-rename-ref/index.ts +1 -1
- package/src/effects/listen-ref-schema-change/index.ts +1 -1
- package/src/effects/listen-ref-value-change/index.ts +1 -1
- package/src/effects/provide-batch-input/index.ts +1 -1
- package/src/form-plugins/batch-outputs-plugin/index.ts +1 -1
- package/src/form-plugins/infer-assign-plugin/index.ts +1 -1
- package/src/form-plugins/infer-inputs-plugin/index.ts +1 -1
- package/src/shared/index.ts +1 -0
- package/src/shared/inject-material/README.md +170 -0
- package/src/shared/inject-material/README.zh.md +174 -0
- package/src/shared/inject-material/index.tsx +87 -0
- package/src/components/assign-row/config.json +0 -11
- package/src/components/assign-rows/config.json +0 -11
- package/src/components/batch-outputs/config.json +0 -13
- package/src/components/batch-variable-selector/config.json +0 -9
- package/src/components/code-editor/config.json +0 -10
- package/src/components/code-editor-mini/config.json +0 -7
- package/src/components/condition-row/config.json +0 -13
- package/src/components/constant-input/config.json +0 -9
- package/src/components/display-flow-value/config.json +0 -8
- package/src/components/display-inputs-values/config.json +0 -9
- package/src/components/display-outputs/config.json +0 -10
- package/src/components/display-schema-tag/config.json +0 -10
- package/src/components/display-schema-tree/config.json +0 -11
- package/src/components/dynamic-value-input/config.json +0 -14
- package/src/components/inputs-values/config.json +0 -13
- package/src/components/json-editor-with-variables/config.json +0 -13
- package/src/components/json-schema-editor/config.json +0 -13
- package/src/components/json-schema-editor/utils.ts +0 -29
- package/src/components/prompt-editor/config.json +0 -9
- package/src/components/prompt-editor-with-inputs/config.json +0 -13
- package/src/components/prompt-editor-with-variables/config.json +0 -13
- package/src/components/type-selector/config.json +0 -9
- package/src/components/variable-selector/config.json +0 -9
- package/src/effects/auto-rename-ref/config.json +0 -5
- package/src/effects/listen-ref-schema-change/config.json +0 -10
- package/src/effects/listen-ref-value-change/config.json +0 -9
- package/src/effects/provide-batch-input/config.json +0 -5
- package/src/effects/provide-json-schema-outputs/config.json +0 -7
- package/src/effects/sync-variable-title/config.json +0 -5
- package/src/effects/validate-when-variable-sync/config.json +0 -5
- package/src/form-plugins/batch-outputs-plugin/config.json +0 -7
- package/src/form-plugins/infer-assign-plugin/config.json +0 -7
- package/src/form-plugins/infer-inputs-plugin/config.json +0 -9
- package/src/hooks/use-object-list/config.json +0 -8
- package/src/shared/format-legacy-refs/config.json +0 -5
- package/src/typings/flow-value/config.json +0 -7
- package/src/validate/validate-flow-value/config.json +0 -7
package/bin/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from 'commander';
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
|
|
7
7
|
import { getProjectInfo, installDependencies, ProjectInfo } from './project.js';
|
|
8
|
-
import {
|
|
8
|
+
import { copyMaterial, listAllMaterials, Material } from './materials.js';
|
|
9
9
|
|
|
10
10
|
const program = new Command();
|
|
11
11
|
|
|
@@ -66,14 +66,13 @@ program
|
|
|
66
66
|
process.exit(1);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// 4. Copy the materials to the project
|
|
70
|
+
console.log(chalk.bold('The following materials will be added to your project'));
|
|
69
71
|
console.log(material);
|
|
70
|
-
|
|
71
|
-
// 3. Get the component dependencies by BFS (include depMaterials and depPackages)
|
|
72
|
-
const { allMaterials, allPackages } = bfsMaterials(material!, materials);
|
|
72
|
+
let { packagesToInstall } = copyMaterial(material, projectInfo);
|
|
73
73
|
|
|
74
74
|
// 4. Install the dependencies
|
|
75
|
-
|
|
76
|
-
const packagesToInstall: string[] = allPackages.map((_pkg) => {
|
|
75
|
+
packagesToInstall = packagesToInstall.map((_pkg) => {
|
|
77
76
|
if (
|
|
78
77
|
_pkg.startsWith(`@flowgram.ai/`) &&
|
|
79
78
|
projectInfo.flowgramVersion !== 'workspace:*' &&
|
|
@@ -87,19 +86,6 @@ program
|
|
|
87
86
|
console.log(chalk.bold('These npm dependencies will be added to your project'));
|
|
88
87
|
console.log(packagesToInstall);
|
|
89
88
|
installDependencies(packagesToInstall, projectInfo);
|
|
90
|
-
|
|
91
|
-
// 5. Copy the materials to the project
|
|
92
|
-
console.log(chalk.bold('These Materials will be added to your project'));
|
|
93
|
-
console.log(allMaterials);
|
|
94
|
-
copyMaterial(material, projectInfo, { overwrite: true });
|
|
95
|
-
|
|
96
|
-
allMaterials.forEach((mat: Material) => {
|
|
97
|
-
if (mat === material) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
// Add type for mat
|
|
101
|
-
copyMaterial(mat, projectInfo, { overwrite: false });
|
|
102
|
-
});
|
|
103
89
|
});
|
|
104
90
|
|
|
105
91
|
program.parse(process.argv);
|
package/bin/materials.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { fileURLToPath } from 'url';
|
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
|
|
10
|
+
import { traverseRecursiveFiles } from './utils/traverse-file';
|
|
11
|
+
import { replaceImport, traverseFileImports } from './utils/import';
|
|
10
12
|
import { ProjectInfo } from './project'; // Import ProjectInfo
|
|
11
13
|
|
|
12
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -17,8 +19,6 @@ export interface Material {
|
|
|
17
19
|
name: string;
|
|
18
20
|
type: string;
|
|
19
21
|
path: string;
|
|
20
|
-
depPackages?: string[];
|
|
21
|
-
depMaterials?: string[];
|
|
22
22
|
[key: string]: any; // For other properties from config.json
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -48,19 +48,7 @@ export function listAllMaterials(): Material[] {
|
|
|
48
48
|
return null;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const configPath = path.join(materialsPath, _path, 'config.json');
|
|
52
|
-
// Check if config.json exists before reading
|
|
53
|
-
if (!fs.existsSync(configPath)) {
|
|
54
|
-
console.warn(
|
|
55
|
-
`Warning: config.json not found for material at ${path.join(materialsPath, _path)}`
|
|
56
|
-
);
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
60
|
-
const config = JSON.parse(configContent);
|
|
61
|
-
|
|
62
51
|
return {
|
|
63
|
-
...config,
|
|
64
52
|
name: _path, // Assuming the folder name is the material name
|
|
65
53
|
type: _type,
|
|
66
54
|
path: path.join(materialsPath, _path),
|
|
@@ -73,66 +61,12 @@ export function listAllMaterials(): Material[] {
|
|
|
73
61
|
return _materials;
|
|
74
62
|
}
|
|
75
63
|
|
|
76
|
-
interface BfsResult {
|
|
77
|
-
allMaterials: Material[];
|
|
78
|
-
allPackages: string[];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function bfsMaterials(material: Material, _materials: Material[] = []): BfsResult {
|
|
82
|
-
function findConfigByName(name: string): Material | undefined {
|
|
83
|
-
return _materials.find(
|
|
84
|
-
(_config) => _config.name === name || `${_config.type}/${_config.name}` === name
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const queue: (Material | undefined)[] = [material]; // Queue can hold undefined if findConfigByName returns undefined
|
|
89
|
-
const allMaterials = new Set<Material>();
|
|
90
|
-
const allPackages = new Set<string>();
|
|
91
|
-
|
|
92
|
-
while (queue.length > 0) {
|
|
93
|
-
const _material = queue.shift();
|
|
94
|
-
if (!_material || allMaterials.has(_material)) {
|
|
95
|
-
// Check if _material is defined
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
allMaterials.add(_material);
|
|
99
|
-
|
|
100
|
-
if (_material.depPackages) {
|
|
101
|
-
for (const _package of _material.depPackages) {
|
|
102
|
-
allPackages.add(_package);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (_material.depMaterials) {
|
|
107
|
-
for (const _materialName of _material.depMaterials) {
|
|
108
|
-
const depMaterial = findConfigByName(_materialName);
|
|
109
|
-
if (depMaterial) {
|
|
110
|
-
// Ensure dependent material is found before adding to queue
|
|
111
|
-
queue.push(depMaterial);
|
|
112
|
-
} else {
|
|
113
|
-
console.warn(
|
|
114
|
-
`Warning: Dependent material "${_materialName}" not found for material "${_material.name}".`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
allMaterials: Array.from(allMaterials),
|
|
123
|
-
allPackages: Array.from(allPackages),
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
64
|
export const copyMaterial = (
|
|
128
65
|
material: Material,
|
|
129
|
-
projectInfo: ProjectInfo
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
overwrite?: boolean;
|
|
134
|
-
} = {}
|
|
135
|
-
): void => {
|
|
66
|
+
projectInfo: ProjectInfo
|
|
67
|
+
): {
|
|
68
|
+
packagesToInstall: string[];
|
|
69
|
+
} => {
|
|
136
70
|
const sourceDir: string = material.path;
|
|
137
71
|
const materialRoot: string = path.join(
|
|
138
72
|
projectInfo.projectPath,
|
|
@@ -141,25 +75,33 @@ export const copyMaterial = (
|
|
|
141
75
|
`${material.type}`
|
|
142
76
|
);
|
|
143
77
|
const targetDir = path.join(materialRoot, material.name);
|
|
144
|
-
|
|
145
|
-
if (!overwrite && fs.readdirSync(targetDir)?.length > 0) {
|
|
146
|
-
console.log(`Material ${material.name} already exists in ${materialRoot}, skip copying.`);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
78
|
+
const packagesToInstall: Set<string> = new Set();
|
|
149
79
|
|
|
150
80
|
fs.cpSync(sourceDir, targetDir, { recursive: true });
|
|
151
81
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
82
|
+
for (const file of traverseRecursiveFiles(targetDir)) {
|
|
83
|
+
if (['.ts', '.tsx'].includes(file.suffix)) {
|
|
84
|
+
for (const importDeclaration of traverseFileImports(file.content)) {
|
|
85
|
+
const { source } = importDeclaration;
|
|
86
|
+
|
|
87
|
+
if (source.startsWith('@/')) {
|
|
88
|
+
// is inner import
|
|
89
|
+
console.log(`Replace Import from ${source} to @flowgram.ai/form-materials`);
|
|
90
|
+
file.replace((content) =>
|
|
91
|
+
replaceImport(content, importDeclaration, [
|
|
92
|
+
{ ...importDeclaration, source: '@flowgram.ai/form-materials' },
|
|
93
|
+
])
|
|
94
|
+
);
|
|
95
|
+
packagesToInstall.add('@flowgram.ai/form-materials');
|
|
96
|
+
} else if (!source.startsWith('.') && !source.startsWith('react')) {
|
|
97
|
+
// check if is third party npm packages
|
|
98
|
+
packagesToInstall.add(source);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
164
102
|
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
packagesToInstall: [...packagesToInstall],
|
|
106
|
+
};
|
|
165
107
|
};
|
package/bin/project.ts
CHANGED
|
@@ -70,22 +70,26 @@ export function findRushJson(startPath: string): string | null {
|
|
|
70
70
|
|
|
71
71
|
export function installDependencies(packages: string[], projectInfo: ProjectInfo): void {
|
|
72
72
|
if (fs.existsSync(path.join(projectInfo.projectPath, 'yarn.lock'))) {
|
|
73
|
+
console.log(`yarn add ${packages.join(' ')}`);
|
|
73
74
|
execSync(`yarn add ${packages.join(' ')}`, { stdio: 'inherit' });
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
if (fs.existsSync(path.join(projectInfo.projectPath, 'pnpm-lock.yaml'))) {
|
|
79
|
+
console.log(`pnpm add ${packages.join(' ')}`);
|
|
78
80
|
execSync(`pnpm add ${packages.join(' ')}`, { stdio: 'inherit' });
|
|
79
81
|
return;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
// rush monorepo
|
|
83
85
|
if (findRushJson(projectInfo.projectPath)) {
|
|
86
|
+
console.log(`rush add ${packages.map((pkg) => `--package ${pkg}`).join(' ')}`);
|
|
84
87
|
execSync(`rush add ${packages.map((pkg) => `--package ${pkg}`).join(' ')}`, {
|
|
85
88
|
stdio: 'inherit',
|
|
86
89
|
});
|
|
87
90
|
return;
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
console.log(`npm install ${packages.join(' ')}`);
|
|
90
94
|
execSync(`npm install ${packages.join(' ')}`, { stdio: 'inherit' });
|
|
91
95
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Cases
|
|
8
|
+
* import { A, B } from 'module';
|
|
9
|
+
* import A from 'module';
|
|
10
|
+
* import * as C from 'module';
|
|
11
|
+
* import D, { type E, F } from 'module';
|
|
12
|
+
* import A, { B as B1 } from 'module';
|
|
13
|
+
*/
|
|
14
|
+
interface ImportDeclaration {
|
|
15
|
+
// origin statement
|
|
16
|
+
statement: string;
|
|
17
|
+
|
|
18
|
+
// import { A, B } from 'module';
|
|
19
|
+
namedImports?: {
|
|
20
|
+
local?: string;
|
|
21
|
+
imported: string;
|
|
22
|
+
typeOnly?: boolean;
|
|
23
|
+
}[];
|
|
24
|
+
|
|
25
|
+
// import A from 'module';
|
|
26
|
+
defaultImport?: string;
|
|
27
|
+
|
|
28
|
+
// import * as C from 'module';
|
|
29
|
+
namespaceImport?: string;
|
|
30
|
+
|
|
31
|
+
source: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function assembleImport(declaration: ImportDeclaration): string {
|
|
35
|
+
const { namedImports, defaultImport, namespaceImport, source } = declaration;
|
|
36
|
+
const importClauses = [];
|
|
37
|
+
if (namedImports) {
|
|
38
|
+
importClauses.push(
|
|
39
|
+
`{ ${namedImports
|
|
40
|
+
.map(
|
|
41
|
+
({ local, imported, typeOnly }) =>
|
|
42
|
+
`${typeOnly ? 'type ' : ''}${imported}${local ? ` as ${local}` : ''}`
|
|
43
|
+
)
|
|
44
|
+
.join(', ')} }`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
if (defaultImport) {
|
|
48
|
+
importClauses.push(defaultImport);
|
|
49
|
+
}
|
|
50
|
+
if (namespaceImport) {
|
|
51
|
+
importClauses.push(`* as ${namespaceImport}`);
|
|
52
|
+
}
|
|
53
|
+
return `import ${importClauses.join(', ')} from '${source}'`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function replaceImport(
|
|
57
|
+
fileContent: string,
|
|
58
|
+
origin: ImportDeclaration,
|
|
59
|
+
replaceTo: ImportDeclaration[]
|
|
60
|
+
): string {
|
|
61
|
+
const replaceImportStatements = replaceTo.map(assembleImport);
|
|
62
|
+
// replace origin statement
|
|
63
|
+
return fileContent.replace(origin.statement, replaceImportStatements.join('\n'));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function* traverseFileImports(fileContent: string): Generator<ImportDeclaration> {
|
|
67
|
+
// 匹配所有 import 语句的正则表达式
|
|
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*['"`]([^'"`]+)['"`]/g;
|
|
70
|
+
|
|
71
|
+
let match;
|
|
72
|
+
while ((match = importRegex.exec(fileContent)) !== null) {
|
|
73
|
+
const [fullMatch, defaultPart, namespacePart, namedPart, defaultPart2, source] = match;
|
|
74
|
+
|
|
75
|
+
const declaration: ImportDeclaration = {
|
|
76
|
+
statement: fullMatch,
|
|
77
|
+
source: source,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// 处理默认导入
|
|
81
|
+
const defaultImport = defaultPart?.trim() || defaultPart2?.trim();
|
|
82
|
+
if (defaultImport && !namespacePart && !namedPart) {
|
|
83
|
+
declaration.defaultImport = defaultImport;
|
|
84
|
+
} else if (defaultImport && (namespacePart || namedPart)) {
|
|
85
|
+
declaration.defaultImport = defaultImport;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 处理命名空间导入 (* as)
|
|
89
|
+
if (namespacePart) {
|
|
90
|
+
declaration.namespaceImport = namespacePart.trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 处理命名导入
|
|
94
|
+
if (namedPart) {
|
|
95
|
+
const namedImports = [];
|
|
96
|
+
const namedItems = namedPart
|
|
97
|
+
.split(',')
|
|
98
|
+
.map((item) => item.trim())
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
|
|
101
|
+
for (const item of namedItems) {
|
|
102
|
+
const typeOnly = item.startsWith('type ');
|
|
103
|
+
const cleanItem = typeOnly ? item.slice(5).trim() : item;
|
|
104
|
+
|
|
105
|
+
if (cleanItem.includes(' as ')) {
|
|
106
|
+
const [imported, local] = cleanItem.split(' as ').map((s) => s.trim());
|
|
107
|
+
namedImports.push({
|
|
108
|
+
imported,
|
|
109
|
+
local,
|
|
110
|
+
typeOnly,
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
namedImports.push({
|
|
114
|
+
imported: cleanItem,
|
|
115
|
+
typeOnly,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (namedImports.length > 0) {
|
|
121
|
+
declaration.namedImports = namedImports;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
yield declaration;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
|
|
9
|
+
class File {
|
|
10
|
+
content: string;
|
|
11
|
+
|
|
12
|
+
isUtf8: boolean;
|
|
13
|
+
|
|
14
|
+
relativePath: string;
|
|
15
|
+
|
|
16
|
+
path: string;
|
|
17
|
+
|
|
18
|
+
suffix: string;
|
|
19
|
+
|
|
20
|
+
constructor(filePath: string, public root: string = '/') {
|
|
21
|
+
this.path = filePath;
|
|
22
|
+
this.relativePath = path.relative(this.root, this.path);
|
|
23
|
+
this.suffix = path.extname(this.path);
|
|
24
|
+
|
|
25
|
+
// Check if exists
|
|
26
|
+
if (!fs.existsSync(this.path)) {
|
|
27
|
+
throw Error(`File ${path} Not Exists`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// If no utf-8, skip
|
|
31
|
+
try {
|
|
32
|
+
this.content = fs.readFileSync(this.path, 'utf-8');
|
|
33
|
+
this.isUtf8 = true;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
this.isUtf8 = false;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
replace(updater: (content: string) => string) {
|
|
41
|
+
if (!this.isUtf8) {
|
|
42
|
+
console.warn('Not UTF-8 file skipped: ', this.path);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.content = updater(this.content);
|
|
46
|
+
fs.writeFileSync(this.path, this.content, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function* traverseRecursiveFiles(folder: string): Generator<File> {
|
|
51
|
+
const files = fs.readdirSync(folder);
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
const filePath = path.join(folder, file);
|
|
54
|
+
if (fs.statSync(filePath).isDirectory()) {
|
|
55
|
+
yield* traverseRecursiveFiles(filePath);
|
|
56
|
+
} else {
|
|
57
|
+
yield new File(filePath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|