UNPKG

4.32 kBJavaScriptView Raw
1'use strict';
2
3const os = require('os');
4const path = require('path');
5const crypto = require('crypto');
6const util = require('util');
7const chalk = require('chalk');
8const lockfile = require('lockfile');
9
10const lfLock = util.promisify(lockfile.lock);
11const lfUnlock = util.promisify(lockfile.unlock);
12
13/**
14 * Display a warning message.
15 *
16 * @param {string} message
17 */
18function warn( message )
19{
20 const prefix = chalk.hex('#CC4A8B')('WARNING:');
21
22 console.warn(chalk`${prefix} ${message}`);
23}
24
25warn.cache = new Set();
26
27/**
28 * Display a warning message once.
29 *
30 * @param {string} message
31 */
32warn.once = function( message ) {
33 if ( warn.cache.has( message ) ) {
34 return;
35 }
36
37 warn( message );
38
39 warn.cache.add( message );
40};
41
42/**
43 * @param {*} data
44 * @return {array}
45 */
46function maybeArrayWrap( data )
47{
48 return Array.isArray( data ) ? data : [ data ];
49}
50
51/**
52 * Filter out invalid hash algorithms.
53 *
54 * @param {array} hashes
55 * @return {array} Valid hash algorithms
56 */
57function filterHashes( hashes )
58{
59 const validHashes = crypto.getHashes();
60
61 return hashes.filter( hash => {
62 if ( validHashes.includes(hash) ) {
63 return true;
64 }
65
66 warn(chalk`{blueBright ${hash}} is not a supported hash algorithm`);
67
68 return false;
69 });
70}
71
72/**
73 * See {@link https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity|Subresource Integrity} at MDN
74 *
75 * @param {array} hashes - The algorithms you want to use when hashing `content`
76 * @param {string} content - File contents you want to hash
77 * @return {string} SRI hash
78 */
79function getSRIHash( hashes, content )
80{
81 return Array.isArray( hashes ) ? hashes.map( hash => {
82 const integrity = crypto.createHash(hash).update(content, 'utf8').digest('base64');
83
84 return `${hash}-${integrity}`;
85 }).join(' ') : '';
86}
87
88/**
89 * Get the data type of an argument.
90 *
91 * @param {*} v - Some variable
92 * @return {string} Data type
93 */
94function varType( v )
95{
96 const [ , type ] = Object.prototype.toString.call( v ).match(/^\[object (\w+)\]$/);
97
98 return type;
99}
100
101/**
102 * Determine if the argument is an Object.
103 *
104 * @param {*} arg
105 */
106function isObject( arg )
107{
108 return varType( arg ) === 'Object';
109}
110
111/**
112 * Get an object sorted by keys.
113 *
114 * @param {object} object
115 * @param {(a: string, b: string) => number} compareFunction
116 * @return {object}
117 */
118function getSortedObject(object, compareFunction)
119{
120 return Object.keys( object ).sort( compareFunction ).reduce(
121 (sorted, key) => (sorted[ key ] = object[ key ], sorted),
122 Object.create(null),
123 );
124}
125
126/**
127 * Find a Map entry key by the value
128 *
129 * @param {Map} map
130 */
131function findMapKeysByValue( map )
132{
133 const entries = [ ...map.entries() ];
134
135 return searchValue => entries
136 .filter( ([ , value ]) => value === searchValue )
137 .map( ([ name ]) => name );
138}
139
140/**
141 * Group items from an array based on a callback return value.
142 *
143 * @param {Array} arr
144 * @param {(item: any) => string} getGroup
145 * @param {(item: any, group: string) => any} mapper
146 */
147function group( arr, getGroup, mapper = item => item )
148{
149 return arr.reduce(
150 (obj, item) => {
151 const group = getGroup( item );
152
153 if ( group ) {
154 obj[ group ] = obj[ group ] || [];
155 obj[ group ].push( mapper( item, group ) );
156 }
157
158 return obj;
159 },
160 Object.create(null),
161 );
162}
163
164function md5( data )
165{
166 return crypto.createHash('md5').update( data ).digest('hex');
167}
168
169/**
170 * Build a file path to a lock file in the tmp directory
171 *
172 * @param {string} filename
173 */
174function getLockFilename( filename )
175{
176 const name = path.basename( filename );
177 const dirHash = md5( path.dirname( filename ) );
178
179 return path.join( os.tmpdir(), `${dirHash}-${name}.lock` );
180}
181
182/**
183 * Create a lockfile (async)
184 *
185 * @param {string} filename
186 */
187async function lock( filename )
188{
189 await lfLock(
190 getLockFilename( filename ),
191 {
192 wait: 6000,
193 retryWait: 100,
194 stale: 5000,
195 retries: 100,
196 },
197 );
198}
199
200/**
201 * Remove a lockfile (async)
202 *
203 * @param {string} filename
204 */
205async function unlock( filename )
206{
207 await lfUnlock( getLockFilename( filename ) );
208}
209
210module.exports = {
211 maybeArrayWrap,
212 filterHashes,
213 getSRIHash,
214 warn,
215 varType,
216 isObject,
217 getSortedObject,
218 findMapKeysByValue,
219 group,
220 getLockFilename,
221 lock,
222 unlock,
223};