UNPKG

7.02 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 ? opts.fn(this) : opts.inverse(this);
16});
17
18Handlebars.registerHelper("unless_eq", function(a, b, opts) {
19 return a === b ? opts.inverse(this) : opts.fn(this);
20});
21
22/**
23 * Generate a template given a `src` and `dest`.
24 *
25 * @param {String} name 项目目录名称
26 * @param {String} src 下载模版的临时路径
27 * @param {String} dest 项目目录路径
28 * @param {Function} done 回调函数 用于在项目生成之后在终端输出一些提示信息
29 */
30
31module.exports = function generate(name, src, dest, done) {
32 const opts = getOptions(name, src); //读取一些配置信息 如git
33 const metalsmith = Metalsmith(path.join(src)); //读取临时文件内下载的项目模版信息
34 const data = Object.assign(metalsmith.metadata(), {
35 destDirName: name,
36 inPlace: dest === process.cwd(),
37 noEscape: true
38 });
39 opts.helpers &&
40 Object.keys(opts.helpers).map(key => {
41 Handlebars.registerHelper(key, opts.helpers[key]);
42 });
43
44 const helpers = { chalk, logger };
45
46 if (opts.metalsmith && typeof opts.metalsmith.before === "function") {
47 opts.metalsmith.before(metalsmith, opts, helpers);
48 }
49
50 metalsmith
51 .use(askQuestions(opts.prompts))
52 .use(filterFiles(opts.filters))
53 .use(renderTemplateFiles(opts.skipInterpolation));
54
55 if (typeof opts.metalsmith === "function") {
56 opts.metalsmith(metalsmith, opts, helpers);
57 } else if (opts.metalsmith && typeof opts.metalsmith.after === "function") {
58 opts.metalsmith.after(metalsmith, opts, helpers);
59 }
60
61 metalsmith
62 .clean(false)
63 .source(".") // start from template root instead of `./src` which is Metalsmith's default for `source`
64 .destination(dest)
65 .build((err, files) => {
66 done(err);
67 if (typeof opts.complete === "function") {
68 const helpers = { chalk, logger, files };
69 opts.complete(data, helpers);
70 } else {
71 logMessage(opts.completeMessage, data);
72 }
73 });
74
75 return data;
76};
77
78/**
79 * Create a middleware for asking questions.
80 *
81 * @param {Object} prompts
82 * @return {Function}
83 */
84
85function askQuestions(prompts) {
86 return (files, metalsmith, done) => {
87 ask(prompts, metalsmith.metadata(), done);
88 };
89}
90
91/**
92 * Create a middleware for filtering files.
93 *
94 * @param {Object} filters
95 * @return {Function}
96 */
97
98function filterFiles(filters) {
99 return (files, metalsmith, done) => {
100 filter(files, filters, metalsmith.metadata(), done);
101 };
102}
103
104/**
105 * Template in place plugin.
106 *
107 * @param {Object} files
108 * @param {Metalsmith} metalsmith
109 * @param {Function} done
110 */
111
112function renderTemplateFiles(skipInterpolation) {
113 skipInterpolation =
114 typeof skipInterpolation === "string"
115 ? [skipInterpolation]
116 : skipInterpolation;
117 return (files, metalsmith, done) => {
118 const keys = Object.keys(files);
119 const metalsmithMetadata = metalsmith.metadata();
120 async.each(
121 keys,
122 (file, next) => {
123 // skipping files with skipInterpolation option
124 if (
125 skipInterpolation &&
126 multimatch([file], skipInterpolation, { dot: true }).length
127 ) {
128 return next();
129 }
130 const str = files[file].contents.toString();
131 // do not attempt to render files that do not have mustaches
132 if (!/{{([^{}]+)}}/g.test(str) && !(/package.json|vender-list.js/g.test(file))) {
133 return next();
134 }
135 render(str, metalsmithMetadata, (err, res) => {
136 if (err) {
137 err.message = `[${file}] ${err.message}`;
138 return next(err);
139 }
140 if (file === "package.json") {
141 const packageFile = generatePackageJson(res);
142 files[file].contents = new Buffer(packageFile);
143 return next();
144 }
145 //如果是dll项目的话
146 if (file === "vender-list.js" && global["talentui_info"]["dllList"]) {
147 const dllListFile = renderDllListFile(res);
148 files[file].contents = new Buffer(dllListFile);
149 return next();
150 }
151 files[file].contents = new Buffer(res);
152 next();
153 });
154 },
155 done
156 );
157 };
158}
159function renderDllListFile(res){
160
161 const str = global["talentui_info"]["dllList"].map(item => `"${item.split('@')[0]}"`).toString()
162 return `module.exports = [${str}];`
163}
164function generatePackageJson(res) {
165 let contentJson = JSON.parse(res);
166 let {
167 name,
168 version,
169 author,
170 repository_type,
171 repository_url,
172 bugs_url,
173 homePage,
174 description
175 } = global["talentui_info"];
176 contentJson.name = name;
177 contentJson.version = version;
178 contentJson.author = author;
179 contentJson.repository = {
180 type: repository_type,
181 url: repository_url
182 };
183 contentJson.bugs = {
184 url: bugs_url
185 };
186 contentJson.homepage = homePage;
187 contentJson.description = description;
188 //如果是打包dll的项目
189 if (global["talentui_info"]["dllList"]) {
190 let dllList = global["talentui_info"]["dllList"];
191 dllList.map(item => {
192 let name = item.split('@')[0];
193 let version = item.split('@')[1];
194 contentJson.devDependencies[name] = version;
195 contentJson.peerDependencies[name] = version;
196 })
197 }
198 return JSON.stringify(contentJson, null, 4);
199}
200/**
201 * Display template complete message.
202 *
203 * @param {String} message
204 * @param {Object} data
205 */
206
207function logMessage(message, data) {
208 if (!message) return;
209 render(message, data, (err, res) => {
210 if (err) {
211 console.error(
212 "\n Error when rendering template complete message: " +
213 err.message.trim()
214 );
215 } else {
216 console.log(
217 "\n" +
218 res
219 .split(/\r?\n/g)
220 .map(line => " " + line)
221 .join("\n")
222 );
223 }
224 });
225}