forked from pattern-lab/patternlab-node
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.js
More file actions
202 lines (190 loc) · 5.09 KB
/
utils.js
File metadata and controls
202 lines (190 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
'use strict';
const fs = require('fs-extra');
const spawn = require('execa');
const glob = require('glob');
const path = require('path');
const chalk = require('chalk');
const EventEmitter = require('events').EventEmitter;
const hasYarn = require('has-yarn');
/**
* @name log
* @desc tiny event-based logger
* @type {*}
*/
const log = Object.assign(
{
debug(msg) {
this.emit(
'patternlab.debug',
`${chalk.green('⊙ patternlab →')} ${chalk.dim(msg)}`
);
},
info(msg) {
this.emit('patternlab.info', `⊙ patternlab → ${chalk.dim(msg)}`);
},
error(msg) {
this.emit(
'patternlab.error',
`${chalk.red('⊙ patternlab →')} ${chalk.dim(msg)}`
);
},
},
EventEmitter.prototype
);
/**
* @func debug
* @desc Coloured debug log
* @param {*} msg - The variadic messages to log out.
* @return {void}
*/
const debug = log.debug.bind(log);
/**
* @func info
* @desc Coloured debug log
* @param {*} msg - The variadic messages to log out.
* @return {void}
*/
const info = log.info.bind(log);
/**
* @func error
* @desc Coloured error log
* @param {*} e - The variadic messages to log out.
* @return {void}
*/
const error = log.error.bind(log);
/**
* @func wrapAsync
* @desc Wraps an generator function to yield out promisified stuff
* @param {function} fn - Takes a generator function
*/
const wrapAsync = fn =>
new Promise((resolve, reject) => {
const generator = fn();
/* eslint-disable */
(function spwn(val) {
let res;
try {
res =
{}.toString.call(val) !== '[object Error]'
? generator.next(val)
: generator.throw(val);
} catch (err) {
return reject(err);
}
const v = res.value;
if (res.done) {
return resolve(v);
}
Promise.resolve(v)
.then(spwn)
.catch(spwn);
})();
/* eslint-enable */
});
/**
* @func glob
* @desc Promisified glob function
* @param {string} pattern - A glob pattern to match against
* @param {object} opts - A configuration object. See glob package for details
* @return {Promise<Error|Array>}
*/
const asyncGlob = (pattern, opts) =>
new Promise((resolve, reject) =>
glob(
pattern,
opts,
(err, matches) => (err !== null ? reject(err) : resolve(matches))
)
);
/**
* @func copyWithPattern
* @desc Copies multiple files asynchronously from one dir to another according to a glob pattern specified
* @param {string} cwd - The path to search for file(s) at
* @param {string} pattern - A glob pattern to match the file(s)
* @param {string} dest - The destination dir path
* @return {Promise}
*/
const copyWithPattern = (cwd, pattern, dest) =>
wrapAsync(function*() {
const files = yield asyncGlob(pattern, { cwd: cwd });
if (files.length === 0) {
debug('copy: Nothing to copy');
}
// Copy concurrently
const promises = files.map(file =>
fs.copy(path.join(cwd, file), path.join(dest, file))
);
return yield Promise.all(promises);
});
/**
* @func fetchPackage
* @desc Fetches and saves packages from npm into node_modules and adds a reference in the package.json under dependencies
* @param {string} packageName - The package name
* @param {string} [url] - A URL which will be used to fetch the package from
*/
const fetchPackage = (packageName, url) =>
wrapAsync(function*() {
const useYarn = hasYarn();
const pm = useYarn ? 'yarn' : 'npm';
const installCmd = useYarn ? 'add' : 'install';
try {
if (packageName || url) {
const cmd = yield spawn(pm, [installCmd, url || packageName]);
error(cmd.stderr);
}
} catch (err) {
error(
`fetchPackage: Fetching required dependencies from ${pm} failed for ${packageName} with ${err}`
);
throw err; // Rethrow error
}
});
/**
* @func checkAndInstallPackage
* Checks whether a package for a given packageName is installed locally. If package cannot be found, fetch and install it
* @param {string} packageName - The package name
* @param {string} [url] - A URL which will be used to fetch the package from
* @return {boolean}
*/
const checkAndInstallPackage = (packageName, url) =>
wrapAsync(function*() {
try {
require.resolve(packageName);
return true;
} catch (err) {
debug(
`checkAndInstallPackage: ${packageName} not installed. Fetching it now …`
);
yield fetchPackage(packageName, url);
return false;
}
});
/**
* @func noop
* @desc Plain arrow expression for noop
*/
const noop = () => {};
const getJSONKey = (packageName, key, fileName = 'package.json') =>
wrapAsync(function*() {
yield checkAndInstallPackage(packageName);
const packageJSON = yield fs.readJson(
path.resolve('node_modules', packageName, fileName)
);
return packageJSON[key];
});
module.exports = {
copyWithPattern,
copyAsync: fs.copy,
mkdirsAsync: fs.mkdirs,
moveAsync: fs.move,
writeJsonAsync: fs.outputJson,
readJsonAsync: fs.readJson,
error,
info,
debug,
log,
wrapAsync,
checkAndInstallPackage,
noop,
getJSONKey,
};