@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
package/config/vite/plugins/{virtual-twig-asset-sources.js → twig/virtual-twig-asset-sources.js}
RENAMED
|
@@ -5,16 +5,26 @@
|
|
|
5
5
|
* resident unless a Twig template calls `source('@assets/...')`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { readdirSync } from 'fs';
|
|
8
|
+
import { readFileSync, readdirSync } from 'fs';
|
|
9
9
|
import { relative, resolve } from 'path';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { safeExists } from '../../utils/fs-safe.js';
|
|
12
|
+
import { toPosixPath } from '../../utils/paths.js';
|
|
13
|
+
import { unique } from '../../../../src/extensions/shared/lists.js';
|
|
14
|
+
import { toRootRelativePath } from '../../../../src/extensions/shared/root-relative.js';
|
|
15
|
+
import { INLINE_ASSET_EXTS } from '../../../../src/storybook/twig/constants.js';
|
|
14
16
|
|
|
15
17
|
export const VIRTUAL_TWIG_ASSET_SOURCES_ID =
|
|
16
18
|
'virtual:emulsify-twig-asset-sources';
|
|
17
19
|
const RESOLVED_VIRTUAL_TWIG_ASSET_SOURCES_ID = `\0${VIRTUAL_TWIG_ASSET_SOURCES_ID}`;
|
|
20
|
+
const VIRTUAL_TWIG_ASSET_SOURCE_RUNTIME_ID =
|
|
21
|
+
'virtual:emulsify-twig-asset-source-runtime';
|
|
22
|
+
const RESOLVED_VIRTUAL_TWIG_ASSET_SOURCE_RUNTIME_ID = `\0${VIRTUAL_TWIG_ASSET_SOURCE_RUNTIME_ID}`;
|
|
23
|
+
const ASSET_SOURCE_RUNTIME_URL = new URL(
|
|
24
|
+
'../../../../src/storybook/twig/asset-source-runtime.js',
|
|
25
|
+
import.meta.url,
|
|
26
|
+
);
|
|
27
|
+
const ASSET_SOURCE_RUNTIME_PATH = fileURLToPath(ASSET_SOURCE_RUNTIME_URL);
|
|
18
28
|
const GENERATED_ASSET_ALIASES = new Set(['icons.svg']);
|
|
19
29
|
const GENERATED_ASSET_ROOTS = ['/dist/assets'];
|
|
20
30
|
const PUBLIC_ASSET_ROOTS = new Map([
|
|
@@ -22,35 +32,6 @@ const PUBLIC_ASSET_ROOTS = new Map([
|
|
|
22
32
|
['/dist/assets', '/assets'],
|
|
23
33
|
]);
|
|
24
34
|
|
|
25
|
-
/**
|
|
26
|
-
* Convert an absolute project path to a Vite root-relative glob base.
|
|
27
|
-
*
|
|
28
|
-
* @param {string} projectDir - Absolute project root.
|
|
29
|
-
* @param {string} absolutePath - Absolute asset root path.
|
|
30
|
-
* @returns {string} Vite root-relative path.
|
|
31
|
-
*/
|
|
32
|
-
function toRootRelativePath(projectDir, absolutePath) {
|
|
33
|
-
if (!absolutePath) return '';
|
|
34
|
-
|
|
35
|
-
const normalizedProjectDir = toPosixPath(projectDir || '').replace(
|
|
36
|
-
/\/+$/,
|
|
37
|
-
'',
|
|
38
|
-
);
|
|
39
|
-
const normalizedPath = toPosixPath(absolutePath).replace(/\/+$/, '');
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
normalizedProjectDir &&
|
|
43
|
-
normalizedPath.startsWith(`${normalizedProjectDir}/`)
|
|
44
|
-
) {
|
|
45
|
-
return `/${normalizedPath.slice(normalizedProjectDir.length + 1)}`.replace(
|
|
46
|
-
/\/{2,}/g,
|
|
47
|
-
'/',
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return `/${normalizedPath.replace(/^\/+/, '')}`.replace(/\/{2,}/g, '/');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
35
|
/**
|
|
55
36
|
* Resolve a configured asset root to an absolute filesystem path.
|
|
56
37
|
*
|
|
@@ -275,11 +256,13 @@ export function generateVirtualTwigAssetSourcesModule(env) {
|
|
|
275
256
|
.join(',\n');
|
|
276
257
|
|
|
277
258
|
return `/**
|
|
278
|
-
* Virtual module generated by config/vite/plugins/virtual-twig-asset-sources.js.
|
|
259
|
+
* Virtual module generated by config/vite/plugins/twig/virtual-twig-asset-sources.js.
|
|
279
260
|
*
|
|
280
261
|
* Raw text assets stay lazy and load only when Twig source() requests them.
|
|
281
262
|
*/
|
|
282
263
|
|
|
264
|
+
import { createAssetSourceRuntime } from '${VIRTUAL_TWIG_ASSET_SOURCE_RUNTIME_ID}';
|
|
265
|
+
|
|
283
266
|
export const assetRootPrefixes = ${JSON.stringify(rootPrefixes)};
|
|
284
267
|
export const generatedAssetRootPrefixes = ${JSON.stringify(allGeneratedRootPrefixes)};
|
|
285
268
|
export const generatedAssetAliases = ${JSON.stringify(
|
|
@@ -299,107 +282,18 @@ ${globEntries}
|
|
|
299
282
|
${fetchEntries ? `${globEntries ? ',\n' : ''}{\n${fetchEntries}\n}` : ''}
|
|
300
283
|
]);
|
|
301
284
|
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
const rawPath = String(assetPath || '');
|
|
315
|
-
const normalized = normalizeAssetPath(rawPath);
|
|
316
|
-
const directPath = rawPath.startsWith('/') ? rawPath : \`/\${rawPath}\`;
|
|
317
|
-
const generatedCandidates = generatedAssetAliases.includes(normalized)
|
|
318
|
-
? generatedAssetRootPrefixes.map((root) =>
|
|
319
|
-
\`\${root.replace(/\\/+$/, '')}/\${normalized}\`,
|
|
320
|
-
)
|
|
321
|
-
: [];
|
|
322
|
-
|
|
323
|
-
return unique([
|
|
324
|
-
rawPath,
|
|
325
|
-
directPath,
|
|
326
|
-
normalized ? \`/\${normalized}\` : '',
|
|
327
|
-
...generatedCandidates,
|
|
328
|
-
...assetRootPrefixes.map((root) =>
|
|
329
|
-
\`\${root.replace(/\\/+$/, '')}/\${normalized}\`,
|
|
330
|
-
),
|
|
331
|
-
]);
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
const findAssetKey = (assetPath) =>
|
|
335
|
-
candidateKeysForAssetPath(assetPath).find((key) =>
|
|
336
|
-
Object.hasOwnProperty.call(assets, key),
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
const normalizeSourceText = (value) => {
|
|
340
|
-
const source = value?.default ?? value;
|
|
341
|
-
return typeof source === 'string' ? source : undefined;
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
export const coversAssetPath = (assetPath) =>
|
|
345
|
-
(assetRootPrefixes.length > 0 || generatedAssetRootPrefixes.length > 0) &&
|
|
346
|
-
normalizeAssetPath(assetPath).length > 0;
|
|
347
|
-
|
|
348
|
-
export const hasAssetText = (assetPath) => Boolean(findAssetKey(assetPath));
|
|
349
|
-
|
|
350
|
-
export const isAssetTextLoading = (assetPath) => {
|
|
351
|
-
const key = findAssetKey(assetPath);
|
|
352
|
-
return Boolean(key && sourceLoadPromises.has(key));
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
export const whenAssetTextLoaded = (assetPath) => {
|
|
356
|
-
const key = findAssetKey(assetPath);
|
|
357
|
-
return key ? sourceLoadPromises.get(key) : undefined;
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
export const getAssetText = (assetPath) => {
|
|
361
|
-
const key = findAssetKey(assetPath);
|
|
362
|
-
if (!key) return undefined;
|
|
363
|
-
if (sourceTextCache.has(key)) {
|
|
364
|
-
return sourceTextCache.get(key);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const loader = assets[key];
|
|
368
|
-
const sourceText = normalizeSourceText(loader);
|
|
369
|
-
if (typeof sourceText === 'string') {
|
|
370
|
-
sourceTextCache.set(key, sourceText);
|
|
371
|
-
return sourceText;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (typeof loader === 'function' && !sourceLoadPromises.has(key)) {
|
|
375
|
-
let loadedSource;
|
|
376
|
-
try {
|
|
377
|
-
loadedSource = loader();
|
|
378
|
-
} catch (error) {
|
|
379
|
-
loadedSource = Promise.reject(error);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const sourceLoad = Promise.resolve(loadedSource)
|
|
383
|
-
.then((loaded) => {
|
|
384
|
-
const loadedText = normalizeSourceText(loaded);
|
|
385
|
-
if (typeof loadedText === 'string') {
|
|
386
|
-
sourceTextCache.set(key, loadedText);
|
|
387
|
-
}
|
|
388
|
-
return loadedText;
|
|
389
|
-
})
|
|
390
|
-
.catch((error) => {
|
|
391
|
-
console.error(\`source(): failed to load asset \${key}\`, error);
|
|
392
|
-
return undefined;
|
|
393
|
-
})
|
|
394
|
-
.finally(() => {
|
|
395
|
-
sourceLoadPromises.delete(key);
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
sourceLoadPromises.set(key, sourceLoad);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return undefined;
|
|
402
|
-
};
|
|
285
|
+
const assetSourceRuntime = createAssetSourceRuntime({
|
|
286
|
+
assets,
|
|
287
|
+
assetRootPrefixes,
|
|
288
|
+
generatedAssetRootPrefixes,
|
|
289
|
+
generatedAssetAliases,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
export const coversAssetPath = assetSourceRuntime.coversAssetPath;
|
|
293
|
+
export const hasAssetText = assetSourceRuntime.hasAssetText;
|
|
294
|
+
export const isAssetTextLoading = assetSourceRuntime.isAssetTextLoading;
|
|
295
|
+
export const whenAssetTextLoaded = assetSourceRuntime.whenAssetTextLoaded;
|
|
296
|
+
export const getAssetText = assetSourceRuntime.getAssetText;
|
|
403
297
|
`;
|
|
404
298
|
}
|
|
405
299
|
|
|
@@ -416,6 +310,9 @@ export function virtualTwigAssetSourcesPlugin(env) {
|
|
|
416
310
|
if (id === VIRTUAL_TWIG_ASSET_SOURCES_ID) {
|
|
417
311
|
return RESOLVED_VIRTUAL_TWIG_ASSET_SOURCES_ID;
|
|
418
312
|
}
|
|
313
|
+
if (id === VIRTUAL_TWIG_ASSET_SOURCE_RUNTIME_ID) {
|
|
314
|
+
return RESOLVED_VIRTUAL_TWIG_ASSET_SOURCE_RUNTIME_ID;
|
|
315
|
+
}
|
|
419
316
|
|
|
420
317
|
return null;
|
|
421
318
|
},
|
|
@@ -423,6 +320,10 @@ export function virtualTwigAssetSourcesPlugin(env) {
|
|
|
423
320
|
if (id === RESOLVED_VIRTUAL_TWIG_ASSET_SOURCES_ID) {
|
|
424
321
|
return generateVirtualTwigAssetSourcesModule(env);
|
|
425
322
|
}
|
|
323
|
+
if (id === RESOLVED_VIRTUAL_TWIG_ASSET_SOURCE_RUNTIME_ID) {
|
|
324
|
+
this.addWatchFile(ASSET_SOURCE_RUNTIME_PATH);
|
|
325
|
+
return readFileSync(ASSET_SOURCE_RUNTIME_PATH, 'utf8');
|
|
326
|
+
}
|
|
426
327
|
|
|
427
328
|
return null;
|
|
428
329
|
},
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* virtual module instead of replacing placeholder strings in resolver source.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { unique } from '../../../../src/extensions/shared/lists.js';
|
|
9
|
+
import { toRootRelativePath } from '../../../../src/extensions/shared/root-relative.js';
|
|
10
10
|
|
|
11
11
|
export const VIRTUAL_TWIG_GLOBS_ID = 'virtual:emulsify-twig-globs';
|
|
12
12
|
const RESOLVED_VIRTUAL_TWIG_GLOBS_ID = `\0${VIRTUAL_TWIG_GLOBS_ID}`;
|
|
@@ -17,35 +17,6 @@ const RESOLVED_VIRTUAL_TWIG_GLOBS_ID = `\0${VIRTUAL_TWIG_GLOBS_ID}`;
|
|
|
17
17
|
* @property {Record<string, Function>} sources - Lazy raw Twig source loaders.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Convert an absolute project path to a Vite root-relative glob base.
|
|
22
|
-
*
|
|
23
|
-
* @param {string} projectDir - Absolute project root.
|
|
24
|
-
* @param {string} absolutePath - Absolute Twig root path.
|
|
25
|
-
* @returns {string} Vite root-relative path.
|
|
26
|
-
*/
|
|
27
|
-
function toRootRelativePath(projectDir, absolutePath) {
|
|
28
|
-
if (!absolutePath) return '';
|
|
29
|
-
|
|
30
|
-
const normalizedProjectDir = toPosixPath(projectDir || '').replace(
|
|
31
|
-
/\/+$/,
|
|
32
|
-
'',
|
|
33
|
-
);
|
|
34
|
-
const normalizedPath = toPosixPath(absolutePath).replace(/\/+$/, '');
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
normalizedProjectDir &&
|
|
38
|
-
normalizedPath.startsWith(`${normalizedProjectDir}/`)
|
|
39
|
-
) {
|
|
40
|
-
return `/${normalizedPath.slice(normalizedProjectDir.length + 1)}`.replace(
|
|
41
|
-
/\/{2,}/g,
|
|
42
|
-
'/',
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return `/${normalizedPath.replace(/^\/+/, '')}`.replace(/\/{2,}/g, '/');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
20
|
/**
|
|
50
21
|
* Build Vite glob patterns from a resolved Emulsify environment.
|
|
51
22
|
*
|
|
@@ -85,7 +56,7 @@ export function generateVirtualTwigGlobsModule(env) {
|
|
|
85
56
|
: '';
|
|
86
57
|
|
|
87
58
|
return `/**
|
|
88
|
-
* Virtual module generated by config/vite/plugins/virtual-twig-globs.js.
|
|
59
|
+
* Virtual module generated by config/vite/plugins/twig/virtual-twig-globs.js.
|
|
89
60
|
*
|
|
90
61
|
* Compiled Twig modules stay eager for synchronous story rendering. Raw source
|
|
91
62
|
* entries stay lazy and load only when Twig source() requests a template.
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
import twig from '@vituum/vite-plugin-twig';
|
|
9
9
|
import Twig from 'twig';
|
|
10
10
|
|
|
11
|
-
import { registerTwigExtensions } from '
|
|
11
|
+
import { registerTwigExtensions } from '../../../../src/extensions/twig/index.js';
|
|
12
12
|
import { makeTwigPluginOptions } from './twig-module.js';
|
|
13
|
-
import {
|
|
13
|
+
import { installProjectTwigExtensions } from './extensions.js';
|
|
14
14
|
|
|
15
15
|
const EXPECTED_PLUGIN_NAMES = [
|
|
16
16
|
'@vituum/vite-plugin-core:bundle',
|
|
@@ -140,7 +140,7 @@ function stripExpectedHooks(pluginOption) {
|
|
|
140
140
|
*/
|
|
141
141
|
export function makeTwigPlugins(env, options = makeTwigPluginOptions(env)) {
|
|
142
142
|
registerTwigExtensions(Twig);
|
|
143
|
-
|
|
143
|
+
installProjectTwigExtensions(Twig, options);
|
|
144
144
|
|
|
145
145
|
const twigPlugins = twig(options);
|
|
146
146
|
const normalizedPlugins = Array.isArray(twigPlugins)
|
package/config/vite/plugins.js
CHANGED
|
@@ -11,7 +11,7 @@ import { normalize, resolve, sep } from 'path';
|
|
|
11
11
|
import { getPlatformAdapter, normalizePlatformName } from './platforms.js';
|
|
12
12
|
import { resolveProjectStructure } from './project-structure.js';
|
|
13
13
|
import { safeExists, safeReadJson } from './utils/fs-safe.js';
|
|
14
|
-
import { unique } from '
|
|
14
|
+
import { unique } from '../../src/extensions/shared/lists.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Cache normalized project config by project root and relevant env signature.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { basename, normalize, relative, resolve, sep } from 'path';
|
|
11
11
|
import { safeExists } from './utils/fs-safe.js';
|
|
12
12
|
import { replaceLastSlash, toPosixPath } from './utils/paths.js';
|
|
13
|
-
import { unique } from '
|
|
13
|
+
import { unique } from '../../src/extensions/shared/lists.js';
|
|
14
14
|
|
|
15
15
|
export { replaceLastSlash, toPosixPath };
|
|
16
16
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tiny Map-like LRU cache helper.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a bounded insertion-order LRU cache.
|
|
7
|
+
*
|
|
8
|
+
* @template K,V
|
|
9
|
+
* @param {number} maxEntries - Maximum number of entries to retain.
|
|
10
|
+
* @returns {{
|
|
11
|
+
* readonly size: number,
|
|
12
|
+
* has(key: K): boolean,
|
|
13
|
+
* get(key: K): V|undefined,
|
|
14
|
+
* set(key: K, value: V): object,
|
|
15
|
+
* delete(key: K): boolean,
|
|
16
|
+
* clear(): void,
|
|
17
|
+
* entries(): IterableIterator<[K, V]>,
|
|
18
|
+
* keys(): IterableIterator<K>,
|
|
19
|
+
* values(): IterableIterator<V>,
|
|
20
|
+
* [Symbol.iterator](): IterableIterator<[K, V]>
|
|
21
|
+
* }} Map-like LRU cache.
|
|
22
|
+
*/
|
|
23
|
+
export function createLruCache(maxEntries) {
|
|
24
|
+
if (!Number.isInteger(maxEntries) || maxEntries < 1) {
|
|
25
|
+
throw new RangeError('LRU cache size must be a positive integer.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const entries = new Map();
|
|
29
|
+
const cache = {
|
|
30
|
+
get size() {
|
|
31
|
+
return entries.size;
|
|
32
|
+
},
|
|
33
|
+
has(key) {
|
|
34
|
+
return entries.has(key);
|
|
35
|
+
},
|
|
36
|
+
get(key) {
|
|
37
|
+
if (!entries.has(key)) return undefined;
|
|
38
|
+
|
|
39
|
+
const value = entries.get(key);
|
|
40
|
+
entries.delete(key);
|
|
41
|
+
entries.set(key, value);
|
|
42
|
+
return value;
|
|
43
|
+
},
|
|
44
|
+
set(key, value) {
|
|
45
|
+
if (entries.has(key)) {
|
|
46
|
+
entries.delete(key);
|
|
47
|
+
}
|
|
48
|
+
entries.set(key, value);
|
|
49
|
+
|
|
50
|
+
while (entries.size > maxEntries) {
|
|
51
|
+
entries.delete(entries.keys().next().value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return cache;
|
|
55
|
+
},
|
|
56
|
+
delete(key) {
|
|
57
|
+
return entries.delete(key);
|
|
58
|
+
},
|
|
59
|
+
clear() {
|
|
60
|
+
entries.clear();
|
|
61
|
+
},
|
|
62
|
+
entries() {
|
|
63
|
+
return entries.entries();
|
|
64
|
+
},
|
|
65
|
+
keys() {
|
|
66
|
+
return entries.keys();
|
|
67
|
+
},
|
|
68
|
+
values() {
|
|
69
|
+
return entries.values();
|
|
70
|
+
},
|
|
71
|
+
[Symbol.iterator]() {
|
|
72
|
+
return entries[Symbol.iterator]();
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return cache;
|
|
77
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Emulsify Core package version resolution.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the component mirror plugin, which stamps the version into its
|
|
5
|
+
* state marker, and the develop reporter, which prints it in the banner.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
import { safeReadJson } from './fs-safe.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the installed Core package version.
|
|
14
|
+
*
|
|
15
|
+
* `import.meta.url` is deliberately avoided so Jest's CommonJS transform can
|
|
16
|
+
* load callers of this module without extra plumbing. Candidates are ordered
|
|
17
|
+
* from most to least specific: the consuming project's installed copy, the
|
|
18
|
+
* current working directory's installed copy, then the working directory
|
|
19
|
+
* itself for the case where Core is building its own source tree.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} [projectDir] - Project directory running the build.
|
|
22
|
+
* @returns {string} Emulsify Core package version, or `0.0.0` when unknown.
|
|
23
|
+
*/
|
|
24
|
+
export function resolvePackageVersion(projectDir) {
|
|
25
|
+
const candidates = [
|
|
26
|
+
projectDir && join(projectDir, 'node_modules/@emulsify/core/package.json'),
|
|
27
|
+
join(process.cwd(), 'node_modules/@emulsify/core/package.json'),
|
|
28
|
+
join(process.cwd(), 'package.json'),
|
|
29
|
+
].filter(Boolean);
|
|
30
|
+
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
const candidatePackage = safeReadJson(candidate).data;
|
|
33
|
+
if (
|
|
34
|
+
candidatePackage?.name === '@emulsify/core' &&
|
|
35
|
+
candidatePackage.version
|
|
36
|
+
) {
|
|
37
|
+
return candidatePackage.version;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return '0.0.0';
|
|
42
|
+
}
|
|
@@ -14,18 +14,10 @@ import { sep } from 'path';
|
|
|
14
14
|
* @param {string} filePath - Filesystem path.
|
|
15
15
|
* @returns {string} Path using forward slashes.
|
|
16
16
|
*/
|
|
17
|
-
export function
|
|
17
|
+
export function toPosixPath(filePath) {
|
|
18
18
|
return filePath.split(sep).join('/').replace(/\\/g, '/');
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
* Normalize a filesystem path to POSIX separators.
|
|
23
|
-
*
|
|
24
|
-
* @param {string} filePath - Filesystem path.
|
|
25
|
-
* @returns {string} Path using forward slashes.
|
|
26
|
-
*/
|
|
27
|
-
export const toPosixPath = toPosix;
|
|
28
|
-
|
|
29
21
|
/**
|
|
30
22
|
* Replace the final slash in a POSIX path with a custom segment.
|
|
31
23
|
*
|
|
@@ -13,17 +13,35 @@
|
|
|
13
13
|
* parts of it by returning a patch object from `extendConfig(...)`.
|
|
14
14
|
*
|
|
15
15
|
* Notes:
|
|
16
|
-
* -
|
|
16
|
+
* - JS sourcemaps come from `build.sourcemap`. Extracted CSS gets no map from
|
|
17
|
+
* `vite build`: `vite:css-post` emits CSS through
|
|
18
|
+
* `this.emitFile({ type: 'asset' })`, Rollup/Rolldown assets carry no map,
|
|
19
|
+
* and `finalizeCss()` -> `minifyCSS()` returns code only. To trace a rule
|
|
20
|
+
* back to its `.scss` partial, let Vite compile the SCSS in Storybook: set
|
|
21
|
+
* `parameters.emulsify.loadAllCSS = false` in
|
|
22
|
+
* `config/emulsify-core/storybook/preview.js` and import the SCSS entry
|
|
23
|
+
* there, so `css.devSourcemap` can chain the map to source. Loading the
|
|
24
|
+
* compiled CSS instead yields an identity map whose only source is the
|
|
25
|
+
* compiled `.css` file.
|
|
26
|
+
* - CSS is left unminified during `vite build --watch` so the develop loop
|
|
27
|
+
* stays readable; one-shot builds keep minification.
|
|
17
28
|
* - CSS assets keep their path and drop the internal `__style` suffix if present.
|
|
18
29
|
*/
|
|
19
30
|
|
|
20
|
-
import { defineConfig, mergeConfig } from 'vite';
|
|
31
|
+
import { createLogger, defineConfig, mergeConfig } from 'vite';
|
|
21
32
|
import path from 'node:path';
|
|
22
33
|
|
|
23
34
|
import { resolveEnvironment } from './environment.js';
|
|
24
35
|
import { makePlugins } from './plugins.js';
|
|
25
36
|
import { buildInputs } from './entries.js';
|
|
26
|
-
import { createSourceFileIndex } from './plugins/source-file-index.js';
|
|
37
|
+
import { createSourceFileIndex } from './plugins/assets/source-file-index.js';
|
|
38
|
+
import { createDiagnosticsCollector } from './plugins/reporter/diagnostics.js';
|
|
39
|
+
import { createSassOptions } from './plugins/reporter/sass-logger.js';
|
|
40
|
+
import {
|
|
41
|
+
createReporterLogger,
|
|
42
|
+
isVerbose,
|
|
43
|
+
} from './plugins/reporter/vite-logger.js';
|
|
44
|
+
import { isWatchInvocation } from './plugins/reporter/watch-mode.js';
|
|
27
45
|
import { loadProjectExtensions } from './project-extensions.js';
|
|
28
46
|
import { mergeReactSingletonResolve } from './utils/react-singleton.js';
|
|
29
47
|
|
|
@@ -44,7 +62,14 @@ export default defineConfig(async () => {
|
|
|
44
62
|
/** @type {EmulsifyEnv} */
|
|
45
63
|
const env = resolveEnvironment();
|
|
46
64
|
const sourceFileIndex = createSourceFileIndex(env.projectStructure);
|
|
47
|
-
|
|
65
|
+
|
|
66
|
+
// The develop reporter takes over output only for `vite build --watch`, the
|
|
67
|
+
// watcher `npm run develop` runs. One-shot builds, Storybook, and the release
|
|
68
|
+
// fixture verifications keep their existing output untouched, so no warning
|
|
69
|
+
// is ever collected without also being reported.
|
|
70
|
+
const watching = isWatchInvocation();
|
|
71
|
+
const diagnostics = watching ? createDiagnosticsCollector() : undefined;
|
|
72
|
+
const envWithSourceFileIndex = { ...env, sourceFileIndex, diagnostics };
|
|
48
73
|
|
|
49
74
|
// Build the Rollup/Vite entry map: keys encode output paths, values source files.
|
|
50
75
|
/** @type {Record<string, string>} */
|
|
@@ -79,12 +104,66 @@ export default defineConfig(async () => {
|
|
|
79
104
|
// Core plugin set + project-provided plugins (if any).
|
|
80
105
|
plugins: [...makePlugins(envWithSourceFileIndex), ...projectPlugins],
|
|
81
106
|
|
|
107
|
+
// Quieting `develop` takes two independent switches, because the output has
|
|
108
|
+
// two independent sources.
|
|
109
|
+
//
|
|
110
|
+
// 1. `logLevel` gates Vite's build reporter, which is a native Rolldown
|
|
111
|
+
// plugin. Vite reads `config.logLevel` directly when constructing it and
|
|
112
|
+
// passes a `logInfo` callback only when the level admits info. Rolldown's
|
|
113
|
+
// Rust side registers its Transform, BuildStart, BuildEnd, RenderChunk,
|
|
114
|
+
// WriteBundle, and GenerateBundle hooks only when that callback exists,
|
|
115
|
+
// so raising the level does not merely hide the report — it stops
|
|
116
|
+
// Rolldown instrumenting transforms and computing gzip sizes at all.
|
|
117
|
+
// That removes the `transforming (N)` progress line, `N modules
|
|
118
|
+
// transformed.`, `rendering chunks...`, `computing gzip size...`, and the
|
|
119
|
+
// per-file asset table, and makes every rebuild cheaper.
|
|
120
|
+
//
|
|
121
|
+
// The progress line is the one that matters most for legibility: Rolldown
|
|
122
|
+
// writes it from Rust with a `\x1b[2K\r` prefix and no trailing newline,
|
|
123
|
+
// which is what collides with Storybook's output when `concurrently`
|
|
124
|
+
// merges both streams onto one pipe. Nothing on the JavaScript side can
|
|
125
|
+
// intercept it, so `logLevel` is the only lever that removes it.
|
|
126
|
+
//
|
|
127
|
+
// 2. `customLogger` gates Vite's own JavaScript chatter — `building client
|
|
128
|
+
// environment...`, `build started...`, `watching for file changes...`,
|
|
129
|
+
// and `built in Nms.` — and routes unresolved CSS asset URLs into the
|
|
130
|
+
// diagnostics collector so they are summarized rather than printed
|
|
131
|
+
// mid-build. The reporter writes straight to stdout rather than through
|
|
132
|
+
// this logger, so its own output survives the higher level, and warnings
|
|
133
|
+
// and errors still come through.
|
|
134
|
+
//
|
|
135
|
+
// Neither switch substitutes for the other: `createLogger` returns a
|
|
136
|
+
// supplied `customLogger` verbatim and never consults the level, while the
|
|
137
|
+
// build reporter consults the level and never consults the logger.
|
|
138
|
+
// `build.reportCompressedSize` is deliberately left alone; it suppresses
|
|
139
|
+
// only the gzip column, and the table it belongs to is already gone.
|
|
140
|
+
...(diagnostics
|
|
141
|
+
? {
|
|
142
|
+
logLevel: isVerbose() ? 'info' : 'warn',
|
|
143
|
+
customLogger: createReporterLogger(
|
|
144
|
+
diagnostics,
|
|
145
|
+
createLogger(isVerbose() ? 'info' : 'warn'),
|
|
146
|
+
),
|
|
147
|
+
}
|
|
148
|
+
: {}),
|
|
149
|
+
|
|
82
150
|
// Keep React-based story helpers on the consumer project's React singleton.
|
|
83
151
|
resolve: mergeReactSingletonResolve(),
|
|
84
152
|
|
|
85
153
|
// Generate CSS sourcemaps in dev; JS sourcemaps are set in `build.sourcemap`.
|
|
154
|
+
// These map only what Vite itself compiles. A preview that imports
|
|
155
|
+
// already-compiled CSS gets an identity map pointing at that `.css` file,
|
|
156
|
+
// so import SCSS entries when styles need to resolve to their partials.
|
|
86
157
|
css: {
|
|
87
158
|
devSourcemap: true,
|
|
159
|
+
|
|
160
|
+
// During a watch build, route Sass warnings into the diagnostics
|
|
161
|
+
// collector instead of letting Dart Sass print a formatted block per
|
|
162
|
+
// occurrence. The reporter prints one deduplicated tally per cycle, so
|
|
163
|
+
// the deprecation debt stays visible without the repetition.
|
|
164
|
+
...(diagnostics
|
|
165
|
+
? { preprocessorOptions: { scss: createSassOptions(diagnostics) } }
|
|
166
|
+
: {}),
|
|
88
167
|
},
|
|
89
168
|
|
|
90
169
|
build: {
|
|
@@ -94,9 +173,16 @@ export default defineConfig(async () => {
|
|
|
94
173
|
// All outputs are written into ./dist/
|
|
95
174
|
outDir: 'dist/',
|
|
96
175
|
|
|
97
|
-
// Emit
|
|
176
|
+
// Emit JS sourcemaps. Extracted CSS is not covered; see the file header.
|
|
98
177
|
sourcemap: true,
|
|
99
178
|
|
|
179
|
+
// Vite cannot map extracted CSS, so during `vite build --watch` the
|
|
180
|
+
// readable stylesheet is the debugging aid: keep it unminified so
|
|
181
|
+
// devtools shows one declaration per line instead of a single long line.
|
|
182
|
+
// One-shot `vite build`, `storybook build`, and the release fixture
|
|
183
|
+
// verifications still minify, so nothing a platform ships changes.
|
|
184
|
+
cssMinify: !watching,
|
|
185
|
+
|
|
100
186
|
rollupOptions: {
|
|
101
187
|
// Multi-entry input map constructed above.
|
|
102
188
|
input: entries,
|