UNPKG

4.33 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 console.log('llll');
56 opts.metalsmith(metalsmith, opts, helpers)
57 } else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {
58 console.log('aaaaaaaaaaaaa');
59 opts.metalsmith.after(metalsmith, opts, helpers)
60 console.log('ddddddddddd');
61 }
62
63 metalsmith.clean(false)
64 .source('.') // start from template root instead of `./src` which is Metalsmith's default for `source`
65 .destination(dest)
66 .build((err, files) => {
67 done(err)
68 console.log('ccccccccccc');
69 if (typeof opts.complete === 'function') {
70 const helpers = { chalk, logger, files }
71 opts.complete(data, helpers)
72 } else {
73 logMessage(opts.completeMessage, data)
74 }
75 })
76
77 return data
78}
79
80/**
81 * Create a middleware for asking questions.
82 *
83 * @param {Object} prompts
84 * @return {Function}
85 */
86
87function askQuestions (prompts) {
88 return (files, metalsmith, done) => {
89 ask(prompts, metalsmith.metadata(), done)
90 }
91}
92
93/**
94 * Create a middleware for filtering files.
95 *
96 * @param {Object} filters
97 * @return {Function}
98 */
99
100function filterFiles (filters) {
101 return (files, metalsmith, done) => {
102 filter(files, filters, metalsmith.metadata(), done)
103 }
104}
105
106/**
107 * Template in place plugin.
108 *
109 * @param {Object} files
110 * @param {Metalsmith} metalsmith
111 * @param {Function} done
112 */
113
114function renderTemplateFiles (skipInterpolation) {
115 skipInterpolation = typeof skipInterpolation === 'string'
116 ? [skipInterpolation]
117 : skipInterpolation
118 return (files, metalsmith, done) => {
119 const keys = Object.keys(files)
120 const metalsmithMetadata = metalsmith.metadata()
121 async.each(keys, (file, next) => {
122 // skipping files with skipInterpolation option
123 if (skipInterpolation && multimatch([file], skipInterpolation, { dot: true }).length) {
124 return next()
125 }
126 const str = files[file].contents.toString()
127 // do not attempt to render files that do not have mustaches
128 if (!/{{([^{}]+)}}/g.test(str)) {
129 return next()
130 }
131 render(str, metalsmithMetadata, (err, res) => {
132 if (err) {
133 err.message = `[${file}] ${err.message}`
134 return next(err)
135 }
136 files[file].contents = new Buffer(res)
137 next()
138 })
139 }, done)
140 }
141}
142
143/**
144 * Display template complete message.
145 *
146 * @param {String} message
147 * @param {Object} data
148 */
149
150function logMessage (message, data) {
151 if (!message) return
152 render(message, data, (err, res) => {
153 if (err) {
154 console.error('\n Error when rendering template complete message: ' + err.message.trim())
155 } else {
156 console.log('\n' + res.split(/\r?\n/g).map(line => ' ' + line).join('\n'))
157 }
158 })
159}