有了以前的基礎(前端如何搭建一個簡單的腳手架),咱們如今能夠講講一個成熟的腳手架是怎麼作了。javascript
這裏咱們參考vue-cli的源碼,基於rollup和typescript一步步搭建。vue-cli做爲vue的腳手架,給如此多的前端開發者使用,已經算是成熟了吧。前端
如下咱們的命令仍然是ds~,模板是ds-cli-lib-templatevue
├─ bin # 打包文件目錄
│ ├─ ds.js # package.json裏的bin字段引用文件
├─ src
│ ├─ lib # 具體命令目錄
│ ├─ list # ds list
│ ├─ init # ds init
│ ├─ utils # 工具函數
├─ main.ts # 入口文件
├─ typings # typescript類型文件目錄
├─ rullup.config.js # rollpu構建配置
├── test # 測試用例
複製代碼
現現在,webpack用來開發應用(熱更新hmr,代碼拆分等),rollup用來開發類庫(簡單易上手,打包後代碼能讀懂,至於其餘的特性webpack目前基本已支持)。java
如今來明確咱們的需求node
//rollup.config.js
import typescript from "rollup-plugin-typescript2";
import commonjs from 'rollup-plugin-commonjs'
import { uglify } from 'rollup-plugin-uglify'
export default {
//入口文件
input: "src/main.ts",
output: [
{
banner: "#!/usr/bin/env node",
/** * 頭部插入這段代碼 * */
name: "ds",
file: "bin/ds.js",
//打包成umd模塊規範
format: "umd"
}
],
plugins: [
typescript(),
commonjs({
include: "node_modules/**",
extensions: ['.js', '.ts']
}),
uglify()
],
};
複製代碼
npm腳本命令("scripts"字段)webpack
{
"clean": "rm -rf ./bin && mkdir bin",
"build": "npm run clean && rollup --config"
}
複製代碼
是一些很是基礎的東西,咱們通常不放很複雜的邏輯在入口文件裏。git
const cmd = require('commander');
const config = require('../package.json');
//這裏cli-init.ts和cli-list.ts咱們能夠簡單導出一個函數,如
// export default function(...args) {
// console.log('init')
// }
import init from './lib/init/cli-init';
import list from './lib/list/cli-list';
const command= {
init,
list
};
//map對應的type,從而執行
function exec(type, ...args) {
config.debug = args[0].debug;
command[type](...args);
}
cmd
.usage('<command>')
.version(config.version)
.description('歡迎使用ds-cli');
cmd
.command('init')
.description('初始化組件模板')
.action((...args) => exec('init', ...args));
cmd
.command('list')
.description('查看線上組件模板')
.action((...args) => exec('list', ...args));
cmd.command('help')
.description('查看幫助')
.action(() => cmd.help());
// 解析輸入的參數
cmd.parse(process.argv);
if (!cmd.args.length) {
cmd.help();
}
複製代碼
咱們打包(執行npm run build)到bin文件夾下後,配置一下package.json的bin字段爲bin/ds.js,而後發佈npm(參考快速發佈一個vue-fullpage組件到npm試一下命令。github
若是失敗,請從新審視上述之流程。web
ds init <template-name> <app-name> #模板名字和應用名字
複製代碼
if(當前目錄下構建){
詢問一下是否是當前目錄,是的話進入run函數
}else{
進入主流程run函數
}
//run函數
function run(){
if(模板路徑是本地文件路徑){
//支持本地模板如ds init /usr/webpack test
if(路徑存在){
//動態構造模板到你的目錄如test
generate()
}else{
//報錯日誌
}
}else{
//1.檢查當前process的node版本,大於6才能夠用
//2.檢查當前package.json的版本,跟遠程倉庫的版本比較一下。若是不同,就提醒一下用戶有新版本
//3.下載遠程倉庫到本地(本地通常存放用戶目錄裏的.ds-template文件夾下),而後執行generate函數
}
}
複製代碼
你們也看到了,其實最重要的就是generate函數~vue-cli
function generate(){
const opts = getOptions(name, templatePath) as meta; // 獲取meta.js配置,存到opts裏
// 咱們把所需文件放在源文件的template目錄下,其餘一些如測試放在外面。初始化一下metalsmith
const metalsmith = Metalsmith(path.join(templatePath, 'template')) //咱們約定,將模板全部文件放在ds-cli-lib-template/template裏
//中間件
metalsmith.use(askQuestions(opts.prompts)) // 詢問問題,將信息存metalsmith.metadata()
.use(filterFiles(opts.filters)) // 經過問題交互過濾掉不須要的文件
.use(renderTemplateFiles()); // 模板裏面可使用handlebar語法,做爲佔位符,咱們這裏從新渲染模板文件
// 源目錄打包到目標目錄to
metalsmith.clean(false)
.source('.')
.destination(to)
.build((err, files) => {
done(err);
});
}
複製代碼
module.exports={
//會經過中間件把這些字段存在metalsmith.metadata(),方便接下來的中間件調用
prompts:{
//格式能夠參考https://github.com/SBoudrias/Inquirer.js/#question
name: {
type: 'string',
required: true,
message: 'Project name',
},
author: {
type: 'string',
message: 'Author',
},
description: {
type: 'string',
required: false,
message: 'Project description',
default: '構建一個lib',
},
lint: {
"type": "confirm",
"message": "是否用tslint"
},
},
filters: {
//當上面prompts的lint爲false的時候,就過濾掉文件
"tslint.json": "lint",
"tsconfig.json": "lint"
}
}
複製代碼
至此,這裏咱們已經說明了基本原理,你應該已經能夠搭出適合你團隊的腳手架和模板了。
通常狀況下都會有list命令
感謝github給咱們提供了api,有如下比較好玩的接口
const logSymbols = require('log-symbols');
const chalk = require('chalk');
export default async function(...args) {
// 獲取倉庫列表
const res=await request({
url: 'https://api.github.com/users/yokiyokiyoki/repos',
method: 'GET'
}
);
let list;
if(res.status===200) {
console.log(logSymbols.info,chalk.green('共有下列模板'));
list=res.data.filter((item)=> {
return item.name.includes('ds-cli')&&item.name.includes('template');
}).forEach(item=> {
console.log();
console.log(chalk.green(item.name));
});
} else {
console.log(logSymbols.error,`獲取倉庫列表失敗${res.data}`);
}
}
複製代碼
須要注意的是,有時候github抽風,請求的會比較慢,這樣對用戶體驗不友好(嘗試了幾回ds list
若是很慢,開發者會失去耐心)。
若是對用戶體驗有追求的同窗,這裏提供一個思路,就是緩存。能夠把該結果寫入文件,緩存到本地存模板的文件夾裏(~/.ds-templates),同時把該請求時間(主要是用來判斷是不是今天)也進去,而後咱們在代碼裏判斷一下:下次請求的時候,是否本本地有該文件,有的話就讀一下,讀了以後判斷裏面的時間和如今的時候是不是相隔一天,相隔一天就從新請求(時間間隔本身把握),覆蓋原有緩存文件。