@wordpress/env 6.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -66
- package/lib/build-docker-compose-config.js +67 -96
- package/lib/cache.js +1 -0
- package/lib/cli.js +39 -10
- package/lib/commands/clean.js +13 -1
- package/lib/commands/destroy.js +21 -42
- package/lib/commands/index.js +1 -0
- package/lib/commands/install-path.js +1 -0
- package/lib/commands/logs.js +1 -0
- package/lib/commands/run.js +45 -21
- package/lib/commands/start.js +29 -8
- package/lib/commands/stop.js +1 -0
- package/lib/config/add-or-replace-port.js +12 -3
- package/lib/config/db-env.js +1 -0
- package/lib/config/detect-directory-type.js +1 -1
- package/lib/config/get-cache-directory.js +39 -0
- package/lib/config/get-config-from-environment-vars.js +106 -0
- package/lib/config/index.js +7 -5
- package/lib/config/load-config.js +92 -0
- package/lib/config/merge-configs.js +105 -0
- package/lib/config/parse-config.js +501 -157
- package/lib/config/parse-source-string.js +164 -0
- package/lib/config/post-process-config.js +202 -0
- package/lib/config/read-raw-config-file.js +7 -33
- package/lib/config/test/__snapshots__/config-integration.js.snap +295 -0
- package/lib/config/test/add-or-replace-port.js +29 -1
- package/lib/config/test/config-integration.js +164 -0
- package/lib/config/test/get-cache-directory.js +57 -0
- package/lib/config/test/merge-configs.js +121 -0
- package/lib/config/test/parse-config.js +393 -0
- package/lib/config/test/parse-source-string.js +154 -0
- package/lib/config/test/post-process-config.js +299 -0
- package/lib/config/test/read-raw-config-file.js +19 -63
- package/lib/config/test/validate-config.js +363 -0
- package/lib/config/validate-config.js +119 -54
- package/lib/env.js +2 -0
- package/lib/execute-lifecycle-script.js +86 -0
- package/lib/get-host-user.js +27 -0
- package/lib/init-config.js +186 -63
- package/lib/md5.js +1 -0
- package/lib/parse-xdebug-mode.js +1 -0
- package/lib/retry.js +1 -0
- package/lib/test/__snapshots__/md5.js.snap +9 -0
- package/lib/test/build-docker-compose-config.js +134 -0
- package/lib/test/cache.js +154 -0
- package/lib/test/cli.js +149 -0
- package/lib/test/execute-lifecycle-script.js +59 -0
- package/lib/test/md5.js +35 -0
- package/lib/test/parse-xdebug-mode.js +41 -0
- package/lib/validate-run-container.js +41 -0
- package/lib/wordpress.js +4 -19
- package/package.json +2 -2
- package/lib/config/config.js +0 -353
- package/lib/config/test/__snapshots__/config.js.snap +0 -83
- package/lib/config/test/config.js +0 -1241
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Internal dependencies
|
|
4
|
+
*/
|
|
5
|
+
const {
|
|
6
|
+
ValidationError,
|
|
7
|
+
checkString,
|
|
8
|
+
checkPort,
|
|
9
|
+
checkStringArray,
|
|
10
|
+
checkObjectWithValues,
|
|
11
|
+
checkVersion,
|
|
12
|
+
checkValidURL,
|
|
13
|
+
} = require( '../validate-config' );
|
|
14
|
+
|
|
15
|
+
describe( 'validate-config', () => {
|
|
16
|
+
describe( 'checkString', () => {
|
|
17
|
+
it( 'throws when not a string', () => {
|
|
18
|
+
expect( () => checkString( 'test.json', 'test', 1234 ) ).toThrow(
|
|
19
|
+
new ValidationError(
|
|
20
|
+
'Invalid test.json: "test" must be a string.'
|
|
21
|
+
)
|
|
22
|
+
);
|
|
23
|
+
} );
|
|
24
|
+
|
|
25
|
+
it( 'passes for string', () => {
|
|
26
|
+
expect( () =>
|
|
27
|
+
checkString( 'test.json', 'test', 'test' )
|
|
28
|
+
).not.toThrow();
|
|
29
|
+
} );
|
|
30
|
+
} );
|
|
31
|
+
|
|
32
|
+
describe( 'checkPort', () => {
|
|
33
|
+
it( 'throws when not a number', () => {
|
|
34
|
+
expect( () => checkPort( 'test.json', 'test', 'test' ) ).toThrow(
|
|
35
|
+
new ValidationError(
|
|
36
|
+
'Invalid test.json: "test" must be an integer.'
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect( () =>
|
|
41
|
+
checkPort( 'test.json', 'test', { test: 'test' } )
|
|
42
|
+
).toThrow(
|
|
43
|
+
new ValidationError(
|
|
44
|
+
'Invalid test.json: "test" must be an integer.'
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
} );
|
|
48
|
+
|
|
49
|
+
it( 'throws when port out of range', () => {
|
|
50
|
+
expect( () => checkPort( 'test.json', 'test', -1 ) ).toThrow(
|
|
51
|
+
new ValidationError(
|
|
52
|
+
'Invalid test.json: "test" must be a valid port.'
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect( () => checkPort( 'test.json', 'test', 99999999 ) ).toThrow(
|
|
57
|
+
new ValidationError(
|
|
58
|
+
'Invalid test.json: "test" must be a valid port.'
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
it( 'passes for valid port', () => {
|
|
64
|
+
expect( () =>
|
|
65
|
+
checkPort( 'test.json', 'test', 8888 )
|
|
66
|
+
).not.toThrow();
|
|
67
|
+
} );
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
describe( 'checkStringArray', () => {
|
|
71
|
+
it( 'throws when not an array', () => {
|
|
72
|
+
expect( () =>
|
|
73
|
+
checkStringArray( 'test.json', 'test', 'test' )
|
|
74
|
+
).toThrow(
|
|
75
|
+
new ValidationError(
|
|
76
|
+
'Invalid test.json: "test" must be an array.'
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect( () =>
|
|
81
|
+
checkStringArray( 'test.json', 'test', { test: 'test' } )
|
|
82
|
+
).toThrow(
|
|
83
|
+
new ValidationError(
|
|
84
|
+
'Invalid test.json: "test" must be an array.'
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
} );
|
|
88
|
+
|
|
89
|
+
it( 'throws when array contains non-strings', () => {
|
|
90
|
+
expect( () =>
|
|
91
|
+
checkStringArray( 'test.json', 'test', [ 12 ] )
|
|
92
|
+
).toThrow(
|
|
93
|
+
new ValidationError(
|
|
94
|
+
'Invalid test.json: "test" must be an array of strings.'
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect( () =>
|
|
99
|
+
checkStringArray( 'test.json', 'test', [ 'test', 12 ] )
|
|
100
|
+
).toThrow(
|
|
101
|
+
new ValidationError(
|
|
102
|
+
'Invalid test.json: "test" must be an array of strings.'
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
} );
|
|
106
|
+
|
|
107
|
+
it( 'passes for string arrays', () => {
|
|
108
|
+
expect( () =>
|
|
109
|
+
checkStringArray( 'test.json', 'test', [] )
|
|
110
|
+
).not.toThrow();
|
|
111
|
+
expect( () =>
|
|
112
|
+
checkStringArray( 'test.json', 'test', [ 'test' ] )
|
|
113
|
+
).not.toThrow();
|
|
114
|
+
} );
|
|
115
|
+
} );
|
|
116
|
+
|
|
117
|
+
describe( 'checkObjectWithValues', () => {
|
|
118
|
+
it( 'throws when not an object', () => {
|
|
119
|
+
expect( () =>
|
|
120
|
+
checkObjectWithValues( 'test.json', 'test', 'test', [], false )
|
|
121
|
+
).toThrow(
|
|
122
|
+
new ValidationError(
|
|
123
|
+
'Invalid test.json: "test" must be an object.'
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
expect( () =>
|
|
128
|
+
checkObjectWithValues(
|
|
129
|
+
'test.json',
|
|
130
|
+
'test',
|
|
131
|
+
[ 'test' ],
|
|
132
|
+
[],
|
|
133
|
+
false
|
|
134
|
+
)
|
|
135
|
+
).toThrow(
|
|
136
|
+
new ValidationError(
|
|
137
|
+
'Invalid test.json: "test" must be an object.'
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
} );
|
|
141
|
+
|
|
142
|
+
it( 'throws when no allowed types are given', () => {
|
|
143
|
+
expect( () =>
|
|
144
|
+
checkObjectWithValues(
|
|
145
|
+
'test.json',
|
|
146
|
+
'test',
|
|
147
|
+
{ test: 'test' },
|
|
148
|
+
[],
|
|
149
|
+
false
|
|
150
|
+
)
|
|
151
|
+
).toThrow(
|
|
152
|
+
new ValidationError(
|
|
153
|
+
'Invalid test.json: "test.test" must be of type: .'
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
} );
|
|
157
|
+
|
|
158
|
+
it( 'throws when type is not allowed', () => {
|
|
159
|
+
expect( () =>
|
|
160
|
+
checkObjectWithValues(
|
|
161
|
+
'test.json',
|
|
162
|
+
'test',
|
|
163
|
+
{ test: 'test' },
|
|
164
|
+
[ 'number' ],
|
|
165
|
+
false
|
|
166
|
+
)
|
|
167
|
+
).toThrow(
|
|
168
|
+
new ValidationError(
|
|
169
|
+
'Invalid test.json: "test.test" must be of type: number.'
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect( () =>
|
|
174
|
+
checkObjectWithValues(
|
|
175
|
+
'test.json',
|
|
176
|
+
'test',
|
|
177
|
+
{ test: 1 },
|
|
178
|
+
[ 'string' ],
|
|
179
|
+
false
|
|
180
|
+
)
|
|
181
|
+
).toThrow(
|
|
182
|
+
new ValidationError(
|
|
183
|
+
'Invalid test.json: "test.test" must be of type: string.'
|
|
184
|
+
)
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
expect( () =>
|
|
188
|
+
checkObjectWithValues(
|
|
189
|
+
'test.json',
|
|
190
|
+
'test',
|
|
191
|
+
{ test: [ 'test' ] },
|
|
192
|
+
[ 'object', 'string' ],
|
|
193
|
+
false
|
|
194
|
+
)
|
|
195
|
+
).toThrow(
|
|
196
|
+
new ValidationError(
|
|
197
|
+
'Invalid test.json: "test.test" must be of type: object or string.'
|
|
198
|
+
)
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect( () =>
|
|
202
|
+
checkObjectWithValues(
|
|
203
|
+
'test.json',
|
|
204
|
+
'test',
|
|
205
|
+
{ test: null },
|
|
206
|
+
[ 'object' ],
|
|
207
|
+
true
|
|
208
|
+
)
|
|
209
|
+
).toThrow(
|
|
210
|
+
new ValidationError(
|
|
211
|
+
'Invalid test.json: "test.test" must be of type: object.'
|
|
212
|
+
)
|
|
213
|
+
);
|
|
214
|
+
} );
|
|
215
|
+
|
|
216
|
+
it( 'passes when type is allowed', () => {
|
|
217
|
+
expect( () =>
|
|
218
|
+
checkObjectWithValues(
|
|
219
|
+
'test.json',
|
|
220
|
+
'test',
|
|
221
|
+
{ test: 'test' },
|
|
222
|
+
[ 'string' ],
|
|
223
|
+
false
|
|
224
|
+
)
|
|
225
|
+
).not.toThrow();
|
|
226
|
+
expect( () =>
|
|
227
|
+
checkObjectWithValues(
|
|
228
|
+
'test.json',
|
|
229
|
+
'test',
|
|
230
|
+
{ test: '' },
|
|
231
|
+
[ 'string' ],
|
|
232
|
+
true
|
|
233
|
+
)
|
|
234
|
+
).not.toThrow();
|
|
235
|
+
expect( () =>
|
|
236
|
+
checkObjectWithValues(
|
|
237
|
+
'test.json',
|
|
238
|
+
'test',
|
|
239
|
+
{ test: 1 },
|
|
240
|
+
[ 'number' ],
|
|
241
|
+
false
|
|
242
|
+
)
|
|
243
|
+
).not.toThrow();
|
|
244
|
+
expect( () =>
|
|
245
|
+
checkObjectWithValues(
|
|
246
|
+
'test.json',
|
|
247
|
+
'test',
|
|
248
|
+
{ test: { nested: 'test' } },
|
|
249
|
+
[ 'object' ],
|
|
250
|
+
false
|
|
251
|
+
)
|
|
252
|
+
).not.toThrow();
|
|
253
|
+
expect( () =>
|
|
254
|
+
checkObjectWithValues(
|
|
255
|
+
'test.json',
|
|
256
|
+
'test',
|
|
257
|
+
{ test: [ 'test' ] },
|
|
258
|
+
[ 'array' ],
|
|
259
|
+
false
|
|
260
|
+
)
|
|
261
|
+
).not.toThrow();
|
|
262
|
+
expect( () =>
|
|
263
|
+
checkObjectWithValues(
|
|
264
|
+
'test.json',
|
|
265
|
+
'test',
|
|
266
|
+
{ test: null },
|
|
267
|
+
[ 'null' ],
|
|
268
|
+
false
|
|
269
|
+
)
|
|
270
|
+
).not.toThrow();
|
|
271
|
+
} );
|
|
272
|
+
} );
|
|
273
|
+
|
|
274
|
+
describe( 'checkVersion', () => {
|
|
275
|
+
it( 'throws for invalid input', () => {
|
|
276
|
+
expect( () => checkVersion( 'test.json', 'test', 'test' ) ).toThrow(
|
|
277
|
+
new ValidationError(
|
|
278
|
+
'Invalid test.json: "test" must be a string of the format "X", "X.X", or "X.X.X".'
|
|
279
|
+
)
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
expect( () => checkVersion( 'test.json', 'test', 123 ) ).toThrow(
|
|
283
|
+
new ValidationError(
|
|
284
|
+
'Invalid test.json: "test" must be a string.'
|
|
285
|
+
)
|
|
286
|
+
);
|
|
287
|
+
} );
|
|
288
|
+
|
|
289
|
+
it( 'passes for different version formats', () => {
|
|
290
|
+
expect( () =>
|
|
291
|
+
checkVersion( 'test.json', 'test', '1' )
|
|
292
|
+
).not.toThrow();
|
|
293
|
+
expect( () =>
|
|
294
|
+
checkVersion( 'test.json', 'test', '1.1' )
|
|
295
|
+
).not.toThrow();
|
|
296
|
+
expect( () =>
|
|
297
|
+
checkVersion( 'test.json', 'test', '1.1.1' )
|
|
298
|
+
).not.toThrow();
|
|
299
|
+
expect( () =>
|
|
300
|
+
checkVersion( 'test.json', 'test', '15.7.2' )
|
|
301
|
+
).not.toThrow();
|
|
302
|
+
expect( () =>
|
|
303
|
+
checkVersion( 'test.json', 'test', '26634543' )
|
|
304
|
+
).not.toThrow();
|
|
305
|
+
} );
|
|
306
|
+
} );
|
|
307
|
+
|
|
308
|
+
describe( 'checkValidURL', () => {
|
|
309
|
+
it( 'throws for invaid URLs', () => {
|
|
310
|
+
expect( () =>
|
|
311
|
+
checkValidURL( 'test.json', 'test', 'localhost' )
|
|
312
|
+
).toThrow(
|
|
313
|
+
new ValidationError(
|
|
314
|
+
'Invalid test.json: "test" must be a valid URL.'
|
|
315
|
+
)
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
expect( () => checkValidURL( 'test.json', 'test', '' ) ).toThrow(
|
|
319
|
+
new ValidationError(
|
|
320
|
+
'Invalid test.json: "test" must be a valid URL.'
|
|
321
|
+
)
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
expect( () => checkValidURL( 'test.json', 'test', 123 ) ).toThrow(
|
|
325
|
+
new ValidationError(
|
|
326
|
+
'Invalid test.json: "test" must be a valid URL.'
|
|
327
|
+
)
|
|
328
|
+
);
|
|
329
|
+
} );
|
|
330
|
+
|
|
331
|
+
it( 'passes for valid URLs', () => {
|
|
332
|
+
expect( () =>
|
|
333
|
+
checkValidURL( 'test.json', 'test', 'http://test.com' )
|
|
334
|
+
).not.toThrow();
|
|
335
|
+
expect( () =>
|
|
336
|
+
checkValidURL( 'test.json', 'test', 'https://test.com' )
|
|
337
|
+
).not.toThrow();
|
|
338
|
+
expect( () =>
|
|
339
|
+
checkValidURL( 'test.json', 'test', 'http://test' )
|
|
340
|
+
).not.toThrow();
|
|
341
|
+
expect( () =>
|
|
342
|
+
checkValidURL(
|
|
343
|
+
'test.json',
|
|
344
|
+
'test',
|
|
345
|
+
'http://test/test?test=test'
|
|
346
|
+
)
|
|
347
|
+
).not.toThrow();
|
|
348
|
+
expect( () =>
|
|
349
|
+
checkValidURL( 'test.json', 'test', 'http://test.co.uk' )
|
|
350
|
+
).not.toThrow();
|
|
351
|
+
expect( () =>
|
|
352
|
+
checkValidURL( 'test.json', 'test', 'https://test.co.uk:8888' )
|
|
353
|
+
).not.toThrow();
|
|
354
|
+
expect( () =>
|
|
355
|
+
checkValidURL(
|
|
356
|
+
'test.json',
|
|
357
|
+
'test',
|
|
358
|
+
'http://test.co.uk:8888/test?test=test#test'
|
|
359
|
+
)
|
|
360
|
+
).not.toThrow();
|
|
361
|
+
} );
|
|
362
|
+
} );
|
|
363
|
+
} );
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @typedef {import('./
|
|
5
|
-
* @typedef {import('./config').WPSource} WPSource
|
|
4
|
+
* @typedef {import('./parse-source-string').WPSource} WPSource
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -12,105 +11,171 @@
|
|
|
12
11
|
class ValidationError extends Error {}
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
|
-
* Validates
|
|
16
|
-
* do not match the required format.
|
|
14
|
+
* Validates that the value is a string.
|
|
17
15
|
*
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
20
|
-
* @
|
|
16
|
+
* @param {string} configFile The configuration file we're validating.
|
|
17
|
+
* @param {string} configKey The configuration key we're validating.
|
|
18
|
+
* @param {number} value The value to check.
|
|
21
19
|
*/
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
if ( config.core !== null && typeof config.core !== 'string' ) {
|
|
20
|
+
function checkString( configFile, configKey, value ) {
|
|
21
|
+
if ( typeof value !== 'string' ) {
|
|
25
22
|
throw new ValidationError(
|
|
26
|
-
`Invalid
|
|
23
|
+
`Invalid ${ configFile }: "${ configKey }" must be a string.`
|
|
27
24
|
);
|
|
28
25
|
}
|
|
26
|
+
}
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Validates the port and throws if it isn't valid.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} configFile The configuration file we're validating.
|
|
32
|
+
* @param {string} configKey The configuration key we're validating.
|
|
33
|
+
* @param {number} port The port to check.
|
|
34
|
+
*/
|
|
35
|
+
function checkPort( configFile, configKey, port ) {
|
|
36
|
+
if ( ! Number.isInteger( port ) ) {
|
|
34
37
|
throw new ValidationError(
|
|
35
|
-
`Invalid
|
|
38
|
+
`Invalid ${ configFile }: "${ configKey }" must be an integer.`
|
|
36
39
|
);
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
if (
|
|
40
|
-
! Array.isArray( config.themes ) ||
|
|
41
|
-
config.themes.some( ( theme ) => typeof theme !== 'string' )
|
|
42
|
-
) {
|
|
42
|
+
if ( port < 0 || port > 65535 ) {
|
|
43
43
|
throw new ValidationError(
|
|
44
|
-
`Invalid
|
|
44
|
+
`Invalid ${ configFile }: "${ configKey }" must be a valid port.`
|
|
45
45
|
);
|
|
46
46
|
}
|
|
47
|
+
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Validates the array and throws if it isn't valid.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} configFile The config file we're validating.
|
|
53
|
+
* @param {string} configKey The configuration key we're validating.
|
|
54
|
+
* @param {string[]} array The array that we're checking.
|
|
55
|
+
*/
|
|
56
|
+
function checkStringArray( configFile, configKey, array ) {
|
|
57
|
+
if ( ! Array.isArray( array ) ) {
|
|
49
58
|
throw new ValidationError(
|
|
50
|
-
`Invalid
|
|
59
|
+
`Invalid ${ configFile }: "${ configKey }" must be an array.`
|
|
51
60
|
);
|
|
52
61
|
}
|
|
53
62
|
|
|
54
|
-
if ( typeof
|
|
63
|
+
if ( array.some( ( value ) => typeof value !== 'string' ) ) {
|
|
55
64
|
throw new ValidationError(
|
|
56
|
-
`Invalid
|
|
65
|
+
`Invalid ${ configFile }: "${ configKey }" must be an array of strings.`
|
|
57
66
|
);
|
|
58
67
|
}
|
|
68
|
+
}
|
|
59
69
|
|
|
60
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Validates the object and throws if it isn't valid.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} configFile The config file we're validating.
|
|
74
|
+
* @param {string} configKey The configuration key we're validating.
|
|
75
|
+
* @param {string[]} obj The object that we're checking.
|
|
76
|
+
* @param {string[]} allowTypes The types that are allowed.
|
|
77
|
+
* @param {boolean} allowEmpty Indicates whether or not empty values are allowed.
|
|
78
|
+
*/
|
|
79
|
+
function checkObjectWithValues(
|
|
80
|
+
configFile,
|
|
81
|
+
configKey,
|
|
82
|
+
obj,
|
|
83
|
+
allowTypes,
|
|
84
|
+
allowEmpty
|
|
85
|
+
) {
|
|
86
|
+
if ( allowTypes === undefined ) {
|
|
87
|
+
allowTypes = [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if ( typeof obj !== 'object' || Array.isArray( obj ) ) {
|
|
61
91
|
throw new ValidationError(
|
|
62
|
-
`Invalid
|
|
92
|
+
`Invalid ${ configFile }: "${ configKey }" must be an object.`
|
|
63
93
|
);
|
|
64
94
|
}
|
|
65
95
|
|
|
66
|
-
for ( const
|
|
67
|
-
|
|
96
|
+
for ( const key in obj ) {
|
|
97
|
+
// Some values need to be uniquely validated.
|
|
98
|
+
switch ( obj[ key ] ) {
|
|
99
|
+
case null:
|
|
100
|
+
case undefined: {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
default: {
|
|
105
|
+
if ( ! obj[ key ] && ! allowEmpty ) {
|
|
106
|
+
throw new ValidationError(
|
|
107
|
+
`Invalid ${ configFile }: "${ configKey }.${ key }" must not be empty.`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Some types need to be uniquely identified.
|
|
114
|
+
let type;
|
|
115
|
+
if ( obj[ key ] === undefined ) {
|
|
116
|
+
type = 'undefined';
|
|
117
|
+
} else if ( obj[ key ] === null ) {
|
|
118
|
+
type = 'null';
|
|
119
|
+
} else if ( Array.isArray( obj[ key ] ) ) {
|
|
120
|
+
type = 'array';
|
|
121
|
+
} else {
|
|
122
|
+
type = typeof obj[ key ];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if ( ! allowTypes.includes( type ) ) {
|
|
68
126
|
throw new ValidationError(
|
|
69
|
-
`Invalid
|
|
127
|
+
`Invalid ${ configFile }: "${ configKey }.${ key }" must be of type: ${ allowTypes.join(
|
|
128
|
+
' or '
|
|
129
|
+
) }.`
|
|
70
130
|
);
|
|
71
131
|
}
|
|
72
132
|
}
|
|
133
|
+
}
|
|
73
134
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Validates the version and throws if it isn't valid.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} configFile The config file we're validating.
|
|
139
|
+
* @param {string} configKey The configuration key we're validating.
|
|
140
|
+
* @param {string} version The version that we're checking.
|
|
141
|
+
*/
|
|
142
|
+
function checkVersion( configFile, configKey, version ) {
|
|
143
|
+
if ( typeof version !== 'string' ) {
|
|
81
144
|
throw new ValidationError(
|
|
82
|
-
`Invalid
|
|
145
|
+
`Invalid ${ configFile }: "${ configKey }" must be a string.`
|
|
83
146
|
);
|
|
84
147
|
}
|
|
85
148
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
149
|
+
if ( ! version.match( /[0-9]+(?:\.[0-9]+)*/ ) ) {
|
|
150
|
+
throw new ValidationError(
|
|
151
|
+
`Invalid ${ configFile }: "${ configKey }" must be a string of the format "X", "X.X", or "X.X.X".`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
90
154
|
}
|
|
91
155
|
|
|
92
156
|
/**
|
|
93
|
-
* Validates the
|
|
157
|
+
* Validates the url and throws if it isn't valid.
|
|
94
158
|
*
|
|
95
|
-
* @param {string}
|
|
96
|
-
* @param {
|
|
97
|
-
* @param {string}
|
|
159
|
+
* @param {string} configFile The config file we're validating.
|
|
160
|
+
* @param {string} configKey The configuration key we're validating.
|
|
161
|
+
* @param {string} url The URL that we're checking.
|
|
98
162
|
*/
|
|
99
|
-
function checkValidURL(
|
|
100
|
-
if ( config[ configKey ] === undefined ) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
163
|
+
function checkValidURL( configFile, configKey, url ) {
|
|
104
164
|
try {
|
|
105
|
-
new URL(
|
|
165
|
+
new URL( url );
|
|
106
166
|
} catch {
|
|
107
167
|
throw new ValidationError(
|
|
108
|
-
`Invalid
|
|
168
|
+
`Invalid ${ configFile }: "${ configKey }" must be a valid URL.`
|
|
109
169
|
);
|
|
110
170
|
}
|
|
111
171
|
}
|
|
112
172
|
|
|
113
173
|
module.exports = {
|
|
114
|
-
validateConfig,
|
|
115
174
|
ValidationError,
|
|
175
|
+
checkString,
|
|
176
|
+
checkPort,
|
|
177
|
+
checkStringArray,
|
|
178
|
+
checkObjectWithValues,
|
|
179
|
+
checkVersion,
|
|
180
|
+
checkValidURL,
|
|
116
181
|
};
|
package/lib/env.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* Internal dependencies
|
|
4
4
|
*/
|
|
5
5
|
const { ValidationError } = require( './config' );
|
|
6
|
+
const { LifecycleScriptError } = require( './execute-lifecycle-script' );
|
|
6
7
|
const commands = require( './commands' );
|
|
7
8
|
|
|
8
9
|
module.exports = {
|
|
9
10
|
...commands,
|
|
10
11
|
ValidationError,
|
|
12
|
+
LifecycleScriptError,
|
|
11
13
|
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
const { exec } = require( 'child_process' );
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('./config').WPConfig} WPConfig
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Error subtype which indicates that the lifecycle script failed.
|
|
13
|
+
*/
|
|
14
|
+
class LifecycleScriptError extends Error {
|
|
15
|
+
constructor( event, stderr ) {
|
|
16
|
+
super( `${ event } Error:\n${ stderr }` );
|
|
17
|
+
|
|
18
|
+
this.event = event;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Executes any defined life cycle script.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} event The lifecycle event to run the script for.
|
|
26
|
+
* @param {WPConfig} config The config object to use.
|
|
27
|
+
* @param {Object} spinner A CLI spinner which indciates progress.
|
|
28
|
+
*
|
|
29
|
+
* @return {Promise} Resolves when the script has completed and rejects when there is an error.
|
|
30
|
+
*/
|
|
31
|
+
function executeLifecycleScript( event, config, spinner ) {
|
|
32
|
+
if ( ! config.lifecycleScripts[ event ] ) {
|
|
33
|
+
return Promise.resolve();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new Promise( ( resolve, reject ) => {
|
|
37
|
+
// We're going to append the script output to the spinner while it's executing.
|
|
38
|
+
const spinnerMessage = `Executing ${ event } Script`;
|
|
39
|
+
spinner.text = spinnerMessage;
|
|
40
|
+
|
|
41
|
+
// Execute the script asynchronously so that it won't block the spinner.
|
|
42
|
+
const childProc = exec( config.lifecycleScripts[ event ], {
|
|
43
|
+
encoding: 'utf-8',
|
|
44
|
+
stdio: 'pipe',
|
|
45
|
+
env: process.env,
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
// Collect all of the output so that we can make use of it.
|
|
49
|
+
let output = '';
|
|
50
|
+
childProc.stdout.on( 'data', ( data ) => {
|
|
51
|
+
output += data;
|
|
52
|
+
|
|
53
|
+
// Keep the spinner updated with the command output.
|
|
54
|
+
spinner.text = `${ spinnerMessage }\n${ output }`;
|
|
55
|
+
} );
|
|
56
|
+
|
|
57
|
+
// Listen for any error output so we can display it if the command fails.
|
|
58
|
+
let error = '';
|
|
59
|
+
childProc.stderr.on( 'data', ( data ) => {
|
|
60
|
+
error += data;
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
// Pass any process creation errors directly up.
|
|
64
|
+
childProc.on( 'error', reject );
|
|
65
|
+
|
|
66
|
+
// Handle the completion of the command based on whether it was successful or not.
|
|
67
|
+
childProc.on( 'exit', ( code ) => {
|
|
68
|
+
if ( code === 0 ) {
|
|
69
|
+
// Keep the output of the command in debug mode.
|
|
70
|
+
if ( config.debug ) {
|
|
71
|
+
spinner.info( `${ event } Script:\n${ output.trimEnd() }` );
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
resolve();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
reject( new LifecycleScriptError( event, error.trimEnd() ) );
|
|
79
|
+
} );
|
|
80
|
+
} );
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = {
|
|
84
|
+
LifecycleScriptError,
|
|
85
|
+
executeLifecycleScript,
|
|
86
|
+
};
|