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