UNPKG

5.6 kBJavaScriptView Raw
1'use strict';
2const format = require('date-fns/format'),
3 _ = require('lodash'),
4 chalk = require('chalk'),
5 fs = require('fs-extra'),
6 path = require('path'),
7 amphoraFs = require('amphora-fs'),
8 configFile = require('./config-file-helpers');
9
10/**
11 * determine how long a compilation task took
12 * @param {number} t2 unix timestamp
13 * @param {number} t1 unix timestamp
14 * @return {string}
15 */
16function time(t2, t1) {
17 const diff = t2 - t1;
18
19 if (diff > 1000 * 60) {
20 // more than a minute (60,000ms)
21 return format(new Date(diff), 'm[m] s.SS[s]');
22 } else {
23 // less than a minute
24 return format(new Date(diff), 's.SS[s]');
25 }
26}
27
28/**
29 * set up a watcher that logs when a file has changed
30 * used by all scripts
31 * @param {string} e event type
32 * @param {string} filepath
33 */
34function watcher(e, filepath) {
35 if (!_.includes(filepath, '.DS_Store')) {
36 console.log(chalk.green('✓ ') + chalk.grey(filepath.replace(process.cwd(), '')));
37 }
38}
39
40/**
41 * determine what bucket of the alphabet the first letter of a name falls into
42 * note: six buckets is the sweet spot for filesize / file bundling on http 1.1 and http2/spdy
43 * note: non-alphabetic stuff goes in the last bucket, because statistically it will be the smallest
44 * @param {string} name
45 * @return {string} bucket, e.g. 'a-d', 'e-h', 'i-l', 'm-p', 'q-t', 'u-z'
46 */
47function bucket(name) {
48 if (name.match(/^[a-d]/i)) {
49 return 'a-d';
50 } else if (name.match(/^[e-h]/i)) {
51 return 'e-h';
52 } else if (name.match(/^[i-l]/i)) {
53 return 'i-l';
54 } else if (name.match(/^[m-p]/i)) {
55 return 'm-p';
56 } else if (name.match(/^[q-t]/i)) {
57 return 'q-t';
58 } else {
59 return 'u-z';
60 }
61}
62
63/**
64 * find the matcher for a bucket
65 * @param {string} name e.g. _templates-a-d
66 * @return {string}
67 */
68function unbucket(name) {
69 return _.find(['a-d', 'e-h', 'i-l', 'm-p', 'q-t', 'u-z'], (matcher) => _.includes(name, matcher));
70}
71
72/**
73 * generate bundles for gulp-group-concat, based on the buckets above
74 * @param {string} prefix without ending hyphen
75 * @param {string} ext without dot
76 * @return {object}
77 */
78function generateBundles(prefix, ext) {
79 return _.reduce(['a-d', 'e-h', 'i-l', 'm-p', 'q-t', 'u-z'], (bundles, matcher) => {
80 bundles[`${prefix}-${matcher}.${ext}`] = `**/[${matcher}]*.${ext}`;
81 return bundles;
82 }, {});
83}
84
85
86/**
87 * determine if a file has changed based on ctimes
88 * @param {Stream} stream
89 * @param {Vinyl} sourceFile
90 * @param {string} targetPath
91 * @return {Promise}
92 */
93/* istanbul ignore next */
94function hasChanged(stream, sourceFile, targetPath) {
95 return fs.stat(targetPath).then((targetStat) => {
96 if (sourceFile.stat && sourceFile.stat.ctime > targetStat.ctime) {
97 stream.push(sourceFile);
98 }
99 }).catch(() => {
100 // targetPath doesn't exist! gotta compile the source
101 stream.push(sourceFile);
102 });
103}
104
105/**
106 * transform the filepath if we're minifying the files and putting them into bundles
107 * @param {string} prefix e.g. '_templates', '_models'
108 * @param {string} destPath path to the destination directory
109 * @param {boolean} shouldMinify
110 * @return {Functio }
111 */
112function transformPath(prefix, destPath, shouldMinify) {
113 return (filepath) => {
114 if (shouldMinify) {
115 // bundle into one of six bundle files based on the first letter of the component/template
116 const name = _.head(path.basename(filepath).toLowerCase().split('.'));
117
118 return path.join(destPath, `${prefix}-${bucket(name)}.js`);
119 } else {
120 // no changes, use the path from rename()
121 return filepath;
122 }
123 };
124}
125
126/**
127 * Find the additional plugins to use in PostCSS
128 * compilation. Either accept the values from command
129 * arguments and require them in or use the config file
130 *
131 * @param {Object} argv
132 * @returns {Array}
133 */
134function determinePostCSSPlugins(argv) {
135 const configPlugins = configFile.getConfigValue('plugins');
136
137 if (configPlugins) {
138 if (!Array.isArray(configPlugins)) {
139 console.error(`${chalk.red('Error: Plugins supplied in config file is not an array')}`);
140 }
141
142 // Return the array of plugins defined in the config file
143 return configPlugins;
144 } else {
145 return _.map(argv.plugins, (pluginName) => {
146 const plugin = amphoraFs.tryRequire(pluginName);
147
148 // If no plugin, log it can't be found
149 if (!plugin) throw new Error(`${chalk.red(`Error: Cannot find plugin "${pluginName}"`)}`);
150
151 try { // if plugin, invoke it
152 return plugin();
153 } catch (e) { // or log when it fails
154 console.error(`${chalk.red(`Error: Cannot init plugin "${pluginName}"`)}\n${chalk.grey(e.message)}`);
155 }
156 });
157 }
158}
159
160/**
161 * Given an key, grab the value from the config file
162 * or pull from the browserlist that's supported
163 *
164 * @param {String} key
165 * @returns {Object|String}
166 */
167function getConfigFileOrBrowsersList(key) {
168 const configFileValue = configFile.getConfigValue(key);
169
170 return configFileValue ? configFileValue : module.exports.browserslist;
171}
172
173module.exports.time = time;
174module.exports.debouncedWatcher = _.debounce(watcher, 200);
175module.exports.bucket = bucket;
176module.exports.unbucket = unbucket;
177module.exports.generateBundles = generateBundles;
178module.exports.hasChanged = hasChanged;
179module.exports.transformPath = transformPath;
180module.exports.browserslist = { browsers: ['> 3%', 'not and_uc > 0'] }; // used by styles, and vueify, and babel/preset-env
181module.exports.determinePostCSSPlugins = determinePostCSSPlugins;
182module.exports.getConfigFileOrBrowsersList = getConfigFileOrBrowsersList;
183
184// for testing
185module.exports.watcher = watcher;