UNPKG

jiuye-cli

Version:

A simple CLI for scaffolding Jiuye projects.

226 lines (201 loc) 7.03 kB
const chalk = require('chalk') // 用于高亮终端打印出来的信息 const Metalsmith = require('metalsmith') // 静态网站生成器 const Handlebars = require('handlebars') // 知名的模板引擎 const async = require('async') // 非常强大的异步处理工具 const render = require('consolidate').handlebars.render // 支持各种模板引擎的渲染 const path = require('path') // const multimatch = require('multimatch') // 可以支持多个条件的匹配 const getOptions = require('./options') // 自定义工具-用于获取模板配置 const ask = require('./ask') // 自定义工具-用于询问开发者 // const filter = require('./filter') // 自定义工具-用于文件过滤 const logger = require('./logger') // 自定义工具-用于日志打印 // register handlebars helper 注册handlebars的helper // Handlebars.registerHelper('if_eq', function (a, b, opts) { // return a === b // ? opts.fn(this) // : opts.inverse(this) // }) // Handlebars.registerHelper('unless_eq', function (a, b, opts) { // return a === b // ? opts.inverse(this) // : opts.fn(this) // }) // 来自@zhuangwei的测试 Handlebars.registerHelper('if_zw_test', function (name, opts) { return name === 'zw' ? opts.fn(this) : opts.inverse(this) }) /** * Generate a template given a `src` and `dest`. * * @param {String} name * @param {String} src * @param {String} dest * @param {Function} done */ module.exports = function generate (name, src, dest, done) { // 获取配置meta.json数据 const opts = getOptions(name, src) // console.log('options:', opts) // 初始化Metalsmith对象 为: // { // plugins: [], // ignores: [], // _directory: 'C:\\Users\\zhuangwei\\.vue-templates\\tempalte-test\\template', // _metadata: {}, // _source: 'src', // _destination: 'build', // _concurrency: Infinity, // _clean: true, // _frontmatter: true // } const metalsmith = Metalsmith(path.join(src, 'template')) // 添加一些变量至metalsmith中,并获取metalsmith中全部变量 const data = Object.assign(metalsmith.metadata(), { destDirName: name, inPlace: dest === process.cwd(), noEscape: true }) // 注册配置对象中的helper // opts.helpers: { // if_or: [Function: if_or], // template_version: [Function: template_version] // } // opts.helpers && Object.keys(opts.helpers).map(key => { // Handlebars.registerHelper(key, opts.helpers[key]) // opts.helpers[key] 为函数 // }) // const helpers = { // chalk, // logger // } // 配置对象是否有before函数,是则执行 // if (opts.metalsmith && typeof opts.metalsmith.before === 'function') { // opts.metalsmith.before(metalsmith, opts, helpers) // } // 重点 metalsmith .use(askQuestions(opts.prompts)) // 询问问题 // .use(filterFiles(opts.filters)) // 过滤文件 .use(renderTemplateFiles(opts.skipInterpolation)) // 渲染模板文件 // 配置对象是否有after函数,是则执行 // if (typeof opts.metalsmith === 'function') { // opts.metalsmith(metalsmith, opts, helpers) // } else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') { // opts.metalsmith.after(metalsmith, opts, helpers) // } metalsmith .clean(false) // clean 设置是否在写入之前删除目标目录,或获取当前设置。默认为true .source('.') // 设置相path对于源目录的相对,或者如果未path提供则获取完整的目录。源目录默认为./src .destination(dest) .build((err, files) => { done(err) // 配置对象有complete函数则执行 if (typeof opts.complete === 'function') { const helpers = { chalk, logger, files } opts.complete(data, helpers) } else { // 配置对象有completeMessage,执行logMessage函数 logMessage(opts.completeMessage, data) } }) return data } /** * Create a middleware for asking questions. * * @param {Object} prompts * @return {Function} */ function askQuestions (prompts) { return (files, metalsmith, done) => { // console.log(1313213213213, prompts, metalsmith.metadata()) ask(prompts, metalsmith.metadata(), done) } } /** * Create a middleware for filtering files. * * @param {Object} filters * @return {Function} */ // function filterFiles (filters) { // return (files, metalsmith, done) => { // // console.log('filters---metalsmith:', metalsmith.metadata(), filters) // filter(files, filters, metalsmith.metadata(), done) // } // } /** * Template in place plugin. * * @param {Object} files * @param {Metalsmith} metalsmith * @param {Function} done */ // 渲染模板文件 function renderTemplateFiles (skipInterpolation) { // console.log('skipInterpolation:::::', skipInterpolation, typeof skipInterpolation) // skipInterpolation = typeof skipInterpolation === 'string' ? [skipInterpolation] : skipInterpolation // 保证skipInterpolation是一个数组 return (files, metalsmith, done) => { const keys = Object.keys(files) // 获取files的所有key const metalsmithMetadata = metalsmith.metadata() // 获取metalsmith的所有变量 async.each( keys, (file, next) => { // 异步处理所有files // skipping files with skipInterpolation option // 跳过符合skipInterpolation的要求的file // if (skipInterpolation && multimatch([file], skipInterpolation, { // dot: true // }).length) { // return next() // } // 获取文件的文本内容 const str = files[file].contents.toString() // do not attempt to render files that do not have mustaches // 跳过不符合handlebars语法的file if (!/{{([^{}]+)}}/g.test(str)) { return next() } // 渲染文件 render(str, metalsmithMetadata, (err, res) => { if (err) { err.message = `[${file}] ${err.message}` return next(err) } files[file].contents = new Buffer(res) next() }) }, done ) } } /** * Display template complete message. * * @param {String} message * @param {Object} data */ function logMessage (message, data) { if (!message) return // 没有message直接退出函数 render(message, data, (err, res) => { if (err) { console.error( '\n Error when rendering template complete message: ' + err.message.trim() ) } else { // 渲染成功打印最终渲染的结果 console.log( '\n' + res .split(/\r?\n/g) .map(line => ' ' + line) .join('\n') ) } }) }