@emulsify/core 4.2.1 → 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} +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 +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/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
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,32 @@
|
|
|
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 { createReporterLogger } from './plugins/reporter/vite-logger.js';
|
|
41
|
+
import { isWatchInvocation } from './plugins/reporter/watch-mode.js';
|
|
27
42
|
import { loadProjectExtensions } from './project-extensions.js';
|
|
28
43
|
import { mergeReactSingletonResolve } from './utils/react-singleton.js';
|
|
29
44
|
|
|
@@ -44,7 +59,14 @@ export default defineConfig(async () => {
|
|
|
44
59
|
/** @type {EmulsifyEnv} */
|
|
45
60
|
const env = resolveEnvironment();
|
|
46
61
|
const sourceFileIndex = createSourceFileIndex(env.projectStructure);
|
|
47
|
-
|
|
62
|
+
|
|
63
|
+
// The develop reporter takes over output only for `vite build --watch`, the
|
|
64
|
+
// watcher `npm run develop` runs. One-shot builds, Storybook, and the release
|
|
65
|
+
// fixture verifications keep their existing output untouched, so no warning
|
|
66
|
+
// is ever collected without also being reported.
|
|
67
|
+
const watching = isWatchInvocation();
|
|
68
|
+
const diagnostics = watching ? createDiagnosticsCollector() : undefined;
|
|
69
|
+
const envWithSourceFileIndex = { ...env, sourceFileIndex, diagnostics };
|
|
48
70
|
|
|
49
71
|
// Build the Rollup/Vite entry map: keys encode output paths, values source files.
|
|
50
72
|
/** @type {Record<string, string>} */
|
|
@@ -79,12 +101,29 @@ export default defineConfig(async () => {
|
|
|
79
101
|
// Core plugin set + project-provided plugins (if any).
|
|
80
102
|
plugins: [...makePlugins(envWithSourceFileIndex), ...projectPlugins],
|
|
81
103
|
|
|
104
|
+
// Route Vite's own diagnostics through the reporter during a watch build so
|
|
105
|
+
// unresolved CSS asset URLs are summarized rather than printed mid-build.
|
|
106
|
+
...(diagnostics
|
|
107
|
+
? { customLogger: createReporterLogger(diagnostics, createLogger()) }
|
|
108
|
+
: {}),
|
|
109
|
+
|
|
82
110
|
// Keep React-based story helpers on the consumer project's React singleton.
|
|
83
111
|
resolve: mergeReactSingletonResolve(),
|
|
84
112
|
|
|
85
113
|
// Generate CSS sourcemaps in dev; JS sourcemaps are set in `build.sourcemap`.
|
|
114
|
+
// These map only what Vite itself compiles. A preview that imports
|
|
115
|
+
// already-compiled CSS gets an identity map pointing at that `.css` file,
|
|
116
|
+
// so import SCSS entries when styles need to resolve to their partials.
|
|
86
117
|
css: {
|
|
87
118
|
devSourcemap: true,
|
|
119
|
+
|
|
120
|
+
// During a watch build, route Sass warnings into the diagnostics
|
|
121
|
+
// collector instead of letting Dart Sass print a formatted block per
|
|
122
|
+
// occurrence. The reporter prints one deduplicated tally per cycle, so
|
|
123
|
+
// the deprecation debt stays visible without the repetition.
|
|
124
|
+
...(diagnostics
|
|
125
|
+
? { preprocessorOptions: { scss: createSassOptions(diagnostics) } }
|
|
126
|
+
: {}),
|
|
88
127
|
},
|
|
89
128
|
|
|
90
129
|
build: {
|
|
@@ -94,9 +133,16 @@ export default defineConfig(async () => {
|
|
|
94
133
|
// All outputs are written into ./dist/
|
|
95
134
|
outDir: 'dist/',
|
|
96
135
|
|
|
97
|
-
// Emit
|
|
136
|
+
// Emit JS sourcemaps. Extracted CSS is not covered; see the file header.
|
|
98
137
|
sourcemap: true,
|
|
99
138
|
|
|
139
|
+
// Vite cannot map extracted CSS, so during `vite build --watch` the
|
|
140
|
+
// readable stylesheet is the debugging aid: keep it unminified so
|
|
141
|
+
// devtools shows one declaration per line instead of a single long line.
|
|
142
|
+
// One-shot `vite build`, `storybook build`, and the release fixture
|
|
143
|
+
// verifications still minify, so nothing a platform ships changes.
|
|
144
|
+
cssMinify: !watching,
|
|
145
|
+
|
|
100
146
|
rollupOptions: {
|
|
101
147
|
// Multi-entry input map constructed above.
|
|
102
148
|
input: entries,
|