一鍵初始化新頁面 npm script -- 實踐技能總結

背景

業務的 h5 工程是基於 Koa 封裝的 node 應用,前端採用的是多頁面的方式,目錄大概以下:javascript

├── bin
├── app
│   ├── controllers
│   ├── routes
│   └── views
├── src
│   ├── pages
│   ├── common
複製代碼

在新增一個頁面的時候,須要這樣作:html

  • app/routes 中增長路由
  • app/controllers 中增長路由對應的中間件函數
  • app/views 中增長視圖模版 html
  • src/pages 中新建頁面文件夾,以及裏邊的 .vue .js .less 等文件

對一個懶人來講,這種複製粘貼的過程簡直不能忍!並且極容易漏掉某個地方,服務啓不起來還要查 pm2 日誌來找錯誤,僅僅初始化一個新頁面就要幾分鐘。。。前端

因而就寫了一個 npm script 來一鍵搞定,在此記錄下流程和技能點。vue

功能預覽:

1. 定義命令

既然是初始化頁面,就叫他 initPage,傳參遵循了工程的 devbuild 命令的傳參方式(雖然有點怪):npm run initPage --{path},例如:java

npm run initPage --pages/test
複製代碼

2. 校驗參數

在 bin 目錄下新建 init-page/index.js,首先進行參數的獲取和校驗node

// 校驗參數(文件名)
const argv = JSON.parse(process.env.npm_config_argv).original;
let fileName;
argv.forEach((item) => {
    if (item.indexOf('--') === 0) {
        fileName = item.replace('--', '');
    }
});
if (/[^\w-/]/g.test(fileName)) {
    error(`請輸入正確的文件路徑!\n${fileName}`);
    process.exit(0);
}
複製代碼

3. 肯定目錄

參數校驗完畢,須要肯定新頁面加在哪裏,也就是目標路徑。這裏約定輸入參數路徑的基路徑爲 src/pages,也就是說若是輸入的參數爲 test ,目標路徑就是 src/pages/test,可是一般咱們組的頁面大多寫在 src/pages/main,因此作了一個提示。 這裏須要用到 inquirer 定義命令行交互。git

if (!bizDir.includes(fileName.split('/')[0])) {
    warn(`src/pages 下沒有該目錄:${fileName.split('/')[0]}\n`);
    inquirer
        .prompt([
            {
                name: 'useDefault',
                message: '是否要加在 src/pages/main 目錄下?(Y/n)'
            },
        ])
        .then((answer) => {
            let targetDirPath;
            if (answer.useDefault === '' || answer.useDefault === 'Y' || answer.useDefault === 'y') {
                targetDirPath = path.resolve(bizDirPath, 'main', fileName);
            } else {
                targetDirPath = path.resolve(bizDirPath, fileName);
            }
            initPage(targetDirPath, fileName);
        });
} else {
    const targetDirPath = path.resolve(bizDirPath, fileName);
    initPage(targetDirPath, fileName);
}
複製代碼

4. 添加 controller、router

這裏須要在相應文件裏新增函數或屬性,因此天然須要用到 AST,相關的工具:babylonbabel-traversebabel-typesbabel-templatebabel-generator。 以添加 controller 爲例github

const addController = (fileName) => new Promise((resolve, reject) => {
    const nameArr = fileName.split('/');
    const name = nameArr[0];
    const controllerName = toCamel(fileName);
    const controller = path.resolve(projectRootPath, 'app/controllers', `${name}.js`);
    isExist(controller).then((exist) => {
        controllerExist = exist;
        if (exist) {
            fs.readFile(controller, 'utf8', (err, data) => {
                if (err) throw err;
                const ast = parse(data);
                traverse(ast, {
                    ExpressionStatement(path) {
                        const operatePath = path;
                        path.traverse({
                            MemberExpression(path) {
                                if (path.get('object').isIdentifier({name: 'module'})) {
                                    // 添加控制器
                                    const ast = controllerTmpl({
                                        CONTROLLER_NAME: t.identifier(controllerName),
                                        PATH: t.stringLiteral(fileName),
                                    });
                                    operatePath.insertBefore(ast);
                                    // 添加導出的屬性
                                    const rightPath = path.parentPath.get('right');
                                    if (rightPath) {
                                        const propertiesPath = rightPath.get('properties');
                                        const lastChild = propertiesPath[propertiesPath.length - 1];
                                        lastChild.insertAfter(t.objectProperty(t.identifier(controllerName), t.identifier(controllerName), false, true));
                                    }
                                }
                                path.skip();
                            },
                        });
                        path.skip();
                    },
                });
                const output = generate(ast, {}, data);
                fs.writeFile(controller, output.code, (err) => {
                    if (err) throw err;
                    success(`${name}.js 已配置`);
                    resolve();
                });
            });
        } else {
            fs.writeFile(controller, newControllerFile(fileName), (err) => {
                if (err) throw err;
                success(`${name}.js 已配置`);
                resolve();
            });
        }
    });
});
複製代碼

5. Eslint fix

經過語法轉換生成的新文件存在不符合 eslint 規範的問題,這裏用到 node child_process 模塊,以及好看的命令行 loading oradocker

const {exec} = require('child_process');
const ora = require('ora');

const loading = ora('eslint fixing ...');
const projectRootPath = path.resolve(__dirname, '../../');

const eslintFix = () => new Promise((resolve, reject) => {
    loading.start();
    exec('./node_modules/.bin/eslint --ext .js --fix app/controllers/** app/route/**', {
        cwd: projectRootPath,
    }, (err, stdout, stderr) => {
        if (err) {
            loading.fail();
            error(err);
            resolve();
            return;
        }
        loading.succeed();
        success('eslint fix 已完成');
        resolve();
    });
});
複製代碼

6. 啓動服務監聽頁面

判斷 docker 容器是否運行並進入容器啓動服務npm

const ifStartNow = (runing, fileName) => {
    inquirer
        .prompt([
            {
                name: 'startNow',
                message: '是否當即啓動項目?(Y/n)',
            },
        ])
        .then((answer) => {
            if (answer.startNow === '' || answer.startNow === 'Y' || answer.startNow === 'y') {
                if (runing) {
                    console.log('\n正在進入容器...');
                    execSync(`docker exec -it xxx-container bash -c "npm run dev --${fileName}"`, {
                        cwd: projectRootPath,
                        stdio: 'inherit',
                    });
                } else {
                    console.log('\n正在啓動容器:npm start ...');
                    console.log(`稍後請輸入監聽頁面${fileName}`);
                    execSync('npm start', {
                        cwd: projectRootPath,
                        stdio: 'inherit',
                    });
                }
            } else {
                console.log(`\n稍後啓動容器執行 ${chalk.bold.blue(`npm run dev --${fileName}`)} 查看頁面~`);
            }
        });
};
複製代碼

Github: github.com/onlyil/init…

相關文章
相關標籤/搜索