@emulsify/core 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main-vite.js +91 -36
- package/README.md +185 -56
- package/config/a11y.config.js +2 -1
- package/config/vite/entries.js +4 -4
- package/config/vite/plugins/{copy-src-assets.js → assets/copy-src-assets.js} +1 -1
- package/config/vite/plugins/{copy-twig-files.js → assets/copy-twig-files.js} +1 -1
- package/config/vite/plugins/{css-asset-relativizer.js → assets/css-asset-relativizer.js} +9 -0
- package/config/vite/plugins/{mirror-components.js → assets/mirror-components.js} +2 -28
- package/config/vite/plugins/{source-file-index.js → assets/source-file-index.js} +1 -1
- package/config/vite/plugins/{svg-sprite.js → assets/svg-sprite.js} +2 -2
- package/config/vite/plugins/index.js +23 -14
- package/config/vite/plugins/reporter/asset-resolver.js +541 -0
- package/config/vite/plugins/reporter/build-errors.js +284 -0
- package/config/vite/plugins/reporter/diagnostics.js +366 -0
- package/config/vite/plugins/reporter/format.js +199 -0
- package/config/vite/plugins/reporter/index.js +308 -0
- package/config/vite/plugins/reporter/render.js +758 -0
- package/config/vite/plugins/reporter/sass-logger.js +237 -0
- package/config/vite/plugins/reporter/vite-logger.js +188 -0
- package/config/vite/plugins/reporter/watch-mode.js +40 -0
- package/config/vite/plugins/{twig-extension-installers.js → twig/extension-installers.js} +1 -1
- package/config/vite/{twig-extensions.js → plugins/twig/extensions.js} +2 -2
- package/config/vite/plugins/{twig-module.js → twig/twig-module.js} +387 -115
- package/config/vite/plugins/{virtual-twig-asset-sources.js → twig/virtual-twig-asset-sources.js} +37 -136
- package/config/vite/plugins/{virtual-twig-globs.js → twig/virtual-twig-globs.js} +3 -32
- package/config/vite/plugins/{vituum-patch.js → twig/vituum-patch.js} +3 -3
- package/config/vite/plugins.js +1 -1
- package/config/vite/project-config.js +1 -1
- package/config/vite/project-structure.js +1 -1
- package/config/vite/utils/lru.js +77 -0
- package/config/vite/utils/package-version.js +42 -0
- package/config/vite/utils/paths.js +1 -9
- package/config/vite/utils/react-singleton.js +1 -1
- package/config/vite/vite.config.js +51 -5
- package/package.json +93 -58
- package/scripts/a11y.js +115 -23
- package/scripts/audit/checks/core-imports.js +78 -0
- package/scripts/audit/checks/css-asset-references.js +99 -0
- package/scripts/audit/checks/drupal-assumptions.js +48 -0
- package/scripts/audit/checks/files-outside-roots.js +53 -0
- package/scripts/audit/checks/generated-package-scripts.js +113 -0
- package/scripts/audit/checks/legacy-twig-stories.js +33 -0
- package/scripts/audit/checks/package-overrides.js +91 -0
- package/scripts/audit/checks/project-config.js +71 -0
- package/scripts/audit/checks/story-discovery.js +35 -0
- package/scripts/audit/checks/twig-references.js +69 -0
- package/scripts/audit/checks/twig-volume.js +54 -0
- package/scripts/audit/checks/webpack-patterns.js +86 -0
- package/scripts/audit/index.js +177 -0
- package/scripts/audit/lib/css.js +165 -0
- package/scripts/audit/lib/files.js +168 -0
- package/scripts/audit/lib/findings.js +31 -0
- package/scripts/audit/lib/package-json.js +65 -0
- package/scripts/audit/lib/twig.js +227 -0
- package/scripts/audit/report.js +273 -0
- package/scripts/audit-twig-stories.js +115 -78
- package/scripts/audit.js +150 -1632
- package/scripts/check-node-version.js +136 -10
- package/scripts/inspect-components.js +456 -0
- package/scripts/lib/cli.js +179 -0
- package/scripts/lib/fs.js +31 -0
- package/scripts/lib/proc.js +78 -0
- package/scripts/lib/text.js +14 -0
- package/scripts/loadYaml.js +2 -2
- package/src/extensions/shared/attributes.js +3 -3
- package/src/extensions/shared/lists.js +2 -6
- package/src/extensions/shared/root-relative.js +38 -0
- package/src/storybook/index.js +4 -0
- package/src/storybook/render-twig.js +1 -1
- package/src/storybook/render-web-component.js +459 -0
- package/src/storybook/twig/asset-source-runtime.js +193 -0
- package/src/storybook/twig/{source-extensions.js → constants.js} +3 -1
- package/src/storybook/twig/reference-paths.js +2 -13
- package/src/storybook/twig/resolver.js +20 -3
- package/src/storybook/twig/setup.js +12 -2
- package/src/storybook/twig/source-function.js +5 -2
- package/config/vite/utils/unique.js +0 -36
- package/src/storybook/twig/include.js +0 -28
- package/src/storybook/twig/source-events.js +0 -5
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Sass logger that feeds the Emulsify develop reporter.
|
|
3
|
+
*
|
|
4
|
+
* Dart Sass prints a fully formatted warning — message, source excerpt, caret,
|
|
5
|
+
* and import chain — for every deprecation it encounters, every time it
|
|
6
|
+
* encounters it. A project with a handful of legacy `$space/2` divisions in a
|
|
7
|
+
* shared partial can emit several hundred lines per build, because each entry
|
|
8
|
+
* that imports the partial re-triggers the same warning.
|
|
9
|
+
*
|
|
10
|
+
* Supplying a custom logger replaces that output entirely. Rather than calling
|
|
11
|
+
* `silenceDeprecations`, which hides the warnings and the fact that they exist,
|
|
12
|
+
* this logger routes each report into the shared diagnostics collector so the
|
|
13
|
+
* reporter can print one deduplicated summary with a migration hint. The debt
|
|
14
|
+
* stays visible; only the repetition goes away.
|
|
15
|
+
*
|
|
16
|
+
* @see https://sass-lang.com/documentation/js-api/interfaces/logger/
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* What each Sass deprecation means and how to resolve it.
|
|
23
|
+
*
|
|
24
|
+
* `fix` is the substitution to make, written as `before → after` so the row
|
|
25
|
+
* reads as an instruction rather than an identifier. A deprecation ID like
|
|
26
|
+
* `slash-div` tells a themer nothing on its own; `$a/$b → math.div($a, $b)`
|
|
27
|
+
* tells them exactly what to type.
|
|
28
|
+
*
|
|
29
|
+
* `migrator` names the `sass-migrator` migration that rewrites the code
|
|
30
|
+
* automatically, and is omitted for deprecations that have to be fixed by hand.
|
|
31
|
+
* The migrator only ships five migrations, so most IDs here have no entry.
|
|
32
|
+
*
|
|
33
|
+
* @type {Record<string, {fix: string, migrator?: string}>}
|
|
34
|
+
* @see https://sass-lang.com/documentation/cli/migrator/
|
|
35
|
+
*/
|
|
36
|
+
export const DEPRECATION_GUIDE = {
|
|
37
|
+
'slash-div': {
|
|
38
|
+
fix: '$a/$b → math.div($a, $b)',
|
|
39
|
+
migrator: 'division',
|
|
40
|
+
},
|
|
41
|
+
'global-builtin': {
|
|
42
|
+
fix: 'map-get() → map.get()',
|
|
43
|
+
migrator: 'module',
|
|
44
|
+
},
|
|
45
|
+
import: {
|
|
46
|
+
fix: '@import → @use',
|
|
47
|
+
migrator: 'module',
|
|
48
|
+
},
|
|
49
|
+
'color-functions': {
|
|
50
|
+
fix: 'lighten()/darken() → color.adjust()',
|
|
51
|
+
migrator: 'color',
|
|
52
|
+
},
|
|
53
|
+
'if-function': {
|
|
54
|
+
fix: 'if() → CSS if()',
|
|
55
|
+
migrator: 'if',
|
|
56
|
+
},
|
|
57
|
+
'color-4-api': { fix: 'color.red($c) → color.channel($c, "red")' },
|
|
58
|
+
'legacy-js-api': { fix: 'render() → compile()' },
|
|
59
|
+
'mixed-decls': { fix: 'move declarations above nested rules' },
|
|
60
|
+
'strict-unary': { fix: '$a -$b → $a - $b' },
|
|
61
|
+
'abs-percent': { fix: 'abs(10%) → math.abs(10%)' },
|
|
62
|
+
'duplicate-var-flags': { fix: 'remove the repeated !default or !global' },
|
|
63
|
+
'null-alpha': { fix: 'rgb($c, null) → rgb($c)' },
|
|
64
|
+
'feature-exists': { fix: 'remove meta.feature-exists()' },
|
|
65
|
+
'moz-document': { fix: 'remove @-moz-document' },
|
|
66
|
+
'bogus-combinators': { fix: 'remove the dangling combinator' },
|
|
67
|
+
elseif: { fix: '@elseif → @else if' },
|
|
68
|
+
'call-string': { fix: 'call($name) → call(get-function($name))' },
|
|
69
|
+
'new-global': { fix: 'declare the variable before assigning it !global' },
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Look up the human-readable fix for a deprecation ID.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} id - Sass deprecation ID.
|
|
76
|
+
* @returns {string|undefined} Fix instruction, when one is known.
|
|
77
|
+
*/
|
|
78
|
+
export function deprecationFix(id) {
|
|
79
|
+
return DEPRECATION_GUIDE[id]?.fix;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Look up the `sass-migrator` migration that resolves a deprecation ID.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} id - Sass deprecation ID.
|
|
86
|
+
* @returns {string|undefined} Migration name, when one exists.
|
|
87
|
+
*/
|
|
88
|
+
export function deprecationMigrator(id) {
|
|
89
|
+
return DEPRECATION_GUIDE[id]?.migrator;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Convert a Sass span URL into a readable filesystem path.
|
|
94
|
+
*
|
|
95
|
+
* Spans carry a `URL` for on-disk stylesheets, but string URLs and custom
|
|
96
|
+
* importer schemes both appear in practice, so every branch is guarded.
|
|
97
|
+
*
|
|
98
|
+
* @param {URL|string|undefined} url - Span URL.
|
|
99
|
+
* @returns {string|undefined} Filesystem path, or undefined when unavailable.
|
|
100
|
+
*/
|
|
101
|
+
export function spanUrlToPath(url) {
|
|
102
|
+
if (!url) return undefined;
|
|
103
|
+
|
|
104
|
+
const href = typeof url === 'string' ? url : url.href;
|
|
105
|
+
if (!href) return undefined;
|
|
106
|
+
if (!href.startsWith('file:')) return href;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
return fileURLToPath(href);
|
|
110
|
+
} catch {
|
|
111
|
+
return href;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Read the 1-based line number from a Sass span.
|
|
117
|
+
*
|
|
118
|
+
* Sass reports zero-based line numbers; editors and the rest of the Emulsify
|
|
119
|
+
* tooling are one-based.
|
|
120
|
+
*
|
|
121
|
+
* @param {{start?: {line?: number}}|undefined} span - Sass source span.
|
|
122
|
+
* @returns {number|undefined} 1-based line number, when available.
|
|
123
|
+
*/
|
|
124
|
+
export function spanLineNumber(span) {
|
|
125
|
+
const line = span?.start?.line;
|
|
126
|
+
return typeof line === 'number' ? line + 1 : undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Reduce a multi-line Sass message to its first meaningful line.
|
|
131
|
+
*
|
|
132
|
+
* @param {string|undefined} message - Raw Sass message.
|
|
133
|
+
* @returns {string|undefined} Condensed message.
|
|
134
|
+
*/
|
|
135
|
+
export function condenseMessage(message) {
|
|
136
|
+
if (typeof message !== 'string') return undefined;
|
|
137
|
+
const [firstLine] = message.split('\n');
|
|
138
|
+
return firstLine?.trim() || undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Detect Dart Sass's own truncation notice.
|
|
143
|
+
*
|
|
144
|
+
* Without `verbose`, Sass prints at most five instances of each deprecation and
|
|
145
|
+
* then emits `"N repetitive deprecation warnings omitted."` as a warning with no
|
|
146
|
+
* span attached. That notice is not a problem in the stylesheet — it is Sass
|
|
147
|
+
* reporting that it hid some. Recording it would put a locationless row in the
|
|
148
|
+
* summary and, worse, imply the reporter's totals are complete when they are
|
|
149
|
+
* short by exactly the number Sass suppressed.
|
|
150
|
+
*
|
|
151
|
+
* {@link createSassOptions} sets `verbose` so the notice should never appear,
|
|
152
|
+
* but a project that overrides `preprocessorOptions` could reintroduce it.
|
|
153
|
+
*
|
|
154
|
+
* @param {string|undefined} message - Raw Sass message.
|
|
155
|
+
* @returns {boolean} TRUE when the message is a truncation notice.
|
|
156
|
+
*/
|
|
157
|
+
export function isRepetitionNotice(message) {
|
|
158
|
+
if (typeof message !== 'string') return false;
|
|
159
|
+
return /^\s*\d+\s+repetitive\s+deprecation\s+warnings?\s+omitted\b/i.test(
|
|
160
|
+
message,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Create a Sass logger that records into a diagnostics collector.
|
|
166
|
+
*
|
|
167
|
+
* @param {ReturnType<import('./diagnostics.js').createDiagnosticsCollector>} collector - Shared collector.
|
|
168
|
+
* @param {{passthrough?: (message: string, options: object) => void}} [options] - Logger options.
|
|
169
|
+
* @returns {{warn: Function, debug: Function}} Sass logger.
|
|
170
|
+
*/
|
|
171
|
+
export function createSassLogger(collector, { passthrough } = {}) {
|
|
172
|
+
return {
|
|
173
|
+
/**
|
|
174
|
+
* Record a Sass warning instead of printing it.
|
|
175
|
+
*
|
|
176
|
+
* @param {string} message - Warning message.
|
|
177
|
+
* @param {object} [options] - Sass warning metadata.
|
|
178
|
+
* @returns {void}
|
|
179
|
+
*/
|
|
180
|
+
warn(message, options = {}) {
|
|
181
|
+
// Sass counting its own suppressed warnings is not a finding to report.
|
|
182
|
+
if (isRepetitionNotice(message)) return;
|
|
183
|
+
|
|
184
|
+
const file = spanUrlToPath(options.span?.url);
|
|
185
|
+
const line = spanLineNumber(options.span);
|
|
186
|
+
|
|
187
|
+
if (options.deprecation) {
|
|
188
|
+
collector.recordDeprecation({
|
|
189
|
+
id: options.deprecationType?.id,
|
|
190
|
+
file,
|
|
191
|
+
line,
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
collector.recordWarning({
|
|
195
|
+
message: condenseMessage(message),
|
|
196
|
+
file,
|
|
197
|
+
line,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Projects that want the original firehose back can opt in per build.
|
|
202
|
+
if (typeof passthrough === 'function') {
|
|
203
|
+
passthrough(message, options);
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Discard Sass debug output.
|
|
209
|
+
*
|
|
210
|
+
* `@debug` is a authoring aid and has no place in the develop summary.
|
|
211
|
+
*
|
|
212
|
+
* @returns {void}
|
|
213
|
+
*/
|
|
214
|
+
debug() {},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Build the Sass preprocessor options used by the develop watcher.
|
|
220
|
+
*
|
|
221
|
+
* `verbose` and the custom logger have to travel together. Dart Sass caps each
|
|
222
|
+
* deprecation at five reported instances by default, so a reporter that counts
|
|
223
|
+
* what the logger receives would silently undercount — a project with 135 real
|
|
224
|
+
* occurrences would be told it had 85. Turning `verbose` on hands every
|
|
225
|
+
* occurrence to the logger, which then deduplicates them properly. The console
|
|
226
|
+
* stays quiet either way, because the logger prints nothing.
|
|
227
|
+
*
|
|
228
|
+
* @param {ReturnType<import('./diagnostics.js').createDiagnosticsCollector>} collector - Shared collector.
|
|
229
|
+
* @param {{passthrough?: (message: string, options: object) => void}} [options] - Logger options.
|
|
230
|
+
* @returns {{logger: {warn: Function, debug: Function}, verbose: boolean}} Sass options.
|
|
231
|
+
*/
|
|
232
|
+
export function createSassOptions(collector, options = {}) {
|
|
233
|
+
return {
|
|
234
|
+
logger: createSassLogger(collector, options),
|
|
235
|
+
verbose: true,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Vite logger wrapper for the Emulsify develop reporter.
|
|
3
|
+
*
|
|
4
|
+
* Sass warnings reach the reporter through a Sass logger, but Vite emits its
|
|
5
|
+
* own diagnostics through `config.logger`. The most common of those in an
|
|
6
|
+
* Emulsify project is the unresolved CSS asset notice:
|
|
7
|
+
*
|
|
8
|
+
* ../images/bg-lines.png referenced in ../images/bg-lines.png didn't resolve
|
|
9
|
+
* at build time, it will remain unchanged to be resolved at runtime
|
|
10
|
+
*
|
|
11
|
+
* Vite emits one per `url()` it cannot resolve, mid-build, so they land
|
|
12
|
+
* interleaved with transform progress and Storybook's startup. The wording is
|
|
13
|
+
* also unhelpful: the "referenced in" file is frequently identical to the URL
|
|
14
|
+
* itself, and nothing in the sentence says whether this is a problem.
|
|
15
|
+
*
|
|
16
|
+
* This wrapper intercepts those and routes them into the shared diagnostics
|
|
17
|
+
* collector so they surface once, grouped, in the build summary. Every other
|
|
18
|
+
* message passes straight through to Vite's own logger untouched.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Matches Vite's unresolved CSS asset notice.
|
|
23
|
+
*
|
|
24
|
+
* @type {RegExp}
|
|
25
|
+
* @see https://github.com/vitejs/vite - `vite:css` url replacer
|
|
26
|
+
*/
|
|
27
|
+
const UNRESOLVED_ASSET_PATTERN =
|
|
28
|
+
/^(.+?) referenced in (.+?) didn't resolve at build time/;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Remove ANSI escape sequences so pattern matching sees plain text.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} value - Possibly styled text.
|
|
34
|
+
* @returns {string} Plain text.
|
|
35
|
+
*/
|
|
36
|
+
const stripAnsi = (value) =>
|
|
37
|
+
// eslint-disable-next-line no-control-regex
|
|
38
|
+
String(value).replace(/\[[0-9;]*m/g, '');
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Matches Rolldown's raw failure dump.
|
|
42
|
+
*
|
|
43
|
+
* The dump repeats every error up to three times — message, cause, and stack —
|
|
44
|
+
* and appends a Dart Sass JS trace that points into `sass.dart.js` rather than
|
|
45
|
+
* anywhere in the project.
|
|
46
|
+
*
|
|
47
|
+
* @type {RegExp}
|
|
48
|
+
*/
|
|
49
|
+
const RAW_BUILD_DUMP =
|
|
50
|
+
/^\s*(?:Build failed with \d+ error|\[plugin [\w:-]+\]|Error: \[sass\]|\[sass\] )/;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Matches a Dart Sass JS stack frame.
|
|
54
|
+
*
|
|
55
|
+
* These point into the compiler bundle rather than the project and are noise
|
|
56
|
+
* wherever they appear, so their presence alone identifies a raw dump.
|
|
57
|
+
*
|
|
58
|
+
* @type {RegExp}
|
|
59
|
+
*/
|
|
60
|
+
const DART_SASS_FRAME = /\bsass\.dart\.js:\d+/;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detect a message that is nothing but JavaScript stack frames.
|
|
64
|
+
*
|
|
65
|
+
* Rolldown emits the trailing stack of a failure as its own log call. Those
|
|
66
|
+
* frames point into `lightningcss`, `vite`, or `sass` internals rather than
|
|
67
|
+
* anywhere in the project, so a message made only of them carries nothing once
|
|
68
|
+
* the failure itself has been reported.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} message - Plain-text log message.
|
|
71
|
+
* @returns {boolean} TRUE when every line is a stack frame.
|
|
72
|
+
*/
|
|
73
|
+
function isBareStackTrace(message) {
|
|
74
|
+
const lines = message.split('\n').filter((line) => line.trim());
|
|
75
|
+
|
|
76
|
+
return lines.length > 0 && lines.every((line) => /^\s*at\s+\S/.test(line));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Determine whether the reporter should stand aside and let Vite speak.
|
|
81
|
+
*
|
|
82
|
+
* @param {object} [env] - Environment variables.
|
|
83
|
+
* @returns {boolean} TRUE when raw output should pass through.
|
|
84
|
+
*/
|
|
85
|
+
export function isVerbose(env = process.env) {
|
|
86
|
+
return Boolean(env.EMULSIFY_VERBOSE) && env.EMULSIFY_VERBOSE !== '0';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parse Vite's unresolved CSS asset notice.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} message - Raw log message.
|
|
93
|
+
* @returns {{url: string, importer: string|undefined}|undefined} Parsed notice.
|
|
94
|
+
*/
|
|
95
|
+
export function parseUnresolvedAsset(message) {
|
|
96
|
+
if (typeof message !== 'string') return undefined;
|
|
97
|
+
|
|
98
|
+
const match = UNRESOLVED_ASSET_PATTERN.exec(stripAnsi(message).trim());
|
|
99
|
+
if (!match) return undefined;
|
|
100
|
+
|
|
101
|
+
const [, url, importer] = match;
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
url,
|
|
105
|
+
// Vite reports the URL as its own importer when the referencing stylesheet
|
|
106
|
+
// is not known. Recording that adds nothing, so it is dropped.
|
|
107
|
+
importer: importer === url ? undefined : importer,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Wrap a Vite logger so reportable diagnostics are collected instead of printed.
|
|
113
|
+
*
|
|
114
|
+
* The wrapper delegates rather than spreading, because `hasWarned` is a mutable
|
|
115
|
+
* property that Vite reads back after logging; a spread copy would freeze it.
|
|
116
|
+
*
|
|
117
|
+
* @param {ReturnType<import('./diagnostics.js').createDiagnosticsCollector>} collector - Shared collector.
|
|
118
|
+
* @param {import('vite').Logger} baseLogger - Logger to delegate to.
|
|
119
|
+
* @returns {import('vite').Logger} Wrapped logger.
|
|
120
|
+
*/
|
|
121
|
+
export function createReporterLogger(collector, baseLogger, { verbose } = {}) {
|
|
122
|
+
const passRawThrough = verbose === undefined ? isVerbose() : verbose;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Record a message if it is one the reporter owns.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} message - Raw log message.
|
|
128
|
+
* @returns {boolean} TRUE when the message was captured.
|
|
129
|
+
*/
|
|
130
|
+
const capture = (message) => {
|
|
131
|
+
const unresolvedAsset = parseUnresolvedAsset(message);
|
|
132
|
+
if (!unresolvedAsset) return false;
|
|
133
|
+
|
|
134
|
+
collector.recordUnresolvedAsset(unresolvedAsset);
|
|
135
|
+
return true;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Determine whether a failure dump has already been reported in table form.
|
|
140
|
+
*
|
|
141
|
+
* Output is only ever dropped once the reporter holds the same information
|
|
142
|
+
* in a readable shape; an error it failed to parse still reaches the user.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} message - Raw log message.
|
|
145
|
+
* @returns {boolean} TRUE when the dump is redundant.
|
|
146
|
+
*/
|
|
147
|
+
const isRedundantDump = (message) => {
|
|
148
|
+
if (passRawThrough) return false;
|
|
149
|
+
if (!collector.hasCapturedBuildErrors?.()) return false;
|
|
150
|
+
|
|
151
|
+
const plain = stripAnsi(String(message));
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
RAW_BUILD_DUMP.test(plain) ||
|
|
155
|
+
DART_SASS_FRAME.test(plain) ||
|
|
156
|
+
isBareStackTrace(plain)
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
get hasWarned() {
|
|
162
|
+
return baseLogger.hasWarned;
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
set hasWarned(value) {
|
|
166
|
+
baseLogger.hasWarned = value;
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
info: (message, options) => baseLogger.info(message, options),
|
|
170
|
+
|
|
171
|
+
warn(message, options) {
|
|
172
|
+
if (!capture(message)) baseLogger.warn(message, options);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
warnOnce(message, options) {
|
|
176
|
+
if (!capture(message)) baseLogger.warnOnce(message, options);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
error(message, options) {
|
|
180
|
+
if (isRedundantDump(message)) return;
|
|
181
|
+
baseLogger.error(message, options);
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
clearScreen: (type) => baseLogger.clearScreen(type),
|
|
185
|
+
|
|
186
|
+
hasErrorLogged: (error) => baseLogger.hasErrorLogged(error),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Watch-invocation detection for the Emulsify develop reporter.
|
|
3
|
+
*
|
|
4
|
+
* The reporter and its quiet Sass logger should only take over output for the
|
|
5
|
+
* long-running watcher that `npm run develop` starts. Every other command that
|
|
6
|
+
* loads this Vite config — `npm run build`, `storybook build`, `storybook dev`,
|
|
7
|
+
* and the release fixture verifications — must keep its current output exactly
|
|
8
|
+
* as it is today.
|
|
9
|
+
*
|
|
10
|
+
* Vite resolves `command: 'build'` for both `vite build` and
|
|
11
|
+
* `vite build --watch`, so the resolved config cannot distinguish them at the
|
|
12
|
+
* point where `css.preprocessorOptions` has to be decided. The CLI flag is the
|
|
13
|
+
* only signal available that early, so it is read directly. The plugin
|
|
14
|
+
* independently confirms the decision against `config.build.watch` once the
|
|
15
|
+
* config is resolved, which covers projects that enable watch mode through
|
|
16
|
+
* their own config patch rather than the flag.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Flags that put the Vite CLI into watch mode.
|
|
21
|
+
*
|
|
22
|
+
* @type {string[]}
|
|
23
|
+
*/
|
|
24
|
+
const WATCH_FLAGS = ['--watch', '-w'];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Determine whether the current process was invoked as a Vite watch build.
|
|
28
|
+
*
|
|
29
|
+
* @param {string[]} [argv] - Process arguments.
|
|
30
|
+
* @returns {boolean} TRUE when a watch flag is present.
|
|
31
|
+
*/
|
|
32
|
+
export function isWatchInvocation(argv = process.argv) {
|
|
33
|
+
if (!Array.isArray(argv)) return false;
|
|
34
|
+
|
|
35
|
+
return argv.some(
|
|
36
|
+
(arg) =>
|
|
37
|
+
WATCH_FLAGS.includes(arg) ||
|
|
38
|
+
WATCH_FLAGS.some((flag) => arg.startsWith(`${flag}=`)),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @file Virtual Twig.js extension installer module for Storybook.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { generateTwigExtensionInstallersModule } from '
|
|
5
|
+
import { generateTwigExtensionInstallersModule } from './extensions.js';
|
|
6
6
|
|
|
7
7
|
export const VIRTUAL_TWIG_EXTENSION_INSTALLERS_ID =
|
|
8
8
|
'virtual:emulsify-twig-extension-installers';
|
|
@@ -60,7 +60,7 @@ function normalizeInstaller(moduleValue) {
|
|
|
60
60
|
* @param {object} [env={}] - Normalized Emulsify environment or Twig options.
|
|
61
61
|
* @returns {object} The provided Twig.js instance.
|
|
62
62
|
*/
|
|
63
|
-
export function
|
|
63
|
+
export function installProjectTwigExtensions(twig, env = {}) {
|
|
64
64
|
if (shouldRegisterDrupalTwigFilters(env)) {
|
|
65
65
|
const installer = normalizeInstaller(require('twig-drupal-filters'));
|
|
66
66
|
if (installer) {
|
|
@@ -98,7 +98,7 @@ const installers = [${installerNames}].filter(
|
|
|
98
98
|
|
|
99
99
|
export const twigExtensionInstallers = installers;
|
|
100
100
|
|
|
101
|
-
export function
|
|
101
|
+
export function installProjectTwigExtensions(Twig) {
|
|
102
102
|
for (const installer of installers) {
|
|
103
103
|
installer(Twig);
|
|
104
104
|
}
|