@flowgram.ai/cli 0.4.11 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +576 -428
- package/dist/index.js +576 -428
- package/package.json +9 -6
- package/src/create-app/index.ts +63 -32
- package/src/find-materials/index.ts +75 -0
- package/src/index.ts +33 -18
- package/src/materials/copy.ts +60 -0
- package/src/materials/index.ts +45 -74
- package/src/materials/material.ts +61 -0
- package/src/materials/refresh-project-import.ts +55 -36
- package/src/materials/select.ts +69 -0
- package/src/materials/types.ts +23 -0
- package/src/update-version/index.ts +12 -11
- package/src/utils/export.ts +8 -11
- package/src/utils/file.ts +14 -13
- package/src/utils/import.ts +7 -5
- package/src/utils/npm.ts +92 -19
- package/src/utils/project.ts +20 -27
- package/src/utils/ts-file.ts +23 -38
- package/src/materials/materials.ts +0 -127
|
@@ -3,54 +3,73 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
}
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
|
|
10
|
+
import { traverseRecursiveTsFiles } from '../utils/ts-file';
|
|
11
|
+
import { ImportDeclaration, NamedImport } from '../utils/import';
|
|
12
|
+
import { SyncMaterialContext } from './types';
|
|
13
|
+
import { Material } from './material';
|
|
22
14
|
|
|
23
|
-
|
|
15
|
+
export function executeRefreshProjectImport(context: SyncMaterialContext) {
|
|
16
|
+
const { selectedMaterials, project, targetFormMaterialRoot } = context;
|
|
24
17
|
|
|
25
|
-
const
|
|
18
|
+
const exportName2Material = new Map<string, Material>();
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
const targetModule = `@/${path.relative(project.srcPath, targetFormMaterialRoot)}`;
|
|
21
|
+
|
|
22
|
+
for (const material of selectedMaterials) {
|
|
23
|
+
if (!material.indexFile) {
|
|
24
|
+
console.warn(`Material ${material.name} not found`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(`👀 The exports of ${material.name} is ${material.allExportNames.join(',')}`);
|
|
29
|
+
|
|
30
|
+
material.allExportNames.forEach((exportName) => {
|
|
31
|
+
exportName2Material.set(exportName, material);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
for (const tsFile of traverseRecursiveTsFiles(project.srcPath)) {
|
|
30
36
|
for (const importDeclaration of tsFile.imports) {
|
|
31
|
-
if (importDeclaration.source
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if (importDeclaration.source.startsWith('@flowgram.ai/form-materials')) {
|
|
38
|
+
// Import Module and its related named Imported
|
|
39
|
+
const restImports: NamedImport[] = [];
|
|
40
|
+
const importMap: Record<string, NamedImport[]> = {};
|
|
41
|
+
|
|
42
|
+
if (!importDeclaration.namedImports) {
|
|
36
43
|
continue;
|
|
37
44
|
}
|
|
38
|
-
const nextImports: ImportDeclaration[] = [
|
|
39
|
-
{
|
|
40
|
-
...importDeclaration,
|
|
41
|
-
namedImports: currentMaterialImports,
|
|
42
|
-
source: targetDir,
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
45
|
|
|
46
|
-
const
|
|
47
|
-
|
|
46
|
+
for (const nameImport of importDeclaration.namedImports) {
|
|
47
|
+
const material = exportName2Material.get(nameImport.imported);
|
|
48
|
+
if (material) {
|
|
49
|
+
const importModule = `${targetModule}/${material.fullName}`;
|
|
50
|
+
importMap[importModule] = importMap[importModule] || [];
|
|
51
|
+
importMap[importModule].push(nameImport);
|
|
52
|
+
} else {
|
|
53
|
+
restImports.push(nameImport);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (Object.keys(importMap).length === 0) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const nextImports: ImportDeclaration[] = Object.entries(importMap).map(
|
|
62
|
+
([importModule, namedImports]) => ({
|
|
63
|
+
...importDeclaration,
|
|
64
|
+
namedImports,
|
|
65
|
+
source: importModule,
|
|
66
|
+
})
|
|
48
67
|
);
|
|
49
68
|
|
|
50
|
-
if (
|
|
69
|
+
if (restImports?.length) {
|
|
51
70
|
nextImports.unshift({
|
|
52
71
|
...importDeclaration,
|
|
53
|
-
namedImports:
|
|
72
|
+
namedImports: restImports,
|
|
54
73
|
});
|
|
55
74
|
}
|
|
56
75
|
|
|
@@ -60,7 +79,7 @@ export function executeRefreshProjectImport(
|
|
|
60
79
|
console.log(
|
|
61
80
|
`From:\n${importDeclaration.statement}\nTo:\n${nextImports
|
|
62
81
|
.map((item) => item.statement)
|
|
63
|
-
.join(
|
|
82
|
+
.join('\n')}`
|
|
64
83
|
);
|
|
65
84
|
}
|
|
66
85
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
import { LoadedNpmPkg } from '../utils/npm';
|
|
10
|
+
import { MaterialCliOptions } from './types';
|
|
11
|
+
import { Material } from './material';
|
|
12
|
+
|
|
13
|
+
export const getSelectedMaterials = async (
|
|
14
|
+
cliOpts: MaterialCliOptions,
|
|
15
|
+
formMaterialPkg: LoadedNpmPkg
|
|
16
|
+
) => {
|
|
17
|
+
const { materialName, selectMultiple } = cliOpts;
|
|
18
|
+
|
|
19
|
+
const materials: Material[] = Material.listAll(formMaterialPkg);
|
|
20
|
+
|
|
21
|
+
let selectedMaterials: Material[] = [];
|
|
22
|
+
|
|
23
|
+
// 1. Check if materialName is provided and exists in materials
|
|
24
|
+
if (materialName) {
|
|
25
|
+
selectedMaterials = materialName
|
|
26
|
+
.split(',')
|
|
27
|
+
.map((_name) => materials.find((_m) => _m.fullName === _name.trim())!)
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 2. If material not found or materialName not provided, prompt user to select
|
|
32
|
+
if (!selectedMaterials.length) {
|
|
33
|
+
console.log(chalk.yellow(`Material "${materialName}" not found. Please select from the list:`));
|
|
34
|
+
|
|
35
|
+
const choices = materials.map((_material) => ({
|
|
36
|
+
name: _material.fullName,
|
|
37
|
+
value: _material,
|
|
38
|
+
}));
|
|
39
|
+
if (selectMultiple) {
|
|
40
|
+
// User select one component
|
|
41
|
+
const result = await inquirer.prompt<{
|
|
42
|
+
material: Material[]; // Specify type for prompt result
|
|
43
|
+
}>([
|
|
44
|
+
{
|
|
45
|
+
type: 'checkbox',
|
|
46
|
+
name: 'material',
|
|
47
|
+
message: 'Select multiple materials to add:',
|
|
48
|
+
choices: choices,
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
selectedMaterials = result.material;
|
|
52
|
+
} else {
|
|
53
|
+
// User select one component
|
|
54
|
+
const result = await inquirer.prompt<{
|
|
55
|
+
material: Material; // Specify type for prompt result
|
|
56
|
+
}>([
|
|
57
|
+
{
|
|
58
|
+
type: 'list',
|
|
59
|
+
name: 'material',
|
|
60
|
+
message: 'Select one material to add:',
|
|
61
|
+
choices: choices,
|
|
62
|
+
},
|
|
63
|
+
]);
|
|
64
|
+
selectedMaterials = [result.material];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return selectedMaterials;
|
|
69
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Project } from '../utils/project';
|
|
7
|
+
import { LoadedNpmPkg } from '../utils/npm';
|
|
8
|
+
import { Material } from './material';
|
|
9
|
+
|
|
10
|
+
export interface MaterialCliOptions {
|
|
11
|
+
materialName?: string;
|
|
12
|
+
refreshProjectImports?: boolean;
|
|
13
|
+
targetMaterialRootDir?: string;
|
|
14
|
+
selectMultiple?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SyncMaterialContext {
|
|
18
|
+
selectedMaterials: Material[];
|
|
19
|
+
project: Project;
|
|
20
|
+
formMaterialPkg: LoadedNpmPkg;
|
|
21
|
+
cliOpts: MaterialCliOptions;
|
|
22
|
+
targetFormMaterialRoot: string;
|
|
23
|
+
}
|
|
@@ -3,30 +3,31 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
import
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
import { getLatestVersion } from '../utils/npm';
|
|
9
|
+
import { traverseRecursiveFiles } from '../utils/file';
|
|
9
10
|
|
|
10
11
|
export async function updateFlowgramVersion(inputVersion?: string) {
|
|
11
|
-
console.log(chalk.bold(
|
|
12
|
+
console.log(chalk.bold('🚀 Welcome to @flowgram.ai update-version helper'));
|
|
12
13
|
|
|
13
14
|
// Get latest version
|
|
14
|
-
const latestVersion = await getLatestVersion(
|
|
15
|
+
const latestVersion = await getLatestVersion('@flowgram.ai/editor');
|
|
15
16
|
const currentPath = process.cwd();
|
|
16
|
-
console.log(
|
|
17
|
-
console.log(
|
|
17
|
+
console.log('- Latest flowgram version: ', latestVersion);
|
|
18
|
+
console.log('- Current Path: ', currentPath);
|
|
18
19
|
|
|
19
20
|
// User Input flowgram version, default is latestVersion
|
|
20
21
|
const flowgramVersion: string = inputVersion || latestVersion;
|
|
21
22
|
|
|
22
23
|
for (const file of traverseRecursiveFiles(currentPath)) {
|
|
23
|
-
if (file.path.endsWith(
|
|
24
|
-
console.log(
|
|
24
|
+
if (file.path.endsWith('package.json')) {
|
|
25
|
+
console.log('👀 Find package.json: ', file.path);
|
|
25
26
|
let updated = false;
|
|
26
27
|
const json = JSON.parse(file.content);
|
|
27
28
|
if (json.dependencies) {
|
|
28
29
|
for (const key in json.dependencies) {
|
|
29
|
-
if (key.startsWith(
|
|
30
|
+
if (key.startsWith('@flowgram.ai/')) {
|
|
30
31
|
updated = true;
|
|
31
32
|
json.dependencies[key] = flowgramVersion;
|
|
32
33
|
console.log(`- Update ${key} to ${flowgramVersion}`);
|
|
@@ -35,7 +36,7 @@ export async function updateFlowgramVersion(inputVersion?: string) {
|
|
|
35
36
|
}
|
|
36
37
|
if (json.devDependencies) {
|
|
37
38
|
for (const key in json.devDependencies) {
|
|
38
|
-
if (key.startsWith(
|
|
39
|
+
if (key.startsWith('@flowgram.ai/')) {
|
|
39
40
|
updated = true;
|
|
40
41
|
json.devDependencies[key] = flowgramVersion;
|
|
41
42
|
console.log(`- Update ${key} to ${flowgramVersion}`);
|
package/src/utils/export.ts
CHANGED
|
@@ -9,10 +9,7 @@ export function extractNamedExports(content: string) {
|
|
|
9
9
|
|
|
10
10
|
// Collect all type definition names
|
|
11
11
|
const typeDefinitions = new Set();
|
|
12
|
-
const typePatterns = [
|
|
13
|
-
/\b(?:type|interface)\s+(\w+)/g,
|
|
14
|
-
/\bexport\s+(?:type|interface)\s+(\w+)/g,
|
|
15
|
-
];
|
|
12
|
+
const typePatterns = [/\b(?:type|interface)\s+(\w+)/g, /\bexport\s+(?:type|interface)\s+(\w+)/g];
|
|
16
13
|
|
|
17
14
|
let match;
|
|
18
15
|
for (const pattern of typePatterns) {
|
|
@@ -41,7 +38,7 @@ export function extractNamedExports(content: string) {
|
|
|
41
38
|
exportPatterns[0].lastIndex = 0;
|
|
42
39
|
while ((match = exportPatterns[0].exec(content)) !== null) {
|
|
43
40
|
const [, kind, name] = match;
|
|
44
|
-
if (kind ===
|
|
41
|
+
if (kind === 'type' || kind === 'interface' || typeDefinitions.has(name)) {
|
|
45
42
|
typeExports.push(name);
|
|
46
43
|
} else {
|
|
47
44
|
valueExports.push(name);
|
|
@@ -52,13 +49,13 @@ export function extractNamedExports(content: string) {
|
|
|
52
49
|
exportPatterns[1].lastIndex = 0;
|
|
53
50
|
while ((match = exportPatterns[1].exec(content)) !== null) {
|
|
54
51
|
const exportsList = match[1]
|
|
55
|
-
.split(
|
|
52
|
+
.split(',')
|
|
56
53
|
.map((item) => item.trim())
|
|
57
|
-
.filter((item) => item && !item.includes(
|
|
54
|
+
.filter((item) => item && !item.includes(' as '));
|
|
58
55
|
|
|
59
56
|
for (const name of exportsList) {
|
|
60
|
-
if (name.startsWith(
|
|
61
|
-
typeExports.push(name.replace(
|
|
57
|
+
if (name.startsWith('type ')) {
|
|
58
|
+
typeExports.push(name.replace('type ', '').trim());
|
|
62
59
|
} else if (typeDefinitions.has(name)) {
|
|
63
60
|
typeExports.push(name);
|
|
64
61
|
} else {
|
|
@@ -93,9 +90,9 @@ export function extractNamedExports(content: string) {
|
|
|
93
90
|
exportPatterns[4].lastIndex = 0;
|
|
94
91
|
while ((match = exportPatterns[4].exec(content)) !== null) {
|
|
95
92
|
const exportsList = match[1]
|
|
96
|
-
.split(
|
|
93
|
+
.split(',')
|
|
97
94
|
.map((item) => item.trim())
|
|
98
|
-
.filter((item) => item && !item.includes(
|
|
95
|
+
.filter((item) => item && !item.includes(' as '));
|
|
99
96
|
|
|
100
97
|
for (const name of exportsList) {
|
|
101
98
|
typeExports.push(name);
|
package/src/utils/file.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import path from
|
|
7
|
-
import fs from
|
|
8
|
-
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
|
|
9
|
+
import ignore, { Ignore } from 'ignore';
|
|
9
10
|
|
|
10
11
|
export class File {
|
|
11
12
|
content: string;
|
|
@@ -18,7 +19,7 @@ export class File {
|
|
|
18
19
|
|
|
19
20
|
suffix: string;
|
|
20
21
|
|
|
21
|
-
constructor(filePath: string, public root: string =
|
|
22
|
+
constructor(filePath: string, public root: string = '/') {
|
|
22
23
|
this.path = filePath;
|
|
23
24
|
this.relativePath = path.relative(this.root, this.path);
|
|
24
25
|
this.suffix = path.extname(this.path);
|
|
@@ -30,7 +31,7 @@ export class File {
|
|
|
30
31
|
|
|
31
32
|
// If no utf-8, skip
|
|
32
33
|
try {
|
|
33
|
-
this.content = fs.readFileSync(this.path,
|
|
34
|
+
this.content = fs.readFileSync(this.path, 'utf-8');
|
|
34
35
|
this.isUtf8 = true;
|
|
35
36
|
} catch (e) {
|
|
36
37
|
this.isUtf8 = false;
|
|
@@ -40,29 +41,29 @@ export class File {
|
|
|
40
41
|
|
|
41
42
|
replace(updater: (content: string) => string) {
|
|
42
43
|
if (!this.isUtf8) {
|
|
43
|
-
console.warn(
|
|
44
|
+
console.warn('Not UTF-8 file skipped: ', this.path);
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
this.content = updater(this.content);
|
|
47
|
-
fs.writeFileSync(this.path, this.content,
|
|
48
|
+
fs.writeFileSync(this.path, this.content, 'utf-8');
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
write(nextContent: string) {
|
|
51
52
|
this.content = nextContent;
|
|
52
|
-
fs.writeFileSync(this.path, this.content,
|
|
53
|
+
fs.writeFileSync(this.path, this.content, 'utf-8');
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
export function* traverseRecursiveFilePaths(
|
|
57
58
|
folder: string,
|
|
58
|
-
ig: Ignore = ignore().add(
|
|
59
|
-
root: string = folder
|
|
59
|
+
ig: Ignore = ignore().add('.git'),
|
|
60
|
+
root: string = folder
|
|
60
61
|
): Generator<string> {
|
|
61
62
|
const files = fs.readdirSync(folder);
|
|
62
63
|
|
|
63
64
|
// add .gitignore to ignore if exists
|
|
64
|
-
if (fs.existsSync(path.join(folder,
|
|
65
|
-
ig.add(fs.readFileSync(path.join(folder,
|
|
65
|
+
if (fs.existsSync(path.join(folder, '.gitignore'))) {
|
|
66
|
+
ig.add(fs.readFileSync(path.join(folder, '.gitignore'), 'utf-8'));
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
for (const file of files) {
|
|
@@ -82,6 +83,6 @@ export function* traverseRecursiveFilePaths(
|
|
|
82
83
|
|
|
83
84
|
export function* traverseRecursiveFiles(folder: string): Generator<File> {
|
|
84
85
|
for (const filePath of traverseRecursiveFilePaths(folder)) {
|
|
85
|
-
yield new File(filePath);
|
|
86
|
+
yield new File(filePath, folder);
|
|
86
87
|
}
|
|
87
88
|
}
|
package/src/utils/import.ts
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
export interface NamedImport {
|
|
7
|
+
local?: string;
|
|
8
|
+
imported: string;
|
|
9
|
+
typeOnly?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
6
12
|
/**
|
|
7
13
|
* Cases
|
|
8
14
|
* import { A, B } from 'module';
|
|
@@ -16,11 +22,7 @@ export interface ImportDeclaration {
|
|
|
16
22
|
statement: string;
|
|
17
23
|
|
|
18
24
|
// import { A, B } from 'module';
|
|
19
|
-
namedImports?:
|
|
20
|
-
local?: string;
|
|
21
|
-
imported: string;
|
|
22
|
-
typeOnly?: boolean;
|
|
23
|
-
}[];
|
|
25
|
+
namedImports?: NamedImport[];
|
|
24
26
|
|
|
25
27
|
// import A from 'module';
|
|
26
28
|
defaultImport?: string;
|
package/src/utils/npm.ts
CHANGED
|
@@ -3,41 +3,114 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { execSync } from 'child_process';
|
|
7
|
-
import { existsSync } from 'fs';
|
|
8
6
|
import path from 'path';
|
|
9
|
-
import
|
|
7
|
+
import https from 'https';
|
|
8
|
+
import { existsSync, readFileSync } from 'fs';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
|
|
11
|
+
import * as tar from 'tar';
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
|
|
14
|
+
export class LoadedNpmPkg {
|
|
15
|
+
constructor(public name: string, public version: string, public path: string) {}
|
|
16
|
+
|
|
17
|
+
get srcPath() {
|
|
18
|
+
return path.join(this.path, 'src');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get distPath() {
|
|
22
|
+
return path.join(this.path, 'dist');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected _packageJson: Record<string, any>;
|
|
26
|
+
|
|
27
|
+
get packageJson() {
|
|
28
|
+
if (!this._packageJson) {
|
|
29
|
+
this._packageJson = JSON.parse(readFileSync(path.join(this.path, 'package.json'), 'utf8'));
|
|
30
|
+
}
|
|
31
|
+
return this._packageJson;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get dependencies(): Record<string, string> {
|
|
35
|
+
return this.packageJson.dependencies;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 使用 https 下载文件
|
|
40
|
+
function downloadFile(url: string, dest: string): Promise<void> {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const file = fs.createWriteStream(dest);
|
|
43
|
+
|
|
44
|
+
https
|
|
45
|
+
.get(url, (response) => {
|
|
46
|
+
if (response.statusCode !== 200) {
|
|
47
|
+
reject(new Error(`Download failed: ${response.statusCode}`));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
response.pipe(file);
|
|
52
|
+
|
|
53
|
+
file.on('finish', () => {
|
|
54
|
+
file.close();
|
|
55
|
+
resolve();
|
|
56
|
+
});
|
|
57
|
+
})
|
|
58
|
+
.on('error', (err) => {
|
|
59
|
+
fs.unlink(dest, () => reject(err));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
file.on('error', (err) => {
|
|
63
|
+
fs.unlink(dest, () => reject(err));
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
10
67
|
|
|
11
68
|
export async function getLatestVersion(packageName: string): Promise<string> {
|
|
12
|
-
return execSync(`npm view ${packageName} version --tag=latest`)
|
|
13
|
-
.toString()
|
|
14
|
-
.trim();
|
|
69
|
+
return execSync(`npm view ${packageName} version --tag=latest`).toString().trim();
|
|
15
70
|
}
|
|
16
71
|
|
|
17
|
-
export async function loadNpm(packageName: string): Promise<
|
|
72
|
+
export async function loadNpm(packageName: string): Promise<LoadedNpmPkg> {
|
|
18
73
|
const packageLatestVersion = await getLatestVersion(packageName);
|
|
19
74
|
|
|
20
|
-
const packagePath = path.join(
|
|
21
|
-
__dirname,
|
|
22
|
-
`./.download/${packageName}-${packageLatestVersion}`,
|
|
23
|
-
);
|
|
75
|
+
const packagePath = path.join(__dirname, `./.download/${packageName}-${packageLatestVersion}`);
|
|
24
76
|
|
|
25
77
|
if (existsSync(packagePath)) {
|
|
26
|
-
return packagePath;
|
|
78
|
+
return new LoadedNpmPkg(packageName, packageLatestVersion, packagePath);
|
|
27
79
|
}
|
|
28
80
|
|
|
29
|
-
// download else
|
|
30
81
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
82
|
+
// 获取 tarball 地址
|
|
83
|
+
const tarballUrl = execSync(`npm view ${packageName}@${packageLatestVersion} dist.tarball`)
|
|
34
84
|
.toString()
|
|
35
85
|
.trim();
|
|
36
|
-
|
|
37
|
-
|
|
86
|
+
|
|
87
|
+
// 临时 tarball 路径
|
|
88
|
+
const tempTarballPath = path.join(
|
|
89
|
+
__dirname,
|
|
90
|
+
`./.download/${packageName}-${packageLatestVersion}.tgz`
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// 确保目录存在
|
|
94
|
+
fs.ensureDirSync(path.dirname(tempTarballPath));
|
|
95
|
+
|
|
96
|
+
// 下载 tarball
|
|
97
|
+
await downloadFile(tarballUrl, tempTarballPath);
|
|
98
|
+
|
|
99
|
+
fs.ensureDirSync(packagePath);
|
|
100
|
+
|
|
101
|
+
// 解压到目标目录
|
|
102
|
+
await tar.x({
|
|
103
|
+
file: tempTarballPath,
|
|
104
|
+
cwd: packagePath,
|
|
105
|
+
strip: 1,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 删除临时文件
|
|
109
|
+
fs.unlinkSync(tempTarballPath);
|
|
110
|
+
|
|
111
|
+
return new LoadedNpmPkg(packageName, packageLatestVersion, packagePath);
|
|
38
112
|
} catch (error) {
|
|
39
113
|
console.error(`Error downloading or extracting package: ${error}`);
|
|
40
114
|
throw error;
|
|
41
115
|
}
|
|
42
116
|
}
|
|
43
|
-
|
package/src/utils/project.ts
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
import chalk from
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
8
|
+
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
|
|
11
|
+
import { getLatestVersion } from './npm';
|
|
10
12
|
|
|
11
13
|
interface PackageJson {
|
|
12
14
|
dependencies: { [key: string]: string };
|
|
@@ -32,26 +34,23 @@ export class Project {
|
|
|
32
34
|
// get nearest package.json
|
|
33
35
|
let projectPath: string = process.cwd();
|
|
34
36
|
|
|
35
|
-
while (
|
|
36
|
-
projectPath
|
|
37
|
-
!existsSync(path.join(projectPath, "package.json"))
|
|
38
|
-
) {
|
|
39
|
-
projectPath = path.join(projectPath, "..");
|
|
37
|
+
while (projectPath !== '/' && !existsSync(path.join(projectPath, 'package.json'))) {
|
|
38
|
+
projectPath = path.join(projectPath, '..');
|
|
40
39
|
}
|
|
41
|
-
if (projectPath ===
|
|
42
|
-
throw new Error(
|
|
40
|
+
if (projectPath === '/') {
|
|
41
|
+
throw new Error('Please run this command in a valid project');
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
this.projectPath = projectPath;
|
|
46
45
|
|
|
47
|
-
this.srcPath = path.join(projectPath,
|
|
48
|
-
this.packageJsonPath = path.join(projectPath,
|
|
49
|
-
this.packageJson = JSON.parse(readFileSync(this.packageJsonPath,
|
|
46
|
+
this.srcPath = path.join(projectPath, 'src');
|
|
47
|
+
this.packageJsonPath = path.join(projectPath, 'package.json');
|
|
48
|
+
this.packageJson = JSON.parse(readFileSync(this.packageJsonPath, 'utf8'));
|
|
50
49
|
|
|
51
50
|
this.flowgramVersion =
|
|
52
|
-
this.packageJson.dependencies[
|
|
53
|
-
this.packageJson.dependencies[
|
|
54
|
-
this.packageJson.dependencies[
|
|
51
|
+
this.packageJson.dependencies['@flowgram.ai/fixed-layout-editor'] ||
|
|
52
|
+
this.packageJson.dependencies['@flowgram.ai/free-layout-editor'] ||
|
|
53
|
+
this.packageJson.dependencies['@flowgram.ai/editor'];
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
async addDependency(dependency: string) {
|
|
@@ -59,7 +58,7 @@ export class Project {
|
|
|
59
58
|
let version: string;
|
|
60
59
|
|
|
61
60
|
// 处理作用域包(如 @types/react@1.0.0)
|
|
62
|
-
const lastAtIndex = dependency.lastIndexOf(
|
|
61
|
+
const lastAtIndex = dependency.lastIndexOf('@');
|
|
63
62
|
|
|
64
63
|
if (lastAtIndex <= 0) {
|
|
65
64
|
// 没有@符号 或者@在开头(如 @types/react)
|
|
@@ -77,10 +76,7 @@ export class Project {
|
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
this.packageJson.dependencies[name] = version;
|
|
80
|
-
writeFileSync(
|
|
81
|
-
this.packageJsonPath,
|
|
82
|
-
JSON.stringify(this.packageJson, null, 2),
|
|
83
|
-
);
|
|
79
|
+
writeFileSync(this.packageJsonPath, JSON.stringify(this.packageJson, null, 2));
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
async addDependencies(dependencies: string[]) {
|
|
@@ -90,14 +86,11 @@ export class Project {
|
|
|
90
86
|
}
|
|
91
87
|
|
|
92
88
|
writeToPackageJsonFile() {
|
|
93
|
-
writeFileSync(
|
|
94
|
-
this.packageJsonPath,
|
|
95
|
-
JSON.stringify(this.packageJson, null, 2),
|
|
96
|
-
);
|
|
89
|
+
writeFileSync(this.packageJsonPath, JSON.stringify(this.packageJson, null, 2));
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
printInfo() {
|
|
100
|
-
console.log(chalk.bold(
|
|
93
|
+
console.log(chalk.bold('Project Info:'));
|
|
101
94
|
console.log(chalk.black(` - Flowgram Version: ${this.flowgramVersion}`));
|
|
102
95
|
console.log(chalk.black(` - Project Path: ${this.projectPath}`));
|
|
103
96
|
}
|