mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
feat(material): use tsx in cli (#295)
This commit is contained in:
parent
8a6b50ba78
commit
b477181502
6
common/config/rush/pnpm-lock.yaml
generated
6
common/config/rush/pnpm-lock.yaml
generated
@ -1961,9 +1961,15 @@ importers:
|
|||||||
'@flowgram.ai/ts-config':
|
'@flowgram.ai/ts-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../config/ts-config
|
version: link:../../../config/ts-config
|
||||||
|
'@types/inquirer':
|
||||||
|
specifier: 9.0.7
|
||||||
|
version: 9.0.7
|
||||||
'@types/lodash':
|
'@types/lodash':
|
||||||
specifier: ^4.14.137
|
specifier: ^4.14.137
|
||||||
version: 4.17.13
|
version: 4.17.13
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^18
|
||||||
|
version: 18.19.68
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18
|
specifier: ^18
|
||||||
version: 18.3.16
|
version: 18.3.16
|
||||||
|
|||||||
@ -1,30 +1,31 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env -S npx tsx@latest
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import { Command } from 'commander';
|
|
||||||
import inquirer from 'inquirer';
|
import inquirer from 'inquirer';
|
||||||
|
import { Command } from 'commander';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
import { bfsMaterials, copyMaterial, listAllMaterials } from './materials.js';
|
import { getProjectInfo, installDependencies, ProjectInfo } from './project.js';
|
||||||
import { getProjectInfo, installDependencies } from './project.js';
|
import { bfsMaterials, copyMaterial, listAllMaterials, Material } from './materials.js';
|
||||||
|
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
|
|
||||||
program
|
program
|
||||||
.version('1.0.0')
|
.version('1.0.1')
|
||||||
.description('Add official materials to your project')
|
.description('Add official materials to your project')
|
||||||
.argument('[materialName]', 'Optional material name to skip selection (format: type/name)')
|
.argument('[materialName]', 'Optional material name to skip selection (format: type/name)')
|
||||||
.action(async (materialName) => {
|
.action(async (materialName?: string) => {
|
||||||
|
// materialName can be undefined
|
||||||
console.log(chalk.bgGreenBright('Welcome to @flowgram.ai/form-materials CLI!'));
|
console.log(chalk.bgGreenBright('Welcome to @flowgram.ai/form-materials CLI!'));
|
||||||
|
|
||||||
const projectInfo = getProjectInfo();
|
const projectInfo: ProjectInfo = getProjectInfo();
|
||||||
|
|
||||||
console.log(chalk.bold('Project Info:'));
|
console.log(chalk.bold('Project Info:'));
|
||||||
console.log(chalk.black(` - Flowgram Version: ${projectInfo.flowgramVersion}`));
|
console.log(chalk.black(` - Flowgram Version: ${projectInfo.flowgramVersion}`));
|
||||||
console.log(chalk.black(` - Project Path: ${projectInfo.projectPath}`));
|
console.log(chalk.black(` - Project Path: ${projectInfo.projectPath}`));
|
||||||
|
|
||||||
const materials = listAllMaterials();
|
const materials: Material[] = listAllMaterials();
|
||||||
|
|
||||||
let material;
|
let material: Material | undefined; // material can be undefined
|
||||||
|
|
||||||
// Check if materialName is provided and exists in materials
|
// Check if materialName is provided and exists in materials
|
||||||
if (materialName) {
|
if (materialName) {
|
||||||
@ -42,7 +43,9 @@ program
|
|||||||
// If material not found or materialName not provided, prompt user to select
|
// If material not found or materialName not provided, prompt user to select
|
||||||
if (!material) {
|
if (!material) {
|
||||||
// User select one component
|
// User select one component
|
||||||
const result = await inquirer.prompt([
|
const result = await inquirer.prompt<{
|
||||||
|
material: Material; // Specify type for prompt result
|
||||||
|
}>([
|
||||||
{
|
{
|
||||||
type: 'list',
|
type: 'list',
|
||||||
name: 'material',
|
name: 'material',
|
||||||
@ -57,18 +60,23 @@ program
|
|||||||
]);
|
]);
|
||||||
material = result.material;
|
material = result.material;
|
||||||
}
|
}
|
||||||
|
// Ensure material is defined before proceeding
|
||||||
|
if (!material) {
|
||||||
|
console.error(chalk.red('No material selected. Exiting.'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(material);
|
console.log(material);
|
||||||
|
|
||||||
// 3. Get the component dependencies by BFS (include depMaterials and depPackages)
|
// 3. Get the component dependencies by BFS (include depMaterials and depPackages)
|
||||||
const { allMaterials, allPackages } = bfsMaterials(material, materials);
|
const { allMaterials, allPackages } = bfsMaterials(material!, materials);
|
||||||
|
|
||||||
// 4. Install the dependencies
|
// 4. Install the dependencies
|
||||||
let flowgramPackage = `@flowgram.ai/editor`;
|
let flowgramPackage = `@flowgram.ai/editor`;
|
||||||
if (projectInfo.flowgramVersion !== 'workspace:*') {
|
if (projectInfo.flowgramVersion !== 'workspace:*') {
|
||||||
flowgramPackage = `@flowgram.ai/editor@${projectInfo.flowgramVersion}`;
|
flowgramPackage = `@flowgram.ai/editor@${projectInfo.flowgramVersion}`;
|
||||||
}
|
}
|
||||||
const packagesToInstall = [flowgramPackage, ...allPackages];
|
const packagesToInstall: string[] = [flowgramPackage, ...allPackages];
|
||||||
|
|
||||||
console.log(chalk.bold('These npm dependencies will be added to your project'));
|
console.log(chalk.bold('These npm dependencies will be added to your project'));
|
||||||
console.log(packagesToInstall);
|
console.log(packagesToInstall);
|
||||||
@ -77,8 +85,9 @@ program
|
|||||||
// 5. Copy the materials to the project
|
// 5. Copy the materials to the project
|
||||||
console.log(chalk.bold('These Materials will be added to your project'));
|
console.log(chalk.bold('These Materials will be added to your project'));
|
||||||
console.log(allMaterials);
|
console.log(allMaterials);
|
||||||
allMaterials.forEach((material) => {
|
allMaterials.forEach((mat: Material) => {
|
||||||
copyMaterial(material, projectInfo);
|
// Add type for mat
|
||||||
|
copyMaterial(mat, projectInfo);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
const _types = ['components', 'effects', 'utils', 'typings'];
|
|
||||||
|
|
||||||
export function listAllMaterials() {
|
|
||||||
const _materials = [];
|
|
||||||
|
|
||||||
for (const _type of _types) {
|
|
||||||
const materialsPath = path.join(import.meta.dirname, '..', 'src', _type);
|
|
||||||
_materials.push(
|
|
||||||
...fs
|
|
||||||
.readdirSync(materialsPath)
|
|
||||||
.map((_path) => {
|
|
||||||
if (_path === 'index.ts') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = fs.readFileSync(path.join(materialsPath, _path, 'config.json'), 'utf8');
|
|
||||||
return {
|
|
||||||
...JSON.parse(config),
|
|
||||||
type: _type,
|
|
||||||
path: path.join(materialsPath, _path),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _materials;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bfsMaterials(material, _materials = []) {
|
|
||||||
function findConfigByName(name) {
|
|
||||||
return _materials.find(
|
|
||||||
(_config) => _config.name === name || `${_config.type}/${_config.name}` === name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const queue = [material];
|
|
||||||
const allMaterials = new Set();
|
|
||||||
const allPackages = new Set();
|
|
||||||
|
|
||||||
while (queue.length > 0) {
|
|
||||||
const _material = queue.shift();
|
|
||||||
if (allMaterials.has(_material)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
allMaterials.add(_material);
|
|
||||||
|
|
||||||
if (_material.depPackages) {
|
|
||||||
for (const _package of _material.depPackages) {
|
|
||||||
allPackages.add(_package);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_material.depMaterials) {
|
|
||||||
for (const _materialName of _material.depMaterials) {
|
|
||||||
queue.push(findConfigByName(_materialName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
allMaterials: Array.from(allMaterials),
|
|
||||||
allPackages: Array.from(allPackages),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const copyMaterial = (material, projectInfo) => {
|
|
||||||
const sourceDir = material.path;
|
|
||||||
const materialRoot = path.join(
|
|
||||||
projectInfo.projectPath,
|
|
||||||
'src',
|
|
||||||
'form-materials',
|
|
||||||
`${material.type}`
|
|
||||||
);
|
|
||||||
const targetDir = path.join(materialRoot, material.name);
|
|
||||||
fs.cpSync(sourceDir, targetDir, { recursive: true });
|
|
||||||
|
|
||||||
let materialRootIndexTs = '';
|
|
||||||
if (fs.existsSync(path.join(materialRoot, 'index.ts'))) {
|
|
||||||
materialRootIndexTs = fs.readFileSync(path.join(materialRoot, 'index.ts'), 'utf8');
|
|
||||||
}
|
|
||||||
if (!materialRootIndexTs.includes(material.name)) {
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(materialRoot, 'index.ts'),
|
|
||||||
`${materialRootIndexTs}${materialRootIndexTs.endsWith('\n') ? '' : '\n'}export * from './${
|
|
||||||
material.name
|
|
||||||
}';\n`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
137
packages/materials/form-materials/bin/materials.ts
Normal file
137
packages/materials/form-materials/bin/materials.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import { ProjectInfo } from './project'; // Import ProjectInfo
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
// Added type definitions
|
||||||
|
export interface Material {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
path: string;
|
||||||
|
depPackages?: string[];
|
||||||
|
depMaterials?: string[];
|
||||||
|
[key: string]: any; // For other properties from config.json
|
||||||
|
}
|
||||||
|
|
||||||
|
const _types: string[] = ['components', 'effects', 'utils', 'typings'];
|
||||||
|
|
||||||
|
export function listAllMaterials(): Material[] {
|
||||||
|
const _materials: Material[] = [];
|
||||||
|
|
||||||
|
for (const _type of _types) {
|
||||||
|
// 在 Node.js 中,import.meta.dirname 不可用,可使用 import.meta.url 结合 url 模块来获取目录路径
|
||||||
|
|
||||||
|
const materialsPath: string = path.join(__dirname, '..', 'src', _type);
|
||||||
|
_materials.push(
|
||||||
|
...fs
|
||||||
|
.readdirSync(materialsPath)
|
||||||
|
.map((_path: string) => {
|
||||||
|
if (_path === 'index.ts') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configPath = path.join(materialsPath, _path, 'config.json');
|
||||||
|
// Check if config.json exists before reading
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
console.warn(
|
||||||
|
`Warning: config.json not found for material at ${path.join(materialsPath, _path)}`
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
||||||
|
const config = JSON.parse(configContent);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
name: _path, // Assuming the folder name is the material name
|
||||||
|
type: _type,
|
||||||
|
path: path.join(materialsPath, _path),
|
||||||
|
} as Material;
|
||||||
|
})
|
||||||
|
.filter((material): material is Material => material !== null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _materials;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BfsResult {
|
||||||
|
allMaterials: Material[];
|
||||||
|
allPackages: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bfsMaterials(material: Material, _materials: Material[] = []): BfsResult {
|
||||||
|
function findConfigByName(name: string): Material | undefined {
|
||||||
|
return _materials.find(
|
||||||
|
(_config) => _config.name === name || `${_config.type}/${_config.name}` === name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queue: (Material | undefined)[] = [material]; // Queue can hold undefined if findConfigByName returns undefined
|
||||||
|
const allMaterials = new Set<Material>();
|
||||||
|
const allPackages = new Set<string>();
|
||||||
|
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const _material = queue.shift();
|
||||||
|
if (!_material || allMaterials.has(_material)) {
|
||||||
|
// Check if _material is defined
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
allMaterials.add(_material);
|
||||||
|
|
||||||
|
if (_material.depPackages) {
|
||||||
|
for (const _package of _material.depPackages) {
|
||||||
|
allPackages.add(_package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_material.depMaterials) {
|
||||||
|
for (const _materialName of _material.depMaterials) {
|
||||||
|
const depMaterial = findConfigByName(_materialName);
|
||||||
|
if (depMaterial) {
|
||||||
|
// Ensure dependent material is found before adding to queue
|
||||||
|
queue.push(depMaterial);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Warning: Dependent material "${_materialName}" not found for material "${_material.name}".`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
allMaterials: Array.from(allMaterials),
|
||||||
|
allPackages: Array.from(allPackages),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const copyMaterial = (material: Material, projectInfo: ProjectInfo): void => {
|
||||||
|
const sourceDir: string = material.path;
|
||||||
|
const materialRoot: string = path.join(
|
||||||
|
projectInfo.projectPath,
|
||||||
|
'src',
|
||||||
|
'form-materials',
|
||||||
|
`${material.type}`
|
||||||
|
);
|
||||||
|
const targetDir = path.join(materialRoot, material.name);
|
||||||
|
fs.cpSync(sourceDir, targetDir, { recursive: true });
|
||||||
|
|
||||||
|
let materialRootIndexTs: string = '';
|
||||||
|
const indexTsPath = path.join(materialRoot, 'index.ts');
|
||||||
|
if (fs.existsSync(indexTsPath)) {
|
||||||
|
materialRootIndexTs = fs.readFileSync(indexTsPath, 'utf8');
|
||||||
|
}
|
||||||
|
if (!materialRootIndexTs.includes(material.name)) {
|
||||||
|
fs.writeFileSync(
|
||||||
|
indexTsPath,
|
||||||
|
`${materialRootIndexTs}${materialRootIndexTs.endsWith('\n') ? '' : '\n'}export * from './${
|
||||||
|
material.name
|
||||||
|
}';\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,10 +1,25 @@
|
|||||||
import { execSync } from 'child_process';
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
export function getProjectInfo() {
|
// Added type definitions
|
||||||
|
interface PackageJson {
|
||||||
|
dependencies: { [key: string]: string };
|
||||||
|
devDependencies?: { [key: string]: string };
|
||||||
|
peerDependencies?: { [key: string]: string };
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjectInfo {
|
||||||
|
projectPath: string;
|
||||||
|
packageJsonPath: string;
|
||||||
|
packageJson: PackageJson;
|
||||||
|
flowgramVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectInfo(): ProjectInfo {
|
||||||
// get nearest package.json
|
// get nearest package.json
|
||||||
let projectPath = process.cwd();
|
let projectPath: string = process.cwd();
|
||||||
|
|
||||||
while (projectPath !== '/' && !fs.existsSync(path.join(projectPath, 'package.json'))) {
|
while (projectPath !== '/' && !fs.existsSync(path.join(projectPath, 'package.json'))) {
|
||||||
projectPath = path.join(projectPath, '..');
|
projectPath = path.join(projectPath, '..');
|
||||||
@ -14,12 +29,11 @@ export function getProjectInfo() {
|
|||||||
throw new Error('Please run this command in a valid project');
|
throw new Error('Please run this command in a valid project');
|
||||||
}
|
}
|
||||||
|
|
||||||
const packageJsonPath = path.join(projectPath, 'package.json');
|
const packageJsonPath: string = path.join(projectPath, 'package.json');
|
||||||
|
const packageJson: PackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
||||||
|
|
||||||
// fixed layout or free layout
|
// fixed layout or free layout
|
||||||
const flowgramVersion =
|
const flowgramVersion: string | undefined =
|
||||||
packageJson.dependencies['@flowgram.ai/fixed-layout-editor'] ||
|
packageJson.dependencies['@flowgram.ai/fixed-layout-editor'] ||
|
||||||
packageJson.dependencies['@flowgram.ai/free-layout-editor'] ||
|
packageJson.dependencies['@flowgram.ai/free-layout-editor'] ||
|
||||||
packageJson.dependencies['@flowgram.ai/editor'];
|
packageJson.dependencies['@flowgram.ai/editor'];
|
||||||
@ -34,12 +48,12 @@ export function getProjectInfo() {
|
|||||||
projectPath,
|
projectPath,
|
||||||
packageJsonPath,
|
packageJsonPath,
|
||||||
packageJson,
|
packageJson,
|
||||||
flowgramVersion,
|
flowgramVersion, // TypeScript will ensure this is string due to the check above
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findRushJson(startPath) {
|
export function findRushJson(startPath: string): string | null {
|
||||||
let currentPath = startPath;
|
let currentPath: string = startPath;
|
||||||
while (currentPath !== '/' && !fs.existsSync(path.join(currentPath, 'rush.json'))) {
|
while (currentPath !== '/' && !fs.existsSync(path.join(currentPath, 'rush.json'))) {
|
||||||
currentPath = path.join(currentPath, '..');
|
currentPath = path.join(currentPath, '..');
|
||||||
}
|
}
|
||||||
@ -49,7 +63,7 @@ export function findRushJson(startPath) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function installDependencies(packages, projectInfo) {
|
export function installDependencies(packages: string[], projectInfo: ProjectInfo): void {
|
||||||
if (fs.existsSync(path.join(projectInfo.projectPath, 'yarn.lock'))) {
|
if (fs.existsSync(path.join(projectInfo.projectPath, 'yarn.lock'))) {
|
||||||
execSync(`yarn add ${packages.join(' ')}`, { stdio: 'inherit' });
|
execSync(`yarn add ${packages.join(' ')}`, { stdio: 'inherit' });
|
||||||
return;
|
return;
|
||||||
@ -4,7 +4,6 @@
|
|||||||
"homepage": "https://flowgram.ai/",
|
"homepage": "https://flowgram.ai/",
|
||||||
"repository": "https://github.com/bytedance/flowgram.ai",
|
"repository": "https://github.com/bytedance/flowgram.ai",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
"exports": {
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"import": "./dist/esm/index.js",
|
"import": "./dist/esm/index.js",
|
||||||
@ -14,7 +13,7 @@
|
|||||||
"module": "./dist/esm/index.js",
|
"module": "./dist/esm/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
"flowgram-form-materials": "./bin/index.js"
|
"flowgram-form-materials": "./bin/index.ts"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
@ -48,8 +47,10 @@
|
|||||||
"@flowgram.ai/eslint-config": "workspace:*",
|
"@flowgram.ai/eslint-config": "workspace:*",
|
||||||
"@flowgram.ai/ts-config": "workspace:*",
|
"@flowgram.ai/ts-config": "workspace:*",
|
||||||
"@types/lodash": "^4.14.137",
|
"@types/lodash": "^4.14.137",
|
||||||
|
"@types/node": "^18",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@types/inquirer": "9.0.7",
|
||||||
"@types/styled-components": "^5",
|
"@types/styled-components": "^5",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.54.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "@flowgram.ai/ts-config/tsconfig.flow.path.json",
|
"extends": "@flowgram.ai/ts-config/tsconfig.flow.path.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "react",
|
"jsx": "react"
|
||||||
},
|
},
|
||||||
"include": ["./src"],
|
"include": ["./src", "./bin/**/*.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user