1 | 'use strict';
|
2 |
|
3 | var exit = process.exit.bind(process);
|
4 |
|
5 | var fs = require('fs'),
|
6 | path = require('path'),
|
7 | util = require('gulp-util'),
|
8 | debug = require('gulp-debug'),
|
9 | watch = require('gulp-watch'),
|
10 | filter = require('gulp-filter'),
|
11 | sequence = require('gulp-sequence');
|
12 |
|
13 | var getData = require('./utils/data');
|
14 |
|
15 | var mainBowerFiles = require('main-bower-files');
|
16 |
|
17 | var errorHandler = function(e) {
|
18 | util.log(util.colors.red(e.message || e.toString()));
|
19 | this.emit('end');
|
20 | };
|
21 |
|
22 | function expandVariables(obj, data) {
|
23 | if (typeof obj === 'string') {
|
24 | return obj.replace(/\$\{(\w+)\}/g, function(match, key) {
|
25 | return data[key] || match;
|
26 | });
|
27 | }
|
28 |
|
29 | if (typeof obj === 'object') {
|
30 | for (var key in obj) {
|
31 | obj[key] = expandVariables(obj[key], data);
|
32 | }
|
33 | }
|
34 |
|
35 | return obj;
|
36 | }
|
37 |
|
38 | function to_array(obj) {
|
39 | return Array.isArray(obj) ? obj : (obj ? [obj] : []);
|
40 | }
|
41 |
|
42 | function is_file(fullpath) {
|
43 | return fs.existsSync(fullpath) && fs.statSync(fullpath).isFile();
|
44 | }
|
45 |
|
46 | function is_dir(fullpath) {
|
47 | return fs.existsSync(fullpath) && fs.statSync(fullpath).isDirectory();
|
48 | }
|
49 |
|
50 | function hook(chain, set) {
|
51 | if (set) {
|
52 | (Array.isArray(set) ? set : [set])
|
53 | .forEach(function(task) {
|
54 | chain = task(chain, errorHandler)
|
55 | .on('error', errorHandler);
|
56 | });
|
57 | }
|
58 |
|
59 | return chain;
|
60 | }
|
61 |
|
62 | module.exports = function(options) {
|
63 | options = options || {};
|
64 |
|
65 | var gulp = options.gulp;
|
66 |
|
67 | if (!gulp) {
|
68 | util.log(util.colors.red('Missing the main `gulp` instance to work'));
|
69 | exit(1);
|
70 | }
|
71 |
|
72 | var base = typeof util.env.base === 'string' ? util.env.base : 'default',
|
73 | base_dir = path.join(process.cwd(), 'src', options.cwd || '', (util.env.base || options.base) !== false ? base : '');
|
74 |
|
75 | if (!is_dir(base_dir)) {
|
76 | util.log(util.colors.red('The specified base directory `' + base + '` is missing'));
|
77 | exit(1);
|
78 | }
|
79 |
|
80 | var isBuild = options.build || util.env.build,
|
81 | sources = options.files || {},
|
82 | params = options.params || {},
|
83 | iconOptions = options.icons || {},
|
84 | autoprefixerOptions = options.autoprefixer;
|
85 |
|
86 | params.base = base;
|
87 | options.build = !!isBuild;
|
88 |
|
89 | if (options.bundle === true) {
|
90 | options.bundle = {
|
91 | compact: true
|
92 | };
|
93 | }
|
94 |
|
95 | if (typeof options.bundle === 'object') {
|
96 | options.bundle.compact = options.bundle.compact !== false;
|
97 | }
|
98 |
|
99 | var _errorHandler = errorHandler;
|
100 |
|
101 | errorHandler = function(e) {
|
102 | _errorHandler.call(this, e);
|
103 |
|
104 | if (isBuild) {
|
105 | exit(1);
|
106 | }
|
107 | };
|
108 |
|
109 | sources.filter = sources.filter || [];
|
110 | sources.vendor = sources.vendor || '';
|
111 | sources.bower = sources.bower || '';
|
112 | sources.env = sources.env || '';
|
113 | sources.dest = sources.dest || '';
|
114 | sources.views = sources.views || {};
|
115 | sources.fonts = sources.fonts || {};
|
116 | sources.styles = sources.styles || {};
|
117 | sources.images = sources.images || {};
|
118 | sources.sprites = sources.sprites || {};
|
119 | sources.scripts = sources.scripts || {};
|
120 | sources.icons = sources.icons || {};
|
121 |
|
122 | var env_file = path.join(base_dir, sources.env || 'env.yml');
|
123 |
|
124 | options.paths = expandVariables({
|
125 | fonts: {
|
126 | on: path.join(base_dir, sources.fonts.src || 'fonts', '**/*.{ttf,otf,eot,woff,woff2,svg}'),
|
127 | cwd: path.join(base_dir, sources.fonts.src || 'fonts'),
|
128 | dest: sources.fonts.dest || 'fonts',
|
129 | glob: '**/*.{ttf,otf,eot,woff,woff2,svg}'
|
130 | },
|
131 | icons: {
|
132 | on: path.join(base_dir, sources.icons.src || 'icons', '**/*.svg'),
|
133 | cwd: path.join(base_dir, sources.icons.src || 'icons'),
|
134 | dest: sources.fonts.dest || 'fonts',
|
135 | glob: '**/*.svg',
|
136 | iconFont: {
|
137 | fontName: iconOptions.fontName || 'icons',
|
138 | svg: true,
|
139 | appendUnicode: false,
|
140 | fontHeight: 1024,
|
141 | startUnicode: iconOptions.startUnicode || 0xF000,
|
142 | normalize: iconOptions.normalize || true
|
143 | },
|
144 | clean: path.join(base_dir, sources.styles.src || 'styles', '_generated'),
|
145 | defaultTemplateDest: iconOptions.defaultOutputDir || path.join(base_dir, sources.styles.src || 'styles', '_generated'),
|
146 | defaultFontPath: iconOptions.defaultFontPath || sources.fonts.dest || 'fonts',
|
147 | className: iconOptions.className || 'icon',
|
148 | templates: iconOptions.templates || {
|
149 | less: {
|
150 | template: path.join(__dirname, 'stubs/icons/template.less'),
|
151 | outputName: 'icons.less'
|
152 | },
|
153 | htmlCatalog: {
|
154 | template: path.join(__dirname, 'stubs/icons/templateCatalog.html'),
|
155 | outputName: 'iconCatalog.html',
|
156 | fontPath: path.join(process.cwd(), sources.dest || 'generated', sources.fonts.dest || 'fonts')
|
157 | }
|
158 | }
|
159 | },
|
160 | images: {
|
161 | on: path.join(base_dir, sources.images.src || 'images', '**/*.{jpg,jpeg,png,svg}'),
|
162 | cwd: path.join(base_dir, sources.images.src || 'images'),
|
163 | dest: sources.images.dest || 'img',
|
164 | glob: '**/*.{jpg,jpeg,png,svg}'
|
165 | },
|
166 | sprites: {
|
167 | on: path.join(base_dir, sources.sprites.src || 'sprites', '**/*.png'),
|
168 | cwd: path.join(base_dir, sources.sprites.src || 'sprites'),
|
169 | dest: sources.sprites.dest || 'img',
|
170 | glob: '**/*.png'
|
171 | },
|
172 | styles: {
|
173 | on: [
|
174 | path.join(path.dirname(env_file), '_site/**/*.{variables,overrides}'),
|
175 | path.join(base_dir, sources.styles.src || 'styles', '**/*.less'),
|
176 | env_file
|
177 | ],
|
178 | autoprefixer: autoprefixerOptions || { browsers: [ '> 1%', 'last 2 versions' ] },
|
179 | cwd: path.join(base_dir, sources.styles.src || 'styles'),
|
180 | dest: sources.styles.dest || 'css',
|
181 | glob: '**/' + (options.bundle ? 'index' : '*') + '.less',
|
182 | filter: sources.styles.filter || [],
|
183 | watch: sources.styles.watch || []
|
184 | },
|
185 | scripts: {
|
186 | on: path.join(base_dir, sources.scripts.src || 'scripts', '**/*.{coffee,litcoffee}'),
|
187 | cwd: path.join(base_dir, sources.scripts.src || 'scripts'),
|
188 | dest: sources.scripts.dest || 'js',
|
189 | glob: '**/*.{coffee,litcoffee}',
|
190 | filter: sources.scripts.filter || [],
|
191 | watch: sources.scripts.watch || []
|
192 | },
|
193 | views: {
|
194 | on: [
|
195 | path.join(base_dir, sources.views.src || 'views', '**/*.*'),
|
196 | path.join(path.dirname(env_file), 'data/**/*.yml'),
|
197 | path.join(process.cwd(), 'data/**/*.yml')
|
198 | ],
|
199 | ext: sources.views.ext || '.html',
|
200 | cwd: path.join(base_dir, sources.views.src || 'views'),
|
201 | dest: sources.views.dest || '',
|
202 | glob: '**/*.jade',
|
203 | filter: sources.views.filter || [],
|
204 | watch: sources.views.watch || []
|
205 | },
|
206 | data: {
|
207 | src: [
|
208 | path.join(path.dirname(env_file), 'data'),
|
209 | path.join(process.cwd(), 'data')
|
210 | ],
|
211 | glob: '**/*.yml'
|
212 | },
|
213 | vendor: sources.vendor || 'vendor',
|
214 | bower: path.join(process.cwd(), sources.bower || ''),
|
215 | dest: path.join(process.cwd(), sources.dest || 'generated'),
|
216 | env: env_file
|
217 | }, params);
|
218 |
|
219 | options.env = util.env;
|
220 |
|
221 | var copyTask = require('./tasks/copy');
|
222 |
|
223 | var rainbow = {
|
224 | clean: require('./tasks/clean')(options),
|
225 | fonts: copyTask(options, 'fonts'),
|
226 | images: copyTask(options, 'images'),
|
227 | icons: require('./tasks/icons')(options),
|
228 | sprites: require('./tasks/sprites')(options),
|
229 | scripts: require('./tasks/scripts')(options),
|
230 | install: require('./tasks/install')(options),
|
231 | server: require('./tasks/server')(options),
|
232 | styles: require('./tasks/styles')(options),
|
233 | vendor: require('./tasks/vendor')(options),
|
234 | views: require('./tasks/views')(options)
|
235 | };
|
236 |
|
237 | var main = [],
|
238 | tasks = [];
|
239 |
|
240 | if (isBuild || options.env.clean === true) {
|
241 | tasks.push('clean');
|
242 | }
|
243 |
|
244 | if (is_file(path.join(options.paths.bower, 'bower.json'))) {
|
245 | Array.prototype.push.call(tasks, 'install', 'vendor');
|
246 | }
|
247 |
|
248 | ['fonts', 'images', 'icons', 'sprites', 'styles', 'scripts', 'views']
|
249 | .forEach(function(task) {
|
250 | if (is_dir(options.paths[task].cwd)) {
|
251 | tasks.push(task);
|
252 | }
|
253 | });
|
254 |
|
255 | if (options.server === true && !isBuild) {
|
256 | tasks.push('server');
|
257 | }
|
258 |
|
259 | tasks
|
260 | .forEach(function(task) {
|
261 | var files = options.paths[task],
|
262 | callback = rainbow[task];
|
263 |
|
264 | if (to_array(sources.skip).indexOf(task) > -1) {
|
265 | return;
|
266 | }
|
267 |
|
268 | var prefix = options.prefix ? options.prefix + ':' : 'rainbow:';
|
269 |
|
270 | if (callback) {
|
271 | main.push(prefix + task);
|
272 |
|
273 | if (typeof callback === 'function') {
|
274 | gulp.task(prefix + task, callback);
|
275 | } else {
|
276 | gulp.task(prefix + task, function() {
|
277 | var chain = gulp.src(callback.src);
|
278 |
|
279 | chain = chain
|
280 | .pipe(filter(['**', '!**/_*', '!**/_*/**']
|
281 | .concat(to_array(sources.filter))
|
282 | .concat(to_array(files.filter))));
|
283 |
|
284 | if (typeof callback.check === 'function' && (options.env.check !== false)) {
|
285 | chain = hook(chain, callback.check);
|
286 | }
|
287 |
|
288 | chain = hook(chain, sources[task].before);
|
289 |
|
290 | if (typeof callback.pipe === 'function') {
|
291 | chain = hook(chain, callback.pipe);
|
292 | }
|
293 |
|
294 | return hook(chain, sources[task].after)
|
295 | .pipe(gulp.dest(callback.dest))
|
296 | .pipe(debug({ title: prefix + task }));
|
297 | });
|
298 | }
|
299 |
|
300 | if (files && files.on && !isBuild) {
|
301 | watch(to_array(files.on).concat(to_array(files.watch)), function() {
|
302 | gulp.start(prefix + task);
|
303 | });
|
304 | }
|
305 | } else {
|
306 | util.log(util.colors.red('Unknown rainbow-task `' + task + '`'));
|
307 | exit(1);
|
308 | }
|
309 | });
|
310 |
|
311 | if (options.before) {
|
312 | Array.prototype.unshift.apply(main, to_array(options.before));
|
313 | }
|
314 |
|
315 | if (options.after) {
|
316 | Array.prototype.push.apply(main, to_array(options.after));
|
317 | }
|
318 |
|
319 | if (options.prefix) {
|
320 | return sequence.use(gulp).apply(null, main);
|
321 | }
|
322 |
|
323 | gulp.task('rainbow', sequence.use(gulp).apply(null, main));
|
324 | };
|
325 |
|
326 | module.exports.bowerFiles = function(from) {
|
327 | var bower_dir = path.resolve(from, 'bower_components');
|
328 |
|
329 | return mainBowerFiles({
|
330 | paths: {
|
331 | bowerDirectory: path.relative(process.cwd(), bower_dir),
|
332 | bowerJson: path.join(from, 'bower.json')
|
333 | }
|
334 | });
|
335 | };
|
336 |
|
337 | module.exports.server = function(root) {
|
338 | return require('./tasks/server')({
|
339 | paths: { dest: root },
|
340 | env: util.env
|
341 | });
|
342 | };
|
343 |
|
344 | module.exports.data = getData;
|