shipthis 0.0.41 → 0.1.1

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.
Files changed (93) hide show
  1. package/README.md +17 -17
  2. package/dist/{AppleBundleIdDetails-n1_U8IqK.js → AppleBundleIdDetails-BF-Pm1Ia.js} +19 -7
  3. package/dist/Command-BrfJSeOC.js +1077 -0
  4. package/dist/CommandGame-D9wl8hfX.js +8 -0
  5. package/dist/{NextSteps-CK9zHOCt.js → NextSteps-DKcjSHZ3.js} +1 -1
  6. package/dist/ProjectCredentialsTable-BVvkIPjN.js +36 -0
  7. package/dist/{StatusTable-CxuX_R1D.js → StatusTable-BzsNF75L.js} +2 -7
  8. package/dist/{UserCredentialsTable-A_YlFeJX.js → UserCredentialsTable-DUFQqHVt.js} +11 -5
  9. package/dist/{baseAppleCommand-DK5-Cvg4.js → baseAppleCommand-BSJhK8GA.js} +1 -1
  10. package/dist/baseGameAndroidCommand-CPAtReqy.js +43 -0
  11. package/dist/commands/apple/apiKey/create.js +24 -10
  12. package/dist/commands/apple/apiKey/export.js +23 -9
  13. package/dist/commands/apple/apiKey/import.js +23 -9
  14. package/dist/commands/apple/apiKey/status.js +25 -13
  15. package/dist/commands/apple/certificate/create.js +25 -11
  16. package/dist/commands/apple/certificate/export.js +23 -9
  17. package/dist/commands/apple/certificate/import.js +23 -9
  18. package/dist/commands/apple/certificate/status.js +26 -14
  19. package/dist/commands/apple/login.js +9 -3
  20. package/dist/commands/apple/status.js +27 -13
  21. package/dist/commands/dashboard.js +11 -5
  22. package/dist/commands/game/android/apiKey/connect.js +73 -0
  23. package/dist/commands/game/android/apiKey/create.js +70 -0
  24. package/dist/commands/game/android/apiKey/export.js +83 -0
  25. package/dist/commands/game/android/apiKey/import.js +92 -0
  26. package/dist/commands/game/android/apiKey/invite.js +81 -0
  27. package/dist/commands/game/android/apiKey/status.js +86 -0
  28. package/dist/commands/game/android/keyStore/create.js +67 -0
  29. package/dist/commands/game/android/keyStore/export.js +82 -0
  30. package/dist/commands/game/android/keyStore/import.js +91 -0
  31. package/dist/commands/game/android/keyStore/status.js +69 -0
  32. package/dist/commands/game/android/status.js +84 -0
  33. package/dist/commands/game/android/wizard.js +53 -0
  34. package/dist/commands/game/build/download.js +23 -9
  35. package/dist/commands/game/build/list.js +25 -14
  36. package/dist/commands/game/create.js +18 -10
  37. package/dist/commands/game/details.js +28 -19
  38. package/dist/commands/game/export.js +11 -5
  39. package/dist/commands/game/ios/app/addTester.js +25 -12
  40. package/dist/commands/game/ios/app/create.js +26 -13
  41. package/dist/commands/game/ios/app/status.js +27 -13
  42. package/dist/commands/game/ios/app/sync.js +23 -9
  43. package/dist/commands/game/ios/profile/create.js +22 -8
  44. package/dist/commands/game/ios/profile/export.js +23 -9
  45. package/dist/commands/game/ios/profile/import.js +23 -9
  46. package/dist/commands/game/ios/profile/status.js +26 -71
  47. package/dist/commands/game/ios/status.js +28 -15
  48. package/dist/commands/game/ios/wizard.js +125 -0
  49. package/dist/commands/game/job/list.js +26 -14
  50. package/dist/commands/game/job/status.js +26 -71
  51. package/dist/commands/game/list.js +26 -12
  52. package/dist/commands/game/ship.js +7 -4
  53. package/dist/commands/game/status.js +63 -25
  54. package/dist/commands/game/wizard.js +41 -99
  55. package/dist/commands/internal/fastlane.js +74 -0
  56. package/dist/commands/internal/readme.js +1452 -0
  57. package/dist/commands/login.js +9 -3
  58. package/dist/commands/status.js +23 -10
  59. package/dist/{export-C16psune.js → export-B0FJT0EU.js} +1 -1
  60. package/dist/{import-hte2Jy0K.js → import-CLDJ2iPu.js} +1 -1
  61. package/dist/{baseGameCommand-B3NbuvDu.js → index-CF0fIsx2.js} +112 -37
  62. package/dist/index-CFHmtzfq.js +24 -0
  63. package/dist/{index-ZHJdUrwJ.js → index-Df8uXQ4s.js} +14 -3
  64. package/dist/{upload-DADrkIUB.js → upload-C5L82Yq0.js} +1 -1
  65. package/dist/useAndroidServiceAccountTestResult-BnxNuoG3.js +52 -0
  66. package/dist/{useAppleApp-Cr3VcGVs.js → useAppleApp-IXRdsK5w.js} +1 -1
  67. package/dist/{useAppleBundleId-BxtyGpHy.js → useAppleBundleId-DYC5ISKT.js} +1 -1
  68. package/docs/game/android/apiKey/connect.md +27 -0
  69. package/docs/game/android/apiKey/create.md +25 -0
  70. package/docs/game/android/apiKey/export.md +25 -0
  71. package/docs/game/android/apiKey/import.md +25 -0
  72. package/docs/game/android/apiKey/invite.md +27 -0
  73. package/docs/game/android/apiKey/status.md +23 -0
  74. package/docs/game/android/apiKey.md +165 -0
  75. package/docs/game/android/keyStore/create.md +24 -0
  76. package/docs/game/android/keyStore/export.md +25 -0
  77. package/docs/game/android/keyStore/import.md +25 -0
  78. package/docs/game/android/keyStore/status.md +23 -0
  79. package/docs/game/android/keyStore.md +108 -0
  80. package/docs/game/android/status.md +23 -0
  81. package/docs/game/android/wizard.md +28 -0
  82. package/docs/game/android.md +339 -0
  83. package/docs/game/create.md +12 -7
  84. package/docs/game/ios/wizard.md +21 -0
  85. package/docs/game/wizard.md +12 -18
  86. package/oclif.manifest.json +914 -161
  87. package/package.json +54 -19
  88. package/dist/App-BfoOFeZ-.js +0 -21
  89. package/dist/RunWithSpinner-BVXNWGD3.js +0 -27
  90. package/dist/Title-BCQtayg6.js +0 -6
  91. package/dist/cacheKeys-CShA-ZjE.js +0 -9
  92. package/dist/useBuilds-JHqMwpWS.js +0 -41
  93. package/dist/useJob-Bc9fdiyP.js +0 -34
@@ -0,0 +1,1452 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import fs__default from 'fs';
3
+ import path from 'path';
4
+ import { S as BaseCommand } from '../../index-CF0fIsx2.js';
5
+ import CustomHelp from '../../utils/help.js';
6
+ import '@expo/apple-utils/build/index.js';
7
+ import 'axios';
8
+ import 'crypto-js';
9
+ import 'uuid';
10
+ import 'luxon';
11
+ import 'crypto';
12
+ import 'readline-sync';
13
+ import 'node:readline';
14
+ import 'node:path';
15
+ import 'node:url';
16
+ import 'react';
17
+ import '@tanstack/react-query';
18
+ import 'ini';
19
+ import 'deepmerge';
20
+ import 'fast-glob';
21
+ import 'yazl';
22
+ import 'socket.io-client';
23
+ import 'isomorphic-git';
24
+
25
+ function getDefaultExportFromCjs (x) {
26
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
27
+ }
28
+
29
+ var ejs$1 = {};
30
+
31
+ var utils = {};
32
+
33
+ /*
34
+ * EJS Embedded JavaScript templates
35
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
36
+ *
37
+ * Licensed under the Apache License, Version 2.0 (the "License");
38
+ * you may not use this file except in compliance with the License.
39
+ * You may obtain a copy of the License at
40
+ *
41
+ * http://www.apache.org/licenses/LICENSE-2.0
42
+ *
43
+ * Unless required by applicable law or agreed to in writing, software
44
+ * distributed under the License is distributed on an "AS IS" BASIS,
45
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46
+ * See the License for the specific language governing permissions and
47
+ * limitations under the License.
48
+ *
49
+ */
50
+
51
+ var hasRequiredUtils;
52
+
53
+ function requireUtils () {
54
+ if (hasRequiredUtils) return utils;
55
+ hasRequiredUtils = 1;
56
+ (function (exports) {
57
+
58
+ var regExpChars = /[|\\{}()[\]^$+*?.]/g;
59
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
60
+ var hasOwn = function (obj, key) { return hasOwnProperty.apply(obj, [key]); };
61
+
62
+ /**
63
+ * Escape characters reserved in regular expressions.
64
+ *
65
+ * If `string` is `undefined` or `null`, the empty string is returned.
66
+ *
67
+ * @param {String} string Input string
68
+ * @return {String} Escaped string
69
+ * @static
70
+ * @private
71
+ */
72
+ exports.escapeRegExpChars = function (string) {
73
+ // istanbul ignore if
74
+ if (!string) {
75
+ return '';
76
+ }
77
+ return String(string).replace(regExpChars, '\\$&');
78
+ };
79
+
80
+ var _ENCODE_HTML_RULES = {
81
+ '&': '&',
82
+ '<': '&lt;',
83
+ '>': '&gt;',
84
+ '"': '&#34;',
85
+ "'": '&#39;'
86
+ };
87
+ var _MATCH_HTML = /[&<>'"]/g;
88
+
89
+ function encode_char(c) {
90
+ return _ENCODE_HTML_RULES[c] || c;
91
+ }
92
+
93
+ /**
94
+ * Stringified version of constants used by {@link module:utils.escapeXML}.
95
+ *
96
+ * It is used in the process of generating {@link ClientFunction}s.
97
+ *
98
+ * @readonly
99
+ * @type {String}
100
+ */
101
+
102
+ var escapeFuncStr =
103
+ 'var _ENCODE_HTML_RULES = {\n'
104
+ + ' "&": "&amp;"\n'
105
+ + ' , "<": "&lt;"\n'
106
+ + ' , ">": "&gt;"\n'
107
+ + ' , \'"\': "&#34;"\n'
108
+ + ' , "\'": "&#39;"\n'
109
+ + ' }\n'
110
+ + ' , _MATCH_HTML = /[&<>\'"]/g;\n'
111
+ + 'function encode_char(c) {\n'
112
+ + ' return _ENCODE_HTML_RULES[c] || c;\n'
113
+ + '};\n';
114
+
115
+ /**
116
+ * Escape characters reserved in XML.
117
+ *
118
+ * If `markup` is `undefined` or `null`, the empty string is returned.
119
+ *
120
+ * @implements {EscapeCallback}
121
+ * @param {String} markup Input string
122
+ * @return {String} Escaped string
123
+ * @static
124
+ * @private
125
+ */
126
+
127
+ exports.escapeXML = function (markup) {
128
+ return markup == undefined
129
+ ? ''
130
+ : String(markup)
131
+ .replace(_MATCH_HTML, encode_char);
132
+ };
133
+
134
+ function escapeXMLToString() {
135
+ return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr;
136
+ }
137
+
138
+ try {
139
+ if (typeof Object.defineProperty === 'function') {
140
+ // If the Function prototype is frozen, the "toString" property is non-writable. This means that any objects which inherit this property
141
+ // cannot have the property changed using an assignment. If using strict mode, attempting that will cause an error. If not using strict
142
+ // mode, attempting that will be silently ignored.
143
+ // However, we can still explicitly shadow the prototype's "toString" property by defining a new "toString" property on this object.
144
+ Object.defineProperty(exports.escapeXML, 'toString', { value: escapeXMLToString });
145
+ } else {
146
+ // If Object.defineProperty() doesn't exist, attempt to shadow this property using the assignment operator.
147
+ exports.escapeXML.toString = escapeXMLToString;
148
+ }
149
+ } catch (err) {
150
+ console.warn('Unable to set escapeXML.toString (is the Function prototype frozen?)');
151
+ }
152
+
153
+ /**
154
+ * Naive copy of properties from one object to another.
155
+ * Does not recurse into non-scalar properties
156
+ * Does not check to see if the property has a value before copying
157
+ *
158
+ * @param {Object} to Destination object
159
+ * @param {Object} from Source object
160
+ * @return {Object} Destination object
161
+ * @static
162
+ * @private
163
+ */
164
+ exports.shallowCopy = function (to, from) {
165
+ from = from || {};
166
+ if ((to !== null) && (to !== undefined)) {
167
+ for (var p in from) {
168
+ if (!hasOwn(from, p)) {
169
+ continue;
170
+ }
171
+ if (p === '__proto__' || p === 'constructor') {
172
+ continue;
173
+ }
174
+ to[p] = from[p];
175
+ }
176
+ }
177
+ return to;
178
+ };
179
+
180
+ /**
181
+ * Naive copy of a list of key names, from one object to another.
182
+ * Only copies property if it is actually defined
183
+ * Does not recurse into non-scalar properties
184
+ *
185
+ * @param {Object} to Destination object
186
+ * @param {Object} from Source object
187
+ * @param {Array} list List of properties to copy
188
+ * @return {Object} Destination object
189
+ * @static
190
+ * @private
191
+ */
192
+ exports.shallowCopyFromList = function (to, from, list) {
193
+ list = list || [];
194
+ from = from || {};
195
+ if ((to !== null) && (to !== undefined)) {
196
+ for (var i = 0; i < list.length; i++) {
197
+ var p = list[i];
198
+ if (typeof from[p] != 'undefined') {
199
+ if (!hasOwn(from, p)) {
200
+ continue;
201
+ }
202
+ if (p === '__proto__' || p === 'constructor') {
203
+ continue;
204
+ }
205
+ to[p] = from[p];
206
+ }
207
+ }
208
+ }
209
+ return to;
210
+ };
211
+
212
+ /**
213
+ * Simple in-process cache implementation. Does not implement limits of any
214
+ * sort.
215
+ *
216
+ * @implements {Cache}
217
+ * @static
218
+ * @private
219
+ */
220
+ exports.cache = {
221
+ _data: {},
222
+ set: function (key, val) {
223
+ this._data[key] = val;
224
+ },
225
+ get: function (key) {
226
+ return this._data[key];
227
+ },
228
+ remove: function (key) {
229
+ delete this._data[key];
230
+ },
231
+ reset: function () {
232
+ this._data = {};
233
+ }
234
+ };
235
+
236
+ /**
237
+ * Transforms hyphen case variable into camel case.
238
+ *
239
+ * @param {String} string Hyphen case string
240
+ * @return {String} Camel case string
241
+ * @static
242
+ * @private
243
+ */
244
+ exports.hyphenToCamel = function (str) {
245
+ return str.replace(/-[a-z]/g, function (match) { return match[1].toUpperCase(); });
246
+ };
247
+
248
+ /**
249
+ * Returns a null-prototype object in runtimes that support it
250
+ *
251
+ * @return {Object} Object, prototype will be set to null where possible
252
+ * @static
253
+ * @private
254
+ */
255
+ exports.createNullProtoObjWherePossible = (function () {
256
+ if (typeof Object.create == 'function') {
257
+ return function () {
258
+ return Object.create(null);
259
+ };
260
+ }
261
+ if (!({__proto__: null} instanceof Object)) {
262
+ return function () {
263
+ return {__proto__: null};
264
+ };
265
+ }
266
+ // Not possible, just pass through
267
+ return function () {
268
+ return {};
269
+ };
270
+ })();
271
+
272
+ exports.hasOwnOnlyObject = function (obj) {
273
+ var o = exports.createNullProtoObjWherePossible();
274
+ for (var p in obj) {
275
+ if (hasOwn(obj, p)) {
276
+ o[p] = obj[p];
277
+ }
278
+ }
279
+ return o;
280
+ };
281
+ } (utils));
282
+ return utils;
283
+ }
284
+
285
+ var version = "3.1.10";
286
+ var require$$3 = {
287
+ version: version};
288
+
289
+ /*
290
+ * EJS Embedded JavaScript templates
291
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
292
+ *
293
+ * Licensed under the Apache License, Version 2.0 (the "License");
294
+ * you may not use this file except in compliance with the License.
295
+ * You may obtain a copy of the License at
296
+ *
297
+ * http://www.apache.org/licenses/LICENSE-2.0
298
+ *
299
+ * Unless required by applicable law or agreed to in writing, software
300
+ * distributed under the License is distributed on an "AS IS" BASIS,
301
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
302
+ * See the License for the specific language governing permissions and
303
+ * limitations under the License.
304
+ *
305
+ */
306
+
307
+ var hasRequiredEjs;
308
+
309
+ function requireEjs () {
310
+ if (hasRequiredEjs) return ejs$1;
311
+ hasRequiredEjs = 1;
312
+ (function (exports) {
313
+
314
+ /**
315
+ * @file Embedded JavaScript templating engine. {@link http://ejs.co}
316
+ * @author Matthew Eernisse <mde@fleegix.org>
317
+ * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com>
318
+ * @project EJS
319
+ * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0}
320
+ */
321
+
322
+ /**
323
+ * EJS internal functions.
324
+ *
325
+ * Technically this "module" lies in the same file as {@link module:ejs}, for
326
+ * the sake of organization all the private functions re grouped into this
327
+ * module.
328
+ *
329
+ * @module ejs-internal
330
+ * @private
331
+ */
332
+
333
+ /**
334
+ * Embedded JavaScript templating engine.
335
+ *
336
+ * @module ejs
337
+ * @public
338
+ */
339
+
340
+
341
+ var fs = fs__default;
342
+ var path$1 = path;
343
+ var utils = requireUtils();
344
+
345
+ var scopeOptionWarned = false;
346
+ /** @type {string} */
347
+ var _VERSION_STRING = require$$3.version;
348
+ var _DEFAULT_OPEN_DELIMITER = '<';
349
+ var _DEFAULT_CLOSE_DELIMITER = '>';
350
+ var _DEFAULT_DELIMITER = '%';
351
+ var _DEFAULT_LOCALS_NAME = 'locals';
352
+ var _NAME = 'ejs';
353
+ var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';
354
+ var _OPTS_PASSABLE_WITH_DATA = ['delimiter', 'scope', 'context', 'debug', 'compileDebug',
355
+ 'client', '_with', 'rmWhitespace', 'strict', 'filename', 'async'];
356
+ // We don't allow 'cache' option to be passed in the data obj for
357
+ // the normal `render` call, but this is where Express 2 & 3 put it
358
+ // so we make an exception for `renderFile`
359
+ var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache');
360
+ var _BOM = /^\uFEFF/;
361
+ var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
362
+
363
+ /**
364
+ * EJS template function cache. This can be a LRU object from lru-cache NPM
365
+ * module. By default, it is {@link module:utils.cache}, a simple in-process
366
+ * cache that grows continuously.
367
+ *
368
+ * @type {Cache}
369
+ */
370
+
371
+ exports.cache = utils.cache;
372
+
373
+ /**
374
+ * Custom file loader. Useful for template preprocessing or restricting access
375
+ * to a certain part of the filesystem.
376
+ *
377
+ * @type {fileLoader}
378
+ */
379
+
380
+ exports.fileLoader = fs.readFileSync;
381
+
382
+ /**
383
+ * Name of the object containing the locals.
384
+ *
385
+ * This variable is overridden by {@link Options}`.localsName` if it is not
386
+ * `undefined`.
387
+ *
388
+ * @type {String}
389
+ * @public
390
+ */
391
+
392
+ exports.localsName = _DEFAULT_LOCALS_NAME;
393
+
394
+ /**
395
+ * Promise implementation -- defaults to the native implementation if available
396
+ * This is mostly just for testability
397
+ *
398
+ * @type {PromiseConstructorLike}
399
+ * @public
400
+ */
401
+
402
+ exports.promiseImpl = (new Function('return this;'))().Promise;
403
+
404
+ /**
405
+ * Get the path to the included file from the parent file path and the
406
+ * specified path.
407
+ *
408
+ * @param {String} name specified path
409
+ * @param {String} filename parent file path
410
+ * @param {Boolean} [isDir=false] whether the parent file path is a directory
411
+ * @return {String}
412
+ */
413
+ exports.resolveInclude = function(name, filename, isDir) {
414
+ var dirname = path$1.dirname;
415
+ var extname = path$1.extname;
416
+ var resolve = path$1.resolve;
417
+ var includePath = resolve(isDir ? filename : dirname(filename), name);
418
+ var ext = extname(name);
419
+ if (!ext) {
420
+ includePath += '.ejs';
421
+ }
422
+ return includePath;
423
+ };
424
+
425
+ /**
426
+ * Try to resolve file path on multiple directories
427
+ *
428
+ * @param {String} name specified path
429
+ * @param {Array<String>} paths list of possible parent directory paths
430
+ * @return {String}
431
+ */
432
+ function resolvePaths(name, paths) {
433
+ var filePath;
434
+ if (paths.some(function (v) {
435
+ filePath = exports.resolveInclude(name, v, true);
436
+ return fs.existsSync(filePath);
437
+ })) {
438
+ return filePath;
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Get the path to the included file by Options
444
+ *
445
+ * @param {String} path specified path
446
+ * @param {Options} options compilation options
447
+ * @return {String}
448
+ */
449
+ function getIncludePath(path, options) {
450
+ var includePath;
451
+ var filePath;
452
+ var views = options.views;
453
+ var match = /^[A-Za-z]+:\\|^\//.exec(path);
454
+
455
+ // Abs path
456
+ if (match && match.length) {
457
+ path = path.replace(/^\/*/, '');
458
+ if (Array.isArray(options.root)) {
459
+ includePath = resolvePaths(path, options.root);
460
+ } else {
461
+ includePath = exports.resolveInclude(path, options.root || '/', true);
462
+ }
463
+ }
464
+ // Relative paths
465
+ else {
466
+ // Look relative to a passed filename first
467
+ if (options.filename) {
468
+ filePath = exports.resolveInclude(path, options.filename);
469
+ if (fs.existsSync(filePath)) {
470
+ includePath = filePath;
471
+ }
472
+ }
473
+ // Then look in any views directories
474
+ if (!includePath && Array.isArray(views)) {
475
+ includePath = resolvePaths(path, views);
476
+ }
477
+ if (!includePath && typeof options.includer !== 'function') {
478
+ throw new Error('Could not find the include file "' +
479
+ options.escapeFunction(path) + '"');
480
+ }
481
+ }
482
+ return includePath;
483
+ }
484
+
485
+ /**
486
+ * Get the template from a string or a file, either compiled on-the-fly or
487
+ * read from cache (if enabled), and cache the template if needed.
488
+ *
489
+ * If `template` is not set, the file specified in `options.filename` will be
490
+ * read.
491
+ *
492
+ * If `options.cache` is true, this function reads the file from
493
+ * `options.filename` so it must be set prior to calling this function.
494
+ *
495
+ * @memberof module:ejs-internal
496
+ * @param {Options} options compilation options
497
+ * @param {String} [template] template source
498
+ * @return {(TemplateFunction|ClientFunction)}
499
+ * Depending on the value of `options.client`, either type might be returned.
500
+ * @static
501
+ */
502
+
503
+ function handleCache(options, template) {
504
+ var func;
505
+ var filename = options.filename;
506
+ var hasTemplate = arguments.length > 1;
507
+
508
+ if (options.cache) {
509
+ if (!filename) {
510
+ throw new Error('cache option requires a filename');
511
+ }
512
+ func = exports.cache.get(filename);
513
+ if (func) {
514
+ return func;
515
+ }
516
+ if (!hasTemplate) {
517
+ template = fileLoader(filename).toString().replace(_BOM, '');
518
+ }
519
+ }
520
+ else if (!hasTemplate) {
521
+ // istanbul ignore if: should not happen at all
522
+ if (!filename) {
523
+ throw new Error('Internal EJS error: no file name or template '
524
+ + 'provided');
525
+ }
526
+ template = fileLoader(filename).toString().replace(_BOM, '');
527
+ }
528
+ func = exports.compile(template, options);
529
+ if (options.cache) {
530
+ exports.cache.set(filename, func);
531
+ }
532
+ return func;
533
+ }
534
+
535
+ /**
536
+ * Try calling handleCache with the given options and data and call the
537
+ * callback with the result. If an error occurs, call the callback with
538
+ * the error. Used by renderFile().
539
+ *
540
+ * @memberof module:ejs-internal
541
+ * @param {Options} options compilation options
542
+ * @param {Object} data template data
543
+ * @param {RenderFileCallback} cb callback
544
+ * @static
545
+ */
546
+
547
+ function tryHandleCache(options, data, cb) {
548
+ var result;
549
+ if (!cb) {
550
+ if (typeof exports.promiseImpl == 'function') {
551
+ return new exports.promiseImpl(function (resolve, reject) {
552
+ try {
553
+ result = handleCache(options)(data);
554
+ resolve(result);
555
+ }
556
+ catch (err) {
557
+ reject(err);
558
+ }
559
+ });
560
+ }
561
+ else {
562
+ throw new Error('Please provide a callback function');
563
+ }
564
+ }
565
+ else {
566
+ try {
567
+ result = handleCache(options)(data);
568
+ }
569
+ catch (err) {
570
+ return cb(err);
571
+ }
572
+
573
+ cb(null, result);
574
+ }
575
+ }
576
+
577
+ /**
578
+ * fileLoader is independent
579
+ *
580
+ * @param {String} filePath ejs file path.
581
+ * @return {String} The contents of the specified file.
582
+ * @static
583
+ */
584
+
585
+ function fileLoader(filePath){
586
+ return exports.fileLoader(filePath);
587
+ }
588
+
589
+ /**
590
+ * Get the template function.
591
+ *
592
+ * If `options.cache` is `true`, then the template is cached.
593
+ *
594
+ * @memberof module:ejs-internal
595
+ * @param {String} path path for the specified file
596
+ * @param {Options} options compilation options
597
+ * @return {(TemplateFunction|ClientFunction)}
598
+ * Depending on the value of `options.client`, either type might be returned
599
+ * @static
600
+ */
601
+
602
+ function includeFile(path, options) {
603
+ var opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options);
604
+ opts.filename = getIncludePath(path, opts);
605
+ if (typeof options.includer === 'function') {
606
+ var includerResult = options.includer(path, opts.filename);
607
+ if (includerResult) {
608
+ if (includerResult.filename) {
609
+ opts.filename = includerResult.filename;
610
+ }
611
+ if (includerResult.template) {
612
+ return handleCache(opts, includerResult.template);
613
+ }
614
+ }
615
+ }
616
+ return handleCache(opts);
617
+ }
618
+
619
+ /**
620
+ * Re-throw the given `err` in context to the `str` of ejs, `filename`, and
621
+ * `lineno`.
622
+ *
623
+ * @implements {RethrowCallback}
624
+ * @memberof module:ejs-internal
625
+ * @param {Error} err Error object
626
+ * @param {String} str EJS source
627
+ * @param {String} flnm file name of the EJS file
628
+ * @param {Number} lineno line number of the error
629
+ * @param {EscapeCallback} esc
630
+ * @static
631
+ */
632
+
633
+ function rethrow(err, str, flnm, lineno, esc) {
634
+ var lines = str.split('\n');
635
+ var start = Math.max(lineno - 3, 0);
636
+ var end = Math.min(lines.length, lineno + 3);
637
+ var filename = esc(flnm);
638
+ // Error context
639
+ var context = lines.slice(start, end).map(function (line, i){
640
+ var curr = i + start + 1;
641
+ return (curr == lineno ? ' >> ' : ' ')
642
+ + curr
643
+ + '| '
644
+ + line;
645
+ }).join('\n');
646
+
647
+ // Alter exception message
648
+ err.path = filename;
649
+ err.message = (filename || 'ejs') + ':'
650
+ + lineno + '\n'
651
+ + context + '\n\n'
652
+ + err.message;
653
+
654
+ throw err;
655
+ }
656
+
657
+ function stripSemi(str){
658
+ return str.replace(/;(\s*$)/, '$1');
659
+ }
660
+
661
+ /**
662
+ * Compile the given `str` of ejs into a template function.
663
+ *
664
+ * @param {String} template EJS template
665
+ *
666
+ * @param {Options} [opts] compilation options
667
+ *
668
+ * @return {(TemplateFunction|ClientFunction)}
669
+ * Depending on the value of `opts.client`, either type might be returned.
670
+ * Note that the return type of the function also depends on the value of `opts.async`.
671
+ * @public
672
+ */
673
+
674
+ exports.compile = function compile(template, opts) {
675
+ var templ;
676
+
677
+ // v1 compat
678
+ // 'scope' is 'context'
679
+ // FIXME: Remove this in a future version
680
+ if (opts && opts.scope) {
681
+ if (!scopeOptionWarned){
682
+ console.warn('`scope` option is deprecated and will be removed in EJS 3');
683
+ scopeOptionWarned = true;
684
+ }
685
+ if (!opts.context) {
686
+ opts.context = opts.scope;
687
+ }
688
+ delete opts.scope;
689
+ }
690
+ templ = new Template(template, opts);
691
+ return templ.compile();
692
+ };
693
+
694
+ /**
695
+ * Render the given `template` of ejs.
696
+ *
697
+ * If you would like to include options but not data, you need to explicitly
698
+ * call this function with `data` being an empty object or `null`.
699
+ *
700
+ * @param {String} template EJS template
701
+ * @param {Object} [data={}] template data
702
+ * @param {Options} [opts={}] compilation and rendering options
703
+ * @return {(String|Promise<String>)}
704
+ * Return value type depends on `opts.async`.
705
+ * @public
706
+ */
707
+
708
+ exports.render = function (template, d, o) {
709
+ var data = d || utils.createNullProtoObjWherePossible();
710
+ var opts = o || utils.createNullProtoObjWherePossible();
711
+
712
+ // No options object -- if there are optiony names
713
+ // in the data, copy them to options
714
+ if (arguments.length == 2) {
715
+ utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);
716
+ }
717
+
718
+ return handleCache(opts, template)(data);
719
+ };
720
+
721
+ /**
722
+ * Render an EJS file at the given `path` and callback `cb(err, str)`.
723
+ *
724
+ * If you would like to include options but not data, you need to explicitly
725
+ * call this function with `data` being an empty object or `null`.
726
+ *
727
+ * @param {String} path path to the EJS file
728
+ * @param {Object} [data={}] template data
729
+ * @param {Options} [opts={}] compilation and rendering options
730
+ * @param {RenderFileCallback} cb callback
731
+ * @public
732
+ */
733
+
734
+ exports.renderFile = function () {
735
+ var args = Array.prototype.slice.call(arguments);
736
+ var filename = args.shift();
737
+ var cb;
738
+ var opts = {filename: filename};
739
+ var data;
740
+ var viewOpts;
741
+
742
+ // Do we have a callback?
743
+ if (typeof arguments[arguments.length - 1] == 'function') {
744
+ cb = args.pop();
745
+ }
746
+ // Do we have data/opts?
747
+ if (args.length) {
748
+ // Should always have data obj
749
+ data = args.shift();
750
+ // Normal passed opts (data obj + opts obj)
751
+ if (args.length) {
752
+ // Use shallowCopy so we don't pollute passed in opts obj with new vals
753
+ utils.shallowCopy(opts, args.pop());
754
+ }
755
+ // Special casing for Express (settings + opts-in-data)
756
+ else {
757
+ // Express 3 and 4
758
+ if (data.settings) {
759
+ // Pull a few things from known locations
760
+ if (data.settings.views) {
761
+ opts.views = data.settings.views;
762
+ }
763
+ if (data.settings['view cache']) {
764
+ opts.cache = true;
765
+ }
766
+ // Undocumented after Express 2, but still usable, esp. for
767
+ // items that are unsafe to be passed along with data, like `root`
768
+ viewOpts = data.settings['view options'];
769
+ if (viewOpts) {
770
+ utils.shallowCopy(opts, viewOpts);
771
+ }
772
+ }
773
+ // Express 2 and lower, values set in app.locals, or people who just
774
+ // want to pass options in their data. NOTE: These values will override
775
+ // anything previously set in settings or settings['view options']
776
+ utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS);
777
+ }
778
+ opts.filename = filename;
779
+ }
780
+ else {
781
+ data = utils.createNullProtoObjWherePossible();
782
+ }
783
+
784
+ return tryHandleCache(opts, data, cb);
785
+ };
786
+
787
+ /**
788
+ * Clear intermediate JavaScript cache. Calls {@link Cache#reset}.
789
+ * @public
790
+ */
791
+
792
+ /**
793
+ * EJS template class
794
+ * @public
795
+ */
796
+ exports.Template = Template;
797
+
798
+ exports.clearCache = function () {
799
+ exports.cache.reset();
800
+ };
801
+
802
+ function Template(text, optsParam) {
803
+ var opts = utils.hasOwnOnlyObject(optsParam);
804
+ var options = utils.createNullProtoObjWherePossible();
805
+ this.templateText = text;
806
+ /** @type {string | null} */
807
+ this.mode = null;
808
+ this.truncate = false;
809
+ this.currentLine = 1;
810
+ this.source = '';
811
+ options.client = opts.client || false;
812
+ options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML;
813
+ options.compileDebug = opts.compileDebug !== false;
814
+ options.debug = !!opts.debug;
815
+ options.filename = opts.filename;
816
+ options.openDelimiter = opts.openDelimiter || exports.openDelimiter || _DEFAULT_OPEN_DELIMITER;
817
+ options.closeDelimiter = opts.closeDelimiter || exports.closeDelimiter || _DEFAULT_CLOSE_DELIMITER;
818
+ options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;
819
+ options.strict = opts.strict || false;
820
+ options.context = opts.context;
821
+ options.cache = opts.cache || false;
822
+ options.rmWhitespace = opts.rmWhitespace;
823
+ options.root = opts.root;
824
+ options.includer = opts.includer;
825
+ options.outputFunctionName = opts.outputFunctionName;
826
+ options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
827
+ options.views = opts.views;
828
+ options.async = opts.async;
829
+ options.destructuredLocals = opts.destructuredLocals;
830
+ options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true;
831
+
832
+ if (options.strict) {
833
+ options._with = false;
834
+ }
835
+ else {
836
+ options._with = typeof opts._with != 'undefined' ? opts._with : true;
837
+ }
838
+
839
+ this.opts = options;
840
+
841
+ this.regex = this.createRegex();
842
+ }
843
+
844
+ Template.modes = {
845
+ EVAL: 'eval',
846
+ ESCAPED: 'escaped',
847
+ RAW: 'raw',
848
+ COMMENT: 'comment',
849
+ LITERAL: 'literal'
850
+ };
851
+
852
+ Template.prototype = {
853
+ createRegex: function () {
854
+ var str = _REGEX_STRING;
855
+ var delim = utils.escapeRegExpChars(this.opts.delimiter);
856
+ var open = utils.escapeRegExpChars(this.opts.openDelimiter);
857
+ var close = utils.escapeRegExpChars(this.opts.closeDelimiter);
858
+ str = str.replace(/%/g, delim)
859
+ .replace(/</g, open)
860
+ .replace(/>/g, close);
861
+ return new RegExp(str);
862
+ },
863
+
864
+ compile: function () {
865
+ /** @type {string} */
866
+ var src;
867
+ /** @type {ClientFunction} */
868
+ var fn;
869
+ var opts = this.opts;
870
+ var prepended = '';
871
+ var appended = '';
872
+ /** @type {EscapeCallback} */
873
+ var escapeFn = opts.escapeFunction;
874
+ /** @type {FunctionConstructor} */
875
+ var ctor;
876
+ /** @type {string} */
877
+ var sanitizedFilename = opts.filename ? JSON.stringify(opts.filename) : 'undefined';
878
+
879
+ if (!this.source) {
880
+ this.generateSource();
881
+ prepended +=
882
+ ' var __output = "";\n' +
883
+ ' function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
884
+ if (opts.outputFunctionName) {
885
+ if (!_JS_IDENTIFIER.test(opts.outputFunctionName)) {
886
+ throw new Error('outputFunctionName is not a valid JS identifier.');
887
+ }
888
+ prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
889
+ }
890
+ if (opts.localsName && !_JS_IDENTIFIER.test(opts.localsName)) {
891
+ throw new Error('localsName is not a valid JS identifier.');
892
+ }
893
+ if (opts.destructuredLocals && opts.destructuredLocals.length) {
894
+ var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
895
+ for (var i = 0; i < opts.destructuredLocals.length; i++) {
896
+ var name = opts.destructuredLocals[i];
897
+ if (!_JS_IDENTIFIER.test(name)) {
898
+ throw new Error('destructuredLocals[' + i + '] is not a valid JS identifier.');
899
+ }
900
+ if (i > 0) {
901
+ destructuring += ',\n ';
902
+ }
903
+ destructuring += name + ' = __locals.' + name;
904
+ }
905
+ prepended += destructuring + ';\n';
906
+ }
907
+ if (opts._with !== false) {
908
+ prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
909
+ appended += ' }' + '\n';
910
+ }
911
+ appended += ' return __output;' + '\n';
912
+ this.source = prepended + this.source + appended;
913
+ }
914
+
915
+ if (opts.compileDebug) {
916
+ src = 'var __line = 1' + '\n'
917
+ + ' , __lines = ' + JSON.stringify(this.templateText) + '\n'
918
+ + ' , __filename = ' + sanitizedFilename + ';' + '\n'
919
+ + 'try {' + '\n'
920
+ + this.source
921
+ + '} catch (e) {' + '\n'
922
+ + ' rethrow(e, __lines, __filename, __line, escapeFn);' + '\n'
923
+ + '}' + '\n';
924
+ }
925
+ else {
926
+ src = this.source;
927
+ }
928
+
929
+ if (opts.client) {
930
+ src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src;
931
+ if (opts.compileDebug) {
932
+ src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
933
+ }
934
+ }
935
+
936
+ if (opts.strict) {
937
+ src = '"use strict";\n' + src;
938
+ }
939
+ if (opts.debug) {
940
+ console.log(src);
941
+ }
942
+ if (opts.compileDebug && opts.filename) {
943
+ src = src + '\n'
944
+ + '//# sourceURL=' + sanitizedFilename + '\n';
945
+ }
946
+
947
+ try {
948
+ if (opts.async) {
949
+ // Have to use generated function for this, since in envs without support,
950
+ // it breaks in parsing
951
+ try {
952
+ ctor = (new Function('return (async function(){}).constructor;'))();
953
+ }
954
+ catch(e) {
955
+ if (e instanceof SyntaxError) {
956
+ throw new Error('This environment does not support async/await');
957
+ }
958
+ else {
959
+ throw e;
960
+ }
961
+ }
962
+ }
963
+ else {
964
+ ctor = Function;
965
+ }
966
+ fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src);
967
+ }
968
+ catch(e) {
969
+ // istanbul ignore else
970
+ if (e instanceof SyntaxError) {
971
+ if (opts.filename) {
972
+ e.message += ' in ' + opts.filename;
973
+ }
974
+ e.message += ' while compiling ejs\n\n';
975
+ e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n';
976
+ e.message += 'https://github.com/RyanZim/EJS-Lint';
977
+ if (!opts.async) {
978
+ e.message += '\n';
979
+ e.message += 'Or, if you meant to create an async function, pass `async: true` as an option.';
980
+ }
981
+ }
982
+ throw e;
983
+ }
984
+
985
+ // Return a callable function which will execute the function
986
+ // created by the source-code, with the passed data as locals
987
+ // Adds a local `include` function which allows full recursive include
988
+ var returnedFn = opts.client ? fn : function anonymous(data) {
989
+ var include = function (path, includeData) {
990
+ var d = utils.shallowCopy(utils.createNullProtoObjWherePossible(), data);
991
+ if (includeData) {
992
+ d = utils.shallowCopy(d, includeData);
993
+ }
994
+ return includeFile(path, opts)(d);
995
+ };
996
+ return fn.apply(opts.context,
997
+ [data || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]);
998
+ };
999
+ if (opts.filename && typeof Object.defineProperty === 'function') {
1000
+ var filename = opts.filename;
1001
+ var basename = path$1.basename(filename, path$1.extname(filename));
1002
+ try {
1003
+ Object.defineProperty(returnedFn, 'name', {
1004
+ value: basename,
1005
+ writable: false,
1006
+ enumerable: false,
1007
+ configurable: true
1008
+ });
1009
+ } catch (e) {/* ignore */}
1010
+ }
1011
+ return returnedFn;
1012
+ },
1013
+
1014
+ generateSource: function () {
1015
+ var opts = this.opts;
1016
+
1017
+ if (opts.rmWhitespace) {
1018
+ // Have to use two separate replace here as `^` and `$` operators don't
1019
+ // work well with `\r` and empty lines don't work well with the `m` flag.
1020
+ this.templateText =
1021
+ this.templateText.replace(/[\r\n]+/g, '\n').replace(/^\s+|\s+$/gm, '');
1022
+ }
1023
+
1024
+ // Slurp spaces and tabs before <%_ and after _%>
1025
+ this.templateText =
1026
+ this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>');
1027
+
1028
+ var self = this;
1029
+ var matches = this.parseTemplateText();
1030
+ var d = this.opts.delimiter;
1031
+ var o = this.opts.openDelimiter;
1032
+ var c = this.opts.closeDelimiter;
1033
+
1034
+ if (matches && matches.length) {
1035
+ matches.forEach(function (line, index) {
1036
+ var closing;
1037
+ // If this is an opening tag, check for closing tags
1038
+ // FIXME: May end up with some false positives here
1039
+ // Better to store modes as k/v with openDelimiter + delimiter as key
1040
+ // Then this can simply check against the map
1041
+ if ( line.indexOf(o + d) === 0 // If it is a tag
1042
+ && line.indexOf(o + d + d) !== 0) { // and is not escaped
1043
+ closing = matches[index + 2];
1044
+ if (!(closing == d + c || closing == '-' + d + c || closing == '_' + d + c)) {
1045
+ throw new Error('Could not find matching close tag for "' + line + '".');
1046
+ }
1047
+ }
1048
+ self.scanLine(line);
1049
+ });
1050
+ }
1051
+
1052
+ },
1053
+
1054
+ parseTemplateText: function () {
1055
+ var str = this.templateText;
1056
+ var pat = this.regex;
1057
+ var result = pat.exec(str);
1058
+ var arr = [];
1059
+ var firstPos;
1060
+
1061
+ while (result) {
1062
+ firstPos = result.index;
1063
+
1064
+ if (firstPos !== 0) {
1065
+ arr.push(str.substring(0, firstPos));
1066
+ str = str.slice(firstPos);
1067
+ }
1068
+
1069
+ arr.push(result[0]);
1070
+ str = str.slice(result[0].length);
1071
+ result = pat.exec(str);
1072
+ }
1073
+
1074
+ if (str) {
1075
+ arr.push(str);
1076
+ }
1077
+
1078
+ return arr;
1079
+ },
1080
+
1081
+ _addOutput: function (line) {
1082
+ if (this.truncate) {
1083
+ // Only replace single leading linebreak in the line after
1084
+ // -%> tag -- this is the single, trailing linebreak
1085
+ // after the tag that the truncation mode replaces
1086
+ // Handle Win / Unix / old Mac linebreaks -- do the \r\n
1087
+ // combo first in the regex-or
1088
+ line = line.replace(/^(?:\r\n|\r|\n)/, '');
1089
+ this.truncate = false;
1090
+ }
1091
+ if (!line) {
1092
+ return line;
1093
+ }
1094
+
1095
+ // Preserve literal slashes
1096
+ line = line.replace(/\\/g, '\\\\');
1097
+
1098
+ // Convert linebreaks
1099
+ line = line.replace(/\n/g, '\\n');
1100
+ line = line.replace(/\r/g, '\\r');
1101
+
1102
+ // Escape double-quotes
1103
+ // - this will be the delimiter during execution
1104
+ line = line.replace(/"/g, '\\"');
1105
+ this.source += ' ; __append("' + line + '")' + '\n';
1106
+ },
1107
+
1108
+ scanLine: function (line) {
1109
+ var self = this;
1110
+ var d = this.opts.delimiter;
1111
+ var o = this.opts.openDelimiter;
1112
+ var c = this.opts.closeDelimiter;
1113
+ var newLineCount = 0;
1114
+
1115
+ newLineCount = (line.split('\n').length - 1);
1116
+
1117
+ switch (line) {
1118
+ case o + d:
1119
+ case o + d + '_':
1120
+ this.mode = Template.modes.EVAL;
1121
+ break;
1122
+ case o + d + '=':
1123
+ this.mode = Template.modes.ESCAPED;
1124
+ break;
1125
+ case o + d + '-':
1126
+ this.mode = Template.modes.RAW;
1127
+ break;
1128
+ case o + d + '#':
1129
+ this.mode = Template.modes.COMMENT;
1130
+ break;
1131
+ case o + d + d:
1132
+ this.mode = Template.modes.LITERAL;
1133
+ this.source += ' ; __append("' + line.replace(o + d + d, o + d) + '")' + '\n';
1134
+ break;
1135
+ case d + d + c:
1136
+ this.mode = Template.modes.LITERAL;
1137
+ this.source += ' ; __append("' + line.replace(d + d + c, d + c) + '")' + '\n';
1138
+ break;
1139
+ case d + c:
1140
+ case '-' + d + c:
1141
+ case '_' + d + c:
1142
+ if (this.mode == Template.modes.LITERAL) {
1143
+ this._addOutput(line);
1144
+ }
1145
+
1146
+ this.mode = null;
1147
+ this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;
1148
+ break;
1149
+ default:
1150
+ // In script mode, depends on type of tag
1151
+ if (this.mode) {
1152
+ // If '//' is found without a line break, add a line break.
1153
+ switch (this.mode) {
1154
+ case Template.modes.EVAL:
1155
+ case Template.modes.ESCAPED:
1156
+ case Template.modes.RAW:
1157
+ if (line.lastIndexOf('//') > line.lastIndexOf('\n')) {
1158
+ line += '\n';
1159
+ }
1160
+ }
1161
+ switch (this.mode) {
1162
+ // Just executing code
1163
+ case Template.modes.EVAL:
1164
+ this.source += ' ; ' + line + '\n';
1165
+ break;
1166
+ // Exec, esc, and output
1167
+ case Template.modes.ESCAPED:
1168
+ this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n';
1169
+ break;
1170
+ // Exec and output
1171
+ case Template.modes.RAW:
1172
+ this.source += ' ; __append(' + stripSemi(line) + ')' + '\n';
1173
+ break;
1174
+ case Template.modes.COMMENT:
1175
+ // Do nothing
1176
+ break;
1177
+ // Literal <%% mode, append as raw output
1178
+ case Template.modes.LITERAL:
1179
+ this._addOutput(line);
1180
+ break;
1181
+ }
1182
+ }
1183
+ // In string mode, just add the output
1184
+ else {
1185
+ this._addOutput(line);
1186
+ }
1187
+ }
1188
+
1189
+ if (self.opts.compileDebug && newLineCount) {
1190
+ this.currentLine += newLineCount;
1191
+ this.source += ' ; __line = ' + this.currentLine + '\n';
1192
+ }
1193
+ }
1194
+ };
1195
+
1196
+ /**
1197
+ * Escape characters reserved in XML.
1198
+ *
1199
+ * This is simply an export of {@link module:utils.escapeXML}.
1200
+ *
1201
+ * If `markup` is `undefined` or `null`, the empty string is returned.
1202
+ *
1203
+ * @param {String} markup Input string
1204
+ * @return {String} Escaped string
1205
+ * @public
1206
+ * @func
1207
+ * */
1208
+ exports.escapeXML = utils.escapeXML;
1209
+
1210
+ /**
1211
+ * Express.js support.
1212
+ *
1213
+ * This is an alias for {@link module:ejs.renderFile}, in order to support
1214
+ * Express.js out-of-the-box.
1215
+ *
1216
+ * @func
1217
+ */
1218
+
1219
+ exports.__express = exports.renderFile;
1220
+
1221
+ /**
1222
+ * Version of EJS.
1223
+ *
1224
+ * @readonly
1225
+ * @type {String}
1226
+ * @public
1227
+ */
1228
+
1229
+ exports.VERSION = _VERSION_STRING;
1230
+
1231
+ /**
1232
+ * Name for detection of EJS.
1233
+ *
1234
+ * @readonly
1235
+ * @type {String}
1236
+ * @public
1237
+ */
1238
+
1239
+ exports.name = _NAME;
1240
+
1241
+ /* istanbul ignore if */
1242
+ if (typeof window != 'undefined') {
1243
+ window.ejs = exports;
1244
+ }
1245
+ } (ejs$1));
1246
+ return ejs$1;
1247
+ }
1248
+
1249
+ var ejsExports = requireEjs();
1250
+ var ejs = /*@__PURE__*/getDefaultExportFromCjs(ejsExports);
1251
+
1252
+ const ROOT_TOPIC_NAME = "shipthis";
1253
+ const ROOT_TOPIC_DESCRIPTION = "Root topic";
1254
+ const ROOT_TOPIC_FILENAME = "README.md";
1255
+ const TOPIC_TEMPLATE = `
1256
+ # Topic: \`<%= topic.name.replaceAll(":", " ") %>\`
1257
+
1258
+ <%= topic.description || "" %>
1259
+
1260
+ <% if (subTopics && subTopics.length > 0) { -%>
1261
+ ## Topics
1262
+
1263
+ <% subTopics.forEach(subTopic => { -%>
1264
+ - [<%= subTopic.topic.name %>](<%= subTopic.filePath %>)
1265
+ <% }) -%>
1266
+ <% } -%>
1267
+
1268
+ <% if (commands && commands.length > 0) { -%>
1269
+ ## Commands
1270
+
1271
+ <% commands.forEach(readmeCommand => { -%>
1272
+ - [<%= readmeCommand.command.id %>](<%= readmeCommand.filePath %>)
1273
+ <% }) -%>
1274
+ <% } -%>
1275
+ `.trim();
1276
+ const TOPIC_TEMPLATE_INCLUDE = `
1277
+ # Topic: \`<%= topic.name.replaceAll(":", " ") %>\`
1278
+
1279
+ <%= topic.description || "" %>
1280
+
1281
+ <% if (subTopics && subTopics.length > 0) { -%>
1282
+ ## Topics
1283
+
1284
+ <% subTopics.forEach(subTopic => { %>
1285
+ <%- subTopic.rendered %>
1286
+ <% }) -%>
1287
+ <% } -%>
1288
+
1289
+ <% if (commands && commands.length > 0) { -%>
1290
+ ## Commands
1291
+
1292
+ <% commands.forEach(readmeCommand => { %>
1293
+ <%- readmeCommand.renderedForInclude %>
1294
+ <% }) -%>
1295
+ <% } -%>
1296
+ `.trim();
1297
+ const COMMAND_TEMPLATE = `
1298
+ # Command: \`<%= command.id %>\`
1299
+
1300
+ ## Description
1301
+
1302
+ <%= command.description || "" %>
1303
+
1304
+ ## Help Output
1305
+
1306
+ \`\`\`
1307
+ <%- helpOutput %>
1308
+ \`\`\`
1309
+ `.trim();
1310
+ const COMMAND_TEMPLATE_INCLUDE = `
1311
+ ### \`<%= command.id %>\`
1312
+
1313
+ #### Description
1314
+
1315
+ <%= command.description || "" %>
1316
+
1317
+ #### Help Output
1318
+
1319
+ \`\`\`
1320
+ <%- helpOutput %>
1321
+ \`\`\`
1322
+ `.trim();
1323
+ function getTopicTree(topics, commands, separateFileDepth) {
1324
+ const commandIds = commands.map((command) => command.id);
1325
+ const nonInternalTopics = topics.filter((topic) => topic.name !== "internal");
1326
+ const nonCommandTopics = nonInternalTopics.filter((topic) => !commandIds.includes(topic.name));
1327
+ let topicTree = {
1328
+ [ROOT_TOPIC_NAME]: {
1329
+ filePath: ROOT_TOPIC_FILENAME,
1330
+ topic: { name: ROOT_TOPIC_NAME, description: ROOT_TOPIC_DESCRIPTION },
1331
+ subTopics: [],
1332
+ commands: [],
1333
+ includeTopicsAndCommands: separateFileDepth === 0
1334
+ }
1335
+ };
1336
+ const topicsByName = Object.fromEntries(topics.map((topic) => [topic.name, topic]));
1337
+ for (const topic of nonCommandTopics) {
1338
+ const topicPath = topic.name.split(":");
1339
+ let currentParent = topicTree[ROOT_TOPIC_NAME];
1340
+ for (let i = 0; i < topicPath.length; i++) {
1341
+ const name = topicPath.slice(0, i + 1).join(":");
1342
+ const subTopic = currentParent.subTopics.find((subTopic2) => subTopic2.topic.name === name);
1343
+ if (!subTopic) {
1344
+ const currentDepth = i + 1;
1345
+ const includeTopicsAndCommands = currentParent.includeTopicsAndCommands || currentDepth >= separateFileDepth;
1346
+ const newSubTopic = {
1347
+ topic: topicsByName[name],
1348
+ subTopics: [],
1349
+ commands: [],
1350
+ filePath: `${path.join(...name.split(":"))}.md`,
1351
+ includeTopicsAndCommands
1352
+ };
1353
+ currentParent.subTopics.push(newSubTopic);
1354
+ currentParent = newSubTopic;
1355
+ } else {
1356
+ currentParent = subTopic;
1357
+ }
1358
+ }
1359
+ }
1360
+ for (const command of commands) {
1361
+ const commandPath = command.id.split(":");
1362
+ if (commandPath[0] === "internal") continue;
1363
+ let currentParent = topicTree[ROOT_TOPIC_NAME];
1364
+ for (let i = 0; i < commandPath.length - 1; i++) {
1365
+ const name = commandPath.slice(0, i + 1).join(":");
1366
+ const subTopic = currentParent.subTopics.find((subTopic2) => subTopic2.topic.name === name);
1367
+ if (!subTopic) throw new Error("Could not find topic for command: " + command.id);
1368
+ currentParent = subTopic;
1369
+ }
1370
+ currentParent.commands.push({ command, filePath: `${path.join(...command.id.split(":"))}.md` });
1371
+ }
1372
+ return topicTree;
1373
+ }
1374
+ function renderTopic(readmeTopic, config) {
1375
+ const renderedSubTopics = readmeTopic.subTopics.map((subTopic) => renderTopic(subTopic, config));
1376
+ const renderedCommands = readmeTopic.commands.map((readmeCommand) => renderCommand(readmeCommand, config));
1377
+ const topicTemplate = readmeTopic.includeTopicsAndCommands ? TOPIC_TEMPLATE_INCLUDE : TOPIC_TEMPLATE;
1378
+ const rendered = ejs.render(topicTemplate, {
1379
+ topic: readmeTopic.topic,
1380
+ subTopics: renderedSubTopics,
1381
+ commands: renderedCommands
1382
+ });
1383
+ return { ...readmeTopic, subTopics: renderedSubTopics, commands: renderedCommands, rendered };
1384
+ }
1385
+ function renderCommand(readmeCommand, config) {
1386
+ const columns = Number.parseInt(process.env.COLUMNS, 10) || 120;
1387
+ const help = new CustomHelp(config, { maxWidth: columns, stripAnsi: true });
1388
+ const helpOutput = help.exposedFormatCommand(readmeCommand.command);
1389
+ const renderedForInclude = ejs.render(COMMAND_TEMPLATE_INCLUDE, { command: readmeCommand.command, helpOutput });
1390
+ const renderedForFile = ejs.render(COMMAND_TEMPLATE, { command: readmeCommand.command, helpOutput });
1391
+ return { ...readmeCommand, renderedForFile, renderedForInclude };
1392
+ }
1393
+ function writeTopic(topic, outputDir, dryRun, overWrite, only) {
1394
+ if (!topic.rendered) throw new Error(`Topic ${topic.topic.name} has not been rendered`);
1395
+ const makeFolderAndSave = (filePath2, rendered) => {
1396
+ const exists = fs__default.existsSync(filePath2);
1397
+ const outputList = exists ? writeOutput.overwritten : writeOutput.created;
1398
+ outputList.push(filePath2);
1399
+ const doWrite = !exists || overWrite;
1400
+ if (!doWrite || dryRun) return;
1401
+ const folder = path.dirname(filePath2);
1402
+ fs__default.mkdirSync(folder, { recursive: true });
1403
+ fs__default.writeFileSync(filePath2, rendered);
1404
+ };
1405
+ const skipFile = (filePath2) => only && !filePath2.match(only);
1406
+ const writeOutput = { created: [], overwritten: [] };
1407
+ const filePath = path.join(outputDir, topic.filePath);
1408
+ if (!skipFile(filePath)) makeFolderAndSave(filePath, topic.rendered);
1409
+ for (const subTopic of topic.subTopics) {
1410
+ const subWriteOutput = writeTopic(subTopic, outputDir, dryRun, overWrite, only);
1411
+ writeOutput.created.push(...subWriteOutput.created);
1412
+ writeOutput.overwritten.push(...subWriteOutput.overwritten);
1413
+ }
1414
+ for (const command of topic.commands) {
1415
+ if (!command.renderedForFile) throw new Error(`Command ${command.command.id} has not been rendered`);
1416
+ const filePath2 = path.join(outputDir, command.filePath);
1417
+ if (!skipFile(filePath2)) makeFolderAndSave(filePath2, command.renderedForFile);
1418
+ }
1419
+ return writeOutput;
1420
+ }
1421
+ class InternalReadme extends BaseCommand {
1422
+ static args = {
1423
+ outputDir: Args.string({ description: "The directory where the readme files will be written", required: true })
1424
+ };
1425
+ static description = "Generate the readme files for the commands";
1426
+ static examples = ["<%= config.bin %> <%= command.id %>"];
1427
+ static flags = {
1428
+ // By default do nothing
1429
+ notDryRun: Flags.boolean({ char: "n", description: "Set to actually write the files (will not overwrite)" }),
1430
+ overWrite: Flags.boolean({ char: "o", description: "Overwrite existing files" }),
1431
+ depth: Flags.integer({ char: "d", description: "The depth of the topic tree to render as separate files" }),
1432
+ only: Flags.string({ char: "l", description: "Glob pattern - will only write the files which match" })
1433
+ };
1434
+ async run() {
1435
+ const { outputDir } = this.args;
1436
+ const { notDryRun, overWrite, depth, only } = this.flags;
1437
+ const dryRun = !notDryRun;
1438
+ const { commands, topics } = this.config;
1439
+ const topicTree = getTopicTree(topics, commands, depth || 0);
1440
+ const renderedTopicTree = renderTopic(topicTree[ROOT_TOPIC_NAME], this.config);
1441
+ if (dryRun) console.log("Dry-run mode: No files will be written.");
1442
+ const writeOutput = writeTopic(renderedTopicTree, outputDir, dryRun, overWrite, only);
1443
+ if (writeOutput.created.length > 0)
1444
+ console.log(dryRun ? "Would create the following files:" : "Created the following files:");
1445
+ writeOutput.created.forEach((file) => console.log(`- ${file}`));
1446
+ if (writeOutput.overwritten.length > 0)
1447
+ console.log(notDryRun && overWrite ? "Overwritten the following files:" : "Would overwrite the following files:");
1448
+ writeOutput.overwritten.forEach((file) => console.log(`- ${file}`));
1449
+ }
1450
+ }
1451
+
1452
+ export { InternalReadme as default };