dva-cli
dva 的命令列工具
原理就是當執行 dva new my-app
時,dva-cli 就將其專案中的boilerplates資料夾拷貝到process.cwd()目錄下。並且執行npm install
安裝專案依賴。
檢視 package.json 入口在bin/dva
// Notify update when process exits
const updater = require('update-notifier');
const pkg = require('../package.json');
updater({ pkg: pkg }).notify({ defer: true });
// dva -v
if (process.argv.slice(2).join('') === '-v') {
// ...
}
program
.usage('<command> [options]')
.on('--help', printHelp)
.parse(process.argv);
const aliases = {
g: 'generate',
};
const args = process.argv.slice(3);
// new、generate
let subcmd = program.args[0];
if (aliases[subcmd]) subcmd = aliases[subcmd];
if (!subcmd) {
program.help();
} else {
const bin = executable(subcmd);
if (bin) {
// 執行檔案
wrap(spawn(bin, args, {stdio: 'inherit', customFds: [0, 1, 2]}));
} else {
program.help();
}
}
function wrap(sp) {
sp.on('close', function(code) {
process.exit(code);
});
}
// 檢查是不是有相應 subcmd 的執行檔案
function executable(subcmd) {
var file = join(__dirname, 'dva-' + subcmd);
if (exists(file)) {
return file;
}
}
複製程式碼
bin/dva 是dva-cli的入口檔案,其中定義了-v命令以及執行相應的subcmd命令。如果你輸入了dva new my-app
,那麼將會執行bin/dva-new,下面看下bin/dva-new檔案
// 沒有定義專案名稱
if (!program.args[0]) {
program.help();
} else {
const dest = join(process.cwd(), program.args[0]);
// 如果已經存在該專案
if (existsSync(dest)) {
console.error(error('Existing directory here, please run new command for an empty folder!'));
process.exit(1);
}
// 建立目錄
mkdirpSync(dest);
// 進入目錄
process.chdir(dest);
require('../lib/init')(program);
}
複製程式碼
bin/dva-new 檢查了命令的有效性,並且建立、進入專案目錄,執行dva-init初始化專案,下面去看一下bin/dva-init
program
.option('--demo', 'Generate dva project for demo')
.option('--no-install', 'Install dependencies after boilerplate, default: true')
.parse(process.argv);
require('../lib/init')(program);
複製程式碼
定義了兩個配置項,然後又呼叫了lib/init,由於lib資料夾是打包出來的,先去看一下src/init檔案吧
function init({ demo, install }) {
const type = demo ? 'demo' : 'app';
// __dirname 為執行檔案所在的目錄
const cwd = join(__dirname, '../boilerplates', type);
const dest = process.cwd();
const projectName = basename(dest);
if (!emptyDir(dest)) {
error('Existing files here, please run init command in an empty folder!');
process.exit(1);
}
console.log(`Creating a new Dva app in ${dest}.`);
console.log();
// vfs:檔案流處理工具,將boilerplates中的檔案拷貝到process.cwd目錄下,
// 如果install為true的話,則執行src/install
vfs.src(['**/*', '!node_modules/**/*'], {cwd: cwd, cwdbase: true, dot: true})
.pipe(template(dest, cwd))
.pipe(vfs.dest(dest))
.on('end', function() {
info('rename', 'gitignore -> .gitignore');
renameSync(join(dest, 'gitignore'), join(dest, '.gitignore'));
if (install) {
info('run', 'npm install');
require('./install')(printSuccess);
} else {
printSuccess();
}
})
.resume();
function printSuccess() {
//...
}
}
複製程式碼
src/install 會去根據系統安裝的依賴管理工具進行install操作
// 好吧 竟然沒有yarn
function findNpm() {
var npms = process.platform === 'win32' ? ['tnpm.cmd', 'cnpm.cmd', 'npm.cmd'] : ['tnpm', 'cnpm', 'npm'];
for (var i = 0; i < npms.length; i++) {
try {
which.sync(npms[i]);
console.log('use npm: ' + npms[i]);
return npms[i];
} catch (e) {
}
}
throw new Error('please install npm');
}
export default function (done) {
const npm = findNpm();
// 因為boilerplates/package.json 中沒有dva,所以在執行install之後,再安裝dva,why?
// 為什麼不乾脆將dva寫在boilerplates/package.json裡呢,
// 因為這樣就可以保證每次安裝的dva都是最新版本啊 騷年。
runCmd(which.sync(npm), ['install'], function () {
runCmd(which.sync(npm), ['install', 'dva', '--save'], function () {
console.log(npm + ' install end');
done();
});
});
};
複製程式碼
在上面我們已經分析了dva new的相關命令,其實dva還支援generate操作(文件上是這麼說的),不過。。。我們來看下generate檔案
//...
console.log(chalk.red('? dva generate is disabled since we don\'t have enough time currently.'));
process.exit(0);
require('../lib/generate')(program, {
cwd: process.cwd(),
});
複製程式碼
四個字,暫未開放... 這個文件更新有點落後啊。dva-generate中又依賴了dva-ast 其中主要使用了 facebook/jscodeshift
這個庫,作用是解析js,將js內容解析成 AST 語法樹,然後提供一些便利的操作介面,方便我們對各個節點進行更改,比如更改所有的屬性名之類的。感興趣的同學可以自己去研究一下,就送到這裡啦~ dva-cli就分析到這裡了,接下來我會去分析dva。