import { exec, execSync } from 'child_process';
import path from 'path';
import { existsSync, outputJsonSync, readdirSync, readFileSync, readJsonSync, statSync } from 'fs-extra';
import isBuiltinModule from 'is-builtin-module';
import { createStream } from 'table';
import ts from 'typescript';
import packageJson from '../package.json';
import workspace from '../workspace.json';
const ROOT_PATH = path.join(__dirname, '..');
const TSCONFIG_OPTIONS = ['tsconfig.app.json', 'tsconfig.lib.json'];
const SKIP_IMPORT = ['.', 'packages', 'vscode'];
const checkMode = process.argv.includes('--check');
const table = createStream({
columnDefault: { width: 50 },
columnCount: 2,
columns: [{ width: 40 }, { width: 10, alignment: 'center' }],
});
if (!checkMode) {
console.info(`Update package.json...`);
}
const getModuleName = (importStr: string) => {
const arr = importStr.split('/');
if (importStr.startsWith('@')) {
return arr.slice(0, 2).join('/');
} else {
return arr[0];
}
};
Object.entries(workspace.projects).forEach(([projectName, projectPath]) => {
const packagePath = path.join(ROOT_PATH, projectPath, 'package.json');
if (existsSync(packagePath)) {
const projectPackageJson = readJsonSync(packagePath);
const handleTS = () => {
return new Promise<void>((resolve) => {
TSCONFIG_OPTIONS.forEach((tsconfig) => {
const tsconfigPath = path.join(ROOT_PATH, projectPath, tsconfig);
if (existsSync(tsconfigPath)) {
exec(`tsc --project ${tsconfigPath} --listFilesOnly`, (error, stdout) => {
const tsFiles = stdout.split('\n');
const reduceDir = (dirPath: string, paths: string[] = []) => {
const files = readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
if (statSync(filePath).isDirectory()) {
reduceDir(filePath, [...paths, file]);
} else if (tsFiles.includes(filePath)) {
ts.createSourceFile(file, readFileSync(filePath, { encoding: 'utf8' }), ts.ScriptTarget.Latest)
.getChildren()
.forEach((node) => {
node.getChildren().forEach((node) => {
if (node.kind === ts.SyntaxKind.ImportDeclaration) {
const importStr = ((node as ts.ImportDeclaration).moduleSpecifier as ts.StringLiteral).text;
if (!isBuiltinModule(importStr) && SKIP_IMPORT.every((skip) => !importStr.startsWith(skip))) {
const dependence = getModuleName(importStr);
const version = dependence.startsWith('@react-devui')
? 'file:../' + dependence.split('/')[1]
: packageJson.devDependencies[dependence];
if (!(projectPackageJson['peerDependencies'] && dependence in projectPackageJson['peerDependencies'])) {
projectPackageJson['dependencies'][dependence] = version;
}
}
}
});
});
}
}
};
reduceDir(path.join(ROOT_PATH, projectPath, 'src'));
resolve();
});
}
});
});
};
const handleSCSS = () => {
return new Promise<void>((resolve) => {
const reduceDir = (dirPath: string, paths: string[] = []) => {
const files = readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
if (statSync(filePath).isDirectory()) {
reduceDir(filePath, [...paths, file]);
} else if (file.endsWith('.scss')) {
const content = readFileSync(filePath, { encoding: 'utf8' });
if (content.includes('~')) {
const importArr = content.match(/(?<=@import ('|")~).+(?=('|"))/g);
if (importArr) {
importArr.forEach((importStr) => {
const dependence = getModuleName(importStr);
projectPackageJson['dependencies'][dependence] = packageJson.devDependencies[dependence];
});
}
}
}
}
};
reduceDir(path.join(ROOT_PATH, projectPath, 'src'));
resolve();
});
};
Promise.all([handleTS(), handleSCSS()]).then(() => {
outputJsonSync(packagePath, projectPackageJson);
execSync(`yarn prettier ${packagePath} --write`);
execSync(`yarn sort-package-json "${packagePath}"`);
if (!checkMode) {
table.write([projectName, '✅']);
} else {
const out = execSync('git status --porcelain', { encoding: 'utf8' });
if (out) {
throw new Error('Make sure all files has been committed!');
}
}
});
}
});