@emulsify/core 4.2.1 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main-vite.js +102 -37
- package/.storybook/main.js +20 -0
- package/.storybook/ready-reporter.js +230 -0
- package/README.md +186 -56
- package/config/a11y.config.js +2 -1
- package/config/vite/entries.js +4 -4
- package/config/vite/plugins/assets/copy-src-assets.js +120 -0
- package/config/vite/plugins/assets/copy-twig-files.js +130 -0
- 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 +367 -0
- package/config/vite/plugins/reporter/format.js +240 -0
- package/config/vite/plugins/reporter/index.js +417 -0
- package/config/vite/plugins/reporter/render.js +1332 -0
- package/config/vite/plugins/reporter/sass-logger.js +237 -0
- package/config/vite/plugins/reporter/source-roots.js +561 -0
- package/config/vite/plugins/reporter/verbosity.js +119 -0
- package/config/vite/plugins/reporter/vite-logger.js +249 -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} +197 -85
- 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 +91 -5
- package/package.json +95 -57
- 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/setup.js +12 -2
- package/src/storybook/twig/source-function.js +5 -2
- package/config/vite/plugins/copy-src-assets.js +0 -76
- package/config/vite/plugins/copy-twig-files.js +0 -84
- 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,249 @@
|
|
|
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
|
+
import { isQuiet, isVerbose } from './verbosity.js';
|
|
22
|
+
|
|
23
|
+
// Re-exported because the Vite config and the reporter both branch on it, and
|
|
24
|
+
// this module was where it lived before verbosity grew a third level.
|
|
25
|
+
export { isVerbose };
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Matches Vite's unresolved CSS asset notice.
|
|
29
|
+
*
|
|
30
|
+
* @type {RegExp}
|
|
31
|
+
* @see https://github.com/vitejs/vite - `vite:css` url replacer
|
|
32
|
+
*/
|
|
33
|
+
const UNRESOLVED_ASSET_PATTERN =
|
|
34
|
+
/^(.+?) referenced in (.+?) didn't resolve at build time/;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Remove ANSI escape sequences so pattern matching sees plain text.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} value - Possibly styled text.
|
|
40
|
+
* @returns {string} Plain text.
|
|
41
|
+
*/
|
|
42
|
+
const stripAnsi = (value) =>
|
|
43
|
+
// eslint-disable-next-line no-control-regex
|
|
44
|
+
String(value).replace(/\[[0-9;]*m/g, '');
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Matches Rolldown's raw failure dump.
|
|
48
|
+
*
|
|
49
|
+
* The dump repeats every error up to three times — message, cause, and stack —
|
|
50
|
+
* and appends a Dart Sass JS trace that points into `sass.dart.js` rather than
|
|
51
|
+
* anywhere in the project.
|
|
52
|
+
*
|
|
53
|
+
* @type {RegExp}
|
|
54
|
+
*/
|
|
55
|
+
const RAW_BUILD_DUMP =
|
|
56
|
+
/^\s*(?:Build failed with \d+ error|\[plugin [\w:-]+\]|Error: \[sass\]|\[sass\] )/;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Matches a Dart Sass JS stack frame.
|
|
60
|
+
*
|
|
61
|
+
* These point into the compiler bundle rather than the project and are noise
|
|
62
|
+
* wherever they appear, so their presence alone identifies a raw dump.
|
|
63
|
+
*
|
|
64
|
+
* @type {RegExp}
|
|
65
|
+
*/
|
|
66
|
+
const DART_SASS_FRAME = /\bsass\.dart\.js:\d+/;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Detect a message that is nothing but JavaScript stack frames.
|
|
70
|
+
*
|
|
71
|
+
* Rolldown emits the trailing stack of a failure as its own log call. Those
|
|
72
|
+
* frames point into `lightningcss`, `vite`, or `sass` internals rather than
|
|
73
|
+
* anywhere in the project, so a message made only of them carries nothing once
|
|
74
|
+
* the failure itself has been reported.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} message - Plain-text log message.
|
|
77
|
+
* @returns {boolean} TRUE when every line is a stack frame.
|
|
78
|
+
*/
|
|
79
|
+
function isBareStackTrace(message) {
|
|
80
|
+
const lines = message.split('\n').filter((line) => line.trim());
|
|
81
|
+
|
|
82
|
+
return lines.length > 0 && lines.every((line) => /^\s*at\s+\S/.test(line));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Matches Vite's HMR notice.
|
|
87
|
+
*
|
|
88
|
+
* Emitted by the dev server as `hmr update <files>` through `logger.info`.
|
|
89
|
+
*
|
|
90
|
+
* @type {RegExp}
|
|
91
|
+
*/
|
|
92
|
+
const HMR_UPDATE_PATTERN = /(^|\s)hmr update\s/;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Wrap the Storybook dev server's logger to drop HMR notices.
|
|
96
|
+
*
|
|
97
|
+
* These come from Storybook's Vite dev server, not from the watch build, and
|
|
98
|
+
* there are a lot of them. The watch build rewrites every file in `dist/` on
|
|
99
|
+
* every cycle — Rollup regenerates the whole bundle — and Storybook imports its
|
|
100
|
+
* compiled CSS from `dist/`, so one saved stylesheet lands as several HMR events.
|
|
101
|
+
* Most name Storybook's own virtual modules:
|
|
102
|
+
*
|
|
103
|
+
* │ Vite hmr update
|
|
104
|
+
* │ /@id/__x00__virtual:/@storybook/builder-vite/project-annotations.js,
|
|
105
|
+
* │ /@id/__x00__virtual:/@storybook/builder-vite/vite-app.js
|
|
106
|
+
*
|
|
107
|
+
* Nothing there is actionable, and the reporter has already printed the rebuild
|
|
108
|
+
* line that says the same thing more precisely. Under `concurrently` both
|
|
109
|
+
* processes share one pipe, so these interleave with the build's output and are
|
|
110
|
+
* the last thing making one command look like two.
|
|
111
|
+
*
|
|
112
|
+
* The wrapper delegates to whatever logger is already configured rather than
|
|
113
|
+
* replacing it, so Storybook keeps its own prefixes and styling for every other
|
|
114
|
+
* message. Verbose modes pass everything through, because someone who asked for
|
|
115
|
+
* more output should not have this filtered away.
|
|
116
|
+
*
|
|
117
|
+
* @param {{
|
|
118
|
+
* baseLogger: import('vite').Logger,
|
|
119
|
+
* verbose?: boolean
|
|
120
|
+
* }} options - Logger options.
|
|
121
|
+
* @returns {import('vite').Logger} Wrapped logger.
|
|
122
|
+
*/
|
|
123
|
+
export function createDevServerLogger({ baseLogger, verbose } = {}) {
|
|
124
|
+
const passThrough = verbose === undefined ? !isQuiet() : verbose;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
get hasWarned() {
|
|
128
|
+
return baseLogger.hasWarned;
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
set hasWarned(value) {
|
|
132
|
+
baseLogger.hasWarned = value;
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
info(message, options) {
|
|
136
|
+
if (!passThrough && HMR_UPDATE_PATTERN.test(stripAnsi(String(message)))) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
baseLogger.info(message, options);
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
warn: (message, options) => baseLogger.warn(message, options),
|
|
143
|
+
warnOnce: (message, options) => baseLogger.warnOnce(message, options),
|
|
144
|
+
error: (message, options) => baseLogger.error(message, options),
|
|
145
|
+
clearScreen: (type) => baseLogger.clearScreen(type),
|
|
146
|
+
hasErrorLogged: (error) => baseLogger.hasErrorLogged(error),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Parse Vite's unresolved CSS asset notice.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} message - Raw log message.
|
|
154
|
+
* @returns {{url: string, importer: string|undefined}|undefined} Parsed notice.
|
|
155
|
+
*/
|
|
156
|
+
export function parseUnresolvedAsset(message) {
|
|
157
|
+
if (typeof message !== 'string') return undefined;
|
|
158
|
+
|
|
159
|
+
const match = UNRESOLVED_ASSET_PATTERN.exec(stripAnsi(message).trim());
|
|
160
|
+
if (!match) return undefined;
|
|
161
|
+
|
|
162
|
+
const [, url, importer] = match;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
url,
|
|
166
|
+
// Vite reports the URL as its own importer when the referencing stylesheet
|
|
167
|
+
// is not known. Recording that adds nothing, so it is dropped.
|
|
168
|
+
importer: importer === url ? undefined : importer,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Wrap a Vite logger so reportable diagnostics are collected instead of printed.
|
|
174
|
+
*
|
|
175
|
+
* The wrapper delegates rather than spreading, because `hasWarned` is a mutable
|
|
176
|
+
* property that Vite reads back after logging; a spread copy would freeze it.
|
|
177
|
+
*
|
|
178
|
+
* @param {ReturnType<import('./diagnostics.js').createDiagnosticsCollector>} collector - Shared collector.
|
|
179
|
+
* @param {import('vite').Logger} baseLogger - Logger to delegate to.
|
|
180
|
+
* @returns {import('vite').Logger} Wrapped logger.
|
|
181
|
+
*/
|
|
182
|
+
export function createReporterLogger(collector, baseLogger, { verbose } = {}) {
|
|
183
|
+
const passRawThrough = verbose === undefined ? isVerbose() : verbose;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Record a message if it is one the reporter owns.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} message - Raw log message.
|
|
189
|
+
* @returns {boolean} TRUE when the message was captured.
|
|
190
|
+
*/
|
|
191
|
+
const capture = (message) => {
|
|
192
|
+
const unresolvedAsset = parseUnresolvedAsset(message);
|
|
193
|
+
if (!unresolvedAsset) return false;
|
|
194
|
+
|
|
195
|
+
collector.recordUnresolvedAsset(unresolvedAsset);
|
|
196
|
+
return true;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Determine whether a failure dump has already been reported in table form.
|
|
201
|
+
*
|
|
202
|
+
* Output is only ever dropped once the reporter holds the same information
|
|
203
|
+
* in a readable shape; an error it failed to parse still reaches the user.
|
|
204
|
+
*
|
|
205
|
+
* @param {string} message - Raw log message.
|
|
206
|
+
* @returns {boolean} TRUE when the dump is redundant.
|
|
207
|
+
*/
|
|
208
|
+
const isRedundantDump = (message) => {
|
|
209
|
+
if (passRawThrough) return false;
|
|
210
|
+
if (!collector.hasCapturedBuildErrors?.()) return false;
|
|
211
|
+
|
|
212
|
+
const plain = stripAnsi(String(message));
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
RAW_BUILD_DUMP.test(plain) ||
|
|
216
|
+
DART_SASS_FRAME.test(plain) ||
|
|
217
|
+
isBareStackTrace(plain)
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
get hasWarned() {
|
|
223
|
+
return baseLogger.hasWarned;
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
set hasWarned(value) {
|
|
227
|
+
baseLogger.hasWarned = value;
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
info: (message, options) => baseLogger.info(message, options),
|
|
231
|
+
|
|
232
|
+
warn(message, options) {
|
|
233
|
+
if (!capture(message)) baseLogger.warn(message, options);
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
warnOnce(message, options) {
|
|
237
|
+
if (!capture(message)) baseLogger.warnOnce(message, options);
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
error(message, options) {
|
|
241
|
+
if (isRedundantDump(message)) return;
|
|
242
|
+
baseLogger.error(message, options);
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
clearScreen: (type) => baseLogger.clearScreen(type),
|
|
246
|
+
|
|
247
|
+
hasErrorLogged: (error) => baseLogger.hasErrorLogged(error),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
@@ -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
|
}
|