create-nicsde-next-app
Version:
A CLI tool to create Next.js applications with custom configurations
273 lines (238 loc) • 8.84 kB
JavaScript
const path = require('path');
const fs = require('fs');
const fse = require('fs-extra');
// 定制化内容(可以根据项目需求进行修改)
const customFiles = [
{ src: '.gitignore', dest: '.gitignore' },
{ src: 'next.config.ts', dest: 'next.config.ts' },
{ src: 'Dockerfile', dest: 'Dockerfile' },
{ src: '.dockerignore', dest: '.dockerignore' },
{ src: '.eslintrc.js', dest: '.eslintrc.js' },
{ src: '.npmrc', dest: '.npmrc' },
{ src: '.prettierignore', dest: '.prettierignore' },
{ src: '.prettierrc', dest: '.prettierrc' },
{ src: 'tailwind.config.ts', dest: 'tailwind.config.ts' },
{ src: 'postcss.config.mjs', dest: 'postcss.config.mjs' },
{ src: 'components.json', dest: 'components.json' }
];
// 模板文件路径配置
const templatePath = path.join(__dirname);
const templateFiles = {
srcApp: path.join(templatePath, 'src', 'app'),
public: path.join(templatePath, 'public')
};
// 清理目标目录并复制模板文件
async function copyTemplateFiles(projectName) {
const targetSrcApp = path.join(process.cwd(), projectName, 'src', 'app');
const targetPublic = path.join(process.cwd(), projectName, 'public');
try {
// 清空目标目录
await fse.emptyDir(targetSrcApp);
await fse.emptyDir(targetPublic);
console.log('Source public path:', templateFiles.public);
console.log('Target public path:', targetPublic);
// 复制模板文件
await fse.copy(templateFiles.srcApp, targetSrcApp);
await fse.copy(templateFiles.public, targetPublic);
console.log('Template files copied successfully!');
} catch (error) {
console.error('Error copying template files:', error);
throw error;
}
}
// 运行 `create-next-app` 命令
async function createApp(projectName) {
const { execa } = await import('execa');
try {
console.log('Creating Next.js app...');
await execa('npx', [
'create-next-app',
projectName,
'--use-npm',
'--typescript',
'--turbo',
'--eslint',
'--tailwind',
'--src-dir',
'--app',
'--import-alias',
'@/*',
'--yes'
], {
stdio: 'inherit',
});
// 复制模板文件
await copyTemplateFiles(projectName);
// 自定义文件复制
customFiles.forEach((file) => {
const srcPath = path.resolve(__dirname, file.src);
const destPath = path.join(process.cwd(), projectName, file.dest);
if (fs.existsSync(srcPath)) {
fs.copyFileSync(srcPath, destPath);
console.log(`Copied ${file.src} to ${file.dest}`);
} else {
console.log(`Source file ${file.src} does not exist`);
}
});
// 安装指定版本的tailwindcss
console.log('Installing tailwindcss@3.4.1...');
await execa('npm', [
'install',
'tailwindcss@3.4.1',
'postcss@latest'
], {
cwd: path.join(process.cwd(), projectName),
stdio: 'inherit'
});
console.log(`Project ${projectName} created successfully!`);
} catch (error) {
console.error('Error creating the project:', error);
}
}
// 询问用户是否要添加同济统一身份认证
async function promptForAuth() {
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise(resolve => {
readline.question('是否要添加同济统一身份认证? (Y/n): ', answer => {
readline.close();
resolve(answer.trim().toLowerCase() === 'y' || answer.trim() === '');
});
});
}
// 询问是否需要对接开放平台 sdk
async function promptForOpenplatform() {
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise(resolve => {
readline.question('是否要添加同济大学开放平台sdk? (Y/n): ', answer => {
readline.close();
resolve(answer.trim().toLowerCase() === 'y' || answer.trim() === '');
});
});
}
// 安装next-auth和相关依赖
async function installAuth(projectName) {
const { execa } = await import('execa');
try {
// 复制auth配置文件
console.log('正在复制同济统一身份认证配置文件...')
const apiSrc = path.join(__dirname, 'auth', 'api');
const apiDest = path.join(process.cwd(), projectName, 'src', 'app', 'api');
await fse.copySync(apiSrc, apiDest);
const componentsSrc = path.join(__dirname, 'auth', 'components');
const componentsDest = path.join(process.cwd(), projectName, 'src', 'components');
await fse.copySync(componentsSrc, componentsDest);
const envSrc = path.join(__dirname, 'auth/.env');
const envDest = path.join(process.cwd(), projectName, '.env');
await fse.copySync(envSrc, envDest);
const packageJsonSrc = path.join(__dirname, 'auth/package.json');
const packageJsonDest = path.join(process.cwd(), projectName, 'package.json');
await fse.copySync(packageJsonSrc, packageJsonDest);
const layoutSrc = path.join(__dirname, 'auth/layout.tsx');
const layoutDest = path.join(process.cwd(), projectName, 'src/app/layout.tsx');
await fse.copySync(layoutSrc, layoutDest);
// const pageSrc = path.join(__dirname, 'auth/page.tsx');
// const pageDest = path.join(process.cwd(), projectName, 'src/app/page.tsx');
// await fse.copySync(pageSrc, pageDest);
const authSrc = path.join(__dirname, 'auth/auth.ts');
const authDest = path.join(process.cwd(), projectName, 'src/auth.ts');
await fse.copySync(authSrc, authDest);
console.log('正在安装next-auth...');
await execa('npm', ['install', 'next-auth@5.0.0-beta.28'], {
cwd: path.join(process.cwd(), projectName),
stdio: 'inherit'
});
console.log('同济统一身份认证配置已添加');
} catch (error) {
console.error('安装next-auth失败:', error);
}
}
// 安装@tongji_api/sdk
async function installOpenplatformSDK(projectName) {
const { execa } = await import('execa');
try {
console.log('正在复制同济开放平台SDK使用文档文件...')
const apiSrc = path.join(__dirname, 'sdk');
const apiDest = path.join(process.cwd(), projectName, 'src', 'app', 'sdk');
await fse.copySync(apiSrc, apiDest);
await execa('npm', ['install', '@tongji_api/sdk@1.0.6-test'], {
cwd: path.join(process.cwd(), projectName),
stdio: 'inherit'
});
} catch (error) {
console.error('安装开放平台sdk失败:', error);
}
}
// 动态修改 page.tsx
async function modifyPageTsx(projectName, needAuth, needOpenplatformSDK) {
const filePath = path.join(process.cwd(), projectName, 'src/app/page.tsx');
// 读取文件内容
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
return console.error('读取文件时出错:', err);
}
// 将文件内容按行分割
const lines = data.split('\n');
// 确定要插入内容的行索引
const insertIndex = lines.length - 4;
// 插入新内容(例如,添加一个注释)
let newContent = ' <div className={styles.grid}>';
let insertAuthImport = 0
if (needAuth) {
// 第三行导入
insertAuthImport = 1
lines.splice(insertAuthImport, 0 , `import Button from "@/components/auth/Button"`)
newContent += `
<div className={styles.card}>
<h2>同济大学统一身份认证 →</h2>
<Button></Button>
</div>`;
}
if (needOpenplatformSDK) {
newContent += `
<a href="/sdk" className={styles.card}>
<h2>sdk 文档 →</h2>
<p>集成 开放平台 sdk调用,快速获取数据 </p>
</a>`;
}
newContent += '\n </div>';
lines.splice(insertIndex+insertAuthImport, 0, newContent);
// 将修改后的内容重新组合成字符串
const updatedData = lines.join('\n');
// 写回文件
fs.writeFile(filePath, updatedData, 'utf8', (err) => {
if (err) {
return console.error('写入文件时出错:', err);
}
console.log('文件已成功修改。');
});
});
}
// 运行脚本
async function main() {
const projectName = process.argv[2] || 'my-next-app';
const needAuth = await promptForAuth();
const needOpenplatformSDK = await promptForOpenplatform();
await createApp(projectName);
if (needAuth || needOpenplatformSDK) {
if (needAuth) await installAuth(projectName);
if (needOpenplatformSDK) await installOpenplatformSDK(projectName);
await modifyPageTsx(projectName, needAuth, needOpenplatformSDK);
}
const { execa } = await import('execa');
await execa('npm', [
'install',
'class-variance-authority@^0.7.0',
'tailwind-merge@^2.5.4'
], {
cwd: path.join(process.cwd(), projectName),
stdio: 'inherit'
});
}
main();