UNPKG

4.28 kBJavaScriptView Raw
1const chalk = require('chalk') //提示高亮
2const Metalsmith = require('metalsmith') //写入模板
3const Handlebars = require('handlebars') //模板库
4const async = require('async')
5const render = require('consolidate').handlebars.render //模板渲染
6const path = require('path')
7const multimatch = require('multimatch')
8const getOptions = require('./options')
9const ask = require('./ask')
10const filter = require('./filter')
11const logger = require('./logger')
12
13// register handlebars helper
14Handlebars.registerHelper('if_eq', function (a, b, opts) {
15 return a === b
16 ? opts.fn(this)
17 : opts.inverse(this)
18})
19
20Handlebars.registerHelper('unless_eq', function (a, b, opts) {
21 return a === b
22 ? opts.inverse(this)
23 : opts.fn(this)
24})
25
26/**
27 * Generate a template given a `src` and `dest`.
28 *
29 * @param {String} name
30 * @param {String} src
31 * @param {String} dest
32 * @param {Function} done
33 */
34
35module.exports = function generate (name, src, dest, done) {
36 const opts = getOptions(name, src)//获取配置
37 const metalsmith = Metalsmith(src)
38 const data = Object.assign(metalsmith.metadata(), {
39 destDirName: name,
40 inPlace: dest === process.cwd(),
41 noEscape: true
42 })
43 opts.helpers && Object.keys(opts.helpers).map(key => {
44 Handlebars.registerHelper(key, opts.helpers[key])
45 })
46 const helpers = { chalk, logger }
47 if (opts.metalsmith && typeof opts.metalsmith.before === 'function') {
48 opts.metalsmith.before(metalsmith, opts, helpers)
49 }
50 metalsmith.use(askQuestions(opts.prompts))
51 .use(filterFiles(opts.filters))
52 .use(renderTemplateFiles(opts.skipInterpolation))
53 console.log('bbbbbbbbbb');
54 if (typeof opts.metalsmith === 'function') {
55 opts.metalsmith(metalsmith, opts, helpers)
56 } else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {
57 opts.metalsmith.after(metalsmith, opts, helpers)
58 }
59
60 metalsmith.clean(false)
61 .source('.') // start from template root instead of `./src` which is Metalsmith's default for `source`
62 .destination(dest)
63 .build((err, files) => {
64 done(err)
65 if (typeof opts.complete === 'function') {
66 const helpers = { chalk, logger, files }
67 opts.complete(data, helpers)
68 } else {
69 logMessage(opts.completeMessage, data)
70 }
71 })
72
73 return data
74}
75
76/**
77 * Create a middleware for asking questions.
78 *
79 * @param {Object} prompts
80 * @return {Function}
81 */
82
83function askQuestions (prompts) {
84 return (files, metalsmith, done) => {
85 ask(prompts, metalsmith.metadata(), done)
86 }
87}
88
89/**
90 * Create a middleware for filtering files.
91 *
92 * @param {Object} filters
93 * @return {Function}
94 */
95
96function filterFiles (filters) {
97 return (files, metalsmith, done) => {
98 filter(files, filters, metalsmith.metadata(), done)
99 }
100}
101
102/**
103 * Template in place plugin.
104 *
105 * @param {Object} files
106 * @param {Metalsmith} metalsmith
107 * @param {Function} done
108 */
109
110function renderTemplateFiles (skipInterpolation) {
111 skipInterpolation = typeof skipInterpolation === 'string'
112 ? [skipInterpolation]
113 : skipInterpolation
114 return (files, metalsmith, done) => {
115 const keys = Object.keys(files)
116 const metalsmithMetadata = metalsmith.metadata()
117 async.each(keys, (file, next) => {
118 // skipping files with skipInterpolation option
119 if (skipInterpolation && multimatch([file], skipInterpolation, { dot: true }).length) {
120 return next()
121 }
122 const str = files[file].contents.toString()
123 // do not attempt to render files that do not have mustaches
124 if (!/{{([^{}]+)}}/g.test(str)) {
125 return next()
126 }
127 render(str, metalsmithMetadata, (err, res) => {
128 if (err) {
129 err.message = `[${file}] ${err.message}`
130 return next(err)
131 }
132 files[file].contents = new Buffer(res)
133 next()
134 })
135 }, done)
136 }
137}
138
139/**
140 * Display template complete message.
141 *
142 * @param {String} message
143 * @param {Object} data
144 */
145
146function logMessage (message, data) {
147 if (!message) return
148 render(message, data, (err, res) => {
149 if (err) {
150 console.error('\n Error when rendering template complete message: ' + err.message.trim())
151 } else {
152 console.log('\n' + res.split(/\r?\n/g).map(line => ' ' + line).join('\n'))
153 }
154 })
155}