@rindo/core 3.0.1 → 3.2.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.
Files changed (112) hide show
  1. package/cli/config-flags.d.ts +122 -122
  2. package/cli/index.cjs +2335 -2432
  3. package/cli/index.d.ts +19 -19
  4. package/cli/index.js +2336 -2433
  5. package/cli/package.json +1 -1
  6. package/compiler/package.json +1 -1
  7. package/compiler/rindo.d.ts +73 -73
  8. package/compiler/rindo.js +66180 -62899
  9. package/compiler/rindo.min.js +2 -2
  10. package/compiler/sys/in-memory-fs.d.ts +218 -218
  11. package/compiler/transpile.d.ts +32 -32
  12. package/dev-server/client/app-error.d.ts +18 -18
  13. package/dev-server/client/events.d.ts +6 -6
  14. package/dev-server/client/hmr-components.d.ts +1 -1
  15. package/dev-server/client/hmr-external-styles.d.ts +1 -1
  16. package/dev-server/client/hmr-images.d.ts +1 -1
  17. package/dev-server/client/hmr-inline-styles.d.ts +1 -1
  18. package/dev-server/client/hmr-util.d.ts +9 -9
  19. package/dev-server/client/hmr-window.d.ts +10 -10
  20. package/dev-server/client/index.d.ts +6 -6
  21. package/dev-server/client/index.js +781 -781
  22. package/dev-server/client/logger.d.ts +5 -5
  23. package/dev-server/client/package.json +1 -1
  24. package/dev-server/client/progress.d.ts +3 -3
  25. package/dev-server/client/status.d.ts +4 -4
  26. package/dev-server/connector.html +2 -2
  27. package/dev-server/index.d.ts +3 -3
  28. package/dev-server/index.js +228 -228
  29. package/dev-server/open-in-editor-api.js +1 -1
  30. package/dev-server/package.json +1 -1
  31. package/dev-server/server-process.js +1300 -1281
  32. package/dev-server/ws.js +1 -1
  33. package/dev-server/xdg-open +0 -0
  34. package/internal/app-data/index.cjs +88 -88
  35. package/internal/app-data/index.d.ts +4 -4
  36. package/internal/app-data/index.js +88 -88
  37. package/internal/app-data/package.json +1 -1
  38. package/internal/client/css-shim.js +1 -1
  39. package/internal/client/dom.js +1 -1
  40. package/internal/client/index.js +3380 -3380
  41. package/internal/client/package.json +1 -1
  42. package/internal/client/patch-browser.js +155 -155
  43. package/internal/client/patch-esm.js +25 -25
  44. package/internal/client/shadow-css.js +382 -382
  45. package/internal/hydrate/package.json +1 -1
  46. package/internal/index.d.ts +2 -2
  47. package/internal/index.js +1 -1
  48. package/internal/package.json +1 -1
  49. package/internal/rindo-private.d.ts +2289 -2268
  50. package/internal/rindo-public-compiler.d.ts +2429 -2356
  51. package/internal/rindo-public-docs.d.ts +139 -139
  52. package/internal/rindo-public-runtime.d.ts +1636 -1636
  53. package/internal/testing/package.json +1 -1
  54. package/mock-doc/index.cjs +4766 -4766
  55. package/mock-doc/index.d.ts +1006 -1006
  56. package/mock-doc/index.js +4766 -4766
  57. package/mock-doc/package.json +1 -1
  58. package/package.json +9 -9
  59. package/screenshot/connector-base.d.ts +42 -42
  60. package/screenshot/connector-local.d.ts +7 -7
  61. package/screenshot/index.d.ts +3 -3
  62. package/screenshot/index.js +615 -615
  63. package/screenshot/package.json +1 -1
  64. package/screenshot/pixel-match.d.ts +1 -1
  65. package/screenshot/pixel-match.js +14 -14
  66. package/screenshot/screenshot-compare.d.ts +3 -3
  67. package/screenshot/screenshot-fs.d.ts +15 -15
  68. package/sys/node/autoprefixer.js +2 -2
  69. package/sys/node/glob.js +1 -1
  70. package/sys/node/graceful-fs.js +1 -1
  71. package/sys/node/index.d.ts +22 -22
  72. package/sys/node/index.js +14 -8
  73. package/sys/node/node-fetch.js +1 -1
  74. package/sys/node/package.json +1 -1
  75. package/sys/node/prompts.js +1 -1
  76. package/sys/node/worker.js +1 -1
  77. package/testing/index.d.ts +12 -12
  78. package/testing/index.js +7 -3
  79. package/testing/jest/jest-config.d.ts +16 -16
  80. package/testing/jest/jest-environment.d.ts +15 -15
  81. package/testing/jest/jest-preprocessor.d.ts +59 -59
  82. package/testing/jest/jest-runner.d.ts +10 -10
  83. package/testing/jest/jest-screenshot.d.ts +2 -2
  84. package/testing/jest/jest-serializer.d.ts +4 -4
  85. package/testing/jest/jest-setup-test-framework.d.ts +1 -1
  86. package/testing/jest-preset.js +1 -1
  87. package/testing/matchers/attributes.d.ts +14 -14
  88. package/testing/matchers/class-list.d.ts +12 -12
  89. package/testing/matchers/events.d.ts +21 -21
  90. package/testing/matchers/html.d.ts +12 -12
  91. package/testing/matchers/index.d.ts +23 -23
  92. package/testing/matchers/screenshot.d.ts +5 -5
  93. package/testing/matchers/text.d.ts +4 -4
  94. package/testing/mock-fetch.d.ts +11 -11
  95. package/testing/mocks.d.ts +56 -56
  96. package/testing/package.json +1 -1
  97. package/testing/puppeteer/index.d.ts +2 -2
  98. package/testing/puppeteer/puppeteer-browser.d.ts +6 -6
  99. package/testing/puppeteer/puppeteer-declarations.d.ts +403 -403
  100. package/testing/puppeteer/puppeteer-element.d.ts +67 -67
  101. package/testing/puppeteer/puppeteer-emulate.d.ts +2 -2
  102. package/testing/puppeteer/puppeteer-events.d.ts +21 -21
  103. package/testing/puppeteer/puppeteer-page.d.ts +2 -2
  104. package/testing/puppeteer/puppeteer-screenshot.d.ts +4 -4
  105. package/testing/reset-build-conditionals.d.ts +2 -2
  106. package/testing/spec-page.d.ts +2 -2
  107. package/testing/test-transpile.d.ts +2 -2
  108. package/testing/testing-logger.d.ts +25 -25
  109. package/testing/testing-sys.d.ts +6 -6
  110. package/testing/testing-utils.d.ts +79 -79
  111. package/testing/testing.d.ts +2 -2
  112. package/dependencies.json +0 -120
package/cli/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Rindo CLI (CommonJS) v3.0.1 | MIT Licensed | https://rindojs.web.app
2
+ Rindo CLI (CommonJS) v3.2.0 | MIT Licensed | https://rindojs.web.app
3
3
  */
4
4
  'use strict';
5
5
 
@@ -25,2195 +25,2101 @@ function _interopNamespace(e) {
25
25
  return Object.freeze(n);
26
26
  }
27
27
 
28
- /**
29
- * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase,
30
- * or whatever you call it!)
31
- *
32
- * @param str a string to convert
33
- * @returns a converted string
34
- */
35
- const dashToPascalCase = (str) => str
36
- .toLowerCase()
37
- .split('-')
38
- .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
39
- .join('');
40
- /**
41
- * Convert a string to 'camelCase'
42
- *
43
- * @param str the string to convert
44
- * @returns the converted string
45
- */
46
- const toCamelCase = (str) => {
47
- const pascalCase = dashToPascalCase(str);
48
- return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
49
- };
50
- const isFunction = (v) => typeof v === 'function';
28
+ /**
29
+ * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase,
30
+ * or whatever you call it!)
31
+ *
32
+ * @param str a string to convert
33
+ * @returns a converted string
34
+ */
35
+ const dashToPascalCase = (str) => str
36
+ .toLowerCase()
37
+ .split('-')
38
+ .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
39
+ .join('');
40
+ /**
41
+ * Convert a string to 'camelCase'
42
+ *
43
+ * @param str the string to convert
44
+ * @returns the converted string
45
+ */
46
+ const toCamelCase = (str) => {
47
+ const pascalCase = dashToPascalCase(str);
48
+ return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
49
+ };
50
+ const isFunction = (v) => typeof v === 'function';
51
51
  const isString = (v) => typeof v === 'string';
52
52
 
53
- /**
54
- * Builds a template `Diagnostic` entity for a build error. The created `Diagnostic` is returned, and have little
55
- * detail attached to it regarding the specifics of the error - it is the responsibility of the caller of this method
56
- * to attach the specifics of the error message.
57
- *
58
- * The created `Diagnostic` is pushed to the `diagnostics` argument as a side effect of calling this method.
59
- *
60
- * @param diagnostics the existing diagnostics that the created template `Diagnostic` should be added to
61
- * @returns the created `Diagnostic`
62
- */
63
- const buildError = (diagnostics) => {
64
- const diagnostic = {
65
- level: 'error',
66
- type: 'build',
67
- header: 'Build Error',
68
- messageText: 'build error',
69
- relFilePath: null,
70
- absFilePath: null,
71
- lines: [],
72
- };
73
- if (diagnostics) {
74
- diagnostics.push(diagnostic);
75
- }
76
- return diagnostic;
77
- };
78
- /**
79
- * Builds a diagnostic from an `Error`, appends it to the `diagnostics` parameter, and returns the created diagnostic
80
- * @param diagnostics the series of diagnostics the newly created diagnostics should be added to
81
- * @param err the error to derive information from in generating the diagnostic
82
- * @param msg an optional message to use in place of `err` to generate the diagnostic
83
- * @returns the generated diagnostic
84
- */
85
- const catchError = (diagnostics, err, msg) => {
86
- const diagnostic = {
87
- level: 'error',
88
- type: 'build',
89
- header: 'Build Error',
90
- messageText: 'build error',
91
- relFilePath: null,
92
- absFilePath: null,
93
- lines: [],
94
- };
95
- if (isString(msg)) {
96
- diagnostic.messageText = msg.length ? msg : 'UNKNOWN ERROR';
97
- }
98
- else if (err != null) {
99
- if (err.stack != null) {
100
- diagnostic.messageText = err.stack.toString();
101
- }
102
- else {
103
- if (err.message != null) {
104
- diagnostic.messageText = err.message.length ? err.message : 'UNKNOWN ERROR';
105
- }
106
- else {
107
- diagnostic.messageText = err.toString();
108
- }
109
- }
110
- }
111
- if (diagnostics != null && !shouldIgnoreError(diagnostic.messageText)) {
112
- diagnostics.push(diagnostic);
113
- }
114
- return diagnostic;
115
- };
116
- /**
117
- * Determine if the provided diagnostics have any build errors
118
- * @param diagnostics the diagnostics to inspect
119
- * @returns true if any of the diagnostics in the list provided are errors that did not occur at runtime. false
120
- * otherwise.
121
- */
122
- const hasError = (diagnostics) => {
123
- if (diagnostics == null || diagnostics.length === 0) {
124
- return false;
125
- }
126
- return diagnostics.some((d) => d.level === 'error' && d.type !== 'runtime');
127
- };
128
- const shouldIgnoreError = (msg) => {
129
- return msg === TASK_CANCELED_MSG;
130
- };
53
+ /**
54
+ * Builds a template `Diagnostic` entity for a build error. The created `Diagnostic` is returned, and have little
55
+ * detail attached to it regarding the specifics of the error - it is the responsibility of the caller of this method
56
+ * to attach the specifics of the error message.
57
+ *
58
+ * The created `Diagnostic` is pushed to the `diagnostics` argument as a side effect of calling this method.
59
+ *
60
+ * @param diagnostics the existing diagnostics that the created template `Diagnostic` should be added to
61
+ * @returns the created `Diagnostic`
62
+ */
63
+ const buildError = (diagnostics) => {
64
+ const diagnostic = {
65
+ level: 'error',
66
+ type: 'build',
67
+ header: 'Build Error',
68
+ messageText: 'build error',
69
+ relFilePath: null,
70
+ absFilePath: null,
71
+ lines: [],
72
+ };
73
+ if (diagnostics) {
74
+ diagnostics.push(diagnostic);
75
+ }
76
+ return diagnostic;
77
+ };
78
+ /**
79
+ * Builds a diagnostic from an `Error`, appends it to the `diagnostics` parameter, and returns the created diagnostic
80
+ * @param diagnostics the series of diagnostics the newly created diagnostics should be added to
81
+ * @param err the error to derive information from in generating the diagnostic
82
+ * @param msg an optional message to use in place of `err` to generate the diagnostic
83
+ * @returns the generated diagnostic
84
+ */
85
+ const catchError = (diagnostics, err, msg) => {
86
+ const diagnostic = {
87
+ level: 'error',
88
+ type: 'build',
89
+ header: 'Build Error',
90
+ messageText: 'build error',
91
+ relFilePath: null,
92
+ absFilePath: null,
93
+ lines: [],
94
+ };
95
+ if (isString(msg)) {
96
+ diagnostic.messageText = msg.length ? msg : 'UNKNOWN ERROR';
97
+ }
98
+ else if (err != null) {
99
+ if (err.stack != null) {
100
+ diagnostic.messageText = err.stack.toString();
101
+ }
102
+ else {
103
+ if (err.message != null) {
104
+ diagnostic.messageText = err.message.length ? err.message : 'UNKNOWN ERROR';
105
+ }
106
+ else {
107
+ diagnostic.messageText = err.toString();
108
+ }
109
+ }
110
+ }
111
+ if (diagnostics != null && !shouldIgnoreError(diagnostic.messageText)) {
112
+ diagnostics.push(diagnostic);
113
+ }
114
+ return diagnostic;
115
+ };
116
+ /**
117
+ * Determine if the provided diagnostics have any build errors
118
+ * @param diagnostics the diagnostics to inspect
119
+ * @returns true if any of the diagnostics in the list provided are errors that did not occur at runtime. false
120
+ * otherwise.
121
+ */
122
+ const hasError = (diagnostics) => {
123
+ if (diagnostics == null || diagnostics.length === 0) {
124
+ return false;
125
+ }
126
+ return diagnostics.some((d) => d.level === 'error' && d.type !== 'runtime');
127
+ };
128
+ const shouldIgnoreError = (msg) => {
129
+ return msg === TASK_CANCELED_MSG;
130
+ };
131
131
  const TASK_CANCELED_MSG = `task canceled`;
132
132
 
133
- /**
134
- * Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
135
- * Forward-slash paths can be used in Windows as long as they're not
136
- * extended-length paths and don't contain any non-ascii characters.
137
- * This was created since the path methods in Node.js outputs \\ paths on Windows.
138
- * @param path the Windows-based path to convert
139
- * @returns the converted path
140
- */
141
- const normalizePath = (path) => {
142
- if (typeof path !== 'string') {
143
- throw new Error(`invalid path to normalize`);
144
- }
145
- path = normalizeSlashes(path.trim());
146
- const components = pathComponents(path, getRootLength(path));
147
- const reducedComponents = reducePathComponents(components);
148
- const rootPart = reducedComponents[0];
149
- const secondPart = reducedComponents[1];
150
- const normalized = rootPart + reducedComponents.slice(1).join('/');
151
- if (normalized === '') {
152
- return '.';
153
- }
154
- if (rootPart === '' &&
155
- secondPart &&
156
- path.includes('/') &&
157
- !secondPart.startsWith('.') &&
158
- !secondPart.startsWith('@')) {
159
- return './' + normalized;
160
- }
161
- return normalized;
162
- };
163
- const normalizeSlashes = (path) => path.replace(backslashRegExp, '/');
164
- const altDirectorySeparator = '\\';
165
- const urlSchemeSeparator = '://';
166
- const backslashRegExp = /\\/g;
167
- const reducePathComponents = (components) => {
168
- if (!Array.isArray(components) || components.length === 0) {
169
- return [];
170
- }
171
- const reduced = [components[0]];
172
- for (let i = 1; i < components.length; i++) {
173
- const component = components[i];
174
- if (!component)
175
- continue;
176
- if (component === '.')
177
- continue;
178
- if (component === '..') {
179
- if (reduced.length > 1) {
180
- if (reduced[reduced.length - 1] !== '..') {
181
- reduced.pop();
182
- continue;
183
- }
184
- }
185
- else if (reduced[0])
186
- continue;
187
- }
188
- reduced.push(component);
189
- }
190
- return reduced;
191
- };
192
- const getRootLength = (path) => {
193
- const rootLength = getEncodedRootLength(path);
194
- return rootLength < 0 ? ~rootLength : rootLength;
195
- };
196
- const getEncodedRootLength = (path) => {
197
- if (!path)
198
- return 0;
199
- const ch0 = path.charCodeAt(0);
200
- // POSIX or UNC
201
- if (ch0 === 47 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) {
202
- if (path.charCodeAt(1) !== ch0)
203
- return 1; // POSIX: "/" (or non-normalized "\")
204
- const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.slash */ ? '/' : altDirectorySeparator, 2);
205
- if (p1 < 0)
206
- return path.length; // UNC: "//server" or "\\server"
207
- return p1 + 1; // UNC: "//server/" or "\\server\"
208
- }
209
- // DOS
210
- if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* CharacterCodes.colon */) {
211
- const ch2 = path.charCodeAt(2);
212
- if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.backslash */)
213
- return 3; // DOS: "c:/" or "c:\"
214
- if (path.length === 2)
215
- return 2; // DOS: "c:" (but not "c:d")
216
- }
217
- // URL
218
- const schemeEnd = path.indexOf(urlSchemeSeparator);
219
- if (schemeEnd !== -1) {
220
- const authorityStart = schemeEnd + urlSchemeSeparator.length;
221
- const authorityEnd = path.indexOf('/', authorityStart);
222
- if (authorityEnd !== -1) {
223
- // URL: "file:///", "file://server/", "file://server/path"
224
- // For local "file" URLs, include the leading DOS volume (if present).
225
- // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
226
- // special case interpreted as "the machine from which the URL is being interpreted".
227
- const scheme = path.slice(0, schemeEnd);
228
- const authority = path.slice(authorityStart, authorityEnd);
229
- if (scheme === 'file' &&
230
- (authority === '' || authority === 'localhost') &&
231
- isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
232
- const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
233
- if (volumeSeparatorEnd !== -1) {
234
- if (path.charCodeAt(volumeSeparatorEnd) === 47 /* CharacterCodes.slash */) {
235
- // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
236
- return ~(volumeSeparatorEnd + 1);
237
- }
238
- if (volumeSeparatorEnd === path.length) {
239
- // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
240
- // but not "file:///c:d" or "file:///c%3ad"
241
- return ~volumeSeparatorEnd;
242
- }
243
- }
244
- }
245
- return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
246
- }
247
- return ~path.length; // URL: "file://server", "http://server"
248
- }
249
- // relative
250
- return 0;
251
- };
252
- const isVolumeCharacter = (charCode) => (charCode >= 97 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) ||
253
- (charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */);
254
- const getFileUrlVolumeSeparatorEnd = (url, start) => {
255
- const ch0 = url.charCodeAt(start);
256
- if (ch0 === 58 /* CharacterCodes.colon */)
257
- return start + 1;
258
- if (ch0 === 37 /* CharacterCodes.percent */ && url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) {
259
- const ch2 = url.charCodeAt(start + 2);
260
- if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.A */)
261
- return start + 3;
262
- }
263
- return -1;
264
- };
265
- const pathComponents = (path, rootLength) => {
266
- const root = path.substring(0, rootLength);
267
- const rest = path.substring(rootLength).split('/');
268
- const restLen = rest.length;
269
- if (restLen > 0 && !rest[restLen - 1]) {
270
- rest.pop();
271
- }
272
- return [root, ...rest];
133
+ /**
134
+ * Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
135
+ * Forward-slash paths can be used in Windows as long as they're not
136
+ * extended-length paths and don't contain any non-ascii characters.
137
+ * This was created since the path methods in Node.js outputs \\ paths on Windows.
138
+ * @param path the Windows-based path to convert
139
+ * @returns the converted path
140
+ */
141
+ const normalizePath = (path) => {
142
+ if (typeof path !== 'string') {
143
+ throw new Error(`invalid path to normalize`);
144
+ }
145
+ path = normalizeSlashes(path.trim());
146
+ const components = pathComponents(path, getRootLength(path));
147
+ const reducedComponents = reducePathComponents(components);
148
+ const rootPart = reducedComponents[0];
149
+ const secondPart = reducedComponents[1];
150
+ const normalized = rootPart + reducedComponents.slice(1).join('/');
151
+ if (normalized === '') {
152
+ return '.';
153
+ }
154
+ if (rootPart === '' &&
155
+ secondPart &&
156
+ path.includes('/') &&
157
+ !secondPart.startsWith('.') &&
158
+ !secondPart.startsWith('@')) {
159
+ return './' + normalized;
160
+ }
161
+ return normalized;
162
+ };
163
+ const normalizeSlashes = (path) => path.replace(backslashRegExp, '/');
164
+ const altDirectorySeparator = '\\';
165
+ const urlSchemeSeparator = '://';
166
+ const backslashRegExp = /\\/g;
167
+ const reducePathComponents = (components) => {
168
+ if (!Array.isArray(components) || components.length === 0) {
169
+ return [];
170
+ }
171
+ const reduced = [components[0]];
172
+ for (let i = 1; i < components.length; i++) {
173
+ const component = components[i];
174
+ if (!component)
175
+ continue;
176
+ if (component === '.')
177
+ continue;
178
+ if (component === '..') {
179
+ if (reduced.length > 1) {
180
+ if (reduced[reduced.length - 1] !== '..') {
181
+ reduced.pop();
182
+ continue;
183
+ }
184
+ }
185
+ else if (reduced[0])
186
+ continue;
187
+ }
188
+ reduced.push(component);
189
+ }
190
+ return reduced;
191
+ };
192
+ const getRootLength = (path) => {
193
+ const rootLength = getEncodedRootLength(path);
194
+ return rootLength < 0 ? ~rootLength : rootLength;
195
+ };
196
+ const getEncodedRootLength = (path) => {
197
+ if (!path)
198
+ return 0;
199
+ const ch0 = path.charCodeAt(0);
200
+ // POSIX or UNC
201
+ if (ch0 === 47 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) {
202
+ if (path.charCodeAt(1) !== ch0)
203
+ return 1; // POSIX: "/" (or non-normalized "\")
204
+ const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.slash */ ? '/' : altDirectorySeparator, 2);
205
+ if (p1 < 0)
206
+ return path.length; // UNC: "//server" or "\\server"
207
+ return p1 + 1; // UNC: "//server/" or "\\server\"
208
+ }
209
+ // DOS
210
+ if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* CharacterCodes.colon */) {
211
+ const ch2 = path.charCodeAt(2);
212
+ if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.backslash */)
213
+ return 3; // DOS: "c:/" or "c:\"
214
+ if (path.length === 2)
215
+ return 2; // DOS: "c:" (but not "c:d")
216
+ }
217
+ // URL
218
+ const schemeEnd = path.indexOf(urlSchemeSeparator);
219
+ if (schemeEnd !== -1) {
220
+ const authorityStart = schemeEnd + urlSchemeSeparator.length;
221
+ const authorityEnd = path.indexOf('/', authorityStart);
222
+ if (authorityEnd !== -1) {
223
+ // URL: "file:///", "file://server/", "file://server/path"
224
+ // For local "file" URLs, include the leading DOS volume (if present).
225
+ // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
226
+ // special case interpreted as "the machine from which the URL is being interpreted".
227
+ const scheme = path.slice(0, schemeEnd);
228
+ const authority = path.slice(authorityStart, authorityEnd);
229
+ if (scheme === 'file' &&
230
+ (authority === '' || authority === 'localhost') &&
231
+ isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
232
+ const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
233
+ if (volumeSeparatorEnd !== -1) {
234
+ if (path.charCodeAt(volumeSeparatorEnd) === 47 /* CharacterCodes.slash */) {
235
+ // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
236
+ return ~(volumeSeparatorEnd + 1);
237
+ }
238
+ if (volumeSeparatorEnd === path.length) {
239
+ // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
240
+ // but not "file:///c:d" or "file:///c%3ad"
241
+ return ~volumeSeparatorEnd;
242
+ }
243
+ }
244
+ }
245
+ return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
246
+ }
247
+ return ~path.length; // URL: "file://server", "http://server"
248
+ }
249
+ // relative
250
+ return 0;
251
+ };
252
+ const isVolumeCharacter = (charCode) => (charCode >= 97 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) ||
253
+ (charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */);
254
+ const getFileUrlVolumeSeparatorEnd = (url, start) => {
255
+ const ch0 = url.charCodeAt(start);
256
+ if (ch0 === 58 /* CharacterCodes.colon */)
257
+ return start + 1;
258
+ if (ch0 === 37 /* CharacterCodes.percent */ && url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) {
259
+ const ch2 = url.charCodeAt(start + 2);
260
+ if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.A */)
261
+ return start + 3;
262
+ }
263
+ return -1;
264
+ };
265
+ const pathComponents = (path, rootLength) => {
266
+ const root = path.substring(0, rootLength);
267
+ const rest = path.substring(rootLength).split('/');
268
+ const restLen = rest.length;
269
+ if (restLen > 0 && !rest[restLen - 1]) {
270
+ rest.pop();
271
+ }
272
+ return [root, ...rest];
273
273
  };
274
274
 
275
- /**
276
- * Check whether a string is a member of a ReadonlyArray<string>
277
- *
278
- * We need a little helper for this because unfortunately `includes` is typed
279
- * on `ReadonlyArray<T>` as `(el: T): boolean` so a `string` cannot be passed
280
- * to `includes` on a `ReadonlyArray` 😢 thus we have a little helper function
281
- * where we do the type coercion just once.
282
- *
283
- * see microsoft/TypeScript#31018 for some discussion of this
284
- *
285
- * @param readOnlyArray the array we're checking
286
- * @param maybeMember a value which is possibly a member of the array
287
- * @returns whether the array contains the member or not
288
- */
275
+ /**
276
+ * Check whether a string is a member of a ReadonlyArray<string>
277
+ *
278
+ * We need a little helper for this because unfortunately `includes` is typed
279
+ * on `ReadonlyArray<T>` as `(el: T): boolean` so a `string` cannot be passed
280
+ * to `includes` on a `ReadonlyArray` 😢 thus we have a little helper function
281
+ * where we do the type coercion just once.
282
+ *
283
+ * see microsoft/TypeScript#31018 for some discussion of this
284
+ *
285
+ * @param readOnlyArray the array we're checking
286
+ * @param maybeMember a value which is possibly a member of the array
287
+ * @returns whether the array contains the member or not
288
+ */
289
289
  const readOnlyArrayHasStringMember = (readOnlyArray, maybeMember) => readOnlyArray.includes(maybeMember);
290
290
 
291
- /**
292
- * Validates that a component tag meets required naming conventions to be used for a web component
293
- * @param tag the tag to validate
294
- * @returns an error message if the tag has an invalid name, undefined if the tag name passes all checks
295
- */
296
- const validateComponentTag = (tag) => {
297
- // we want to check this first since we call some String.prototype methods below
298
- if (typeof tag !== 'string') {
299
- return `Tag "${tag}" must be a string type`;
300
- }
301
- if (tag !== tag.trim()) {
302
- return `Tag can not contain white spaces`;
303
- }
304
- if (tag !== tag.toLowerCase()) {
305
- return `Tag can not contain upper case characters`;
306
- }
307
- if (tag.length === 0) {
308
- return `Received empty tag value`;
309
- }
310
- if (tag.indexOf(' ') > -1) {
311
- return `"${tag}" tag cannot contain a space`;
312
- }
313
- if (tag.indexOf(',') > -1) {
314
- return `"${tag}" tag cannot be used for multiple tags`;
315
- }
316
- const invalidChars = tag.replace(/\w|-/g, '');
317
- if (invalidChars !== '') {
318
- return `"${tag}" tag contains invalid characters: ${invalidChars}`;
319
- }
320
- if (tag.indexOf('-') === -1) {
321
- return `"${tag}" tag must contain a dash (-) to work as a valid web component`;
322
- }
323
- if (tag.indexOf('--') > -1) {
324
- return `"${tag}" tag cannot contain multiple dashes (--) next to each other`;
325
- }
326
- if (tag.indexOf('-') === 0) {
327
- return `"${tag}" tag cannot start with a dash (-)`;
328
- }
329
- if (tag.lastIndexOf('-') === tag.length - 1) {
330
- return `"${tag}" tag cannot end with a dash (-)`;
331
- }
332
- return undefined;
291
+ /**
292
+ * Validates that a component tag meets required naming conventions to be used for a web component
293
+ * @param tag the tag to validate
294
+ * @returns an error message if the tag has an invalid name, undefined if the tag name passes all checks
295
+ */
296
+ const validateComponentTag = (tag) => {
297
+ // we want to check this first since we call some String.prototype methods below
298
+ if (typeof tag !== 'string') {
299
+ return `Tag "${tag}" must be a string type`;
300
+ }
301
+ if (tag !== tag.trim()) {
302
+ return `Tag can not contain white spaces`;
303
+ }
304
+ if (tag !== tag.toLowerCase()) {
305
+ return `Tag can not contain upper case characters`;
306
+ }
307
+ if (tag.length === 0) {
308
+ return `Received empty tag value`;
309
+ }
310
+ if (tag.indexOf(' ') > -1) {
311
+ return `"${tag}" tag cannot contain a space`;
312
+ }
313
+ if (tag.indexOf(',') > -1) {
314
+ return `"${tag}" tag cannot be used for multiple tags`;
315
+ }
316
+ const invalidChars = tag.replace(/\w|-/g, '');
317
+ if (invalidChars !== '') {
318
+ return `"${tag}" tag contains invalid characters: ${invalidChars}`;
319
+ }
320
+ if (tag.indexOf('-') === -1) {
321
+ return `"${tag}" tag must contain a dash (-) to work as a valid web component`;
322
+ }
323
+ if (tag.indexOf('--') > -1) {
324
+ return `"${tag}" tag cannot contain multiple dashes (--) next to each other`;
325
+ }
326
+ if (tag.indexOf('-') === 0) {
327
+ return `"${tag}" tag cannot start with a dash (-)`;
328
+ }
329
+ if (tag.lastIndexOf('-') === tag.length - 1) {
330
+ return `"${tag}" tag cannot end with a dash (-)`;
331
+ }
332
+ return undefined;
333
333
  };
334
334
 
335
- /**
336
- * This sets the log level hierarchy for our terminal logger, ranging from
337
- * most to least verbose.
338
- *
339
- * Ordering the levels like this lets us easily check whether we should log a
340
- * message at a given time. For instance, if the log level is set to `'warn'`,
341
- * then anything passed to the logger with level `'warn'` or `'error'` should
342
- * be logged, but we should _not_ log anything with level `'info'` or `'debug'`.
343
- *
344
- * If we have a current log level `currentLevel` and a message with level
345
- * `msgLevel` is passed to the logger, we can determine whether or not we should
346
- * log it by checking if the log level on the message is further up or at the
347
- * same level in the hierarchy than `currentLevel`, like so:
348
- *
349
- * ```ts
350
- * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel)
351
- * ```
352
- *
353
- * NOTE: for the reasons described above, do not change the order of the entries
354
- * in this array without good reason!
355
- */
335
+ /**
336
+ * This sets the log level hierarchy for our terminal logger, ranging from
337
+ * most to least verbose.
338
+ *
339
+ * Ordering the levels like this lets us easily check whether we should log a
340
+ * message at a given time. For instance, if the log level is set to `'warn'`,
341
+ * then anything passed to the logger with level `'warn'` or `'error'` should
342
+ * be logged, but we should _not_ log anything with level `'info'` or `'debug'`.
343
+ *
344
+ * If we have a current log level `currentLevel` and a message with level
345
+ * `msgLevel` is passed to the logger, we can determine whether or not we should
346
+ * log it by checking if the log level on the message is further up or at the
347
+ * same level in the hierarchy than `currentLevel`, like so:
348
+ *
349
+ * ```ts
350
+ * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel)
351
+ * ```
352
+ *
353
+ * NOTE: for the reasons described above, do not change the order of the entries
354
+ * in this array without good reason!
355
+ */
356
356
  const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
357
357
 
358
- /**
359
- * All the Boolean options supported by the Rindo CLI
360
- */
361
- const BOOLEAN_CLI_FLAGS = [
362
- 'build',
363
- 'cache',
364
- 'checkVersion',
365
- 'ci',
366
- 'compare',
367
- 'debug',
368
- 'dev',
369
- 'devtools',
370
- 'docs',
371
- 'e2e',
372
- 'es5',
373
- 'esm',
374
- 'headless',
375
- 'help',
376
- 'log',
377
- 'open',
378
- 'prerender',
379
- 'prerenderExternal',
380
- 'prod',
381
- 'profile',
382
- 'serviceWorker',
383
- 'screenshot',
384
- 'serve',
385
- 'skipNodeCheck',
386
- 'spec',
387
- 'ssr',
388
- 'stats',
389
- 'updateScreenshot',
390
- 'verbose',
391
- 'version',
392
- 'watch',
393
- // JEST CLI OPTIONS
394
- 'all',
395
- 'automock',
396
- 'bail',
397
- // 'cache', Rindo already supports this argument
398
- 'changedFilesWithAncestor',
399
- // 'ci', Rindo already supports this argument
400
- 'clearCache',
401
- 'clearMocks',
402
- 'collectCoverage',
403
- 'color',
404
- 'colors',
405
- 'coverage',
406
- // 'debug', Rindo already supports this argument
407
- 'detectLeaks',
408
- 'detectOpenHandles',
409
- 'errorOnDeprecated',
410
- 'expand',
411
- 'findRelatedTests',
412
- 'forceExit',
413
- 'init',
414
- 'injectGlobals',
415
- 'json',
416
- 'lastCommit',
417
- 'listTests',
418
- 'logHeapUsage',
419
- 'noStackTrace',
420
- 'notify',
421
- 'onlyChanged',
422
- 'onlyFailures',
423
- 'passWithNoTests',
424
- 'resetMocks',
425
- 'resetModules',
426
- 'restoreMocks',
427
- 'runInBand',
428
- 'runTestsByPath',
429
- 'showConfig',
430
- 'silent',
431
- 'skipFilter',
432
- 'testLocationInResults',
433
- 'updateSnapshot',
434
- 'useStderr',
435
- // 'verbose', Rindo already supports this argument
436
- // 'version', Rindo already supports this argument
437
- // 'watch', Rindo already supports this argument
438
- 'watchAll',
439
- 'watchman',
440
- ];
441
- /**
442
- * All the Number options supported by the Rindo CLI
443
- */
444
- const NUMBER_CLI_FLAGS = [
445
- 'port',
446
- // JEST CLI ARGS
447
- 'maxConcurrency',
448
- 'testTimeout',
449
- ];
450
- /**
451
- * All the String options supported by the Rindo CLI
452
- */
453
- const STRING_CLI_FLAGS = [
454
- 'address',
455
- 'config',
456
- 'docsApi',
457
- 'docsJson',
458
- 'emulate',
459
- 'root',
460
- 'screenshotConnector',
461
- // JEST CLI ARGS
462
- 'cacheDirectory',
463
- 'changedSince',
464
- 'collectCoverageFrom',
465
- // 'config', Rindo already supports this argument
466
- 'coverageDirectory',
467
- 'coverageThreshold',
468
- 'env',
469
- 'filter',
470
- 'globalSetup',
471
- 'globalTeardown',
472
- 'globals',
473
- 'haste',
474
- 'moduleNameMapper',
475
- 'notifyMode',
476
- 'outputFile',
477
- 'preset',
478
- 'prettierPath',
479
- 'resolver',
480
- 'rootDir',
481
- 'runner',
482
- 'testEnvironment',
483
- 'testEnvironmentOptions',
484
- 'testFailureExitCode',
485
- 'testNamePattern',
486
- 'testResultsProcessor',
487
- 'testRunner',
488
- 'testSequencer',
489
- 'testURL',
490
- 'timers',
491
- 'transform',
492
- ];
493
- const STRING_ARRAY_CLI_FLAGS = [
494
- 'collectCoverageOnlyFrom',
495
- 'coveragePathIgnorePatterns',
496
- 'coverageReporters',
497
- 'moduleDirectories',
498
- 'moduleFileExtensions',
499
- 'modulePathIgnorePatterns',
500
- 'modulePaths',
501
- 'projects',
502
- 'reporters',
503
- 'roots',
504
- 'selectProjects',
505
- 'setupFiles',
506
- 'setupFilesAfterEnv',
507
- 'snapshotSerializers',
508
- 'testMatch',
509
- 'testPathIgnorePatterns',
510
- 'testPathPattern',
511
- 'testRegex',
512
- 'transformIgnorePatterns',
513
- 'unmockedModulePathPatterns',
514
- 'watchPathIgnorePatterns',
515
- ];
516
- /**
517
- * All the CLI arguments which may have string or number values
518
- *
519
- * `maxWorkers` is an argument which is used both by Rindo _and_ by Jest,
520
- * which means that we need to support parsing both string and number values.
521
- */
522
- const STRING_NUMBER_CLI_FLAGS = ['maxWorkers'];
523
- /**
524
- * All the LogLevel-type options supported by the Rindo CLI
525
- *
526
- * This is a bit silly since there's only one such argument atm,
527
- * but this approach lets us make sure that we're handling all
528
- * our arguments in a type-safe way.
529
- */
530
- const LOG_LEVEL_CLI_FLAGS = ['logLevel'];
531
- /**
532
- * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'`
533
- */
534
- const CLI_FLAG_ALIASES = {
535
- c: 'config',
536
- h: 'help',
537
- p: 'port',
538
- v: 'version',
539
- };
540
- /**
541
- * A regular expression which can be used to match a CLI flag for one of our
542
- * short aliases.
543
- */
544
- const CLI_FLAG_REGEX = new RegExp(`^-[chpv]{1}$`);
545
- /**
546
- * Helper function for initializing a `ConfigFlags` object. Provide any overrides
547
- * for default values and off you go!
548
- *
549
- * @param init an object with any overrides for default values
550
- * @returns a complete CLI flag object
551
- */
552
- const createConfigFlags = (init = {}) => {
553
- const flags = {
554
- task: null,
555
- args: [],
556
- knownArgs: [],
557
- unknownArgs: [],
558
- ...init,
559
- };
560
- return flags;
358
+ /**
359
+ * All the Boolean options supported by the Rindo CLI
360
+ */
361
+ const BOOLEAN_CLI_FLAGS = [
362
+ 'build',
363
+ 'cache',
364
+ 'checkVersion',
365
+ 'ci',
366
+ 'compare',
367
+ 'debug',
368
+ 'dev',
369
+ 'devtools',
370
+ 'docs',
371
+ 'e2e',
372
+ 'es5',
373
+ 'esm',
374
+ 'headless',
375
+ 'help',
376
+ 'log',
377
+ 'open',
378
+ 'prerender',
379
+ 'prerenderExternal',
380
+ 'prod',
381
+ 'profile',
382
+ 'serviceWorker',
383
+ 'screenshot',
384
+ 'serve',
385
+ 'skipNodeCheck',
386
+ 'spec',
387
+ 'ssr',
388
+ 'stats',
389
+ 'updateScreenshot',
390
+ 'verbose',
391
+ 'version',
392
+ 'watch',
393
+ // JEST CLI OPTIONS
394
+ 'all',
395
+ 'automock',
396
+ 'bail',
397
+ // 'cache', Rindo already supports this argument
398
+ 'changedFilesWithAncestor',
399
+ // 'ci', Rindo already supports this argument
400
+ 'clearCache',
401
+ 'clearMocks',
402
+ 'collectCoverage',
403
+ 'color',
404
+ 'colors',
405
+ 'coverage',
406
+ // 'debug', Rindo already supports this argument
407
+ 'detectLeaks',
408
+ 'detectOpenHandles',
409
+ 'errorOnDeprecated',
410
+ 'expand',
411
+ 'findRelatedTests',
412
+ 'forceExit',
413
+ 'init',
414
+ 'injectGlobals',
415
+ 'json',
416
+ 'lastCommit',
417
+ 'listTests',
418
+ 'logHeapUsage',
419
+ 'noStackTrace',
420
+ 'notify',
421
+ 'onlyChanged',
422
+ 'onlyFailures',
423
+ 'passWithNoTests',
424
+ 'resetMocks',
425
+ 'resetModules',
426
+ 'restoreMocks',
427
+ 'runInBand',
428
+ 'runTestsByPath',
429
+ 'showConfig',
430
+ 'silent',
431
+ 'skipFilter',
432
+ 'testLocationInResults',
433
+ 'updateSnapshot',
434
+ 'useStderr',
435
+ // 'verbose', Rindo already supports this argument
436
+ // 'version', Rindo already supports this argument
437
+ // 'watch', Rindo already supports this argument
438
+ 'watchAll',
439
+ 'watchman',
440
+ ];
441
+ /**
442
+ * All the Number options supported by the Rindo CLI
443
+ */
444
+ const NUMBER_CLI_FLAGS = [
445
+ 'port',
446
+ // JEST CLI ARGS
447
+ 'maxConcurrency',
448
+ 'testTimeout',
449
+ ];
450
+ /**
451
+ * All the String options supported by the Rindo CLI
452
+ */
453
+ const STRING_CLI_FLAGS = [
454
+ 'address',
455
+ 'config',
456
+ 'docsApi',
457
+ 'docsJson',
458
+ 'emulate',
459
+ 'root',
460
+ 'screenshotConnector',
461
+ // JEST CLI ARGS
462
+ 'cacheDirectory',
463
+ 'changedSince',
464
+ 'collectCoverageFrom',
465
+ // 'config', Rindo already supports this argument
466
+ 'coverageDirectory',
467
+ 'coverageThreshold',
468
+ 'env',
469
+ 'filter',
470
+ 'globalSetup',
471
+ 'globalTeardown',
472
+ 'globals',
473
+ 'haste',
474
+ 'moduleNameMapper',
475
+ 'notifyMode',
476
+ 'outputFile',
477
+ 'preset',
478
+ 'prettierPath',
479
+ 'resolver',
480
+ 'rootDir',
481
+ 'runner',
482
+ 'testEnvironment',
483
+ 'testEnvironmentOptions',
484
+ 'testFailureExitCode',
485
+ 'testNamePattern',
486
+ 'testResultsProcessor',
487
+ 'testRunner',
488
+ 'testSequencer',
489
+ 'testURL',
490
+ 'timers',
491
+ 'transform',
492
+ ];
493
+ const STRING_ARRAY_CLI_FLAGS = [
494
+ 'collectCoverageOnlyFrom',
495
+ 'coveragePathIgnorePatterns',
496
+ 'coverageReporters',
497
+ 'moduleDirectories',
498
+ 'moduleFileExtensions',
499
+ 'modulePathIgnorePatterns',
500
+ 'modulePaths',
501
+ 'projects',
502
+ 'reporters',
503
+ 'roots',
504
+ 'selectProjects',
505
+ 'setupFiles',
506
+ 'setupFilesAfterEnv',
507
+ 'snapshotSerializers',
508
+ 'testMatch',
509
+ 'testPathIgnorePatterns',
510
+ 'testPathPattern',
511
+ 'testRegex',
512
+ 'transformIgnorePatterns',
513
+ 'unmockedModulePathPatterns',
514
+ 'watchPathIgnorePatterns',
515
+ ];
516
+ /**
517
+ * All the CLI arguments which may have string or number values
518
+ *
519
+ * `maxWorkers` is an argument which is used both by Rindo _and_ by Jest,
520
+ * which means that we need to support parsing both string and number values.
521
+ */
522
+ const STRING_NUMBER_CLI_FLAGS = ['maxWorkers'];
523
+ /**
524
+ * All the LogLevel-type options supported by the Rindo CLI
525
+ *
526
+ * This is a bit silly since there's only one such argument atm,
527
+ * but this approach lets us make sure that we're handling all
528
+ * our arguments in a type-safe way.
529
+ */
530
+ const LOG_LEVEL_CLI_FLAGS = ['logLevel'];
531
+ /**
532
+ * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'`
533
+ */
534
+ const CLI_FLAG_ALIASES = {
535
+ c: 'config',
536
+ h: 'help',
537
+ p: 'port',
538
+ v: 'version',
539
+ // JEST SPECIFIC CLI FLAGS
540
+ // these are defined in
541
+ // https://github.com/facebook/jest/blob/4156f86/packages/jest-cli/src/args.ts
542
+ b: 'bail',
543
+ e: 'expand',
544
+ f: 'onlyFailures',
545
+ i: 'runInBand',
546
+ o: 'onlyChanged',
547
+ t: 'testNamePattern',
548
+ u: 'updateSnapshot',
549
+ w: 'maxWorkers',
561
550
  };
562
-
563
- /**
564
- * Parse command line arguments into a structured `ConfigFlags` object
565
- *
566
- * @param args an array of CLI flags
567
- * @returns a structured ConfigFlags object
568
- */
569
- const parseFlags = (args) => {
570
- const flags = createConfigFlags();
571
- // cmd line has more priority over npm scripts cmd
572
- flags.args = Array.isArray(args) ? args.slice() : [];
573
- if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
574
- flags.task = flags.args[0];
575
- // if the first argument was a "task" (like `build`, `test`, etc) then
576
- // we go on to parse the _rest_ of the CLI args
577
- parseArgs(flags, args.slice(1));
578
- }
579
- else {
580
- // we didn't find a leading flag, so we should just parse them all
581
- parseArgs(flags, flags.args);
582
- }
583
- if (flags.task != null) {
584
- const i = flags.args.indexOf(flags.task);
585
- if (i > -1) {
586
- flags.args.splice(i, 1);
587
- }
588
- }
589
- // to find unknown / unrecognized arguments we filter `args`, including only
590
- // arguments whose normalized form is not found in `knownArgs`. `knownArgs`
591
- // is populated during the call to `parseArgs` above. For arguments like
592
- // `--foobar` the string `"--foobar"` will be added, while for more
593
- // complicated arguments like `--bizBoz=bop` or `--bizBoz bop` just the
594
- // string `"--bizBoz"` will be added.
595
- flags.unknownArgs = flags.args.filter((arg) => !flags.knownArgs.includes(parseEqualsArg(arg)[0]));
596
- return flags;
597
- };
598
- /**
599
- * Parse the supported command line flags which are enumerated in the
600
- * `config-flags` module. Handles leading dashes on arguments, aliases that are
601
- * defined for a small number of arguments, and parsing values for non-boolean
602
- * arguments (e.g. port number for the dev server).
603
- *
604
- * This parses the following grammar:
605
- *
606
- * CLIArguments → ""
607
- * | CLITerm ( " " CLITerm )* ;
608
- * CLITerm → EqualsArg
609
- * | AliasEqualsArg
610
- * | AliasArg
611
- * | NegativeDashArg
612
- * | NegativeArg
613
- * | SimpleArg ;
614
- * EqualsArg → "--" ArgName "=" CLIValue ;
615
- * AliasEqualsArg → "-" AliasName "=" CLIValue ;
616
- * AliasArg → "-" AliasName ( " " CLIValue )? ;
617
- * NegativeDashArg → "--no-" ArgName ;
618
- * NegativeArg → "--no" ArgName ;
619
- * SimpleArg → "--" ArgName ( " " CLIValue )? ;
620
- * ArgName → /^[a-zA-Z-]+$/ ;
621
- * AliasName → /^[a-z]{1}$/ ;
622
- * CLIValue → '"' /^[a-zA-Z0-9]+$/ '"'
623
- * | /^[a-zA-Z0-9]+$/ ;
624
- *
625
- * There are additional constraints (not shown in the grammar for brevity's sake)
626
- * on the type of `CLIValue` which will be associated with a particular argument.
627
- * We enforce this by declaring lists of boolean, string, etc arguments and
628
- * checking the types of values before setting them.
629
- *
630
- * We don't need to turn the list of CLI arg tokens into any kind of
631
- * intermediate representation since we aren't concerned with doing anything
632
- * other than setting the correct values on our ConfigFlags object. So we just
633
- * parse the array of string arguments using a recursive-descent approach
634
- * (which is not very deep since our grammar is pretty simple) and make the
635
- * modifications we need to make to the {@link ConfigFlags} object as we go.
636
- *
637
- * @param flags a ConfigFlags object to which parsed arguments will be added
638
- * @param args an array of command-line arguments to parse
639
- */
640
- const parseArgs = (flags, args) => {
641
- const argsCopy = args.concat();
642
- while (argsCopy.length > 0) {
643
- // there are still unprocessed args to deal with
644
- parseCLITerm(flags, argsCopy);
645
- }
646
- };
647
- /**
648
- * Given an array of CLI arguments, parse it and perform a series of side
649
- * effects (setting values on the provided `ConfigFlags` object).
650
- *
651
- * @param flags a {@link ConfigFlags} object which is updated as we parse the CLI
652
- * arguments
653
- * @param args a list of args to work through. This function (and some functions
654
- * it calls) calls `Array.prototype.shift` to get the next argument to look at,
655
- * so this parameter will be modified.
656
- */
657
- const parseCLITerm = (flags, args) => {
658
- // pull off the first arg from the argument array
659
- const arg = args.shift();
660
- // array is empty, we're done!
661
- if (arg === undefined)
662
- return;
663
- // EqualsArg → "--" ArgName "=" CLIValue ;
664
- if (arg.startsWith('--') && arg.includes('=')) {
665
- // we're dealing with an EqualsArg, we have a special helper for that
666
- const [originalArg, value] = parseEqualsArg(arg);
667
- setCLIArg(flags, arg.split('=')[0], normalizeFlagName(originalArg), value);
668
- }
669
- // AliasEqualsArg → "-" AliasName "=" CLIValue ;
670
- else if (arg.startsWith('-') && arg.includes('=')) {
671
- // we're dealing with an AliasEqualsArg, we have a special helper for that
672
- const [originalArg, value] = parseEqualsArg(arg);
673
- setCLIArg(flags, arg.split('=')[0], normalizeFlagName(originalArg), value);
674
- }
675
- // AliasArg → "-" AliasName ( " " CLIValue )? ;
676
- else if (CLI_FLAG_REGEX.test(arg)) {
677
- // this is a short alias, like `-c` for Config
678
- setCLIArg(flags, arg, normalizeFlagName(arg), parseCLIValue(args));
679
- }
680
- // NegativeDashArg → "--no-" ArgName ;
681
- else if (arg.startsWith('--no-') && arg.length > '--no-'.length) {
682
- // this is a `NegativeDashArg` term, so we need to normalize the negative
683
- // flag name and then set an appropriate value
684
- const normalized = normalizeNegativeFlagName(arg);
685
- setCLIArg(flags, arg, normalized, '');
686
- }
687
- // NegativeArg → "--no" ArgName ;
688
- else if (arg.startsWith('--no') &&
689
- !readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizeFlagName(arg)) &&
690
- readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizeNegativeFlagName(arg))) {
691
- // possibly dealing with a `NegativeArg` here. There is a little ambiguity
692
- // here because we have arguments that already begin with `no` like
693
- // `notify`, so we need to test if a normalized form of the raw argument is
694
- // a valid and supported boolean flag.
695
- setCLIArg(flags, arg, normalizeNegativeFlagName(arg), '');
696
- }
697
- // SimpleArg → "--" ArgName ( " " CLIValue )? ;
698
- else if (arg.startsWith('--') && arg.length > '--'.length) {
699
- setCLIArg(flags, arg, normalizeFlagName(arg), parseCLIValue(args));
700
- }
701
- // if we get here it is not an argument in our list of supported arguments.
702
- // This doesn't necessarily mean we want to report an error or anything
703
- // though! Instead, with unknown / unrecognized arguments we stick them into
704
- // the `unknownArgs` array, which is used when we pass CLI args to Jest, for
705
- // instance. So we just return void here.
706
- };
707
- /**
708
- * Normalize a 'negative' flag name, just to do a little pre-processing before
709
- * we pass it to `setCLIArg`.
710
- *
711
- * @param flagName the flag name to normalize
712
- * @returns a normalized flag name
713
- */
714
- const normalizeNegativeFlagName = (flagName) => {
715
- const trimmed = flagName.replace(/^--no[-]?/, '');
716
- return normalizeFlagName(trimmed.charAt(0).toLowerCase() + trimmed.slice(1));
717
- };
718
- /**
719
- * Normalize a flag name by:
720
- *
721
- * - replacing any leading dashes (`--foo` -> `foo`)
722
- * - converting `dash-case` to camelCase (if necessary)
723
- *
724
- * Normalizing in this context basically means converting the various
725
- * supported flag spelling variants to the variant defined in our lists of
726
- * supported arguments (e.g. BOOLEAN_CLI_FLAGS, etc). So, for instance,
727
- * `--log-level` should be converted to `logLevel`.
728
- *
729
- * @param flagName the flag name to normalize
730
- * @returns a normalized flag name
731
- *
732
- */
733
- const normalizeFlagName = (flagName) => {
734
- const trimmed = flagName.replace(/^-+/, '');
735
- return trimmed.includes('-') ? toCamelCase(trimmed) : trimmed;
736
- };
737
- /**
738
- * Set a value on a provided {@link ConfigFlags} object, given an argument
739
- * name and a raw string value. This function dispatches to other functions
740
- * which make sure that the string value can be properly parsed into a JS
741
- * runtime value of the right type (e.g. number, string, etc).
742
- *
743
- * @throws if a value cannot be parsed to the right type for a given flag
744
- * @param flags a {@link ConfigFlags} object
745
- * @param rawArg the raw argument name matched by the parser
746
- * @param normalizedArg an argument with leading control characters (`--`,
747
- * `--no-`, etc) removed
748
- * @param value the raw value to be set onto the config flags object
749
- */
750
- const setCLIArg = (flags, rawArg, normalizedArg, value) => {
751
- normalizedArg = dereferenceAlias(normalizedArg);
752
- // We're setting a boolean!
753
- if (readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizedArg)) {
754
- flags[normalizedArg] =
755
- typeof value === 'string'
756
- ? Boolean(value)
757
- : // no value was supplied, default to true
758
- true;
759
- flags.knownArgs.push(rawArg);
760
- }
761
- // We're setting a string!
762
- else if (readOnlyArrayHasStringMember(STRING_CLI_FLAGS, normalizedArg)) {
763
- if (typeof value === 'string') {
764
- flags[normalizedArg] = value;
765
- flags.knownArgs.push(rawArg);
766
- flags.knownArgs.push(value);
767
- }
768
- else {
769
- throwCLIParsingError(rawArg, 'expected a string argument but received nothing');
770
- }
771
- }
772
- // We're setting a string, but it's one where the user can pass multiple values,
773
- // like `--reporters="default" --reporters="jest-junit"`
774
- else if (readOnlyArrayHasStringMember(STRING_ARRAY_CLI_FLAGS, normalizedArg)) {
775
- if (typeof value === 'string') {
776
- if (!Array.isArray(flags[normalizedArg])) {
777
- flags[normalizedArg] = [];
778
- }
779
- const targetArray = flags[normalizedArg];
780
- // this is irritating, but TS doesn't know that the `!Array.isArray`
781
- // check above guarantees we have an array to work with here, and it
782
- // doesn't want to narrow the type of `flags[normalizedArg]`, so we need
783
- // to grab a reference to that array and then `Array.isArray` that. Bah!
784
- if (Array.isArray(targetArray)) {
785
- targetArray.push(value);
786
- flags.knownArgs.push(rawArg);
787
- flags.knownArgs.push(value);
788
- }
789
- }
790
- else {
791
- throwCLIParsingError(rawArg, 'expected a string argument but received nothing');
792
- }
793
- }
794
- // We're setting a number!
795
- else if (readOnlyArrayHasStringMember(NUMBER_CLI_FLAGS, normalizedArg)) {
796
- if (typeof value === 'string') {
797
- const parsed = parseInt(value, 10);
798
- if (isNaN(parsed)) {
799
- throwNumberParsingError(rawArg, value);
800
- }
801
- else {
802
- flags[normalizedArg] = parsed;
803
- flags.knownArgs.push(rawArg);
804
- flags.knownArgs.push(value);
805
- }
806
- }
807
- else {
808
- throwCLIParsingError(rawArg, 'expected a number argument but received nothing');
809
- }
810
- }
811
- // We're setting a value which could be either a string _or_ a number
812
- else if (readOnlyArrayHasStringMember(STRING_NUMBER_CLI_FLAGS, normalizedArg)) {
813
- if (typeof value === 'string') {
814
- if (CLI_ARG_STRING_REGEX.test(value)) {
815
- // if it matches the regex we treat it like a string
816
- flags[normalizedArg] = value;
817
- }
818
- else {
819
- const parsed = Number(value);
820
- if (isNaN(parsed)) {
821
- // parsing didn't go so well, we gotta get out of here
822
- // this is unlikely given our regex guard above
823
- // but hey, this is ultimately JS so let's be safe
824
- throwNumberParsingError(rawArg, value);
825
- }
826
- else {
827
- flags[normalizedArg] = parsed;
828
- }
829
- }
830
- flags.knownArgs.push(rawArg);
831
- flags.knownArgs.push(value);
832
- }
833
- else {
834
- throwCLIParsingError(rawArg, 'expected a string or a number but received nothing');
835
- }
836
- }
837
- // We're setting the log level, which can only be a set of specific string values
838
- else if (readOnlyArrayHasStringMember(LOG_LEVEL_CLI_FLAGS, normalizedArg)) {
839
- if (typeof value === 'string') {
840
- if (isLogLevel(value)) {
841
- flags[normalizedArg] = value;
842
- flags.knownArgs.push(rawArg);
843
- flags.knownArgs.push(value);
844
- }
845
- else {
846
- throwCLIParsingError(rawArg, `expected to receive a valid log level but received "${String(value)}"`);
847
- }
848
- }
849
- else {
850
- throwCLIParsingError(rawArg, 'expected to receive a valid log level but received nothing');
851
- }
852
- }
853
- };
854
- /**
855
- * We use this regular expression to detect CLI parameters which
856
- * should be parsed as string values (as opposed to numbers) for
857
- * the argument types for which we support both a string and a
858
- * number value.
859
- *
860
- * The regex tests for the presence of at least one character which is
861
- * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
862
- * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
863
- * support the admittedly unlikely use of scientific notation, like `"4e+0"`
864
- * for `4`).
865
- *
866
- * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
867
- * `"5.0"`. If it matches a given string we conclude that the string should
868
- * be parsed as a string literal, rather than using `Number` to convert it
869
- * to a number.
870
- */
871
- const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
872
- const Empty = Symbol('Empty');
873
- /**
874
- * A little helper which tries to parse a CLI value (as opposed to a flag) off
875
- * of the argument array.
876
- *
877
- * We support a variety of different argument formats, but all of them start
878
- * with `-`, so we can check the first character to test whether the next token
879
- * in our array of CLI arguments is a flag name or a value.
880
- *
881
- * @param args an array of CLI args
882
- * @returns either a string result or an Empty sentinel
883
- */
884
- const parseCLIValue = (args) => {
885
- // it's possible the arguments array is empty, if so, return empty
886
- if (args[0] === undefined) {
887
- return Empty;
888
- }
889
- // all we're concerned with here is that it does not start with `"-"`,
890
- // which would indicate it should be parsed as a CLI flag and not a value.
891
- if (!args[0].startsWith('-')) {
892
- // It's not a flag, so we return the value and defer any specific parsing
893
- // until later on.
894
- const value = args.shift();
895
- if (typeof value === 'string') {
896
- return value;
897
- }
898
- }
899
- return Empty;
900
- };
901
- /**
902
- * Parse an 'equals' argument, which is a CLI argument-value pair in the
903
- * format `--foobar=12` (as opposed to a space-separated format like
904
- * `--foobar 12`).
905
- *
906
- * To parse this we split on the `=`, returning the first part as the argument
907
- * name and the second part as the value. We join the value on `"="` in case
908
- * there is another `"="` in the argument.
909
- *
910
- * This function is safe to call with any arg, and can therefore be used as
911
- * an argument 'normalizer'. If CLI argument is not an 'equals' argument then
912
- * the return value will be a tuple of the original argument and an empty
913
- * string `""` for the value.
914
- *
915
- * In code terms, if you do:
916
- *
917
- * ```ts
918
- * const [arg, value] = parseEqualsArg("--myArgument")
919
- * ```
920
- *
921
- * Then `arg` will be `"--myArgument"` and `value` will be `""`, whereas if
922
- * you do:
923
- *
924
- *
925
- * ```ts
926
- * const [arg, value] = parseEqualsArg("--myArgument=myValue")
927
- * ```
928
- *
929
- * Then `arg` will be `"--myArgument"` and `value` will be `"myValue"`.
930
- *
931
- * @param arg the arg in question
932
- * @returns a tuple containing the arg name and the value (if present)
933
- */
934
- const parseEqualsArg = (arg) => {
935
- const [originalArg, ...splitSections] = arg.split('=');
936
- const value = splitSections.join('=');
937
- return [originalArg, value === '' ? Empty : value];
938
- };
939
- /**
940
- * Small helper for getting type-system-level assurance that a `string` can be
941
- * narrowed to a `LogLevel`
942
- *
943
- * @param maybeLogLevel the string to check
944
- * @returns whether this is a `LogLevel`
945
- */
946
- const isLogLevel = (maybeLogLevel) => readOnlyArrayHasStringMember(LOG_LEVELS, maybeLogLevel);
947
- /**
948
- * A little helper for constructing and throwing an error message with info
949
- * about what went wrong
950
- *
951
- * @param flag the flag which encountered the error
952
- * @param message a message specific to the error which was encountered
953
- */
954
- const throwCLIParsingError = (flag, message) => {
955
- throw new Error(`when parsing CLI flag "${flag}": ${message}`);
956
- };
957
- /**
958
- * Throw a specific error for the situation where we ran into an issue parsing
959
- * a number.
960
- *
961
- * @param flag the flag for which we encountered the issue
962
- * @param value what we were trying to parse
963
- */
964
- const throwNumberParsingError = (flag, value) => {
965
- throwCLIParsingError(flag, `expected a number but received "${value}"`);
966
- };
967
- /**
968
- * A little helper to 'dereference' a flag alias, which if you squint a little
969
- * you can think of like a pointer to a full flag name. Thus 'c' is like a
970
- * pointer to 'config', so here we're doing something like `*c`. Of course, this
971
- * being JS, this is just a metaphor!
972
- *
973
- * If no 'dereference' is found for the possible alias we just return the
974
- * passed string unmodified.
975
- *
976
- * @param maybeAlias a string which _could_ be an alias to a full flag name
977
- * @returns the full aliased flag name, if found, or the passed string if not
978
- */
979
- const dereferenceAlias = (maybeAlias) => {
980
- const possibleDereference = CLI_FLAG_ALIASES[maybeAlias];
981
- if (typeof possibleDereference === 'string') {
982
- return possibleDereference;
983
- }
984
- return maybeAlias;
551
+ /**
552
+ * A regular expression which can be used to match a CLI flag for one of our
553
+ * short aliases.
554
+ */
555
+ const CLI_FLAG_REGEX = new RegExp(`^-[chpvbewofitu]{1}$`);
556
+ /**
557
+ * Helper function for initializing a `ConfigFlags` object. Provide any overrides
558
+ * for default values and off you go!
559
+ *
560
+ * @param init an object with any overrides for default values
561
+ * @returns a complete CLI flag object
562
+ */
563
+ const createConfigFlags = (init = {}) => {
564
+ const flags = {
565
+ task: null,
566
+ args: [],
567
+ knownArgs: [],
568
+ unknownArgs: [],
569
+ ...init,
570
+ };
571
+ return flags;
985
572
  };
986
573
 
987
- const dependencies = [
988
- {
989
- name: "@rindo/core",
990
- version: "3.0.1",
991
- main: "compiler/rindo.js",
992
- resources: [
993
- "package.json",
994
- "compiler/lib.d.ts",
995
- "compiler/lib.dom.d.ts",
996
- "compiler/lib.dom.iterable.d.ts",
997
- "compiler/lib.es2015.collection.d.ts",
998
- "compiler/lib.es2015.core.d.ts",
999
- "compiler/lib.es2015.d.ts",
1000
- "compiler/lib.es2015.generator.d.ts",
1001
- "compiler/lib.es2015.iterable.d.ts",
1002
- "compiler/lib.es2015.promise.d.ts",
1003
- "compiler/lib.es2015.proxy.d.ts",
1004
- "compiler/lib.es2015.reflect.d.ts",
1005
- "compiler/lib.es2015.symbol.d.ts",
1006
- "compiler/lib.es2015.symbol.wellknown.d.ts",
1007
- "compiler/lib.es2016.array.include.d.ts",
1008
- "compiler/lib.es2016.d.ts",
1009
- "compiler/lib.es2016.full.d.ts",
1010
- "compiler/lib.es2017.d.ts",
1011
- "compiler/lib.es2017.full.d.ts",
1012
- "compiler/lib.es2017.intl.d.ts",
1013
- "compiler/lib.es2017.object.d.ts",
1014
- "compiler/lib.es2017.sharedmemory.d.ts",
1015
- "compiler/lib.es2017.string.d.ts",
1016
- "compiler/lib.es2017.typedarrays.d.ts",
1017
- "compiler/lib.es2018.asyncgenerator.d.ts",
1018
- "compiler/lib.es2018.asynciterable.d.ts",
1019
- "compiler/lib.es2018.d.ts",
1020
- "compiler/lib.es2018.full.d.ts",
1021
- "compiler/lib.es2018.intl.d.ts",
1022
- "compiler/lib.es2018.promise.d.ts",
1023
- "compiler/lib.es2018.regexp.d.ts",
1024
- "compiler/lib.es2019.array.d.ts",
1025
- "compiler/lib.es2019.d.ts",
1026
- "compiler/lib.es2019.full.d.ts",
1027
- "compiler/lib.es2019.intl.d.ts",
1028
- "compiler/lib.es2019.object.d.ts",
1029
- "compiler/lib.es2019.string.d.ts",
1030
- "compiler/lib.es2019.symbol.d.ts",
1031
- "compiler/lib.es2020.bigint.d.ts",
1032
- "compiler/lib.es2020.d.ts",
1033
- "compiler/lib.es2020.date.d.ts",
1034
- "compiler/lib.es2020.full.d.ts",
1035
- "compiler/lib.es2020.intl.d.ts",
1036
- "compiler/lib.es2020.number.d.ts",
1037
- "compiler/lib.es2020.promise.d.ts",
1038
- "compiler/lib.es2020.sharedmemory.d.ts",
1039
- "compiler/lib.es2020.string.d.ts",
1040
- "compiler/lib.es2020.symbol.wellknown.d.ts",
1041
- "compiler/lib.es2021.d.ts",
1042
- "compiler/lib.es2021.full.d.ts",
1043
- "compiler/lib.es2021.intl.d.ts",
1044
- "compiler/lib.es2021.promise.d.ts",
1045
- "compiler/lib.es2021.string.d.ts",
1046
- "compiler/lib.es2021.weakref.d.ts",
1047
- "compiler/lib.es2022.array.d.ts",
1048
- "compiler/lib.es2022.d.ts",
1049
- "compiler/lib.es2022.error.d.ts",
1050
- "compiler/lib.es2022.full.d.ts",
1051
- "compiler/lib.es2022.intl.d.ts",
1052
- "compiler/lib.es2022.object.d.ts",
1053
- "compiler/lib.es2022.sharedmemory.d.ts",
1054
- "compiler/lib.es2022.string.d.ts",
1055
- "compiler/lib.es5.d.ts",
1056
- "compiler/lib.es6.d.ts",
1057
- "compiler/lib.esnext.d.ts",
1058
- "compiler/lib.esnext.full.d.ts",
1059
- "compiler/lib.esnext.intl.d.ts",
1060
- "compiler/lib.esnext.promise.d.ts",
1061
- "compiler/lib.esnext.string.d.ts",
1062
- "compiler/lib.esnext.weakref.d.ts",
1063
- "compiler/lib.scripthost.d.ts",
1064
- "compiler/lib.webworker.d.ts",
1065
- "compiler/lib.webworker.importscripts.d.ts",
1066
- "compiler/lib.webworker.iterable.d.ts",
1067
- "internal/index.d.ts",
1068
- "internal/index.js",
1069
- "internal/package.json",
1070
- "internal/rindo-ext-modules.d.ts",
1071
- "internal/rindo-private.d.ts",
1072
- "internal/rindo-public-compiler.d.ts",
1073
- "internal/rindo-public-docs.d.ts",
1074
- "internal/rindo-public-runtime.d.ts",
1075
- "mock-doc/index.js",
1076
- "mock-doc/package.json",
1077
- "internal/client/css-shim.js",
1078
- "internal/client/dom.js",
1079
- "internal/client/index.js",
1080
- "internal/client/package.json",
1081
- "internal/client/patch-browser.js",
1082
- "internal/client/patch-esm.js",
1083
- "internal/client/shadow-css.js",
1084
- "internal/hydrate/index.js",
1085
- "internal/hydrate/package.json",
1086
- "internal/hydrate/runner.js",
1087
- "internal/hydrate/shadow-css.js",
1088
- "internal/rindo-core/index.d.ts",
1089
- "internal/rindo-core/index.js"
1090
- ]
1091
- },
1092
- {
1093
- name: "rollup",
1094
- version: "2.42.3",
1095
- main: "dist/es/rollup.browser.js"
1096
- },
1097
- {
1098
- name: "terser",
1099
- version: "5.16.1",
1100
- main: "dist/bundle.min.js"
1101
- },
1102
- {
1103
- name: "typescript",
1104
- version: "4.9.4",
1105
- main: "lib/typescript.js"
1106
- }
1107
- ];
574
+ /**
575
+ * Parse command line arguments into a structured `ConfigFlags` object
576
+ *
577
+ * @param args an array of CLI flags
578
+ * @returns a structured ConfigFlags object
579
+ */
580
+ const parseFlags = (args) => {
581
+ const flags = createConfigFlags();
582
+ // cmd line has more priority over npm scripts cmd
583
+ flags.args = Array.isArray(args) ? args.slice() : [];
584
+ if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
585
+ flags.task = flags.args[0];
586
+ // if the first argument was a "task" (like `build`, `test`, etc) then
587
+ // we go on to parse the _rest_ of the CLI args
588
+ parseArgs(flags, args.slice(1));
589
+ }
590
+ else {
591
+ // we didn't find a leading flag, so we should just parse them all
592
+ parseArgs(flags, flags.args);
593
+ }
594
+ if (flags.task != null) {
595
+ const i = flags.args.indexOf(flags.task);
596
+ if (i > -1) {
597
+ flags.args.splice(i, 1);
598
+ }
599
+ }
600
+ return flags;
601
+ };
602
+ /**
603
+ * Parse the supported command line flags which are enumerated in the
604
+ * `config-flags` module. Handles leading dashes on arguments, aliases that are
605
+ * defined for a small number of arguments, and parsing values for non-boolean
606
+ * arguments (e.g. port number for the dev server).
607
+ *
608
+ * This parses the following grammar:
609
+ *
610
+ * CLIArguments → ""
611
+ * | CLITerm ( " " CLITerm )* ;
612
+ * CLITerm → EqualsArg
613
+ * | AliasEqualsArg
614
+ * | AliasArg
615
+ * | NegativeDashArg
616
+ * | NegativeArg
617
+ * | SimpleArg ;
618
+ * EqualsArg → "--" ArgName "=" CLIValue ;
619
+ * AliasEqualsArg → "-" AliasName "=" CLIValue ;
620
+ * AliasArg → "-" AliasName ( " " CLIValue )? ;
621
+ * NegativeDashArg → "--no-" ArgName ;
622
+ * NegativeArg → "--no" ArgName ;
623
+ * SimpleArg → "--" ArgName ( " " CLIValue )? ;
624
+ * ArgName → /^[a-zA-Z-]+$/ ;
625
+ * AliasName → /^[a-z]{1}$/ ;
626
+ * CLIValue → '"' /^[a-zA-Z0-9]+$/ '"'
627
+ * | /^[a-zA-Z0-9]+$/ ;
628
+ *
629
+ * There are additional constraints (not shown in the grammar for brevity's sake)
630
+ * on the type of `CLIValue` which will be associated with a particular argument.
631
+ * We enforce this by declaring lists of boolean, string, etc arguments and
632
+ * checking the types of values before setting them.
633
+ *
634
+ * We don't need to turn the list of CLI arg tokens into any kind of
635
+ * intermediate representation since we aren't concerned with doing anything
636
+ * other than setting the correct values on our ConfigFlags object. So we just
637
+ * parse the array of string arguments using a recursive-descent approach
638
+ * (which is not very deep since our grammar is pretty simple) and make the
639
+ * modifications we need to make to the {@link ConfigFlags} object as we go.
640
+ *
641
+ * @param flags a ConfigFlags object to which parsed arguments will be added
642
+ * @param args an array of command-line arguments to parse
643
+ */
644
+ const parseArgs = (flags, args) => {
645
+ const argsCopy = args.concat();
646
+ while (argsCopy.length > 0) {
647
+ // there are still unprocessed args to deal with
648
+ parseCLITerm(flags, argsCopy);
649
+ }
650
+ };
651
+ /**
652
+ * Given an array of CLI arguments, parse it and perform a series of side
653
+ * effects (setting values on the provided `ConfigFlags` object).
654
+ *
655
+ * @param flags a {@link ConfigFlags} object which is updated as we parse the CLI
656
+ * arguments
657
+ * @param args a list of args to work through. This function (and some functions
658
+ * it calls) calls `Array.prototype.shift` to get the next argument to look at,
659
+ * so this parameter will be modified.
660
+ */
661
+ const parseCLITerm = (flags, args) => {
662
+ // pull off the first arg from the argument array
663
+ const arg = args.shift();
664
+ // array is empty, we're done!
665
+ if (arg === undefined)
666
+ return;
667
+ // EqualsArg → "--" ArgName "=" CLIValue ;
668
+ if (arg.startsWith('--') && arg.includes('=')) {
669
+ // we're dealing with an EqualsArg, we have a special helper for that
670
+ const [originalArg, value] = parseEqualsArg(arg);
671
+ setCLIArg(flags, arg.split('=')[0], normalizeFlagName(originalArg), value);
672
+ }
673
+ // AliasEqualsArg → "-" AliasName "=" CLIValue ;
674
+ else if (arg.startsWith('-') && arg.includes('=')) {
675
+ // we're dealing with an AliasEqualsArg, we have a special helper for that
676
+ const [originalArg, value] = parseEqualsArg(arg);
677
+ setCLIArg(flags, desugarRawAlias(originalArg), normalizeFlagName(originalArg), value);
678
+ }
679
+ // AliasArg → "-" AliasName ( " " CLIValue )? ;
680
+ else if (CLI_FLAG_REGEX.test(arg)) {
681
+ // this is a short alias, like `-c` for Config
682
+ setCLIArg(flags, desugarRawAlias(arg), normalizeFlagName(arg), parseCLIValue(args));
683
+ }
684
+ // NegativeDashArg → "--no-" ArgName ;
685
+ else if (arg.startsWith('--no-') && arg.length > '--no-'.length) {
686
+ // this is a `NegativeDashArg` term, so we need to normalize the negative
687
+ // flag name and then set an appropriate value
688
+ const normalized = normalizeNegativeFlagName(arg);
689
+ setCLIArg(flags, arg, normalized, '');
690
+ }
691
+ // NegativeArg → "--no" ArgName ;
692
+ else if (arg.startsWith('--no') &&
693
+ !readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizeFlagName(arg)) &&
694
+ readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizeNegativeFlagName(arg))) {
695
+ // possibly dealing with a `NegativeArg` here. There is a little ambiguity
696
+ // here because we have arguments that already begin with `no` like
697
+ // `notify`, so we need to test if a normalized form of the raw argument is
698
+ // a valid and supported boolean flag.
699
+ setCLIArg(flags, arg, normalizeNegativeFlagName(arg), '');
700
+ }
701
+ // SimpleArg → "--" ArgName ( " " CLIValue )? ;
702
+ else if (arg.startsWith('--') && arg.length > '--'.length) {
703
+ setCLIArg(flags, arg, normalizeFlagName(arg), parseCLIValue(args));
704
+ }
705
+ else {
706
+ // if we get here then `arg` is not an argument in our list of supported
707
+ // arguments. This doesn't necessarily mean we want to report an error or
708
+ // anything though! Instead, with unknown / unrecognized arguments we want
709
+ // to stick them into the `unknownArgs` array, which is used when we pass
710
+ // CLI args to Jest, for instance.
711
+ flags.unknownArgs.push(arg);
712
+ }
713
+ };
714
+ /**
715
+ * Normalize a 'negative' flag name, just to do a little pre-processing before
716
+ * we pass it to `setCLIArg`.
717
+ *
718
+ * @param flagName the flag name to normalize
719
+ * @returns a normalized flag name
720
+ */
721
+ const normalizeNegativeFlagName = (flagName) => {
722
+ const trimmed = flagName.replace(/^--no[-]?/, '');
723
+ return normalizeFlagName(trimmed.charAt(0).toLowerCase() + trimmed.slice(1));
724
+ };
725
+ /**
726
+ * Normalize a flag name by:
727
+ *
728
+ * - replacing any leading dashes (`--foo` -> `foo`)
729
+ * - converting `dash-case` to camelCase (if necessary)
730
+ *
731
+ * Normalizing in this context basically means converting the various
732
+ * supported flag spelling variants to the variant defined in our lists of
733
+ * supported arguments (e.g. BOOLEAN_CLI_FLAGS, etc). So, for instance,
734
+ * `--log-level` should be converted to `logLevel`.
735
+ *
736
+ * @param flagName the flag name to normalize
737
+ * @returns a normalized flag name
738
+ *
739
+ */
740
+ const normalizeFlagName = (flagName) => {
741
+ const trimmed = flagName.replace(/^-+/, '');
742
+ return trimmed.includes('-') ? toCamelCase(trimmed) : trimmed;
743
+ };
744
+ /**
745
+ * Set a value on a provided {@link ConfigFlags} object, given an argument
746
+ * name and a raw string value. This function dispatches to other functions
747
+ * which make sure that the string value can be properly parsed into a JS
748
+ * runtime value of the right type (e.g. number, string, etc).
749
+ *
750
+ * @throws if a value cannot be parsed to the right type for a given flag
751
+ * @param flags a {@link ConfigFlags} object
752
+ * @param rawArg the raw argument name matched by the parser
753
+ * @param normalizedArg an argument with leading control characters (`--`,
754
+ * `--no-`, etc) removed
755
+ * @param value the raw value to be set onto the config flags object
756
+ */
757
+ const setCLIArg = (flags, rawArg, normalizedArg, value) => {
758
+ normalizedArg = desugarAlias(normalizedArg);
759
+ // We're setting a boolean!
760
+ if (readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizedArg)) {
761
+ flags[normalizedArg] =
762
+ typeof value === 'string'
763
+ ? Boolean(value)
764
+ : // no value was supplied, default to true
765
+ true;
766
+ flags.knownArgs.push(rawArg);
767
+ }
768
+ // We're setting a string!
769
+ else if (readOnlyArrayHasStringMember(STRING_CLI_FLAGS, normalizedArg)) {
770
+ if (typeof value === 'string') {
771
+ flags[normalizedArg] = value;
772
+ flags.knownArgs.push(rawArg);
773
+ flags.knownArgs.push(value);
774
+ }
775
+ else {
776
+ throwCLIParsingError(rawArg, 'expected a string argument but received nothing');
777
+ }
778
+ }
779
+ // We're setting a string, but it's one where the user can pass multiple values,
780
+ // like `--reporters="default" --reporters="jest-junit"`
781
+ else if (readOnlyArrayHasStringMember(STRING_ARRAY_CLI_FLAGS, normalizedArg)) {
782
+ if (typeof value === 'string') {
783
+ if (!Array.isArray(flags[normalizedArg])) {
784
+ flags[normalizedArg] = [];
785
+ }
786
+ const targetArray = flags[normalizedArg];
787
+ // this is irritating, but TS doesn't know that the `!Array.isArray`
788
+ // check above guarantees we have an array to work with here, and it
789
+ // doesn't want to narrow the type of `flags[normalizedArg]`, so we need
790
+ // to grab a reference to that array and then `Array.isArray` that. Bah!
791
+ if (Array.isArray(targetArray)) {
792
+ targetArray.push(value);
793
+ flags.knownArgs.push(rawArg);
794
+ flags.knownArgs.push(value);
795
+ }
796
+ }
797
+ else {
798
+ throwCLIParsingError(rawArg, 'expected a string argument but received nothing');
799
+ }
800
+ }
801
+ // We're setting a number!
802
+ else if (readOnlyArrayHasStringMember(NUMBER_CLI_FLAGS, normalizedArg)) {
803
+ if (typeof value === 'string') {
804
+ const parsed = parseInt(value, 10);
805
+ if (isNaN(parsed)) {
806
+ throwNumberParsingError(rawArg, value);
807
+ }
808
+ else {
809
+ flags[normalizedArg] = parsed;
810
+ flags.knownArgs.push(rawArg);
811
+ flags.knownArgs.push(value);
812
+ }
813
+ }
814
+ else {
815
+ throwCLIParsingError(rawArg, 'expected a number argument but received nothing');
816
+ }
817
+ }
818
+ // We're setting a value which could be either a string _or_ a number
819
+ else if (readOnlyArrayHasStringMember(STRING_NUMBER_CLI_FLAGS, normalizedArg)) {
820
+ if (typeof value === 'string') {
821
+ if (CLI_ARG_STRING_REGEX.test(value)) {
822
+ // if it matches the regex we treat it like a string
823
+ flags[normalizedArg] = value;
824
+ }
825
+ else {
826
+ const parsed = Number(value);
827
+ if (isNaN(parsed)) {
828
+ // parsing didn't go so well, we gotta get out of here
829
+ // this is unlikely given our regex guard above
830
+ // but hey, this is ultimately JS so let's be safe
831
+ throwNumberParsingError(rawArg, value);
832
+ }
833
+ else {
834
+ flags[normalizedArg] = parsed;
835
+ }
836
+ }
837
+ flags.knownArgs.push(rawArg);
838
+ flags.knownArgs.push(value);
839
+ }
840
+ else {
841
+ throwCLIParsingError(rawArg, 'expected a string or a number but received nothing');
842
+ }
843
+ }
844
+ // We're setting the log level, which can only be a set of specific string values
845
+ else if (readOnlyArrayHasStringMember(LOG_LEVEL_CLI_FLAGS, normalizedArg)) {
846
+ if (typeof value === 'string') {
847
+ if (isLogLevel(value)) {
848
+ flags[normalizedArg] = value;
849
+ flags.knownArgs.push(rawArg);
850
+ flags.knownArgs.push(value);
851
+ }
852
+ else {
853
+ throwCLIParsingError(rawArg, `expected to receive a valid log level but received "${String(value)}"`);
854
+ }
855
+ }
856
+ else {
857
+ throwCLIParsingError(rawArg, 'expected to receive a valid log level but received nothing');
858
+ }
859
+ }
860
+ else {
861
+ // we haven't found this flag in any of our lists of arguments, so we
862
+ // should put it in our list of unknown arguments
863
+ flags.unknownArgs.push(rawArg);
864
+ if (typeof value === 'string') {
865
+ flags.unknownArgs.push(value);
866
+ }
867
+ }
868
+ };
869
+ /**
870
+ * We use this regular expression to detect CLI parameters which
871
+ * should be parsed as string values (as opposed to numbers) for
872
+ * the argument types for which we support both a string and a
873
+ * number value.
874
+ *
875
+ * The regex tests for the presence of at least one character which is
876
+ * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
877
+ * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
878
+ * support the admittedly unlikely use of scientific notation, like `"4e+0"`
879
+ * for `4`).
880
+ *
881
+ * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
882
+ * `"5.0"`. If it matches a given string we conclude that the string should
883
+ * be parsed as a string literal, rather than using `Number` to convert it
884
+ * to a number.
885
+ */
886
+ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
887
+ const Empty = Symbol('Empty');
888
+ /**
889
+ * A little helper which tries to parse a CLI value (as opposed to a flag) off
890
+ * of the argument array.
891
+ *
892
+ * We support a variety of different argument formats for flags (as opposed to
893
+ * values), but all of them start with `-`, so we can check the first character
894
+ * to test whether the next token in our array of CLI arguments is a flag name
895
+ * or a value.
896
+ *
897
+ * @param args an array of CLI args
898
+ * @returns either a string result or an Empty sentinel
899
+ */
900
+ const parseCLIValue = (args) => {
901
+ // it's possible the arguments array is empty, if so, return empty
902
+ if (args[0] === undefined) {
903
+ return Empty;
904
+ }
905
+ // all we're concerned with here is that it does not start with `"-"`,
906
+ // which would indicate it should be parsed as a CLI flag and not a value.
907
+ if (!args[0].startsWith('-')) {
908
+ // It's not a flag, so we return the value and defer any specific parsing
909
+ // until later on.
910
+ const value = args.shift();
911
+ if (typeof value === 'string') {
912
+ return value;
913
+ }
914
+ }
915
+ return Empty;
916
+ };
917
+ /**
918
+ * Parse an 'equals' argument, which is a CLI argument-value pair in the
919
+ * format `--foobar=12` (as opposed to a space-separated format like
920
+ * `--foobar 12`).
921
+ *
922
+ * To parse this we split on the `=`, returning the first part as the argument
923
+ * name and the second part as the value. We join the value on `"="` in case
924
+ * there is another `"="` in the argument.
925
+ *
926
+ * This function is safe to call with any arg, and can therefore be used as
927
+ * an argument 'normalizer'. If CLI argument is not an 'equals' argument then
928
+ * the return value will be a tuple of the original argument and an empty
929
+ * string `""` for the value.
930
+ *
931
+ * In code terms, if you do:
932
+ *
933
+ * ```ts
934
+ * const [arg, value] = parseEqualsArg("--myArgument")
935
+ * ```
936
+ *
937
+ * Then `arg` will be `"--myArgument"` and `value` will be `""`, whereas if
938
+ * you do:
939
+ *
940
+ *
941
+ * ```ts
942
+ * const [arg, value] = parseEqualsArg("--myArgument=myValue")
943
+ * ```
944
+ *
945
+ * Then `arg` will be `"--myArgument"` and `value` will be `"myValue"`.
946
+ *
947
+ * @param arg the arg in question
948
+ * @returns a tuple containing the arg name and the value (if present)
949
+ */
950
+ const parseEqualsArg = (arg) => {
951
+ const [originalArg, ...splitSections] = arg.split('=');
952
+ const value = splitSections.join('=');
953
+ return [originalArg, value === '' ? Empty : value];
954
+ };
955
+ /**
956
+ * Small helper for getting type-system-level assurance that a `string` can be
957
+ * narrowed to a `LogLevel`
958
+ *
959
+ * @param maybeLogLevel the string to check
960
+ * @returns whether this is a `LogLevel`
961
+ */
962
+ const isLogLevel = (maybeLogLevel) => readOnlyArrayHasStringMember(LOG_LEVELS, maybeLogLevel);
963
+ /**
964
+ * A little helper for constructing and throwing an error message with info
965
+ * about what went wrong
966
+ *
967
+ * @param flag the flag which encountered the error
968
+ * @param message a message specific to the error which was encountered
969
+ */
970
+ const throwCLIParsingError = (flag, message) => {
971
+ throw new Error(`when parsing CLI flag "${flag}": ${message}`);
972
+ };
973
+ /**
974
+ * Throw a specific error for the situation where we ran into an issue parsing
975
+ * a number.
976
+ *
977
+ * @param flag the flag for which we encountered the issue
978
+ * @param value what we were trying to parse
979
+ */
980
+ const throwNumberParsingError = (flag, value) => {
981
+ throwCLIParsingError(flag, `expected a number but received "${value}"`);
982
+ };
983
+ /**
984
+ * A little helper to 'desugar' a flag alias, meaning expand it to its full
985
+ * name. For instance, the alias `"c"` will desugar to `"config"`.
986
+ *
987
+ * If no expansion is found for the possible alias we just return the passed
988
+ * string unmodified.
989
+ *
990
+ * @param maybeAlias a string which _could_ be an alias to a full flag name
991
+ * @returns the full aliased flag name, if found, or the passed string if not
992
+ */
993
+ const desugarAlias = (maybeAlias) => {
994
+ const possiblyDesugared = CLI_FLAG_ALIASES[maybeAlias];
995
+ if (typeof possiblyDesugared === 'string') {
996
+ return possiblyDesugared;
997
+ }
998
+ return maybeAlias;
999
+ };
1000
+ /**
1001
+ * Desugar a 'raw' alias (with a leading dash) and return an equivalent,
1002
+ * desugared argument.
1003
+ *
1004
+ * For instance, passing `"-c` will return `"--config"`.
1005
+ *
1006
+ * The reason we'd like to do this is not so much for our own code, but so that
1007
+ * we can transform an alias like `"-u"` to `"--updateSnapshot"` in order to
1008
+ * pass it along to Jest.
1009
+ *
1010
+ * @param rawAlias a CLI flag alias as found on the command line (like `"-c"`)
1011
+ * @returns an equivalent full command (like `"--config"`)
1012
+ */
1013
+ const desugarRawAlias = (rawAlias) => '--' + desugarAlias(normalizeFlagName(rawAlias));
1108
1014
 
1109
- const IS_NODE_ENV = typeof global !== 'undefined' &&
1110
- typeof require === 'function' &&
1111
- !!global.process &&
1112
- typeof __filename === 'string' &&
1113
- (!global.origin || typeof global.origin !== 'string');
1015
+ const IS_NODE_ENV = typeof global !== 'undefined' &&
1016
+ typeof require === 'function' &&
1017
+ !!global.process &&
1018
+ typeof __filename === 'string' &&
1019
+ (!global.origin || typeof global.origin !== 'string');
1114
1020
  const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1115
1021
 
1116
- /**
1117
- * Creates an instance of a logger
1118
- * @returns the new logger instance
1119
- */
1120
- const createLogger = () => {
1121
- let useColors = IS_BROWSER_ENV;
1122
- let level = 'info';
1123
- return {
1124
- enableColors: (uc) => (useColors = uc),
1125
- getLevel: () => level,
1126
- setLevel: (l) => (level = l),
1127
- emoji: (e) => e,
1128
- info: console.log.bind(console),
1129
- warn: console.warn.bind(console),
1130
- error: console.error.bind(console),
1131
- debug: console.debug.bind(console),
1132
- red: (msg) => msg,
1133
- green: (msg) => msg,
1134
- yellow: (msg) => msg,
1135
- blue: (msg) => msg,
1136
- magenta: (msg) => msg,
1137
- cyan: (msg) => msg,
1138
- gray: (msg) => msg,
1139
- bold: (msg) => msg,
1140
- dim: (msg) => msg,
1141
- bgRed: (msg) => msg,
1142
- createTimeSpan: (_startMsg, _debug = false) => ({
1143
- duration: () => 0,
1144
- finish: () => 0,
1145
- }),
1146
- printDiagnostics(diagnostics) {
1147
- diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
1148
- },
1149
- };
1150
- };
1151
- const logDiagnostic = (diagnostic, useColors) => {
1152
- let color = BLUE;
1153
- let prefix = 'Build';
1154
- let msg = '';
1155
- if (diagnostic.level === 'error') {
1156
- color = RED;
1157
- prefix = 'Error';
1158
- }
1159
- else if (diagnostic.level === 'warn') {
1160
- color = YELLOW;
1161
- prefix = 'Warning';
1162
- }
1163
- if (diagnostic.header) {
1164
- prefix = diagnostic.header;
1165
- }
1166
- const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
1167
- if (filePath) {
1168
- msg += filePath;
1169
- if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
1170
- msg += ', line ' + diagnostic.lineNumber;
1171
- if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
1172
- msg += ', column ' + diagnostic.columnNumber;
1173
- }
1174
- }
1175
- msg += '\n';
1176
- }
1177
- msg += diagnostic.messageText;
1178
- if (diagnostic.lines && diagnostic.lines.length > 0) {
1179
- diagnostic.lines.forEach((l) => {
1180
- msg += '\n' + l.lineNumber + ': ' + l.text;
1181
- });
1182
- msg += '\n';
1183
- }
1184
- if (useColors) {
1185
- const styledPrefix = [
1186
- '%c' + prefix,
1187
- `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
1188
- ];
1189
- console.log(...styledPrefix, msg);
1190
- }
1191
- else if (diagnostic.level === 'error') {
1192
- console.error(msg);
1193
- }
1194
- else if (diagnostic.level === 'warn') {
1195
- console.warn(msg);
1196
- }
1197
- else {
1198
- console.log(msg);
1199
- }
1200
- };
1201
- const YELLOW = `#f39c12`;
1202
- const RED = `#c0392b`;
1022
+ /**
1023
+ * Creates an instance of a logger
1024
+ * @returns the new logger instance
1025
+ */
1026
+ const createLogger = () => {
1027
+ let useColors = IS_BROWSER_ENV;
1028
+ let level = 'info';
1029
+ return {
1030
+ enableColors: (uc) => (useColors = uc),
1031
+ getLevel: () => level,
1032
+ setLevel: (l) => (level = l),
1033
+ emoji: (e) => e,
1034
+ info: console.log.bind(console),
1035
+ warn: console.warn.bind(console),
1036
+ error: console.error.bind(console),
1037
+ debug: console.debug.bind(console),
1038
+ red: (msg) => msg,
1039
+ green: (msg) => msg,
1040
+ yellow: (msg) => msg,
1041
+ blue: (msg) => msg,
1042
+ magenta: (msg) => msg,
1043
+ cyan: (msg) => msg,
1044
+ gray: (msg) => msg,
1045
+ bold: (msg) => msg,
1046
+ dim: (msg) => msg,
1047
+ bgRed: (msg) => msg,
1048
+ createTimeSpan: (_startMsg, _debug = false) => ({
1049
+ duration: () => 0,
1050
+ finish: () => 0,
1051
+ }),
1052
+ printDiagnostics(diagnostics) {
1053
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
1054
+ },
1055
+ };
1056
+ };
1057
+ const logDiagnostic = (diagnostic, useColors) => {
1058
+ let color = BLUE;
1059
+ let prefix = 'Build';
1060
+ let msg = '';
1061
+ if (diagnostic.level === 'error') {
1062
+ color = RED;
1063
+ prefix = 'Error';
1064
+ }
1065
+ else if (diagnostic.level === 'warn') {
1066
+ color = YELLOW;
1067
+ prefix = 'Warning';
1068
+ }
1069
+ if (diagnostic.header) {
1070
+ prefix = diagnostic.header;
1071
+ }
1072
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
1073
+ if (filePath) {
1074
+ msg += filePath;
1075
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
1076
+ msg += ', line ' + diagnostic.lineNumber;
1077
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
1078
+ msg += ', column ' + diagnostic.columnNumber;
1079
+ }
1080
+ }
1081
+ msg += '\n';
1082
+ }
1083
+ msg += diagnostic.messageText;
1084
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
1085
+ diagnostic.lines.forEach((l) => {
1086
+ msg += '\n' + l.lineNumber + ': ' + l.text;
1087
+ });
1088
+ msg += '\n';
1089
+ }
1090
+ if (useColors) {
1091
+ const styledPrefix = [
1092
+ '%c' + prefix,
1093
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
1094
+ ];
1095
+ console.log(...styledPrefix, msg);
1096
+ }
1097
+ else if (diagnostic.level === 'error') {
1098
+ console.error(msg);
1099
+ }
1100
+ else if (diagnostic.level === 'warn') {
1101
+ console.warn(msg);
1102
+ }
1103
+ else {
1104
+ console.log(msg);
1105
+ }
1106
+ };
1107
+ const YELLOW = `#f39c12`;
1108
+ const RED = `#c0392b`;
1203
1109
  const BLUE = `#3498db`;
1204
1110
 
1205
- /**
1206
- * Attempt to find a Rindo configuration file on the file system
1207
- * @param opts the options needed to find the configuration file
1208
- * @returns the results of attempting to find a configuration file on disk
1209
- */
1210
- const findConfig = async (opts) => {
1211
- const sys = opts.sys;
1212
- const cwd = sys.getCurrentDirectory();
1213
- const rootDir = normalizePath(cwd);
1214
- let configPath = opts.configPath;
1215
- if (isString(configPath)) {
1216
- if (!sys.platformPath.isAbsolute(configPath)) {
1217
- // passed in a custom rindo config location,
1218
- // but it's relative, so prefix the cwd
1219
- configPath = normalizePath(sys.platformPath.join(cwd, configPath));
1220
- }
1221
- else {
1222
- // config path already an absolute path, we're good here
1223
- configPath = normalizePath(opts.configPath);
1224
- }
1225
- }
1226
- else {
1227
- // nothing was passed in, use the current working directory
1228
- configPath = rootDir;
1229
- }
1230
- const results = {
1231
- configPath,
1232
- rootDir: normalizePath(cwd),
1233
- diagnostics: [],
1234
- };
1235
- const stat = await sys.stat(configPath);
1236
- if (stat.error) {
1237
- const diagnostic = buildError(results.diagnostics);
1238
- diagnostic.absFilePath = configPath;
1239
- diagnostic.header = `Invalid config path`;
1240
- diagnostic.messageText = `Config path "${configPath}" not found`;
1241
- return results;
1242
- }
1243
- if (stat.isFile) {
1244
- results.configPath = configPath;
1245
- results.rootDir = sys.platformPath.dirname(configPath);
1246
- }
1247
- else if (stat.isDirectory) {
1248
- // this is only a directory, so let's make some assumptions
1249
- for (const configName of ['rindo.config.ts', 'rindo.config.js']) {
1250
- const testConfigFilePath = sys.platformPath.join(configPath, configName);
1251
- const stat = await sys.stat(testConfigFilePath);
1252
- if (stat.isFile) {
1253
- results.configPath = testConfigFilePath;
1254
- results.rootDir = sys.platformPath.dirname(testConfigFilePath);
1255
- break;
1256
- }
1257
- }
1258
- }
1259
- return results;
1111
+ /**
1112
+ * Attempt to find a Rindo configuration file on the file system
1113
+ * @param opts the options needed to find the configuration file
1114
+ * @returns the results of attempting to find a configuration file on disk
1115
+ */
1116
+ const findConfig = async (opts) => {
1117
+ const sys = opts.sys;
1118
+ const cwd = sys.getCurrentDirectory();
1119
+ const rootDir = normalizePath(cwd);
1120
+ let configPath = opts.configPath;
1121
+ if (isString(configPath)) {
1122
+ if (!sys.platformPath.isAbsolute(configPath)) {
1123
+ // passed in a custom rindo config location,
1124
+ // but it's relative, so prefix the cwd
1125
+ configPath = normalizePath(sys.platformPath.join(cwd, configPath));
1126
+ }
1127
+ else {
1128
+ // config path already an absolute path, we're good here
1129
+ configPath = normalizePath(opts.configPath);
1130
+ }
1131
+ }
1132
+ else {
1133
+ // nothing was passed in, use the current working directory
1134
+ configPath = rootDir;
1135
+ }
1136
+ const results = {
1137
+ configPath,
1138
+ rootDir: normalizePath(cwd),
1139
+ diagnostics: [],
1140
+ };
1141
+ const stat = await sys.stat(configPath);
1142
+ if (stat.error) {
1143
+ const diagnostic = buildError(results.diagnostics);
1144
+ diagnostic.absFilePath = configPath;
1145
+ diagnostic.header = `Invalid config path`;
1146
+ diagnostic.messageText = `Config path "${configPath}" not found`;
1147
+ return results;
1148
+ }
1149
+ if (stat.isFile) {
1150
+ results.configPath = configPath;
1151
+ results.rootDir = sys.platformPath.dirname(configPath);
1152
+ }
1153
+ else if (stat.isDirectory) {
1154
+ // this is only a directory, so let's make some assumptions
1155
+ for (const configName of ['rindo.config.ts', 'rindo.config.js']) {
1156
+ const testConfigFilePath = sys.platformPath.join(configPath, configName);
1157
+ const stat = await sys.stat(testConfigFilePath);
1158
+ if (stat.isFile) {
1159
+ results.configPath = testConfigFilePath;
1160
+ results.rootDir = sys.platformPath.dirname(testConfigFilePath);
1161
+ break;
1162
+ }
1163
+ }
1164
+ }
1165
+ return results;
1260
1166
  };
1261
1167
 
1262
- const loadCoreCompiler = async (sys) => {
1263
- await sys.dynamicImport(sys.getCompilerExecutingPath());
1264
- return globalThis.rindo;
1168
+ const loadCoreCompiler = async (sys) => {
1169
+ await sys.dynamicImport(sys.getCompilerExecutingPath());
1170
+ return globalThis.rindo;
1265
1171
  };
1266
1172
 
1267
- /**
1268
- * Log the name of this package (`@rindo/core`) to an output stream
1269
- *
1270
- * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1271
- *
1272
- * The name of the package may not be logged, by design, for certain `task` types and logging levels
1273
- *
1274
- * @param logger the logging entity to use to output the name of the package
1275
- * @param task the current task
1276
- */
1277
- const startupLog = (logger, task) => {
1278
- if (task === 'info' || task === 'serve' || task === 'version') {
1279
- return;
1280
- }
1281
- logger.info(logger.cyan(`@rindo/core`));
1282
- };
1283
- /**
1284
- * Log this package's version to an output stream
1285
- *
1286
- * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1287
- *
1288
- * The package version may not be logged, by design, for certain `task` types and logging levels
1289
- *
1290
- * @param logger the logging entity to use for output
1291
- * @param task the current task
1292
- * @param coreCompiler the compiler instance to derive version information from
1293
- */
1294
- const startupLogVersion = (logger, task, coreCompiler) => {
1295
- if (task === 'info' || task === 'serve' || task === 'version') {
1296
- return;
1297
- }
1298
- const isDevBuild = coreCompiler.version.includes('-dev.');
1299
- let startupMsg;
1300
- if (isDevBuild) {
1301
- startupMsg = logger.yellow('[LOCAL DEV]');
1302
- }
1303
- else {
1304
- startupMsg = logger.cyan(`v${coreCompiler.version}`);
1305
- }
1306
- startupMsg += logger.emoji(' ' + coreCompiler.vermoji);
1307
- logger.info(startupMsg);
1308
- };
1309
- /**
1310
- * Log details from a {@link CompilerSystem} used by Rindo to an output stream
1311
- *
1312
- * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1313
- *
1314
- * @param sys the `CompilerSystem` to report details on
1315
- * @param logger the logging entity to use for output
1316
- * @param flags user set flags for the current invocation of Rindo
1317
- * @param coreCompiler the compiler instance being used for this invocation of Rindo
1318
- */
1319
- const loadedCompilerLog = (sys, logger, flags, coreCompiler) => {
1320
- const sysDetails = sys.details;
1321
- const runtimeInfo = `${sys.name} ${sys.version}`;
1322
- const platformInfo = sysDetails
1323
- ? `${sysDetails.platform}, ${sysDetails.cpuModel}`
1324
- : `Unknown Platform, Unknown CPU Model`;
1325
- const statsInfo = sysDetails
1326
- ? `cpus: ${sys.hardwareConcurrency}, freemem: ${Math.round(sysDetails.freemem() / 1000000)}MB, totalmem: ${Math.round(sysDetails.totalmem / 1000000)}MB`
1327
- : 'Unknown CPU Core Count, Unknown Memory';
1328
- if (logger.getLevel() === 'debug') {
1329
- logger.debug(runtimeInfo);
1330
- logger.debug(platformInfo);
1331
- logger.debug(statsInfo);
1332
- logger.debug(`compiler: ${sys.getCompilerExecutingPath()}`);
1333
- logger.debug(`build: ${coreCompiler.buildId}`);
1334
- }
1335
- else if (flags.ci) {
1336
- logger.info(runtimeInfo);
1337
- logger.info(platformInfo);
1338
- logger.info(statsInfo);
1339
- }
1340
- };
1341
- /**
1342
- * Log various warnings to an output stream
1343
- *
1344
- * The output stream is determined by the {@link Logger} instance attached to the `config` argument to this function
1345
- *
1346
- * @param coreCompiler the compiler instance being used for this invocation of Rindo
1347
- * @param config a validated configuration object to be used for this run of Rindo
1348
- */
1349
- const startupCompilerLog = (coreCompiler, config) => {
1350
- if (config.suppressLogs === true) {
1351
- return;
1352
- }
1353
- const { logger } = config;
1354
- const isDebug = logger.getLevel() === 'debug';
1355
- const isPrerelease = coreCompiler.version.includes('-');
1356
- const isDevBuild = coreCompiler.version.includes('-dev.');
1357
- if (isPrerelease && !isDevBuild) {
1358
- logger.warn(logger.yellow(`This is a prerelease build, undocumented changes might happen at any time. Technical support is not available for prereleases, but any assistance testing is appreciated.`));
1359
- }
1360
- if (config.devMode && !isDebug) {
1361
- if (config.buildEs5) {
1362
- logger.warn(`Generating ES5 during development is a very task expensive, initial and incremental builds will be much slower. Drop the '--es5' flag and use a modern browser for development.`);
1363
- }
1364
- if (!config.enableCache) {
1365
- logger.warn(`Disabling cache during development will slow down incremental builds.`);
1366
- }
1367
- }
1173
+ /**
1174
+ * Log the name of this package (`@rindo/core`) to an output stream
1175
+ *
1176
+ * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1177
+ *
1178
+ * The name of the package may not be logged, by design, for certain `task` types and logging levels
1179
+ *
1180
+ * @param logger the logging entity to use to output the name of the package
1181
+ * @param task the current task
1182
+ */
1183
+ const startupLog = (logger, task) => {
1184
+ if (task === 'info' || task === 'serve' || task === 'version') {
1185
+ return;
1186
+ }
1187
+ logger.info(logger.cyan(`@rindo/core`));
1188
+ };
1189
+ /**
1190
+ * Log this package's version to an output stream
1191
+ *
1192
+ * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1193
+ *
1194
+ * The package version may not be logged, by design, for certain `task` types and logging levels
1195
+ *
1196
+ * @param logger the logging entity to use for output
1197
+ * @param task the current task
1198
+ * @param coreCompiler the compiler instance to derive version information from
1199
+ */
1200
+ const startupLogVersion = (logger, task, coreCompiler) => {
1201
+ if (task === 'info' || task === 'serve' || task === 'version') {
1202
+ return;
1203
+ }
1204
+ const isDevBuild = coreCompiler.version.includes('-dev.');
1205
+ let startupMsg;
1206
+ if (isDevBuild) {
1207
+ startupMsg = logger.yellow('[LOCAL DEV]');
1208
+ }
1209
+ else {
1210
+ startupMsg = logger.cyan(`v${coreCompiler.version}`);
1211
+ }
1212
+ startupMsg += logger.emoji(' ' + coreCompiler.vermoji);
1213
+ logger.info(startupMsg);
1214
+ };
1215
+ /**
1216
+ * Log details from a {@link CompilerSystem} used by Rindo to an output stream
1217
+ *
1218
+ * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1219
+ *
1220
+ * @param sys the `CompilerSystem` to report details on
1221
+ * @param logger the logging entity to use for output
1222
+ * @param flags user set flags for the current invocation of Rindo
1223
+ * @param coreCompiler the compiler instance being used for this invocation of Rindo
1224
+ */
1225
+ const loadedCompilerLog = (sys, logger, flags, coreCompiler) => {
1226
+ const sysDetails = sys.details;
1227
+ const runtimeInfo = `${sys.name} ${sys.version}`;
1228
+ const platformInfo = sysDetails
1229
+ ? `${sysDetails.platform}, ${sysDetails.cpuModel}`
1230
+ : `Unknown Platform, Unknown CPU Model`;
1231
+ const statsInfo = sysDetails
1232
+ ? `cpus: ${sys.hardwareConcurrency}, freemem: ${Math.round(sysDetails.freemem() / 1000000)}MB, totalmem: ${Math.round(sysDetails.totalmem / 1000000)}MB`
1233
+ : 'Unknown CPU Core Count, Unknown Memory';
1234
+ if (logger.getLevel() === 'debug') {
1235
+ logger.debug(runtimeInfo);
1236
+ logger.debug(platformInfo);
1237
+ logger.debug(statsInfo);
1238
+ logger.debug(`compiler: ${sys.getCompilerExecutingPath()}`);
1239
+ logger.debug(`build: ${coreCompiler.buildId}`);
1240
+ }
1241
+ else if (flags.ci) {
1242
+ logger.info(runtimeInfo);
1243
+ logger.info(platformInfo);
1244
+ logger.info(statsInfo);
1245
+ }
1246
+ };
1247
+ /**
1248
+ * Log various warnings to an output stream
1249
+ *
1250
+ * The output stream is determined by the {@link Logger} instance attached to the `config` argument to this function
1251
+ *
1252
+ * @param coreCompiler the compiler instance being used for this invocation of Rindo
1253
+ * @param config a validated configuration object to be used for this run of Rindo
1254
+ */
1255
+ const startupCompilerLog = (coreCompiler, config) => {
1256
+ if (config.suppressLogs === true) {
1257
+ return;
1258
+ }
1259
+ const { logger } = config;
1260
+ const isDebug = logger.getLevel() === 'debug';
1261
+ const isPrerelease = coreCompiler.version.includes('-');
1262
+ const isDevBuild = coreCompiler.version.includes('-dev.');
1263
+ if (isPrerelease && !isDevBuild) {
1264
+ logger.warn(logger.yellow(`This is a prerelease build, undocumented changes might happen at any time. Technical support is not available for prereleases, but any assistance testing is appreciated.`));
1265
+ }
1266
+ if (config.devMode && !isDebug) {
1267
+ if (config.buildEs5) {
1268
+ logger.warn(`Generating ES5 during development is a very task expensive, initial and incremental builds will be much slower. Drop the '--es5' flag and use a modern browser for development.`);
1269
+ }
1270
+ if (!config.enableCache) {
1271
+ logger.warn(`Disabling cache during development will slow down incremental builds.`);
1272
+ }
1273
+ }
1368
1274
  };
1369
1275
 
1370
- const startCheckVersion = async (config, currentVersion) => {
1371
- if (config.devMode && !config.flags.ci && !currentVersion.includes('-dev.') && isFunction(config.sys.checkVersion)) {
1372
- return config.sys.checkVersion(config.logger, currentVersion);
1373
- }
1374
- return null;
1375
- };
1376
- const printCheckVersionResults = async (versionChecker) => {
1377
- if (versionChecker) {
1378
- const checkVersionResults = await versionChecker;
1379
- if (isFunction(checkVersionResults)) {
1380
- checkVersionResults();
1381
- }
1382
- }
1276
+ const startCheckVersion = async (config, currentVersion) => {
1277
+ if (config.devMode && !config.flags.ci && !currentVersion.includes('-dev.') && isFunction(config.sys.checkVersion)) {
1278
+ return config.sys.checkVersion(config.logger, currentVersion);
1279
+ }
1280
+ return null;
1281
+ };
1282
+ const printCheckVersionResults = async (versionChecker) => {
1283
+ if (versionChecker) {
1284
+ const checkVersionResults = await versionChecker;
1285
+ if (isFunction(checkVersionResults)) {
1286
+ checkVersionResults();
1287
+ }
1288
+ }
1383
1289
  };
1384
1290
 
1385
- const taskPrerender = async (coreCompiler, config) => {
1386
- startupCompilerLog(coreCompiler, config);
1387
- const hydrateAppFilePath = config.flags.unknownArgs[0];
1388
- if (typeof hydrateAppFilePath !== 'string') {
1389
- config.logger.error(`Missing hydrate app script path`);
1390
- return config.sys.exit(1);
1391
- }
1392
- const srcIndexHtmlPath = config.srcIndexHtml;
1393
- const diagnostics = await runPrerenderTask(coreCompiler, config, hydrateAppFilePath, null, srcIndexHtmlPath);
1394
- config.logger.printDiagnostics(diagnostics);
1395
- if (diagnostics.some((d) => d.level === 'error')) {
1396
- return config.sys.exit(1);
1397
- }
1398
- };
1399
- const runPrerenderTask = async (coreCompiler, config, hydrateAppFilePath, componentGraph, srcIndexHtmlPath) => {
1400
- const diagnostics = [];
1401
- try {
1402
- const prerenderer = await coreCompiler.createPrerenderer(config);
1403
- const results = await prerenderer.start({
1404
- hydrateAppFilePath,
1405
- componentGraph,
1406
- srcIndexHtmlPath,
1407
- });
1408
- diagnostics.push(...results.diagnostics);
1409
- }
1410
- catch (e) {
1411
- catchError(diagnostics, e);
1412
- }
1413
- return diagnostics;
1291
+ const taskPrerender = async (coreCompiler, config) => {
1292
+ startupCompilerLog(coreCompiler, config);
1293
+ const hydrateAppFilePath = config.flags.unknownArgs[0];
1294
+ if (typeof hydrateAppFilePath !== 'string') {
1295
+ config.logger.error(`Missing hydrate app script path`);
1296
+ return config.sys.exit(1);
1297
+ }
1298
+ const srcIndexHtmlPath = config.srcIndexHtml;
1299
+ const diagnostics = await runPrerenderTask(coreCompiler, config, hydrateAppFilePath, null, srcIndexHtmlPath);
1300
+ config.logger.printDiagnostics(diagnostics);
1301
+ if (diagnostics.some((d) => d.level === 'error')) {
1302
+ return config.sys.exit(1);
1303
+ }
1304
+ };
1305
+ const runPrerenderTask = async (coreCompiler, config, hydrateAppFilePath, componentGraph, srcIndexHtmlPath) => {
1306
+ const diagnostics = [];
1307
+ try {
1308
+ const prerenderer = await coreCompiler.createPrerenderer(config);
1309
+ const results = await prerenderer.start({
1310
+ hydrateAppFilePath,
1311
+ componentGraph,
1312
+ srcIndexHtmlPath,
1313
+ });
1314
+ diagnostics.push(...results.diagnostics);
1315
+ }
1316
+ catch (e) {
1317
+ catchError(diagnostics, e);
1318
+ }
1319
+ return diagnostics;
1414
1320
  };
1415
1321
 
1416
- const taskWatch = async (coreCompiler, config) => {
1417
- let devServer = null;
1418
- let exitCode = 0;
1419
- try {
1420
- startupCompilerLog(coreCompiler, config);
1421
- const versionChecker = startCheckVersion(config, coreCompiler.version);
1422
- const compiler = await coreCompiler.createCompiler(config);
1423
- const watcher = await compiler.createWatcher();
1424
- if (config.flags.serve) {
1425
- const devServerPath = config.sys.getDevServerExecutingPath();
1426
- const { start } = await config.sys.dynamicImport(devServerPath);
1427
- devServer = await start(config.devServer, config.logger, watcher);
1428
- }
1429
- config.sys.onProcessInterrupt(() => {
1430
- config.logger.debug(`close watch`);
1431
- compiler && compiler.destroy();
1432
- });
1433
- const rmVersionCheckerLog = watcher.on('buildFinish', async () => {
1434
- // log the version check one time
1435
- rmVersionCheckerLog();
1436
- printCheckVersionResults(versionChecker);
1437
- });
1438
- if (devServer) {
1439
- const rmDevServerLog = watcher.on('buildFinish', () => {
1440
- // log the dev server url one time
1441
- rmDevServerLog();
1442
- config.logger.info(`${config.logger.cyan(devServer.browserUrl)}\n`);
1443
- });
1444
- }
1445
- const closeResults = await watcher.start();
1446
- if (closeResults.exitCode > 0) {
1447
- exitCode = closeResults.exitCode;
1448
- }
1449
- }
1450
- catch (e) {
1451
- exitCode = 1;
1452
- config.logger.error(e);
1453
- }
1454
- if (devServer) {
1455
- await devServer.close();
1456
- }
1457
- if (exitCode > 0) {
1458
- return config.sys.exit(exitCode);
1459
- }
1322
+ const taskWatch = async (coreCompiler, config) => {
1323
+ let devServer = null;
1324
+ let exitCode = 0;
1325
+ try {
1326
+ startupCompilerLog(coreCompiler, config);
1327
+ const versionChecker = startCheckVersion(config, coreCompiler.version);
1328
+ const compiler = await coreCompiler.createCompiler(config);
1329
+ const watcher = await compiler.createWatcher();
1330
+ if (config.flags.serve) {
1331
+ const devServerPath = config.sys.getDevServerExecutingPath();
1332
+ const { start } = await config.sys.dynamicImport(devServerPath);
1333
+ devServer = await start(config.devServer, config.logger, watcher);
1334
+ }
1335
+ config.sys.onProcessInterrupt(() => {
1336
+ config.logger.debug(`close watch`);
1337
+ compiler && compiler.destroy();
1338
+ });
1339
+ const rmVersionCheckerLog = watcher.on('buildFinish', async () => {
1340
+ // log the version check one time
1341
+ rmVersionCheckerLog();
1342
+ printCheckVersionResults(versionChecker);
1343
+ });
1344
+ if (devServer) {
1345
+ const rmDevServerLog = watcher.on('buildFinish', () => {
1346
+ // log the dev server url one time
1347
+ rmDevServerLog();
1348
+ config.logger.info(`${config.logger.cyan(devServer.browserUrl)}\n`);
1349
+ });
1350
+ }
1351
+ const closeResults = await watcher.start();
1352
+ if (closeResults.exitCode > 0) {
1353
+ exitCode = closeResults.exitCode;
1354
+ }
1355
+ }
1356
+ catch (e) {
1357
+ exitCode = 1;
1358
+ config.logger.error(e);
1359
+ }
1360
+ if (devServer) {
1361
+ await devServer.close();
1362
+ }
1363
+ if (exitCode > 0) {
1364
+ return config.sys.exit(exitCode);
1365
+ }
1460
1366
  };
1461
1367
 
1462
- const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
1463
- const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1464
- const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
1465
- const DOCS_CUSTOM = 'docs-custom';
1466
- const DOCS_JSON = 'docs-json';
1467
- const DOCS_README = 'docs-readme';
1468
- const DOCS_VSCODE = 'docs-vscode';
1368
+ const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
1369
+ const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1370
+ const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
1371
+ const DOCS_CUSTOM = 'docs-custom';
1372
+ const DOCS_JSON = 'docs-json';
1373
+ const DOCS_README = 'docs-readme';
1374
+ const DOCS_VSCODE = 'docs-vscode';
1469
1375
  const WWW = 'www';
1470
1376
 
1471
- const tryFn = async (fn, ...args) => {
1472
- try {
1473
- return await fn(...args);
1474
- }
1475
- catch (_a) {
1476
- // ignore
1477
- }
1478
- return null;
1479
- };
1480
- const isInteractive = (sys, flags, object) => {
1481
- const terminalInfo = object ||
1482
- Object.freeze({
1483
- tty: sys.isTTY() ? true : false,
1484
- ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => { var _a; return !!((_a = sys.getEnvironmentVar) === null || _a === void 0 ? void 0 : _a.call(sys, v)); }).length > 0 || !!flags.ci,
1485
- });
1486
- return terminalInfo.tty && !terminalInfo.ci;
1487
- };
1488
- const UUID_REGEX = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i);
1489
- // Plucked from https://github.com/familyjs/jigra/blob/HEAD/cli/src/util/uuid.ts
1490
- function uuidv4() {
1491
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
1492
- const r = (Math.random() * 16) | 0;
1493
- const v = c == 'x' ? r : (r & 0x3) | 0x8;
1494
- return v.toString(16);
1495
- });
1496
- }
1497
- /**
1498
- * Reads and parses a JSON file from the given `path`
1499
- * @param sys The system where the command is invoked
1500
- * @param path the path on the file system to read and parse
1501
- * @returns the parsed JSON
1502
- */
1503
- async function readJson(sys, path) {
1504
- const file = await sys.readFile(path);
1505
- return !!file && JSON.parse(file);
1506
- }
1507
- /**
1508
- * Does the command have the debug flag?
1509
- * @param flags The configuration flags passed into the Rindo command
1510
- * @returns true if --debug has been passed, otherwise false
1511
- */
1512
- function hasDebug(flags) {
1513
- return !!flags.debug;
1514
- }
1515
- /**
1516
- * Does the command have the verbose and debug flags?
1517
- * @param flags The configuration flags passed into the Rindo command
1518
- * @returns true if both --debug and --verbose have been passed, otherwise false
1519
- */
1520
- function hasVerbose(flags) {
1521
- return !!flags.verbose && hasDebug(flags);
1377
+ const tryFn = async (fn, ...args) => {
1378
+ try {
1379
+ return await fn(...args);
1380
+ }
1381
+ catch (_a) {
1382
+ // ignore
1383
+ }
1384
+ return null;
1385
+ };
1386
+ const isInteractive = (sys, flags, object) => {
1387
+ const terminalInfo = object ||
1388
+ Object.freeze({
1389
+ tty: sys.isTTY() ? true : false,
1390
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => { var _a; return !!((_a = sys.getEnvironmentVar) === null || _a === void 0 ? void 0 : _a.call(sys, v)); }).length > 0 || !!flags.ci,
1391
+ });
1392
+ return terminalInfo.tty && !terminalInfo.ci;
1393
+ };
1394
+ const UUID_REGEX = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i);
1395
+ // Plucked from https://github.com/familyjs/jigra/blob/HEAD/cli/src/util/uuid.ts
1396
+ function uuidv4() {
1397
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
1398
+ const r = (Math.random() * 16) | 0;
1399
+ const v = c == 'x' ? r : (r & 0x3) | 0x8;
1400
+ return v.toString(16);
1401
+ });
1402
+ }
1403
+ /**
1404
+ * Reads and parses a JSON file from the given `path`
1405
+ * @param sys The system where the command is invoked
1406
+ * @param path the path on the file system to read and parse
1407
+ * @returns the parsed JSON
1408
+ */
1409
+ async function readJson(sys, path) {
1410
+ const file = await sys.readFile(path);
1411
+ return !!file && JSON.parse(file);
1412
+ }
1413
+ /**
1414
+ * Does the command have the debug flag?
1415
+ * @param flags The configuration flags passed into the Rindo command
1416
+ * @returns true if --debug has been passed, otherwise false
1417
+ */
1418
+ function hasDebug(flags) {
1419
+ return !!flags.debug;
1420
+ }
1421
+ /**
1422
+ * Does the command have the verbose and debug flags?
1423
+ * @param flags The configuration flags passed into the Rindo command
1424
+ * @returns true if both --debug and --verbose have been passed, otherwise false
1425
+ */
1426
+ function hasVerbose(flags) {
1427
+ return !!flags.verbose && hasDebug(flags);
1522
1428
  }
1523
1429
 
1524
- const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
1525
- const defaultConfig = (sys) => sys.resolvePath(`${sys.homeDir()}/.family/${isTest$1() ? 'tmp-config.json' : 'config.json'}`);
1526
- const defaultConfigDirectory = (sys) => sys.resolvePath(`${sys.homeDir()}/.family`);
1527
- /**
1528
- * Reads an Family configuration file from disk, parses it, and performs any necessary corrections to it if certain
1529
- * values are deemed to be malformed
1530
- * @param sys The system where the command is invoked
1531
- * @returns the config read from disk that has been potentially been updated
1532
- */
1533
- async function readConfig(sys) {
1534
- let config = await readJson(sys, defaultConfig(sys));
1535
- if (!config) {
1536
- config = {
1537
- 'tokens.telemetry': uuidv4(),
1538
- 'telemetry.rindo': true,
1539
- };
1540
- await writeConfig(sys, config);
1541
- }
1542
- else if (!config['tokens.telemetry'] || !UUID_REGEX.test(config['tokens.telemetry'])) {
1543
- const newUuid = uuidv4();
1544
- await writeConfig(sys, { ...config, 'tokens.telemetry': newUuid });
1545
- config['tokens.telemetry'] = newUuid;
1546
- }
1547
- return config;
1548
- }
1549
- /**
1550
- * Writes an Family configuration file to disk.
1551
- * @param sys The system where the command is invoked
1552
- * @param config The config passed into the Rindo command
1553
- * @returns boolean If the command was successful
1554
- */
1555
- async function writeConfig(sys, config) {
1556
- let result = false;
1557
- try {
1558
- await sys.createDir(defaultConfigDirectory(sys), { recursive: true });
1559
- await sys.writeFile(defaultConfig(sys), JSON.stringify(config, null, 2));
1560
- result = true;
1561
- }
1562
- catch (error) {
1563
- console.error(`Rindo Telemetry: couldn't write configuration file to ${defaultConfig(sys)} - ${error}.`);
1564
- }
1565
- return result;
1566
- }
1567
- /**
1568
- * Update a subset of the Family config.
1569
- * @param sys The system where the command is invoked
1570
- * @param newOptions The new options to save
1571
- * @returns boolean If the command was successful
1572
- */
1573
- async function updateConfig(sys, newOptions) {
1574
- const config = await readConfig(sys);
1575
- return await writeConfig(sys, Object.assign(config, newOptions));
1430
+ const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
1431
+ const defaultConfig = (sys) => sys.resolvePath(`${sys.homeDir()}/.family/${isTest$1() ? 'tmp-config.json' : 'config.json'}`);
1432
+ const defaultConfigDirectory = (sys) => sys.resolvePath(`${sys.homeDir()}/.family`);
1433
+ /**
1434
+ * Reads an Family configuration file from disk, parses it, and performs any necessary corrections to it if certain
1435
+ * values are deemed to be malformed
1436
+ * @param sys The system where the command is invoked
1437
+ * @returns the config read from disk that has been potentially been updated
1438
+ */
1439
+ async function readConfig(sys) {
1440
+ let config = await readJson(sys, defaultConfig(sys));
1441
+ if (!config) {
1442
+ config = {
1443
+ 'tokens.telemetry': uuidv4(),
1444
+ 'telemetry.rindo': true,
1445
+ };
1446
+ await writeConfig(sys, config);
1447
+ }
1448
+ else if (!config['tokens.telemetry'] || !UUID_REGEX.test(config['tokens.telemetry'])) {
1449
+ const newUuid = uuidv4();
1450
+ await writeConfig(sys, { ...config, 'tokens.telemetry': newUuid });
1451
+ config['tokens.telemetry'] = newUuid;
1452
+ }
1453
+ return config;
1454
+ }
1455
+ /**
1456
+ * Writes an Family configuration file to disk.
1457
+ * @param sys The system where the command is invoked
1458
+ * @param config The config passed into the Rindo command
1459
+ * @returns boolean If the command was successful
1460
+ */
1461
+ async function writeConfig(sys, config) {
1462
+ let result = false;
1463
+ try {
1464
+ await sys.createDir(defaultConfigDirectory(sys), { recursive: true });
1465
+ await sys.writeFile(defaultConfig(sys), JSON.stringify(config, null, 2));
1466
+ result = true;
1467
+ }
1468
+ catch (error) {
1469
+ console.error(`Rindo Telemetry: couldn't write configuration file to ${defaultConfig(sys)} - ${error}.`);
1470
+ }
1471
+ return result;
1472
+ }
1473
+ /**
1474
+ * Update a subset of the Family config.
1475
+ * @param sys The system where the command is invoked
1476
+ * @param newOptions The new options to save
1477
+ * @returns boolean If the command was successful
1478
+ */
1479
+ async function updateConfig(sys, newOptions) {
1480
+ const config = await readConfig(sys);
1481
+ return await writeConfig(sys, Object.assign(config, newOptions));
1576
1482
  }
1577
1483
 
1578
- /**
1579
- * Used to determine if tracking should occur.
1580
- * @param config The config passed into the Rindo command
1581
- * @param sys The system where the command is invoked
1582
- * @param ci whether or not the process is running in a Continuous Integration (CI) environment
1583
- * @returns true if telemetry should be sent, false otherwise
1584
- */
1585
- async function shouldTrack(config, sys, ci) {
1586
- return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
1484
+ /**
1485
+ * Used to determine if tracking should occur.
1486
+ * @param config The config passed into the Rindo command
1487
+ * @param sys The system where the command is invoked
1488
+ * @param ci whether or not the process is running in a Continuous Integration (CI) environment
1489
+ * @returns true if telemetry should be sent, false otherwise
1490
+ */
1491
+ async function shouldTrack(config, sys, ci) {
1492
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
1587
1493
  }
1588
1494
 
1589
- /**
1590
- * Used to within taskBuild to provide the component_count property.
1591
- *
1592
- * @param sys The system where the command is invoked
1593
- * @param config The config passed into the Rindo command
1594
- * @param coreCompiler The compiler used to do builds
1595
- * @param result The results of a compiler build.
1596
- */
1597
- async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
1598
- const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1599
- if (!tracking) {
1600
- return;
1601
- }
1602
- const component_count = result.componentGraph ? Object.keys(result.componentGraph).length : undefined;
1603
- const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
1604
- await sendMetric(sys, config, 'rindo_cli_command', data);
1605
- config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1606
- }
1607
- /**
1608
- * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
1609
- *
1610
- * @param sys The system where the command is invoked
1611
- * @param config The config passed into the Rindo command
1612
- * @param coreCompiler The compiler used to do builds
1613
- * @param action A Promise-based function to call in order to get the duration of any given command.
1614
- * @returns void
1615
- */
1616
- async function telemetryAction(sys, config, coreCompiler, action) {
1617
- const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1618
- let duration = undefined;
1619
- let error;
1620
- if (action) {
1621
- const start = new Date();
1622
- try {
1623
- await action();
1624
- }
1625
- catch (e) {
1626
- error = e;
1627
- }
1628
- const end = new Date();
1629
- duration = end.getTime() - start.getTime();
1630
- }
1631
- // We'll get componentCount details inside the taskBuild, so let's not send two messages.
1632
- if (!tracking || (config.flags.task == 'build' && !config.flags.args.includes('--watch'))) {
1633
- return;
1634
- }
1635
- const data = await prepareData(coreCompiler, config, sys, duration);
1636
- await sendMetric(sys, config, 'rindo_cli_command', data);
1637
- config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1638
- if (error) {
1639
- throw error;
1640
- }
1641
- }
1642
- /**
1643
- * Helper function to determine if a Rindo configuration builds an application.
1644
- *
1645
- * This function is a rough approximation whether an application is generated as a part of a Rindo build, based on
1646
- * contents of the project's `rindo.config.ts` file.
1647
- *
1648
- * @param config the configuration used by the Rindo project
1649
- * @returns true if we believe the project generates an application, false otherwise
1650
- */
1651
- function hasAppTarget(config) {
1652
- return config.outputTargets.some((target) => target.type === WWW && (!!target.serviceWorker || (!!target.baseUrl && target.baseUrl !== '/')));
1653
- }
1654
- function isUsingYarn(sys) {
1655
- var _a;
1656
- return ((_a = sys.getEnvironmentVar('npm_execpath')) === null || _a === void 0 ? void 0 : _a.includes('yarn')) || false;
1657
- }
1658
- /**
1659
- * Build a list of the different types of output targets used in a Rindo configuration.
1660
- *
1661
- * Duplicate entries will not be returned from the list
1662
- *
1663
- * @param config the configuration used by the Rindo project
1664
- * @returns a unique list of output target types found in the Rindo configuration
1665
- */
1666
- function getActiveTargets(config) {
1667
- const result = config.outputTargets.map((t) => t.type);
1668
- return Array.from(new Set(result));
1669
- }
1670
- /**
1671
- * Prepare data for telemetry
1672
- *
1673
- * @param coreCompiler the core compiler
1674
- * @param config the current Rindo config
1675
- * @param sys the compiler system instance in use
1676
- * @param duration_ms the duration of the action being tracked
1677
- * @param component_count the number of components being built (optional)
1678
- * @returns a Promise wrapping data for the telemetry endpoint
1679
- */
1680
- const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1681
- var _a, _b, _c;
1682
- const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1683
- const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
1684
- const targets = getActiveTargets(config);
1685
- const yarn = isUsingYarn(sys);
1686
- const rindo = coreCompiler.version || 'unknown';
1687
- const system = `${sys.name} ${sys.version}`;
1688
- const os_name = (_a = sys.details) === null || _a === void 0 ? void 0 : _a.platform;
1689
- const os_version = (_b = sys.details) === null || _b === void 0 ? void 0 : _b.release;
1690
- const cpu_model = (_c = sys.details) === null || _c === void 0 ? void 0 : _c.cpuModel;
1691
- const build = coreCompiler.buildId || 'unknown';
1692
- const has_app_pwa_config = hasAppTarget(config);
1693
- const anonymizedConfig = anonymizeConfigForTelemetry(config);
1694
- const is_browser_env = IS_BROWSER_ENV;
1695
- return {
1696
- arguments: config.flags.args,
1697
- build,
1698
- component_count,
1699
- config: anonymizedConfig,
1700
- cpu_model,
1701
- duration_ms,
1702
- has_app_pwa_config,
1703
- is_browser_env,
1704
- os_name,
1705
- os_version,
1706
- packages,
1707
- packages_no_versions: packagesNoVersions,
1708
- rollup,
1709
- rindo,
1710
- system,
1711
- system_major: getMajorVersion(system),
1712
- targets,
1713
- task: config.flags.task,
1714
- typescript,
1715
- yarn,
1716
- };
1717
- };
1718
- // props in output targets for which we retain their original values when
1719
- // preparing a config for telemetry
1720
- //
1721
- // we omit the values of all other fields on output targets.
1722
- const OUTPUT_TARGET_KEYS_TO_KEEP = ['type'];
1723
- // top-level config props that we anonymize for telemetry
1724
- const CONFIG_PROPS_TO_ANONYMIZE = [
1725
- 'rootDir',
1726
- 'fsNamespace',
1727
- 'packageJsonFilePath',
1728
- 'namespace',
1729
- 'srcDir',
1730
- 'srcIndexHtml',
1731
- 'buildLogFilePath',
1732
- 'cacheDir',
1733
- 'configPath',
1734
- 'tsconfig',
1735
- ];
1736
- // Props we delete entirely from the config for telemetry
1737
- //
1738
- // TODO: Investigate improving anonymization for tsCompilerOptions and devServer
1739
- const CONFIG_PROPS_TO_DELETE = [
1740
- 'commonjs',
1741
- 'devServer',
1742
- 'env',
1743
- 'logger',
1744
- 'rollupConfig',
1745
- 'sys',
1746
- 'testing',
1747
- 'tsCompilerOptions',
1748
- ];
1749
- /**
1750
- * Anonymize the config for telemetry, replacing potentially revealing config props
1751
- * with a placeholder string if they are present (this lets us still track how frequently
1752
- * these config options are being used)
1753
- *
1754
- * @param config the config to anonymize
1755
- * @returns an anonymized copy of the same config
1756
- */
1757
- const anonymizeConfigForTelemetry = (config) => {
1758
- const anonymizedConfig = { ...config };
1759
- for (const prop of CONFIG_PROPS_TO_ANONYMIZE) {
1760
- if (anonymizedConfig[prop] !== undefined) {
1761
- anonymizedConfig[prop] = 'omitted';
1762
- }
1763
- }
1764
- anonymizedConfig.outputTargets = config.outputTargets.map((target) => {
1765
- // Anonymize the outputTargets on our configuration, taking advantage of the
1766
- // optional 2nd argument to `JSON.stringify`. If anything is not a string
1767
- // we retain it so that any nested properties are handled, else we check
1768
- // whether it's in our 'keep' list to decide whether to keep it or replace it
1769
- // with `"omitted"`.
1770
- const anonymizedOT = JSON.parse(JSON.stringify(target, (key, value) => {
1771
- if (!(typeof value === 'string')) {
1772
- return value;
1773
- }
1774
- if (OUTPUT_TARGET_KEYS_TO_KEEP.includes(key)) {
1775
- return value;
1776
- }
1777
- return 'omitted';
1778
- }));
1779
- // this prop has to be handled separately because it is an array
1780
- // so the replace function above will be called with all of its
1781
- // members, giving us `["omitted", "omitted", ...]`.
1782
- //
1783
- // Instead, we check for its presence and manually copy over.
1784
- if (isOutputTargetHydrate(target) && target.external) {
1785
- anonymizedOT['external'] = target.external.concat();
1786
- }
1787
- return anonymizedOT;
1788
- });
1789
- // TODO: Investigate improving anonymization for tsCompilerOptions and devServer
1790
- for (const prop of CONFIG_PROPS_TO_DELETE) {
1791
- delete anonymizedConfig[prop];
1792
- }
1793
- return anonymizedConfig;
1794
- };
1795
- /**
1796
- * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1797
- * the dependencies and devDependencies properties. Pulls up the current installed version
1798
- * of each package under the @rindo, @familyjs, and @jigra scopes.
1799
- *
1800
- * @param sys the system instance where telemetry is invoked
1801
- * @param config the Rindo configuration associated with the current task that triggered telemetry
1802
- * @returns an object listing all dev and production dependencies under the aforementioned scopes
1803
- */
1804
- async function getInstalledPackages(sys, config) {
1805
- let packages = [];
1806
- let packagesNoVersions = [];
1807
- const yarn = isUsingYarn(sys);
1808
- try {
1809
- // Read package.json and package-lock.json
1810
- const appRootDir = sys.getCurrentDirectory();
1811
- const packageJson = await tryFn(readJson, sys, sys.resolvePath(appRootDir + '/package.json'));
1812
- // They don't have a package.json for some reason? Eject button.
1813
- if (!packageJson) {
1814
- return { packages, packagesNoVersions };
1815
- }
1816
- const rawPackages = Object.entries({
1817
- ...packageJson.devDependencies,
1818
- ...packageJson.dependencies,
1819
- });
1820
- // Collect packages only in the rindo, familyjs, or jigra org's:
1821
- // https://www.npmjs.com/org/rindo
1822
- const familyPackages = rawPackages.filter(([k]) => k.startsWith('@rindo/') || k.startsWith('@familyjs/') || k.startsWith('@jigra/'));
1823
- try {
1824
- packages = yarn ? await yarnPackages(sys, familyPackages) : await npmPackages(sys, familyPackages);
1825
- }
1826
- catch (e) {
1827
- packages = familyPackages.map(([k, v]) => `${k}@${v.replace('^', '')}`);
1828
- }
1829
- packagesNoVersions = familyPackages.map(([k]) => `${k}`);
1830
- return { packages, packagesNoVersions };
1831
- }
1832
- catch (err) {
1833
- hasDebug(config.flags) && console.error(err);
1834
- return { packages, packagesNoVersions };
1835
- }
1836
- }
1837
- /**
1838
- * Visits the npm lock file to find the exact versions that are installed
1839
- * @param sys The system where the command is invoked
1840
- * @param familyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@familyjs` from the package.json file.
1841
- * @returns an array of strings of all the packages and their versions.
1842
- */
1843
- async function npmPackages(sys, familyPackages) {
1844
- const appRootDir = sys.getCurrentDirectory();
1845
- const packageLockJson = await tryFn(readJson, sys, sys.resolvePath(appRootDir + '/package-lock.json'));
1846
- return familyPackages.map(([k, v]) => {
1847
- var _a, _b, _c, _d;
1848
- let version = (_d = (_b = (_a = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.dependencies[k]) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : (_c = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.devDependencies[k]) === null || _c === void 0 ? void 0 : _c.version) !== null && _d !== void 0 ? _d : v;
1849
- version = version.includes('file:') ? sanitizeDeclaredVersion(v) : version;
1850
- return `${k}@${version}`;
1851
- });
1852
- }
1853
- /**
1854
- * Visits the yarn lock file to find the exact versions that are installed
1855
- * @param sys The system where the command is invoked
1856
- * @param familyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@familyjs` from the package.json file.
1857
- * @returns an array of strings of all the packages and their versions.
1858
- */
1859
- async function yarnPackages(sys, familyPackages) {
1860
- const appRootDir = sys.getCurrentDirectory();
1861
- const yarnLock = sys.readFileSync(sys.resolvePath(appRootDir + '/yarn.lock'));
1862
- const yarnLockYml = sys.parseYarnLockFile(yarnLock);
1863
- return familyPackages.map(([k, v]) => {
1864
- var _a;
1865
- const identifiedVersion = `${k}@${v}`;
1866
- let version = (_a = yarnLockYml.object[identifiedVersion]) === null || _a === void 0 ? void 0 : _a.version;
1867
- version = version.includes('undefined') ? sanitizeDeclaredVersion(identifiedVersion) : version;
1868
- return `${k}@${version}`;
1869
- });
1870
- }
1871
- /**
1872
- * This function is used for fallback purposes, where an npm or yarn lock file doesn't exist in the consumers directory.
1873
- * This will strip away '*', '^' and '~' from the declared package versions in a package.json.
1874
- * @param version the raw semver pattern identifier version string
1875
- * @returns a cleaned up representation without any qualifiers
1876
- */
1877
- function sanitizeDeclaredVersion(version) {
1878
- return version.replace(/[*^~]/g, '');
1879
- }
1880
- /**
1881
- * If telemetry is enabled, send a metric to an external data store
1882
- *
1883
- * @param sys the system instance where telemetry is invoked
1884
- * @param config the Rindo configuration associated with the current task that triggered telemetry
1885
- * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
1886
- * "Rindo Version". For example, "rindo_cli_command" is a name that is used to track all CLI command information.
1887
- * @param value the data to send to the external data store under the provided name argument
1888
- */
1889
- async function sendMetric(sys, config, name, value) {
1890
- const session_id = await getTelemetryToken(sys);
1891
- const message = {
1892
- name,
1893
- timestamp: new Date().toISOString(),
1894
- source: 'rindo_cli',
1895
- value,
1896
- session_id,
1897
- };
1898
- await sendTelemetry(sys, config, message);
1899
- }
1900
- /**
1901
- * Used to read the config file's tokens.telemetry property.
1902
- *
1903
- * @param sys The system where the command is invoked
1904
- * @returns string
1905
- */
1906
- async function getTelemetryToken(sys) {
1907
- const config = await readConfig(sys);
1908
- if (config['tokens.telemetry'] === undefined) {
1909
- config['tokens.telemetry'] = uuidv4();
1910
- await writeConfig(sys, config);
1911
- }
1912
- return config['tokens.telemetry'];
1913
- }
1914
- /**
1915
- * Issues a request to the telemetry server.
1916
- * @param sys The system where the command is invoked
1917
- * @param config The config passed into the Rindo command
1918
- * @param data Data to be tracked
1919
- */
1920
- async function sendTelemetry(sys, config, data) {
1921
- try {
1922
- const now = new Date().toISOString();
1923
- const body = {
1924
- metrics: [data],
1925
- sent_at: now,
1926
- };
1927
- // This request is only made if telemetry is on.
1928
- const response = await sys.fetch('https://familyjs-api.web.app/events/metrics', {
1929
- method: 'POST',
1930
- headers: {
1931
- 'Content-Type': 'application/json',
1932
- },
1933
- body: JSON.stringify(body),
1934
- });
1935
- hasVerbose(config.flags) &&
1936
- console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1937
- if (response.status !== 204) {
1938
- hasVerbose(config.flags) &&
1939
- console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1940
- }
1941
- }
1942
- catch (e) {
1943
- hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1944
- }
1945
- }
1946
- /**
1947
- * Checks if telemetry is enabled on this machine
1948
- * @param sys The system where the command is invoked
1949
- * @returns true if telemetry is enabled, false otherwise
1950
- */
1951
- async function checkTelemetry(sys) {
1952
- const config = await readConfig(sys);
1953
- if (config['telemetry.rindo'] === undefined) {
1954
- config['telemetry.rindo'] = true;
1955
- await writeConfig(sys, config);
1956
- }
1957
- return config['telemetry.rindo'];
1958
- }
1959
- /**
1960
- * Writes to the config file, enabling telemetry for this machine.
1961
- * @param sys The system where the command is invoked
1962
- * @returns true if writing the file was successful, false otherwise
1963
- */
1964
- async function enableTelemetry(sys) {
1965
- return await updateConfig(sys, { 'telemetry.rindo': true });
1966
- }
1967
- /**
1968
- * Writes to the config file, disabling telemetry for this machine.
1969
- * @param sys The system where the command is invoked
1970
- * @returns true if writing the file was successful, false otherwise
1971
- */
1972
- async function disableTelemetry(sys) {
1973
- return await updateConfig(sys, { 'telemetry.rindo': false });
1974
- }
1975
- /**
1976
- * Takes in a semver string in order to return the major version.
1977
- * @param version The fully qualified semver version
1978
- * @returns a string of the major version
1979
- */
1980
- function getMajorVersion(version) {
1981
- const parts = version.split('.');
1982
- return parts[0];
1495
+ /**
1496
+ * Used to within taskBuild to provide the component_count property.
1497
+ *
1498
+ * @param sys The system where the command is invoked
1499
+ * @param config The config passed into the Rindo command
1500
+ * @param coreCompiler The compiler used to do builds
1501
+ * @param result The results of a compiler build.
1502
+ */
1503
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
1504
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1505
+ if (!tracking) {
1506
+ return;
1507
+ }
1508
+ const component_count = result.componentGraph ? Object.keys(result.componentGraph).length : undefined;
1509
+ const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
1510
+ await sendMetric(sys, config, 'rindo_cli_command', data);
1511
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1512
+ }
1513
+ /**
1514
+ * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
1515
+ *
1516
+ * @param sys The system where the command is invoked
1517
+ * @param config The config passed into the Rindo command
1518
+ * @param coreCompiler The compiler used to do builds
1519
+ * @param action A Promise-based function to call in order to get the duration of any given command.
1520
+ * @returns void
1521
+ */
1522
+ async function telemetryAction(sys, config, coreCompiler, action) {
1523
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1524
+ let duration = undefined;
1525
+ let error;
1526
+ if (action) {
1527
+ const start = new Date();
1528
+ try {
1529
+ await action();
1530
+ }
1531
+ catch (e) {
1532
+ error = e;
1533
+ }
1534
+ const end = new Date();
1535
+ duration = end.getTime() - start.getTime();
1536
+ }
1537
+ // We'll get componentCount details inside the taskBuild, so let's not send two messages.
1538
+ if (!tracking || (config.flags.task == 'build' && !config.flags.args.includes('--watch'))) {
1539
+ return;
1540
+ }
1541
+ const data = await prepareData(coreCompiler, config, sys, duration);
1542
+ await sendMetric(sys, config, 'rindo_cli_command', data);
1543
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1544
+ if (error) {
1545
+ throw error;
1546
+ }
1547
+ }
1548
+ /**
1549
+ * Helper function to determine if a Rindo configuration builds an application.
1550
+ *
1551
+ * This function is a rough approximation whether an application is generated as a part of a Rindo build, based on
1552
+ * contents of the project's `rindo.config.ts` file.
1553
+ *
1554
+ * @param config the configuration used by the Rindo project
1555
+ * @returns true if we believe the project generates an application, false otherwise
1556
+ */
1557
+ function hasAppTarget(config) {
1558
+ return config.outputTargets.some((target) => target.type === WWW && (!!target.serviceWorker || (!!target.baseUrl && target.baseUrl !== '/')));
1559
+ }
1560
+ function isUsingYarn(sys) {
1561
+ var _a;
1562
+ return ((_a = sys.getEnvironmentVar('npm_execpath')) === null || _a === void 0 ? void 0 : _a.includes('yarn')) || false;
1563
+ }
1564
+ /**
1565
+ * Build a list of the different types of output targets used in a Rindo configuration.
1566
+ *
1567
+ * Duplicate entries will not be returned from the list
1568
+ *
1569
+ * @param config the configuration used by the Rindo project
1570
+ * @returns a unique list of output target types found in the Rindo configuration
1571
+ */
1572
+ function getActiveTargets(config) {
1573
+ const result = config.outputTargets.map((t) => t.type);
1574
+ return Array.from(new Set(result));
1575
+ }
1576
+ /**
1577
+ * Prepare data for telemetry
1578
+ *
1579
+ * @param coreCompiler the core compiler
1580
+ * @param config the current Rindo config
1581
+ * @param sys the compiler system instance in use
1582
+ * @param duration_ms the duration of the action being tracked
1583
+ * @param component_count the number of components being built (optional)
1584
+ * @returns a Promise wrapping data for the telemetry endpoint
1585
+ */
1586
+ const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1587
+ var _a, _b, _c;
1588
+ const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1589
+ const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
1590
+ const targets = getActiveTargets(config);
1591
+ const yarn = isUsingYarn(sys);
1592
+ const rindo = coreCompiler.version || 'unknown';
1593
+ const system = `${sys.name} ${sys.version}`;
1594
+ const os_name = (_a = sys.details) === null || _a === void 0 ? void 0 : _a.platform;
1595
+ const os_version = (_b = sys.details) === null || _b === void 0 ? void 0 : _b.release;
1596
+ const cpu_model = (_c = sys.details) === null || _c === void 0 ? void 0 : _c.cpuModel;
1597
+ const build = coreCompiler.buildId || 'unknown';
1598
+ const has_app_pwa_config = hasAppTarget(config);
1599
+ const anonymizedConfig = anonymizeConfigForTelemetry(config);
1600
+ const is_browser_env = IS_BROWSER_ENV;
1601
+ return {
1602
+ arguments: config.flags.args,
1603
+ build,
1604
+ component_count,
1605
+ config: anonymizedConfig,
1606
+ cpu_model,
1607
+ duration_ms,
1608
+ has_app_pwa_config,
1609
+ is_browser_env,
1610
+ os_name,
1611
+ os_version,
1612
+ packages,
1613
+ packages_no_versions: packagesNoVersions,
1614
+ rollup,
1615
+ rindo,
1616
+ system,
1617
+ system_major: getMajorVersion(system),
1618
+ targets,
1619
+ task: config.flags.task,
1620
+ typescript,
1621
+ yarn,
1622
+ };
1623
+ };
1624
+ // props in output targets for which we retain their original values when
1625
+ // preparing a config for telemetry
1626
+ //
1627
+ // we omit the values of all other fields on output targets.
1628
+ const OUTPUT_TARGET_KEYS_TO_KEEP = ['type'];
1629
+ // top-level config props that we anonymize for telemetry
1630
+ const CONFIG_PROPS_TO_ANONYMIZE = [
1631
+ 'rootDir',
1632
+ 'fsNamespace',
1633
+ 'packageJsonFilePath',
1634
+ 'namespace',
1635
+ 'srcDir',
1636
+ 'srcIndexHtml',
1637
+ 'buildLogFilePath',
1638
+ 'cacheDir',
1639
+ 'configPath',
1640
+ 'tsconfig',
1641
+ ];
1642
+ // Props we delete entirely from the config for telemetry
1643
+ //
1644
+ // TODO: Investigate improving anonymization for tsCompilerOptions and devServer
1645
+ const CONFIG_PROPS_TO_DELETE = [
1646
+ 'commonjs',
1647
+ 'devServer',
1648
+ 'env',
1649
+ 'logger',
1650
+ 'rollupConfig',
1651
+ 'sys',
1652
+ 'testing',
1653
+ 'tsCompilerOptions',
1654
+ ];
1655
+ /**
1656
+ * Anonymize the config for telemetry, replacing potentially revealing config props
1657
+ * with a placeholder string if they are present (this lets us still track how frequently
1658
+ * these config options are being used)
1659
+ *
1660
+ * @param config the config to anonymize
1661
+ * @returns an anonymized copy of the same config
1662
+ */
1663
+ const anonymizeConfigForTelemetry = (config) => {
1664
+ const anonymizedConfig = { ...config };
1665
+ for (const prop of CONFIG_PROPS_TO_ANONYMIZE) {
1666
+ if (anonymizedConfig[prop] !== undefined) {
1667
+ anonymizedConfig[prop] = 'omitted';
1668
+ }
1669
+ }
1670
+ anonymizedConfig.outputTargets = config.outputTargets.map((target) => {
1671
+ // Anonymize the outputTargets on our configuration, taking advantage of the
1672
+ // optional 2nd argument to `JSON.stringify`. If anything is not a string
1673
+ // we retain it so that any nested properties are handled, else we check
1674
+ // whether it's in our 'keep' list to decide whether to keep it or replace it
1675
+ // with `"omitted"`.
1676
+ const anonymizedOT = JSON.parse(JSON.stringify(target, (key, value) => {
1677
+ if (!(typeof value === 'string')) {
1678
+ return value;
1679
+ }
1680
+ if (OUTPUT_TARGET_KEYS_TO_KEEP.includes(key)) {
1681
+ return value;
1682
+ }
1683
+ return 'omitted';
1684
+ }));
1685
+ // this prop has to be handled separately because it is an array
1686
+ // so the replace function above will be called with all of its
1687
+ // members, giving us `["omitted", "omitted", ...]`.
1688
+ //
1689
+ // Instead, we check for its presence and manually copy over.
1690
+ if (isOutputTargetHydrate(target) && target.external) {
1691
+ anonymizedOT['external'] = target.external.concat();
1692
+ }
1693
+ return anonymizedOT;
1694
+ });
1695
+ // TODO: Investigate improving anonymization for tsCompilerOptions and devServer
1696
+ for (const prop of CONFIG_PROPS_TO_DELETE) {
1697
+ delete anonymizedConfig[prop];
1698
+ }
1699
+ return anonymizedConfig;
1700
+ };
1701
+ /**
1702
+ * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1703
+ * the dependencies and devDependencies properties. Pulls up the current installed version
1704
+ * of each package under the @rindo, @familyjs, and @jigra scopes.
1705
+ *
1706
+ * @param sys the system instance where telemetry is invoked
1707
+ * @param config the Rindo configuration associated with the current task that triggered telemetry
1708
+ * @returns an object listing all dev and production dependencies under the aforementioned scopes
1709
+ */
1710
+ async function getInstalledPackages(sys, config) {
1711
+ let packages = [];
1712
+ let packagesNoVersions = [];
1713
+ const yarn = isUsingYarn(sys);
1714
+ try {
1715
+ // Read package.json and package-lock.json
1716
+ const appRootDir = sys.getCurrentDirectory();
1717
+ const packageJson = await tryFn(readJson, sys, sys.resolvePath(appRootDir + '/package.json'));
1718
+ // They don't have a package.json for some reason? Eject button.
1719
+ if (!packageJson) {
1720
+ return { packages, packagesNoVersions };
1721
+ }
1722
+ const rawPackages = Object.entries({
1723
+ ...packageJson.devDependencies,
1724
+ ...packageJson.dependencies,
1725
+ });
1726
+ // Collect packages only in the rindo, familyjs, or jigra org's:
1727
+ // https://www.npmjs.com/org/rindo
1728
+ const familyPackages = rawPackages.filter(([k]) => k.startsWith('@rindo/') || k.startsWith('@familyjs/') || k.startsWith('@jigra/'));
1729
+ try {
1730
+ packages = yarn ? await yarnPackages(sys, familyPackages) : await npmPackages(sys, familyPackages);
1731
+ }
1732
+ catch (e) {
1733
+ packages = familyPackages.map(([k, v]) => `${k}@${v.replace('^', '')}`);
1734
+ }
1735
+ packagesNoVersions = familyPackages.map(([k]) => `${k}`);
1736
+ return { packages, packagesNoVersions };
1737
+ }
1738
+ catch (err) {
1739
+ hasDebug(config.flags) && console.error(err);
1740
+ return { packages, packagesNoVersions };
1741
+ }
1742
+ }
1743
+ /**
1744
+ * Visits the npm lock file to find the exact versions that are installed
1745
+ * @param sys The system where the command is invoked
1746
+ * @param familyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@familyjs` from the package.json file.
1747
+ * @returns an array of strings of all the packages and their versions.
1748
+ */
1749
+ async function npmPackages(sys, familyPackages) {
1750
+ const appRootDir = sys.getCurrentDirectory();
1751
+ const packageLockJson = await tryFn(readJson, sys, sys.resolvePath(appRootDir + '/package-lock.json'));
1752
+ return familyPackages.map(([k, v]) => {
1753
+ var _a, _b, _c, _d;
1754
+ let version = (_d = (_b = (_a = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.dependencies[k]) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : (_c = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.devDependencies[k]) === null || _c === void 0 ? void 0 : _c.version) !== null && _d !== void 0 ? _d : v;
1755
+ version = version.includes('file:') ? sanitizeDeclaredVersion(v) : version;
1756
+ return `${k}@${version}`;
1757
+ });
1758
+ }
1759
+ /**
1760
+ * Visits the yarn lock file to find the exact versions that are installed
1761
+ * @param sys The system where the command is invoked
1762
+ * @param familyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@familyjs` from the package.json file.
1763
+ * @returns an array of strings of all the packages and their versions.
1764
+ */
1765
+ async function yarnPackages(sys, familyPackages) {
1766
+ const appRootDir = sys.getCurrentDirectory();
1767
+ const yarnLock = sys.readFileSync(sys.resolvePath(appRootDir + '/yarn.lock'));
1768
+ const yarnLockYml = sys.parseYarnLockFile(yarnLock);
1769
+ return familyPackages.map(([k, v]) => {
1770
+ var _a;
1771
+ const identifiedVersion = `${k}@${v}`;
1772
+ let version = (_a = yarnLockYml.object[identifiedVersion]) === null || _a === void 0 ? void 0 : _a.version;
1773
+ version = version.includes('undefined') ? sanitizeDeclaredVersion(identifiedVersion) : version;
1774
+ return `${k}@${version}`;
1775
+ });
1776
+ }
1777
+ /**
1778
+ * This function is used for fallback purposes, where an npm or yarn lock file doesn't exist in the consumers directory.
1779
+ * This will strip away '*', '^' and '~' from the declared package versions in a package.json.
1780
+ * @param version the raw semver pattern identifier version string
1781
+ * @returns a cleaned up representation without any qualifiers
1782
+ */
1783
+ function sanitizeDeclaredVersion(version) {
1784
+ return version.replace(/[*^~]/g, '');
1785
+ }
1786
+ /**
1787
+ * If telemetry is enabled, send a metric to an external data store
1788
+ *
1789
+ * @param sys the system instance where telemetry is invoked
1790
+ * @param config the Rindo configuration associated with the current task that triggered telemetry
1791
+ * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
1792
+ * "Rindo Version". For example, "rindo_cli_command" is a name that is used to track all CLI command information.
1793
+ * @param value the data to send to the external data store under the provided name argument
1794
+ */
1795
+ async function sendMetric(sys, config, name, value) {
1796
+ const session_id = await getTelemetryToken(sys);
1797
+ const message = {
1798
+ name,
1799
+ timestamp: new Date().toISOString(),
1800
+ source: 'rindo_cli',
1801
+ value,
1802
+ session_id,
1803
+ };
1804
+ await sendTelemetry(sys, config, message);
1805
+ }
1806
+ /**
1807
+ * Used to read the config file's tokens.telemetry property.
1808
+ *
1809
+ * @param sys The system where the command is invoked
1810
+ * @returns string
1811
+ */
1812
+ async function getTelemetryToken(sys) {
1813
+ const config = await readConfig(sys);
1814
+ if (config['tokens.telemetry'] === undefined) {
1815
+ config['tokens.telemetry'] = uuidv4();
1816
+ await writeConfig(sys, config);
1817
+ }
1818
+ return config['tokens.telemetry'];
1819
+ }
1820
+ /**
1821
+ * Issues a request to the telemetry server.
1822
+ * @param sys The system where the command is invoked
1823
+ * @param config The config passed into the Rindo command
1824
+ * @param data Data to be tracked
1825
+ */
1826
+ async function sendTelemetry(sys, config, data) {
1827
+ try {
1828
+ const now = new Date().toISOString();
1829
+ const body = {
1830
+ metrics: [data],
1831
+ sent_at: now,
1832
+ };
1833
+ // This request is only made if telemetry is on.
1834
+ const response = await sys.fetch('https://familyjs-api.web.app/events/metrics', {
1835
+ method: 'POST',
1836
+ headers: {
1837
+ 'Content-Type': 'application/json',
1838
+ },
1839
+ body: JSON.stringify(body),
1840
+ });
1841
+ hasVerbose(config.flags) &&
1842
+ console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1843
+ if (response.status !== 204) {
1844
+ hasVerbose(config.flags) &&
1845
+ console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1846
+ }
1847
+ }
1848
+ catch (e) {
1849
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1850
+ }
1851
+ }
1852
+ /**
1853
+ * Checks if telemetry is enabled on this machine
1854
+ * @param sys The system where the command is invoked
1855
+ * @returns true if telemetry is enabled, false otherwise
1856
+ */
1857
+ async function checkTelemetry(sys) {
1858
+ const config = await readConfig(sys);
1859
+ if (config['telemetry.rindo'] === undefined) {
1860
+ config['telemetry.rindo'] = true;
1861
+ await writeConfig(sys, config);
1862
+ }
1863
+ return config['telemetry.rindo'];
1864
+ }
1865
+ /**
1866
+ * Writes to the config file, enabling telemetry for this machine.
1867
+ * @param sys The system where the command is invoked
1868
+ * @returns true if writing the file was successful, false otherwise
1869
+ */
1870
+ async function enableTelemetry(sys) {
1871
+ return await updateConfig(sys, { 'telemetry.rindo': true });
1872
+ }
1873
+ /**
1874
+ * Writes to the config file, disabling telemetry for this machine.
1875
+ * @param sys The system where the command is invoked
1876
+ * @returns true if writing the file was successful, false otherwise
1877
+ */
1878
+ async function disableTelemetry(sys) {
1879
+ return await updateConfig(sys, { 'telemetry.rindo': false });
1880
+ }
1881
+ /**
1882
+ * Takes in a semver string in order to return the major version.
1883
+ * @param version The fully qualified semver version
1884
+ * @returns a string of the major version
1885
+ */
1886
+ function getMajorVersion(version) {
1887
+ const parts = version.split('.');
1888
+ return parts[0];
1983
1889
  }
1984
1890
 
1985
- const taskBuild = async (coreCompiler, config) => {
1986
- if (config.flags.watch) {
1987
- // watch build
1988
- await taskWatch(coreCompiler, config);
1989
- return;
1990
- }
1991
- // one-time build
1992
- let exitCode = 0;
1993
- try {
1994
- startupCompilerLog(coreCompiler, config);
1995
- const versionChecker = startCheckVersion(config, coreCompiler.version);
1996
- const compiler = await coreCompiler.createCompiler(config);
1997
- const results = await compiler.build();
1998
- await telemetryBuildFinishedAction(config.sys, config, coreCompiler, results);
1999
- await compiler.destroy();
2000
- if (results.hasError) {
2001
- exitCode = 1;
2002
- }
2003
- else if (config.flags.prerender) {
2004
- const prerenderDiagnostics = await runPrerenderTask(coreCompiler, config, results.hydrateAppFilePath, results.componentGraph, null);
2005
- config.logger.printDiagnostics(prerenderDiagnostics);
2006
- if (prerenderDiagnostics.some((d) => d.level === 'error')) {
2007
- exitCode = 1;
2008
- }
2009
- }
2010
- await printCheckVersionResults(versionChecker);
2011
- }
2012
- catch (e) {
2013
- exitCode = 1;
2014
- config.logger.error(e);
2015
- }
2016
- if (exitCode > 0) {
2017
- return config.sys.exit(exitCode);
2018
- }
1891
+ const taskBuild = async (coreCompiler, config) => {
1892
+ if (config.flags.watch) {
1893
+ // watch build
1894
+ await taskWatch(coreCompiler, config);
1895
+ return;
1896
+ }
1897
+ // one-time build
1898
+ let exitCode = 0;
1899
+ try {
1900
+ startupCompilerLog(coreCompiler, config);
1901
+ const versionChecker = startCheckVersion(config, coreCompiler.version);
1902
+ const compiler = await coreCompiler.createCompiler(config);
1903
+ const results = await compiler.build();
1904
+ await telemetryBuildFinishedAction(config.sys, config, coreCompiler, results);
1905
+ await compiler.destroy();
1906
+ if (results.hasError) {
1907
+ exitCode = 1;
1908
+ }
1909
+ else if (config.flags.prerender) {
1910
+ const prerenderDiagnostics = await runPrerenderTask(coreCompiler, config, results.hydrateAppFilePath, results.componentGraph, null);
1911
+ config.logger.printDiagnostics(prerenderDiagnostics);
1912
+ if (prerenderDiagnostics.some((d) => d.level === 'error')) {
1913
+ exitCode = 1;
1914
+ }
1915
+ }
1916
+ await printCheckVersionResults(versionChecker);
1917
+ }
1918
+ catch (e) {
1919
+ exitCode = 1;
1920
+ config.logger.error(e);
1921
+ }
1922
+ if (exitCode > 0) {
1923
+ return config.sys.exit(exitCode);
1924
+ }
2019
1925
  };
2020
1926
 
2021
- const taskDocs = async (coreCompiler, config) => {
2022
- config.devServer = null;
2023
- config.outputTargets = config.outputTargets.filter(isOutputTargetDocs);
2024
- config.devMode = true;
2025
- startupCompilerLog(coreCompiler, config);
2026
- const compiler = await coreCompiler.createCompiler(config);
2027
- await compiler.build();
2028
- await compiler.destroy();
1927
+ const taskDocs = async (coreCompiler, config) => {
1928
+ config.devServer = null;
1929
+ config.outputTargets = config.outputTargets.filter(isOutputTargetDocs);
1930
+ config.devMode = true;
1931
+ startupCompilerLog(coreCompiler, config);
1932
+ const compiler = await coreCompiler.createCompiler(config);
1933
+ await compiler.build();
1934
+ await compiler.destroy();
2029
1935
  };
2030
1936
 
2031
- /**
2032
- * Task to generate component boilerplate and write it to disk. This task can
2033
- * cause the program to exit with an error under various circumstances, such as
2034
- * being called in an inappropriate place, being asked to overwrite files that
2035
- * already exist, etc.
2036
- *
2037
- * @param coreCompiler the CoreCompiler we're using currently, here we're
2038
- * mainly accessing the `path` module
2039
- * @param config the user-supplied config, which we need here to access `.sys`.
2040
- */
2041
- const taskGenerate = async (coreCompiler, config) => {
2042
- if (!IS_NODE_ENV) {
2043
- config.logger.error(`"generate" command is currently only implemented for a NodeJS environment`);
2044
- return config.sys.exit(1);
2045
- }
2046
- const path = coreCompiler.path;
2047
- if (!config.configPath) {
2048
- config.logger.error('Please run this command in your root directory (i. e. the one containing rindo.config.ts).');
2049
- return config.sys.exit(1);
2050
- }
2051
- const absoluteSrcDir = config.srcDir;
2052
- if (!absoluteSrcDir) {
2053
- config.logger.error(`Rindo's srcDir was not specified.`);
2054
- return config.sys.exit(1);
2055
- }
2056
- const { prompt } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../sys/node/prompts.js')); });
2057
- const input = config.flags.unknownArgs.find((arg) => !arg.startsWith('-')) ||
2058
- (await prompt({ name: 'tagName', type: 'text', message: 'Component tag name (dash-case):' })).tagName;
2059
- if (undefined === input) {
2060
- // in some shells (e.g. Windows PowerShell), hitting Ctrl+C results in a TypeError printed to the console.
2061
- // explicitly return here to avoid printing the error message.
2062
- return;
2063
- }
2064
- const { dir, base: componentName } = path.parse(input);
2065
- const tagError = validateComponentTag(componentName);
2066
- if (tagError) {
2067
- config.logger.error(tagError);
2068
- return config.sys.exit(1);
2069
- }
2070
- const filesToGenerateExt = await chooseFilesToGenerate();
2071
- if (undefined === filesToGenerateExt) {
2072
- // in some shells (e.g. Windows PowerShell), hitting Ctrl+C results in a TypeError printed to the console.
2073
- // explicitly return here to avoid printing the error message.
2074
- return;
2075
- }
2076
- const extensionsToGenerate = ['tsx', ...filesToGenerateExt];
2077
- const testFolder = extensionsToGenerate.some(isTest) ? 'test' : '';
2078
- const outDir = path.join(absoluteSrcDir, 'components', dir, componentName);
2079
- await config.sys.createDir(path.join(outDir, testFolder), { recursive: true });
2080
- const filesToGenerate = extensionsToGenerate.map((extension) => ({
2081
- extension,
2082
- path: getFilepathForFile(coreCompiler, outDir, componentName, extension),
2083
- }));
2084
- await checkForOverwrite(filesToGenerate, config);
2085
- const writtenFiles = await Promise.all(filesToGenerate.map((file) => getBoilerplateAndWriteFile(config, componentName, extensionsToGenerate.includes('css'), file))).catch((error) => config.logger.error(error));
2086
- if (!writtenFiles) {
2087
- return config.sys.exit(1);
2088
- }
2089
- // We use `console.log` here rather than our `config.logger` because we don't want
2090
- // our TUI messages to be prefixed with timestamps and so on.
2091
- console.log();
2092
- console.log(`${config.logger.gray('$')} rindo generate ${input}`);
2093
- console.log();
2094
- console.log(config.logger.bold('The following files have been generated:'));
2095
- const absoluteRootDir = config.rootDir;
2096
- writtenFiles.map((file) => console.log(` - ${path.relative(absoluteRootDir, file)}`));
2097
- };
2098
- /**
2099
- * Show a checkbox prompt to select the files to be generated.
2100
- *
2101
- * @returns a read-only array of `GenerableExtension`, the extensions that the user has decided
2102
- * to generate
2103
- */
2104
- const chooseFilesToGenerate = async () => {
2105
- const { prompt } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../sys/node/prompts.js')); });
2106
- return (await prompt({
2107
- name: 'filesToGenerate',
2108
- type: 'multiselect',
2109
- message: 'Which additional files do you want to generate?',
2110
- choices: [
2111
- { value: 'css', title: 'Stylesheet (.css)', selected: true },
2112
- { value: 'spec.tsx', title: 'Spec Test (.spec.tsx)', selected: true },
2113
- { value: 'e2e.ts', title: 'E2E Test (.e2e.ts)', selected: true },
2114
- ],
2115
- })).filesToGenerate;
2116
- };
2117
- /**
2118
- * Get a filepath for a file we want to generate!
2119
- *
2120
- * The filepath for a given file depends on the path, the user-supplied
2121
- * component name, the extension, and whether we're inside of a test directory.
2122
- *
2123
- * @param coreCompiler the compiler we're using, here to acces the `.path` module
2124
- * @param path path to where we're going to generate the component
2125
- * @param componentName the user-supplied name for the generated component
2126
- * @param extension the file extension
2127
- * @returns the full filepath to the component (with a possible `test` directory
2128
- * added)
2129
- */
2130
- const getFilepathForFile = (coreCompiler, path, componentName, extension) => isTest(extension)
2131
- ? coreCompiler.path.join(path, 'test', `${componentName}.${extension}`)
2132
- : coreCompiler.path.join(path, `${componentName}.${extension}`);
2133
- /**
2134
- * Get the boilerplate for a file and write it to disk
2135
- *
2136
- * @param config the current config, needed for file operations
2137
- * @param componentName the component name (user-supplied)
2138
- * @param withCss are we generating CSS?
2139
- * @param file the file we want to write
2140
- * @returns a `Promise<string>` which holds the full filepath we've written to,
2141
- * used to print out a little summary of our activity to the user.
2142
- */
2143
- const getBoilerplateAndWriteFile = async (config, componentName, withCss, file) => {
2144
- const boilerplate = getBoilerplateByExtension(componentName, file.extension, withCss);
2145
- await config.sys.writeFile(file.path, boilerplate);
2146
- return file.path;
2147
- };
2148
- /**
2149
- * Check to see if any of the files we plan to write already exist and would
2150
- * therefore be overwritten if we proceed, because we'd like to not overwrite
2151
- * people's code!
2152
- *
2153
- * This function will check all the filepaths and if it finds any files log an
2154
- * error and exit with an error code. If it doesn't find anything it will just
2155
- * peacefully return `Promise<void>`.
2156
- *
2157
- * @param files the files we want to check
2158
- * @param config the Config object, used here to get access to `sys.readFile`
2159
- */
2160
- const checkForOverwrite = async (files, config) => {
2161
- const alreadyPresent = [];
2162
- await Promise.all(files.map(async ({ path }) => {
2163
- if ((await config.sys.readFile(path)) !== undefined) {
2164
- alreadyPresent.push(path);
2165
- }
2166
- }));
2167
- if (alreadyPresent.length > 0) {
2168
- config.logger.error('Generating code would overwrite the following files:', ...alreadyPresent.map((path) => '\t' + path));
2169
- await config.sys.exit(1);
2170
- }
2171
- };
2172
- /**
2173
- * Check if an extension is for a test
2174
- *
2175
- * @param extension the extension we want to check
2176
- * @returns a boolean indicating whether or not its a test
2177
- */
2178
- const isTest = (extension) => {
2179
- return extension === 'e2e.ts' || extension === 'spec.tsx';
2180
- };
2181
- /**
2182
- * Get the boilerplate for a file by its extension.
2183
- *
2184
- * @param tagName the name of the component we're generating
2185
- * @param extension the file extension we want boilerplate for (.css, tsx, etc)
2186
- * @param withCss a boolean indicating whether we're generating a CSS file
2187
- * @returns a string container the file boilerplate for the supplied extension
2188
- */
2189
- const getBoilerplateByExtension = (tagName, extension, withCss) => {
2190
- switch (extension) {
2191
- case 'tsx':
2192
- return getComponentBoilerplate(tagName, withCss);
2193
- case 'css':
2194
- return getStyleUrlBoilerplate();
2195
- case 'spec.tsx':
2196
- return getSpecTestBoilerplate(tagName);
2197
- case 'e2e.ts':
2198
- return getE2eTestBoilerplate(tagName);
2199
- default:
2200
- throw new Error(`Unkown extension "${extension}".`);
2201
- }
2202
- };
2203
- /**
2204
- * Get the boilerplate for a file containing the definition of a component
2205
- * @param tagName the name of the tag to give the component
2206
- * @param hasStyle designates if the component has an external stylesheet or not
2207
- * @returns the contents of a file that defines a component
2208
- */
2209
- const getComponentBoilerplate = (tagName, hasStyle) => {
2210
- const decorator = [`{`];
2211
- decorator.push(` tag: '${tagName}',`);
2212
- if (hasStyle) {
2213
- decorator.push(` styleUrl: '${tagName}.css',`);
2214
- }
2215
- decorator.push(` shadow: true,`);
2216
- decorator.push(`}`);
1937
+ /**
1938
+ * Task to generate component boilerplate and write it to disk. This task can
1939
+ * cause the program to exit with an error under various circumstances, such as
1940
+ * being called in an inappropriate place, being asked to overwrite files that
1941
+ * already exist, etc.
1942
+ *
1943
+ * @param coreCompiler the CoreCompiler we're using currently, here we're
1944
+ * mainly accessing the `path` module
1945
+ * @param config the user-supplied config, which we need here to access `.sys`.
1946
+ */
1947
+ const taskGenerate = async (coreCompiler, config) => {
1948
+ if (!IS_NODE_ENV) {
1949
+ config.logger.error(`"generate" command is currently only implemented for a NodeJS environment`);
1950
+ return config.sys.exit(1);
1951
+ }
1952
+ const path = coreCompiler.path;
1953
+ if (!config.configPath) {
1954
+ config.logger.error('Please run this command in your root directory (i. e. the one containing rindo.config.ts).');
1955
+ return config.sys.exit(1);
1956
+ }
1957
+ const absoluteSrcDir = config.srcDir;
1958
+ if (!absoluteSrcDir) {
1959
+ config.logger.error(`Rindo's srcDir was not specified.`);
1960
+ return config.sys.exit(1);
1961
+ }
1962
+ const { prompt } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../sys/node/prompts.js')); });
1963
+ const input = config.flags.unknownArgs.find((arg) => !arg.startsWith('-')) ||
1964
+ (await prompt({ name: 'tagName', type: 'text', message: 'Component tag name (dash-case):' })).tagName;
1965
+ if (undefined === input) {
1966
+ // in some shells (e.g. Windows PowerShell), hitting Ctrl+C results in a TypeError printed to the console.
1967
+ // explicitly return here to avoid printing the error message.
1968
+ return;
1969
+ }
1970
+ const { dir, base: componentName } = path.parse(input);
1971
+ const tagError = validateComponentTag(componentName);
1972
+ if (tagError) {
1973
+ config.logger.error(tagError);
1974
+ return config.sys.exit(1);
1975
+ }
1976
+ const filesToGenerateExt = await chooseFilesToGenerate();
1977
+ if (undefined === filesToGenerateExt) {
1978
+ // in some shells (e.g. Windows PowerShell), hitting Ctrl+C results in a TypeError printed to the console.
1979
+ // explicitly return here to avoid printing the error message.
1980
+ return;
1981
+ }
1982
+ const extensionsToGenerate = ['tsx', ...filesToGenerateExt];
1983
+ const testFolder = extensionsToGenerate.some(isTest) ? 'test' : '';
1984
+ const outDir = path.join(absoluteSrcDir, 'components', dir, componentName);
1985
+ await config.sys.createDir(path.join(outDir, testFolder), { recursive: true });
1986
+ const filesToGenerate = extensionsToGenerate.map((extension) => ({
1987
+ extension,
1988
+ path: getFilepathForFile(coreCompiler, outDir, componentName, extension),
1989
+ }));
1990
+ await checkForOverwrite(filesToGenerate, config);
1991
+ const writtenFiles = await Promise.all(filesToGenerate.map((file) => getBoilerplateAndWriteFile(config, componentName, extensionsToGenerate.includes('css'), file))).catch((error) => config.logger.error(error));
1992
+ if (!writtenFiles) {
1993
+ return config.sys.exit(1);
1994
+ }
1995
+ // We use `console.log` here rather than our `config.logger` because we don't want
1996
+ // our TUI messages to be prefixed with timestamps and so on.
1997
+ console.log();
1998
+ console.log(`${config.logger.gray('$')} rindo generate ${input}`);
1999
+ console.log();
2000
+ console.log(config.logger.bold('The following files have been generated:'));
2001
+ const absoluteRootDir = config.rootDir;
2002
+ writtenFiles.map((file) => console.log(` - ${path.relative(absoluteRootDir, file)}`));
2003
+ };
2004
+ /**
2005
+ * Show a checkbox prompt to select the files to be generated.
2006
+ *
2007
+ * @returns a read-only array of `GenerableExtension`, the extensions that the user has decided
2008
+ * to generate
2009
+ */
2010
+ const chooseFilesToGenerate = async () => {
2011
+ const { prompt } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../sys/node/prompts.js')); });
2012
+ return (await prompt({
2013
+ name: 'filesToGenerate',
2014
+ type: 'multiselect',
2015
+ message: 'Which additional files do you want to generate?',
2016
+ choices: [
2017
+ { value: 'css', title: 'Stylesheet (.css)', selected: true },
2018
+ { value: 'spec.tsx', title: 'Spec Test (.spec.tsx)', selected: true },
2019
+ { value: 'e2e.ts', title: 'E2E Test (.e2e.ts)', selected: true },
2020
+ ],
2021
+ })).filesToGenerate;
2022
+ };
2023
+ /**
2024
+ * Get a filepath for a file we want to generate!
2025
+ *
2026
+ * The filepath for a given file depends on the path, the user-supplied
2027
+ * component name, the extension, and whether we're inside of a test directory.
2028
+ *
2029
+ * @param coreCompiler the compiler we're using, here to acces the `.path` module
2030
+ * @param path path to where we're going to generate the component
2031
+ * @param componentName the user-supplied name for the generated component
2032
+ * @param extension the file extension
2033
+ * @returns the full filepath to the component (with a possible `test` directory
2034
+ * added)
2035
+ */
2036
+ const getFilepathForFile = (coreCompiler, path, componentName, extension) => isTest(extension)
2037
+ ? coreCompiler.path.join(path, 'test', `${componentName}.${extension}`)
2038
+ : coreCompiler.path.join(path, `${componentName}.${extension}`);
2039
+ /**
2040
+ * Get the boilerplate for a file and write it to disk
2041
+ *
2042
+ * @param config the current config, needed for file operations
2043
+ * @param componentName the component name (user-supplied)
2044
+ * @param withCss are we generating CSS?
2045
+ * @param file the file we want to write
2046
+ * @returns a `Promise<string>` which holds the full filepath we've written to,
2047
+ * used to print out a little summary of our activity to the user.
2048
+ */
2049
+ const getBoilerplateAndWriteFile = async (config, componentName, withCss, file) => {
2050
+ const boilerplate = getBoilerplateByExtension(componentName, file.extension, withCss);
2051
+ await config.sys.writeFile(file.path, boilerplate);
2052
+ return file.path;
2053
+ };
2054
+ /**
2055
+ * Check to see if any of the files we plan to write already exist and would
2056
+ * therefore be overwritten if we proceed, because we'd like to not overwrite
2057
+ * people's code!
2058
+ *
2059
+ * This function will check all the filepaths and if it finds any files log an
2060
+ * error and exit with an error code. If it doesn't find anything it will just
2061
+ * peacefully return `Promise<void>`.
2062
+ *
2063
+ * @param files the files we want to check
2064
+ * @param config the Config object, used here to get access to `sys.readFile`
2065
+ */
2066
+ const checkForOverwrite = async (files, config) => {
2067
+ const alreadyPresent = [];
2068
+ await Promise.all(files.map(async ({ path }) => {
2069
+ if ((await config.sys.readFile(path)) !== undefined) {
2070
+ alreadyPresent.push(path);
2071
+ }
2072
+ }));
2073
+ if (alreadyPresent.length > 0) {
2074
+ config.logger.error('Generating code would overwrite the following files:', ...alreadyPresent.map((path) => '\t' + path));
2075
+ await config.sys.exit(1);
2076
+ }
2077
+ };
2078
+ /**
2079
+ * Check if an extension is for a test
2080
+ *
2081
+ * @param extension the extension we want to check
2082
+ * @returns a boolean indicating whether or not its a test
2083
+ */
2084
+ const isTest = (extension) => {
2085
+ return extension === 'e2e.ts' || extension === 'spec.tsx';
2086
+ };
2087
+ /**
2088
+ * Get the boilerplate for a file by its extension.
2089
+ *
2090
+ * @param tagName the name of the component we're generating
2091
+ * @param extension the file extension we want boilerplate for (.css, tsx, etc)
2092
+ * @param withCss a boolean indicating whether we're generating a CSS file
2093
+ * @returns a string container the file boilerplate for the supplied extension
2094
+ */
2095
+ const getBoilerplateByExtension = (tagName, extension, withCss) => {
2096
+ switch (extension) {
2097
+ case 'tsx':
2098
+ return getComponentBoilerplate(tagName, withCss);
2099
+ case 'css':
2100
+ return getStyleUrlBoilerplate();
2101
+ case 'spec.tsx':
2102
+ return getSpecTestBoilerplate(tagName);
2103
+ case 'e2e.ts':
2104
+ return getE2eTestBoilerplate(tagName);
2105
+ default:
2106
+ throw new Error(`Unkown extension "${extension}".`);
2107
+ }
2108
+ };
2109
+ /**
2110
+ * Get the boilerplate for a file containing the definition of a component
2111
+ * @param tagName the name of the tag to give the component
2112
+ * @param hasStyle designates if the component has an external stylesheet or not
2113
+ * @returns the contents of a file that defines a component
2114
+ */
2115
+ const getComponentBoilerplate = (tagName, hasStyle) => {
2116
+ const decorator = [`{`];
2117
+ decorator.push(` tag: '${tagName}',`);
2118
+ if (hasStyle) {
2119
+ decorator.push(` styleUrl: '${tagName}.css',`);
2120
+ }
2121
+ decorator.push(` shadow: true,`);
2122
+ decorator.push(`}`);
2217
2123
  return `import { Component, Host, h } from '@rindo/core';
2218
2124
 
2219
2125
  @Component(${decorator.join('\n')})
@@ -2228,21 +2134,21 @@ export class ${toPascalCase(tagName)} {
2228
2134
  }
2229
2135
 
2230
2136
  }
2231
- `;
2232
- };
2233
- /**
2234
- * Get the boilerplate for style for a generated component
2235
- * @returns a boilerplate CSS block
2236
- */
2137
+ `;
2138
+ };
2139
+ /**
2140
+ * Get the boilerplate for style for a generated component
2141
+ * @returns a boilerplate CSS block
2142
+ */
2237
2143
  const getStyleUrlBoilerplate = () => `:host {
2238
2144
  display: block;
2239
2145
  }
2240
- `;
2241
- /**
2242
- * Get the boilerplate for a file containing a spec (unit) test for a component
2243
- * @param tagName the name of the tag associated with the component under test
2244
- * @returns the contents of a file that unit tests a component
2245
- */
2146
+ `;
2147
+ /**
2148
+ * Get the boilerplate for a file containing a spec (unit) test for a component
2149
+ * @param tagName the name of the tag associated with the component under test
2150
+ * @returns the contents of a file that unit tests a component
2151
+ */
2246
2152
  const getSpecTestBoilerplate = (tagName) => `import { newSpecPage } from '@rindo/core/testing';
2247
2153
  import { ${toPascalCase(tagName)} } from '../${tagName}';
2248
2154
 
@@ -2261,12 +2167,12 @@ describe('${tagName}', () => {
2261
2167
  \`);
2262
2168
  });
2263
2169
  });
2264
- `;
2265
- /**
2266
- * Get the boilerplate for a file containing an end-to-end (E2E) test for a component
2267
- * @param tagName the name of the tag associated with the component under test
2268
- * @returns the contents of a file that E2E tests a component
2269
- */
2170
+ `;
2171
+ /**
2172
+ * Get the boilerplate for a file containing an end-to-end (E2E) test for a component
2173
+ * @param tagName the name of the tag associated with the component under test
2174
+ * @returns the contents of a file that E2E tests a component
2175
+ */
2270
2176
  const getE2eTestBoilerplate = (tagName) => `import { newE2EPage } from '@rindo/core/testing';
2271
2177
 
2272
2178
  describe('${tagName}', () => {
@@ -2278,62 +2184,62 @@ describe('${tagName}', () => {
2278
2184
  expect(element).toHaveClass('hydrated');
2279
2185
  });
2280
2186
  });
2281
- `;
2282
- /**
2283
- * Convert a dash case string to pascal case.
2284
- * @param str the string to convert
2285
- * @returns the converted input as pascal case
2286
- */
2187
+ `;
2188
+ /**
2189
+ * Convert a dash case string to pascal case.
2190
+ * @param str the string to convert
2191
+ * @returns the converted input as pascal case
2192
+ */
2287
2193
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
2288
2194
 
2289
- /**
2290
- * Entrypoint for the Telemetry task
2291
- * @param flags configuration flags provided to Rindo when a task was called (either this task or a task that invokes
2292
- * telemetry)
2293
- * @param sys the abstraction for interfacing with the operating system
2294
- * @param logger a logging implementation to log the results out to the user
2295
- */
2296
- const taskTelemetry = async (flags, sys, logger) => {
2297
- const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
2298
- const isEnabling = flags.args.includes('on');
2299
- const isDisabling = flags.args.includes('off');
2300
- const INFORMATION = `Opt in or out of telemetry. Information about the data we collect is available on our website: ${logger.bold('https://rindojs.web.app/telemetry')}`;
2301
- const THANK_YOU = `Thank you for helping to make Rindo better! 💖`;
2302
- const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
2303
- const DISABLED_MESSAGE = `${logger.red('Disabled')}\n\n`;
2304
- const hasTelemetry = await checkTelemetry(sys);
2305
- if (isEnabling) {
2306
- const result = await enableTelemetry(sys);
2307
- result
2308
- ? console.log(`\n ${logger.bold('Telemetry is now ') + ENABLED_MESSAGE}`)
2309
- : console.log(`Something went wrong when enabling Telemetry.`);
2310
- return;
2311
- }
2312
- if (isDisabling) {
2313
- const result = await disableTelemetry(sys);
2314
- result
2315
- ? console.log(`\n ${logger.bold('Telemetry is now ') + DISABLED_MESSAGE}`)
2316
- : console.log(`Something went wrong when disabling Telemetry.`);
2317
- return;
2318
- }
2319
- console.log(` ${logger.bold('Telemetry:')} ${logger.dim(INFORMATION)}`);
2320
- console.log(`\n ${logger.bold('Status')}: ${hasTelemetry ? ENABLED_MESSAGE : DISABLED_MESSAGE}`);
2195
+ /**
2196
+ * Entrypoint for the Telemetry task
2197
+ * @param flags configuration flags provided to Rindo when a task was called (either this task or a task that invokes
2198
+ * telemetry)
2199
+ * @param sys the abstraction for interfacing with the operating system
2200
+ * @param logger a logging implementation to log the results out to the user
2201
+ */
2202
+ const taskTelemetry = async (flags, sys, logger) => {
2203
+ const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
2204
+ const isEnabling = flags.args.includes('on');
2205
+ const isDisabling = flags.args.includes('off');
2206
+ const INFORMATION = `Opt in or out of telemetry. Information about the data we collect is available on our website: ${logger.bold('https://rindojs.web.app/telemetry')}`;
2207
+ const THANK_YOU = `Thank you for helping to make Rindo better! 💖`;
2208
+ const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
2209
+ const DISABLED_MESSAGE = `${logger.red('Disabled')}\n\n`;
2210
+ const hasTelemetry = await checkTelemetry(sys);
2211
+ if (isEnabling) {
2212
+ const result = await enableTelemetry(sys);
2213
+ result
2214
+ ? console.log(`\n ${logger.bold('Telemetry is now ') + ENABLED_MESSAGE}`)
2215
+ : console.log(`Something went wrong when enabling Telemetry.`);
2216
+ return;
2217
+ }
2218
+ if (isDisabling) {
2219
+ const result = await disableTelemetry(sys);
2220
+ result
2221
+ ? console.log(`\n ${logger.bold('Telemetry is now ') + DISABLED_MESSAGE}`)
2222
+ : console.log(`Something went wrong when disabling Telemetry.`);
2223
+ return;
2224
+ }
2225
+ console.log(` ${logger.bold('Telemetry:')} ${logger.dim(INFORMATION)}`);
2226
+ console.log(`\n ${logger.bold('Status')}: ${hasTelemetry ? ENABLED_MESSAGE : DISABLED_MESSAGE}`);
2321
2227
  console.log(` ${prompt} ${logger.green('rindo telemetry [off|on]')}
2322
2228
 
2323
2229
  ${logger.cyan('off')} ${logger.dim('.............')} Disable sharing anonymous usage data
2324
2230
  ${logger.cyan('on')} ${logger.dim('..............')} Enable sharing anonymous usage data
2325
- `);
2231
+ `);
2326
2232
  };
2327
2233
 
2328
- /**
2329
- * Entrypoint for the Help task, providing Rindo usage context to the user
2330
- * @param flags configuration flags provided to Rindo when a task was call (either this task or a task that invokes
2331
- * telemetry)
2332
- * @param logger a logging implementation to log the results out to the user
2333
- * @param sys the abstraction for interfacing with the operating system
2334
- */
2335
- const taskHelp = async (flags, logger, sys) => {
2336
- const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
2234
+ /**
2235
+ * Entrypoint for the Help task, providing Rindo usage context to the user
2236
+ * @param flags configuration flags provided to Rindo when a task was call (either this task or a task that invokes
2237
+ * telemetry)
2238
+ * @param logger a logging implementation to log the results out to the user
2239
+ * @param sys the abstraction for interfacing with the operating system
2240
+ */
2241
+ const taskHelp = async (flags, logger, sys) => {
2242
+ const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
2337
2243
  console.log(`
2338
2244
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
2339
2245
 
@@ -2362,8 +2268,8 @@ const taskHelp = async (flags, logger, sys) => {
2362
2268
 
2363
2269
  ${prompt} ${logger.green('rindo generate')} or ${logger.green('rindo g')}
2364
2270
 
2365
- `);
2366
- await taskTelemetry(flags, sys, logger);
2271
+ `);
2272
+ await taskTelemetry(flags, sys, logger);
2367
2273
  console.log(`
2368
2274
  ${logger.bold('Examples:')}
2369
2275
 
@@ -2373,234 +2279,231 @@ const taskHelp = async (flags, logger, sys) => {
2373
2279
  ${prompt} ${logger.green('rindo telemetry on')}
2374
2280
  ${prompt} ${logger.green('rindo generate')}
2375
2281
  ${prompt} ${logger.green('rindo g my-component')}
2376
- `);
2282
+ `);
2377
2283
  };
2378
2284
 
2379
- const taskInfo = (coreCompiler, sys, logger) => {
2380
- const details = sys.details;
2381
- const versions = coreCompiler.versions;
2382
- console.log(``);
2383
- console.log(`${logger.cyan(' System:')} ${sys.name} ${sys.version}`);
2384
- console.log(`${logger.cyan(' Plaform:')} ${details.platform} (${details.release})`);
2385
- console.log(`${logger.cyan(' CPU Model:')} ${details.cpuModel} (${sys.hardwareConcurrency} cpu${sys.hardwareConcurrency !== 1 ? 's' : ''})`);
2386
- console.log(`${logger.cyan(' Compiler:')} ${sys.getCompilerExecutingPath()}`);
2387
- console.log(`${logger.cyan(' Build:')} ${coreCompiler.buildId}`);
2388
- console.log(`${logger.cyan(' Rindo:')} ${coreCompiler.version}${logger.emoji(' ' + coreCompiler.vermoji)}`);
2389
- console.log(`${logger.cyan(' TypeScript:')} ${versions.typescript}`);
2390
- console.log(`${logger.cyan(' Rollup:')} ${versions.rollup}`);
2391
- console.log(`${logger.cyan(' Parse5:')} ${versions.parse5}`);
2392
- console.log(`${logger.cyan(' Sizzle:')} ${versions.sizzle}`);
2393
- console.log(`${logger.cyan(' Terser:')} ${versions.terser}`);
2394
- console.log(``);
2285
+ /**
2286
+ * Generate the output for Rindos 'info' task, and log that output - `npx rindo info`
2287
+ * @param coreCompiler the compiler instance to derive certain version information from
2288
+ * @param sys the compiler system instance that provides details about the system Rindo is running on
2289
+ * @param logger the logger instance to use to log information out to
2290
+ */
2291
+ const taskInfo = (coreCompiler, sys, logger) => {
2292
+ const details = sys.details;
2293
+ const versions = coreCompiler.versions;
2294
+ console.log(``);
2295
+ console.log(`${logger.cyan(' System:')} ${sys.name} ${sys.version}`);
2296
+ console.log(`${logger.cyan(' Platform:')} ${details.platform} (${details.release})`);
2297
+ console.log(`${logger.cyan(' CPU Model:')} ${details.cpuModel} (${sys.hardwareConcurrency} cpu${sys.hardwareConcurrency !== 1 ? 's' : ''})`);
2298
+ console.log(`${logger.cyan(' Compiler:')} ${sys.getCompilerExecutingPath()}`);
2299
+ console.log(`${logger.cyan(' Build:')} ${coreCompiler.buildId}`);
2300
+ console.log(`${logger.cyan(' Rindo:')} ${coreCompiler.version}${logger.emoji(' ' + coreCompiler.vermoji)}`);
2301
+ console.log(`${logger.cyan(' TypeScript:')} ${versions.typescript}`);
2302
+ console.log(`${logger.cyan(' Rollup:')} ${versions.rollup}`);
2303
+ console.log(`${logger.cyan(' Parse5:')} ${versions.parse5}`);
2304
+ console.log(`${logger.cyan(' Sizzle:')} ${versions.sizzle}`);
2305
+ console.log(`${logger.cyan(' Terser:')} ${versions.terser}`);
2306
+ console.log(``);
2395
2307
  };
2396
2308
 
2397
- const taskServe = async (config) => {
2398
- config.suppressLogs = true;
2399
- config.flags.serve = true;
2400
- config.devServer.openBrowser = config.flags.open;
2401
- config.devServer.reloadStrategy = null;
2402
- config.devServer.initialLoadUrl = '/';
2403
- config.devServer.websocket = false;
2404
- config.maxConcurrentWorkers = 1;
2405
- config.devServer.root = isString(config.flags.root) ? config.flags.root : config.sys.getCurrentDirectory();
2406
- const devServerPath = config.sys.getDevServerExecutingPath();
2407
- const { start } = await config.sys.dynamicImport(devServerPath);
2408
- const devServer = await start(config.devServer, config.logger);
2409
- console.log(`${config.logger.cyan(' Root:')} ${devServer.root}`);
2410
- console.log(`${config.logger.cyan(' Address:')} ${devServer.address}`);
2411
- console.log(`${config.logger.cyan(' Port:')} ${devServer.port}`);
2412
- console.log(`${config.logger.cyan(' Server:')} ${devServer.browserUrl}`);
2413
- console.log(``);
2414
- config.sys.onProcessInterrupt(() => {
2415
- if (devServer) {
2416
- config.logger.debug(`dev server close: ${devServer.browserUrl}`);
2417
- devServer.close();
2418
- }
2419
- });
2309
+ const taskServe = async (config) => {
2310
+ config.suppressLogs = true;
2311
+ config.flags.serve = true;
2312
+ config.devServer.openBrowser = config.flags.open;
2313
+ config.devServer.reloadStrategy = null;
2314
+ config.devServer.initialLoadUrl = '/';
2315
+ config.devServer.websocket = false;
2316
+ config.maxConcurrentWorkers = 1;
2317
+ config.devServer.root = isString(config.flags.root) ? config.flags.root : config.sys.getCurrentDirectory();
2318
+ const devServerPath = config.sys.getDevServerExecutingPath();
2319
+ const { start } = await config.sys.dynamicImport(devServerPath);
2320
+ const devServer = await start(config.devServer, config.logger);
2321
+ console.log(`${config.logger.cyan(' Root:')} ${devServer.root}`);
2322
+ console.log(`${config.logger.cyan(' Address:')} ${devServer.address}`);
2323
+ console.log(`${config.logger.cyan(' Port:')} ${devServer.port}`);
2324
+ console.log(`${config.logger.cyan(' Server:')} ${devServer.browserUrl}`);
2325
+ console.log(``);
2326
+ config.sys.onProcessInterrupt(() => {
2327
+ if (devServer) {
2328
+ config.logger.debug(`dev server close: ${devServer.browserUrl}`);
2329
+ devServer.close();
2330
+ }
2331
+ });
2420
2332
  };
2421
2333
 
2422
- /**
2423
- * Entrypoint for any Rindo tests
2424
- * @param config a validated Rindo configuration entity
2425
- */
2426
- const taskTest = async (config) => {
2427
- if (!IS_NODE_ENV) {
2428
- config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
2429
- return config.sys.exit(1);
2430
- }
2431
- config.buildDocs = false;
2432
- const testingRunOpts = {
2433
- e2e: !!config.flags.e2e,
2434
- screenshot: !!config.flags.screenshot,
2435
- spec: !!config.flags.spec,
2436
- updateScreenshot: !!config.flags.updateScreenshot,
2437
- };
2438
- // always ensure we have jest modules installed
2439
- const ensureModuleIds = ['@types/jest', 'jest', 'jest-cli'];
2440
- if (testingRunOpts.e2e) {
2441
- // if it's an e2e test, also make sure we're got
2442
- // puppeteer modules installed and if browserExecutablePath is provided don't download Chromium use only puppeteer-core instead
2443
- const puppeteer = config.testing.browserExecutablePath ? 'puppeteer-core' : 'puppeteer';
2444
- ensureModuleIds.push(puppeteer);
2445
- if (testingRunOpts.screenshot) {
2446
- // ensure we've got pixelmatch for screenshots
2447
- config.logger.warn(config.logger.yellow(`EXPERIMENTAL: screenshot visual diff testing is currently under heavy development and has not reached a stable status. However, any assistance testing would be appreciated.`));
2448
- }
2449
- }
2450
- // ensure we've got the required modules installed
2451
- const diagnostics = await config.sys.lazyRequire.ensure(config.rootDir, ensureModuleIds);
2452
- if (diagnostics.length > 0) {
2453
- config.logger.printDiagnostics(diagnostics);
2454
- return config.sys.exit(1);
2455
- }
2456
- try {
2457
- // let's test!
2458
- const { createTesting } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../testing/index.js')); });
2459
- const testing = await createTesting(config);
2460
- const passed = await testing.run(testingRunOpts);
2461
- await testing.destroy();
2462
- if (!passed) {
2463
- return config.sys.exit(1);
2464
- }
2465
- }
2466
- catch (e) {
2467
- config.logger.error(e);
2468
- return config.sys.exit(1);
2469
- }
2334
+ /**
2335
+ * Entrypoint for any Rindo tests
2336
+ * @param config a validated Rindo configuration entity
2337
+ */
2338
+ const taskTest = async (config) => {
2339
+ if (!IS_NODE_ENV) {
2340
+ config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
2341
+ return config.sys.exit(1);
2342
+ }
2343
+ config.buildDocs = false;
2344
+ const testingRunOpts = {
2345
+ e2e: !!config.flags.e2e,
2346
+ screenshot: !!config.flags.screenshot,
2347
+ spec: !!config.flags.spec,
2348
+ updateScreenshot: !!config.flags.updateScreenshot,
2349
+ };
2350
+ // always ensure we have jest modules installed
2351
+ const ensureModuleIds = ['@types/jest', 'jest', 'jest-cli'];
2352
+ if (testingRunOpts.e2e) {
2353
+ // if it's an e2e test, also make sure we're got
2354
+ // puppeteer modules installed and if browserExecutablePath is provided don't download Chromium use only puppeteer-core instead
2355
+ const puppeteer = config.testing.browserExecutablePath ? 'puppeteer-core' : 'puppeteer';
2356
+ ensureModuleIds.push(puppeteer);
2357
+ if (testingRunOpts.screenshot) {
2358
+ // ensure we've got pixelmatch for screenshots
2359
+ config.logger.warn(config.logger.yellow(`EXPERIMENTAL: screenshot visual diff testing is currently under heavy development and has not reached a stable status. However, any assistance testing would be appreciated.`));
2360
+ }
2361
+ }
2362
+ // ensure we've got the required modules installed
2363
+ const diagnostics = await config.sys.lazyRequire.ensure(config.rootDir, ensureModuleIds);
2364
+ if (diagnostics.length > 0) {
2365
+ config.logger.printDiagnostics(diagnostics);
2366
+ return config.sys.exit(1);
2367
+ }
2368
+ try {
2369
+ // let's test!
2370
+ const { createTesting } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../testing/index.js')); });
2371
+ const testing = await createTesting(config);
2372
+ const passed = await testing.run(testingRunOpts);
2373
+ await testing.destroy();
2374
+ if (!passed) {
2375
+ return config.sys.exit(1);
2376
+ }
2377
+ }
2378
+ catch (e) {
2379
+ config.logger.error(e);
2380
+ return config.sys.exit(1);
2381
+ }
2470
2382
  };
2471
2383
 
2472
- const run = async (init) => {
2473
- const { args, logger, sys } = init;
2474
- try {
2475
- const flags = parseFlags(args);
2476
- const task = flags.task;
2477
- if (flags.debug || flags.verbose) {
2478
- logger.setLevel('debug');
2479
- }
2480
- if (flags.ci) {
2481
- logger.enableColors(false);
2482
- }
2483
- if (isFunction(sys.applyGlobalPatch)) {
2484
- sys.applyGlobalPatch(sys.getCurrentDirectory());
2485
- }
2486
- if (!task || task === 'help' || flags.help) {
2487
- await taskHelp(createConfigFlags({ task: 'help', args }), logger, sys);
2488
- return;
2489
- }
2490
- startupLog(logger, task);
2491
- const findConfigResults = await findConfig({ sys, configPath: flags.config });
2492
- if (hasError(findConfigResults.diagnostics)) {
2493
- logger.printDiagnostics(findConfigResults.diagnostics);
2494
- return sys.exit(1);
2495
- }
2496
- const ensureDepsResults = await sys.ensureDependencies({
2497
- rootDir: findConfigResults.rootDir,
2498
- logger,
2499
- dependencies: dependencies,
2500
- });
2501
- if (hasError(ensureDepsResults.diagnostics)) {
2502
- logger.printDiagnostics(ensureDepsResults.diagnostics);
2503
- return sys.exit(1);
2504
- }
2505
- const coreCompiler = await loadCoreCompiler(sys);
2506
- if (task === 'version' || flags.version) {
2507
- console.log(coreCompiler.version);
2508
- return;
2509
- }
2510
- startupLogVersion(logger, task, coreCompiler);
2511
- loadedCompilerLog(sys, logger, flags, coreCompiler);
2512
- if (task === 'info') {
2513
- taskInfo(coreCompiler, sys, logger);
2514
- return;
2515
- }
2516
- const validated = await coreCompiler.loadConfig({
2517
- config: {
2518
- flags,
2519
- },
2520
- configPath: findConfigResults.configPath,
2521
- logger,
2522
- sys,
2523
- });
2524
- if (validated.diagnostics.length > 0) {
2525
- logger.printDiagnostics(validated.diagnostics);
2526
- if (hasError(validated.diagnostics)) {
2527
- return sys.exit(1);
2528
- }
2529
- }
2530
- if (isFunction(sys.applyGlobalPatch)) {
2531
- sys.applyGlobalPatch(validated.config.rootDir);
2532
- }
2533
- await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
2534
- await telemetryAction(sys, validated.config, coreCompiler, async () => {
2535
- await runTask(coreCompiler, validated.config, task, sys);
2536
- });
2537
- }
2538
- catch (e) {
2539
- if (!shouldIgnoreError(e)) {
2540
- const details = `${logger.getLevel() === 'debug' && e instanceof Error ? e.stack : ''}`;
2541
- logger.error(`uncaught cli error: ${e}${details}`);
2542
- return sys.exit(1);
2543
- }
2544
- }
2545
- };
2546
- /**
2547
- * Run a specified task
2548
- * @param coreCompiler an instance of a minimal, bootstrap compiler for running the specified task
2549
- * @param config a configuration for the Rindo project to apply to the task run
2550
- * @param task the task to run
2551
- * @param sys the {@link CompilerSystem} for interacting with the operating system
2552
- * @public
2553
- */
2554
- const runTask = async (coreCompiler, config, task, sys) => {
2555
- var _a, _b, _c, _d, _e, _f, _g;
2556
- const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2557
- const rootDir = (_b = config.rootDir) !== null && _b !== void 0 ? _b : '/';
2558
- const configSys = (_c = sys !== null && sys !== void 0 ? sys : config.sys) !== null && _c !== void 0 ? _c : coreCompiler.createSystem({ logger });
2559
- const strictConfig = {
2560
- ...config,
2561
- flags: createConfigFlags((_d = config.flags) !== null && _d !== void 0 ? _d : { task }),
2562
- hydratedFlag: (_e = config.hydratedFlag) !== null && _e !== void 0 ? _e : null,
2563
- logger,
2564
- outputTargets: (_f = config.outputTargets) !== null && _f !== void 0 ? _f : [],
2565
- packageJsonFilePath: configSys.platformPath.join(rootDir, 'package.json'),
2566
- rootDir,
2567
- sys: configSys,
2568
- testing: (_g = config.testing) !== null && _g !== void 0 ? _g : {},
2569
- };
2570
- switch (task) {
2571
- case 'build':
2572
- await taskBuild(coreCompiler, strictConfig);
2573
- break;
2574
- case 'docs':
2575
- await taskDocs(coreCompiler, strictConfig);
2576
- break;
2577
- case 'generate':
2578
- case 'g':
2579
- await taskGenerate(coreCompiler, strictConfig);
2580
- break;
2581
- case 'help':
2582
- await taskHelp(strictConfig.flags, strictConfig.logger, sys);
2583
- break;
2584
- case 'prerender':
2585
- await taskPrerender(coreCompiler, strictConfig);
2586
- break;
2587
- case 'serve':
2588
- await taskServe(strictConfig);
2589
- break;
2590
- case 'telemetry':
2591
- await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
2592
- break;
2593
- case 'test':
2594
- await taskTest(strictConfig);
2595
- break;
2596
- case 'version':
2597
- console.log(coreCompiler.version);
2598
- break;
2599
- default:
2600
- strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid rindo command, please see the options below:`);
2601
- await taskHelp(strictConfig.flags, strictConfig.logger, sys);
2602
- return config.sys.exit(1);
2603
- }
2384
+ const run = async (init) => {
2385
+ const { args, logger, sys } = init;
2386
+ try {
2387
+ const flags = parseFlags(args);
2388
+ const task = flags.task;
2389
+ if (flags.debug || flags.verbose) {
2390
+ logger.setLevel('debug');
2391
+ }
2392
+ if (flags.ci) {
2393
+ logger.enableColors(false);
2394
+ }
2395
+ if (isFunction(sys.applyGlobalPatch)) {
2396
+ sys.applyGlobalPatch(sys.getCurrentDirectory());
2397
+ }
2398
+ if (!task || task === 'help' || flags.help) {
2399
+ await taskHelp(createConfigFlags({ task: 'help', args }), logger, sys);
2400
+ return;
2401
+ }
2402
+ startupLog(logger, task);
2403
+ const findConfigResults = await findConfig({ sys, configPath: flags.config });
2404
+ if (hasError(findConfigResults.diagnostics)) {
2405
+ logger.printDiagnostics(findConfigResults.diagnostics);
2406
+ return sys.exit(1);
2407
+ }
2408
+ const coreCompiler = await loadCoreCompiler(sys);
2409
+ if (task === 'version' || flags.version) {
2410
+ console.log(coreCompiler.version);
2411
+ return;
2412
+ }
2413
+ startupLogVersion(logger, task, coreCompiler);
2414
+ loadedCompilerLog(sys, logger, flags, coreCompiler);
2415
+ if (task === 'info') {
2416
+ taskInfo(coreCompiler, sys, logger);
2417
+ return;
2418
+ }
2419
+ const validated = await coreCompiler.loadConfig({
2420
+ config: {
2421
+ flags,
2422
+ },
2423
+ configPath: findConfigResults.configPath,
2424
+ logger,
2425
+ sys,
2426
+ });
2427
+ if (validated.diagnostics.length > 0) {
2428
+ logger.printDiagnostics(validated.diagnostics);
2429
+ if (hasError(validated.diagnostics)) {
2430
+ return sys.exit(1);
2431
+ }
2432
+ }
2433
+ if (isFunction(sys.applyGlobalPatch)) {
2434
+ sys.applyGlobalPatch(validated.config.rootDir);
2435
+ }
2436
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
2437
+ await runTask(coreCompiler, validated.config, task, sys);
2438
+ });
2439
+ }
2440
+ catch (e) {
2441
+ if (!shouldIgnoreError(e)) {
2442
+ const details = `${logger.getLevel() === 'debug' && e instanceof Error ? e.stack : ''}`;
2443
+ logger.error(`uncaught cli error: ${e}${details}`);
2444
+ return sys.exit(1);
2445
+ }
2446
+ }
2447
+ };
2448
+ /**
2449
+ * Run a specified task
2450
+ * @param coreCompiler an instance of a minimal, bootstrap compiler for running the specified task
2451
+ * @param config a configuration for the Rindo project to apply to the task run
2452
+ * @param task the task to run
2453
+ * @param sys the {@link CompilerSystem} for interacting with the operating system
2454
+ * @public
2455
+ */
2456
+ const runTask = async (coreCompiler, config, task, sys) => {
2457
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2458
+ const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2459
+ const rootDir = (_b = config.rootDir) !== null && _b !== void 0 ? _b : '/';
2460
+ const configSys = (_c = sys !== null && sys !== void 0 ? sys : config.sys) !== null && _c !== void 0 ? _c : coreCompiler.createSystem({ logger });
2461
+ const strictConfig = {
2462
+ ...config,
2463
+ flags: createConfigFlags((_d = config.flags) !== null && _d !== void 0 ? _d : { task }),
2464
+ hydratedFlag: (_e = config.hydratedFlag) !== null && _e !== void 0 ? _e : null,
2465
+ logger,
2466
+ outputTargets: (_f = config.outputTargets) !== null && _f !== void 0 ? _f : [],
2467
+ packageJsonFilePath: configSys.platformPath.join(rootDir, 'package.json'),
2468
+ rootDir,
2469
+ sys: configSys,
2470
+ testing: (_g = config.testing) !== null && _g !== void 0 ? _g : {},
2471
+ transformAliasedImportPaths: (_h = config.transformAliasedImportPaths) !== null && _h !== void 0 ? _h : false,
2472
+ };
2473
+ switch (task) {
2474
+ case 'build':
2475
+ await taskBuild(coreCompiler, strictConfig);
2476
+ break;
2477
+ case 'docs':
2478
+ await taskDocs(coreCompiler, strictConfig);
2479
+ break;
2480
+ case 'generate':
2481
+ case 'g':
2482
+ await taskGenerate(coreCompiler, strictConfig);
2483
+ break;
2484
+ case 'help':
2485
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
2486
+ break;
2487
+ case 'prerender':
2488
+ await taskPrerender(coreCompiler, strictConfig);
2489
+ break;
2490
+ case 'serve':
2491
+ await taskServe(strictConfig);
2492
+ break;
2493
+ case 'telemetry':
2494
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
2495
+ break;
2496
+ case 'test':
2497
+ await taskTest(strictConfig);
2498
+ break;
2499
+ case 'version':
2500
+ console.log(coreCompiler.version);
2501
+ break;
2502
+ default:
2503
+ strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid rindo command, please see the options below:`);
2504
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
2505
+ return config.sys.exit(1);
2506
+ }
2604
2507
  };
2605
2508
 
2606
2509
  exports.parseFlags = parseFlags;