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':
|
||||
specifier: workspace:*
|
||||
version: link:../../../config/ts-config
|
||||
'@types/inquirer':
|
||||
specifier: 9.0.7
|
||||
version: 9.0.7
|
||||
'@types/lodash':
|
||||
specifier: ^4.14.137
|
||||
version: 4.17.13
|
||||
'@types/node':
|
||||
specifier: ^18
|
||||
version: 18.19.68
|
||||
'@types/react':
|
||||
specifier: ^18
|
||||
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 { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { bfsMaterials, copyMaterial, listAllMaterials } from './materials.js';
|
||||
import { getProjectInfo, installDependencies } from './project.js';
|
||||
import { getProjectInfo, installDependencies, ProjectInfo } from './project.js';
|
||||
import { bfsMaterials, copyMaterial, listAllMaterials, Material } from './materials.js';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.version('1.0.0')
|
||||
.version('1.0.1')
|
||||
.description('Add official materials to your project')
|
||||
.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!'));
|
||||
|
||||
const projectInfo = getProjectInfo();
|
||||
const projectInfo: ProjectInfo = getProjectInfo();
|
||||
|
||||
console.log(chalk.bold('Project Info:'));
|
||||
console.log(chalk.black(` - Flowgram Version: ${projectInfo.flowgramVersion}`));
|
||||
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
|
||||
if (materialName) {
|
||||
@ -42,7 +43,9 @@ program
|
||||
// If material not found or materialName not provided, prompt user to select
|
||||
if (!material) {
|
||||
// User select one component
|
||||
const result = await inquirer.prompt([
|
||||
const result = await inquirer.prompt<{
|
||||
material: Material; // Specify type for prompt result
|
||||
}>([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'material',
|
||||
@ -57,18 +60,23 @@ program
|
||||
]);
|
||||
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);
|
||||
|
||||
// 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
|
||||
let flowgramPackage = `@flowgram.ai/editor`;
|
||||
if (projectInfo.flowgramVersion !== 'workspace:*') {
|
||||
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(packagesToInstall);
|
||||
@ -77,8 +85,9 @@ program
|
||||
// 5. Copy the materials to the project
|
||||
console.log(chalk.bold('These Materials will be added to your project'));
|
||||
console.log(allMaterials);
|
||||
allMaterials.forEach((material) => {
|
||||
copyMaterial(material, projectInfo);
|
||||
allMaterials.forEach((mat: Material) => {
|
||||
// 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 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
|
||||
let projectPath = process.cwd();
|
||||
let projectPath: string = process.cwd();
|
||||
|
||||
while (projectPath !== '/' && !fs.existsSync(path.join(projectPath, 'package.json'))) {
|
||||
projectPath = path.join(projectPath, '..');
|
||||
@ -14,12 +29,11 @@ export function getProjectInfo() {
|
||||
throw new Error('Please run this command in a valid project');
|
||||
}
|
||||
|
||||
const packageJsonPath = path.join(projectPath, 'package.json');
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
const packageJsonPath: string = path.join(projectPath, 'package.json');
|
||||
const packageJson: PackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
// fixed layout or free layout
|
||||
const flowgramVersion =
|
||||
const flowgramVersion: string | undefined =
|
||||
packageJson.dependencies['@flowgram.ai/fixed-layout-editor'] ||
|
||||
packageJson.dependencies['@flowgram.ai/free-layout-editor'] ||
|
||||
packageJson.dependencies['@flowgram.ai/editor'];
|
||||
@ -34,12 +48,12 @@ export function getProjectInfo() {
|
||||
projectPath,
|
||||
packageJsonPath,
|
||||
packageJson,
|
||||
flowgramVersion,
|
||||
flowgramVersion, // TypeScript will ensure this is string due to the check above
|
||||
};
|
||||
}
|
||||
|
||||
export function findRushJson(startPath) {
|
||||
let currentPath = startPath;
|
||||
export function findRushJson(startPath: string): string | null {
|
||||
let currentPath: string = startPath;
|
||||
while (currentPath !== '/' && !fs.existsSync(path.join(currentPath, 'rush.json'))) {
|
||||
currentPath = path.join(currentPath, '..');
|
||||
}
|
||||
@ -49,7 +63,7 @@ export function findRushJson(startPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function installDependencies(packages, projectInfo) {
|
||||
export function installDependencies(packages: string[], projectInfo: ProjectInfo): void {
|
||||
if (fs.existsSync(path.join(projectInfo.projectPath, 'yarn.lock'))) {
|
||||
execSync(`yarn add ${packages.join(' ')}`, { stdio: 'inherit' });
|
||||
return;
|
||||
@ -4,7 +4,6 @@
|
||||
"homepage": "https://flowgram.ai/",
|
||||
"repository": "https://github.com/bytedance/flowgram.ai",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/esm/index.js",
|
||||
@ -14,7 +13,7 @@
|
||||
"module": "./dist/esm/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"bin": {
|
||||
"flowgram-form-materials": "./bin/index.js"
|
||||
"flowgram-form-materials": "./bin/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
@ -48,8 +47,10 @@
|
||||
"@flowgram.ai/eslint-config": "workspace:*",
|
||||
"@flowgram.ai/ts-config": "workspace:*",
|
||||
"@types/lodash": "^4.14.137",
|
||||
"@types/node": "^18",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/inquirer": "9.0.7",
|
||||
"@types/styled-components": "^5",
|
||||
"eslint": "^8.54.0",
|
||||
"react": "^18",
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"extends": "@flowgram.ai/ts-config/tsconfig.flow.path.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"include": ["./src", "./bin/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user