@emulsify/core 4.1.0 → 4.2.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/css-components.js +3 -0
- package/.storybook/css-dist.js +1 -0
- package/.storybook/main-static-assets.js +217 -0
- package/.storybook/main-vite.js +224 -0
- package/.storybook/main.js +9 -400
- package/.storybook/manager-head.css +120 -0
- package/.storybook/preview.js +3 -6
- package/.storybook/utils.js +1 -1
- package/README.md +8 -7
- package/config/vite/platforms.js +37 -10
- package/config/vite/plugins/virtual-twig-asset-sources.js +29 -3
- package/package.json +19 -18
- package/scripts/a11y.js +16 -21
- package/src/storybook/twig/source-function.js +1 -1
package/.storybook/main.js
CHANGED
|
@@ -14,32 +14,13 @@
|
|
|
14
14
|
import fs from 'fs';
|
|
15
15
|
import path, { resolve } from 'path';
|
|
16
16
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
17
|
-
import viteConfig from '../config/vite/vite.config.js';
|
|
18
17
|
import { resolveEnvironment } from '../config/vite/environment.js';
|
|
19
|
-
import {
|
|
20
|
-
mergeReactSingletonOptimizeDeps,
|
|
21
|
-
mergeReactSingletonResolve,
|
|
22
|
-
} from '../config/vite/utils/react-singleton.js';
|
|
23
|
-
import { twigExtensionModuleSpecifiers } from '../config/vite/twig-extensions.js';
|
|
24
18
|
import {
|
|
25
19
|
applyStorybookConfigOverrides,
|
|
26
20
|
normalizeStorybookConfigOverrideModule,
|
|
27
21
|
} from '../src/storybook/main-config.js';
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const twigVirtualModuleIds = [
|
|
32
|
-
'virtual:emulsify-twig-globs',
|
|
33
|
-
'virtual:emulsify-twig-asset-sources',
|
|
34
|
-
'virtual:emulsify-twig-extension-installers',
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
const twigRuntimeOptimizeDepsExclude = [
|
|
38
|
-
...twigVirtualModuleIds,
|
|
39
|
-
'@emulsify/core/storybook/twig/source-function',
|
|
40
|
-
'@emulsify/core/storybook/twig/source',
|
|
41
|
-
'@emulsify/core/storybook/twig/resolver',
|
|
42
|
-
];
|
|
22
|
+
import { buildAssetStaticDirs } from './main-static-assets.js';
|
|
23
|
+
import { createViteFinal } from './main-vite.js';
|
|
43
24
|
|
|
44
25
|
/**
|
|
45
26
|
* Minimal subset of the resolved Emulsify environment used by this file.
|
|
@@ -70,27 +51,6 @@ const _filename = fileURLToPath(import.meta.url);
|
|
|
70
51
|
*/
|
|
71
52
|
const _dirname = path.dirname(_filename);
|
|
72
53
|
|
|
73
|
-
/**
|
|
74
|
-
* The consuming project root for Storybook static mounts.
|
|
75
|
-
*
|
|
76
|
-
* Storybook loads this package config from different physical locations
|
|
77
|
-
* depending on whether Core is linked locally or installed in node_modules, so
|
|
78
|
-
* static paths must be rooted at the process cwd rather than this file.
|
|
79
|
-
*
|
|
80
|
-
* @type {string}
|
|
81
|
-
*/
|
|
82
|
-
const projectRoot = process.cwd();
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Vite-generated Storybook chunks should not share `/assets` with project
|
|
86
|
-
* static files. Storybook copies staticDirs while the preview build runs, so
|
|
87
|
-
* keeping generated chunks in a separate folder avoids concurrent writers in
|
|
88
|
-
* `.out/assets`.
|
|
89
|
-
*
|
|
90
|
-
* @type {string}
|
|
91
|
-
*/
|
|
92
|
-
const storybookViteAssetsDir = 'storybook-assets';
|
|
93
|
-
|
|
94
54
|
/**
|
|
95
55
|
* Reads an optional HTML fragment relative to this config file.
|
|
96
56
|
*
|
|
@@ -110,111 +70,6 @@ function readOptionalHtmlFragment(relativePath) {
|
|
|
110
70
|
return fs.readFileSync(fragmentPath, 'utf8');
|
|
111
71
|
}
|
|
112
72
|
|
|
113
|
-
/**
|
|
114
|
-
* Keeps Storybook static directory config aligned to the consuming project.
|
|
115
|
-
*
|
|
116
|
-
* Storybook errors when a declared static directory is absent, so only expose
|
|
117
|
-
* project asset directories that exist in the current workspace.
|
|
118
|
-
*
|
|
119
|
-
* @param {Array<string|{from: string, to: string}>} staticDirs - Static directory entries.
|
|
120
|
-
* @returns {Array<string|{from: string, to: string}>} Existing static directory entries.
|
|
121
|
-
*/
|
|
122
|
-
function existingStaticDirs(staticDirs) {
|
|
123
|
-
const seen = new Set();
|
|
124
|
-
const existing = [];
|
|
125
|
-
|
|
126
|
-
for (const staticDir of staticDirs) {
|
|
127
|
-
const directory =
|
|
128
|
-
typeof staticDir === 'string' ? staticDir : staticDir.from;
|
|
129
|
-
|
|
130
|
-
if (!directory || !fs.existsSync(directory)) continue;
|
|
131
|
-
|
|
132
|
-
const key =
|
|
133
|
-
typeof staticDir === 'string'
|
|
134
|
-
? staticDir
|
|
135
|
-
: `${staticDir.from || ''}\0${staticDir.to || ''}`;
|
|
136
|
-
if (seen.has(key)) continue;
|
|
137
|
-
|
|
138
|
-
seen.add(key);
|
|
139
|
-
existing.push(staticDir);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return existing;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Build static directory mounts for normalized project asset roots.
|
|
147
|
-
*
|
|
148
|
-
* @param {StorybookEnvironment} env - Resolved project paths used by Storybook.
|
|
149
|
-
* @returns {Array<string|{from: string, to: string}>} Static directory entries.
|
|
150
|
-
*/
|
|
151
|
-
function buildAssetStaticDirs(env) {
|
|
152
|
-
const configuredAssetRoots = Array.isArray(env.projectStructure?.assetRoots)
|
|
153
|
-
? env.projectStructure.assetRoots
|
|
154
|
-
: [];
|
|
155
|
-
const assetRoots = [
|
|
156
|
-
...configuredAssetRoots,
|
|
157
|
-
path.resolve(projectRoot, 'assets'),
|
|
158
|
-
path.resolve(projectRoot, 'src/assets'),
|
|
159
|
-
];
|
|
160
|
-
|
|
161
|
-
return existingStaticDirs([
|
|
162
|
-
...assetRoots.map((root) => ({
|
|
163
|
-
from: root,
|
|
164
|
-
to: '/assets',
|
|
165
|
-
})),
|
|
166
|
-
{
|
|
167
|
-
from: path.resolve(projectRoot, 'dist/assets'),
|
|
168
|
-
to: '/assets',
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
from: path.resolve(projectRoot, 'dist'),
|
|
172
|
-
to: '/dist',
|
|
173
|
-
},
|
|
174
|
-
]);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Merge Storybook and project optimizeDeps excludes with Core Twig runtime IDs.
|
|
179
|
-
*
|
|
180
|
-
* Storybook's dependency optimizer runs before normal Vite virtual module
|
|
181
|
-
* resolution. Core Twig runtime modules import virtual IDs that must stay in
|
|
182
|
-
* the Vite module graph so Emulsify's virtual plugins can resolve them.
|
|
183
|
-
*
|
|
184
|
-
* @param {...string[]} excludeLists - Existing optimizeDeps exclude arrays.
|
|
185
|
-
* @returns {string[]} Merged exclude list.
|
|
186
|
-
*/
|
|
187
|
-
function mergeTwigRuntimeOptimizeDepsExcludes(...excludeLists) {
|
|
188
|
-
return Array.from(
|
|
189
|
-
new Set([
|
|
190
|
-
...excludeLists.flatMap((excludeList) =>
|
|
191
|
-
Array.isArray(excludeList) ? excludeList : [],
|
|
192
|
-
),
|
|
193
|
-
...twigRuntimeOptimizeDepsExclude,
|
|
194
|
-
]),
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Keep Emulsify Twig virtual imports out of Storybook dependency prebundles.
|
|
200
|
-
*
|
|
201
|
-
* @returns {import('esbuild').Plugin} Esbuild plugin for optimizeDeps.
|
|
202
|
-
*/
|
|
203
|
-
function makeTwigVirtualModuleOptimizerPlugin() {
|
|
204
|
-
return {
|
|
205
|
-
name: 'emulsify-twig-virtual-modules',
|
|
206
|
-
setup(build) {
|
|
207
|
-
build.onResolve(
|
|
208
|
-
{ filter: /^virtual:emulsify-twig-(?:globs|asset-sources)$/ },
|
|
209
|
-
(args) => ({
|
|
210
|
-
path: args.path,
|
|
211
|
-
external: true,
|
|
212
|
-
}),
|
|
213
|
-
);
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
73
|
/**
|
|
219
74
|
* Reads optional project-level Storybook overrides.
|
|
220
75
|
*
|
|
@@ -367,109 +222,12 @@ const baseConfig = {
|
|
|
367
222
|
* @returns {string} Manager head markup with Emulsify additions appended.
|
|
368
223
|
*/
|
|
369
224
|
managerHead: (head) => {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
--colors-emulsify-blue-200: #CCECFA;
|
|
377
|
-
--colors-emulsify-blue-300: #99D9F4;
|
|
378
|
-
--colors-emulsify-blue-400: #66c5ef;
|
|
379
|
-
--colors-emulsify-blue-500: #33b2e9;
|
|
380
|
-
--colors-emulsify-blue-600: #009fe4;
|
|
381
|
-
--colors-emulsify-blue-700: #007FB6;
|
|
382
|
-
--colors-emulsify-blue-800: #005f89;
|
|
383
|
-
--colors-emulsify-blue-900: #00405b;
|
|
384
|
-
--colors-emulsify-blue-1000: #00202e;
|
|
385
|
-
--colors-purple: #8B1E7E;
|
|
386
|
-
}
|
|
387
|
-
.sidebar-container {
|
|
388
|
-
background-color: var(--colors-emulsify-blue-900);
|
|
389
|
-
}
|
|
390
|
-
.sidebar-container .sidebar-subheading {
|
|
391
|
-
color: var(--colors-emulsify-blue-200);
|
|
392
|
-
font-size: 13px;
|
|
393
|
-
letter-spacing: 0.15em;
|
|
394
|
-
}
|
|
395
|
-
.sidebar-container .sidebar-subheading button:focus {
|
|
396
|
-
color: var(--colors-emulsify-blue-300);
|
|
397
|
-
}
|
|
398
|
-
/* Triangle icon. */
|
|
399
|
-
.sidebar-container .sidebar-subheading button span {
|
|
400
|
-
color: var(--colors-emulsify-blue-300);
|
|
401
|
-
}
|
|
402
|
-
.sidebar-container .search-field input {
|
|
403
|
-
border-color: var(--colors-emulsify-blue-700);
|
|
404
|
-
}
|
|
405
|
-
.sidebar-container .search-field input:active {
|
|
406
|
-
border-color: var(--colors-emulsify-blue-700);
|
|
407
|
-
}
|
|
408
|
-
.sidebar-container .search-result-recentlyOpened,
|
|
409
|
-
.sidebar-container .search-result-back,
|
|
410
|
-
.sidebar-container .search-result-clearHistory {
|
|
411
|
-
color: var(--colors-emulsify-blue-300) !important;
|
|
412
|
-
letter-spacing: 0.15em;
|
|
413
|
-
}
|
|
414
|
-
.sidebar-container .search-result-back span,
|
|
415
|
-
.sidebar-container .search-result-back svg,
|
|
416
|
-
.sidebar-container .search-result-clearHistory span,
|
|
417
|
-
.sidebar-container .search-result-clearHistory svg {
|
|
418
|
-
letter-spacing: normal;
|
|
419
|
-
color: white;
|
|
420
|
-
}
|
|
421
|
-
.sidebar-container .sidebar-item svg {
|
|
422
|
-
margin-top: 1px;
|
|
423
|
-
}
|
|
424
|
-
.sidebar-container .sidebar-item span {
|
|
425
|
-
margin-top: 4px;
|
|
426
|
-
}
|
|
427
|
-
.sidebar-container .sidebar-subheading-action svg {
|
|
428
|
-
color: var(--colors-emulsify-blue-400);
|
|
429
|
-
}
|
|
430
|
-
.sidebar-container .sidebar-subheading-action:hover svg {
|
|
431
|
-
color: var(--colors-emulsify-blue-300);
|
|
432
|
-
}
|
|
433
|
-
.sidebar-header button[title="Shortcuts"] {
|
|
434
|
-
box-shadow: none;
|
|
435
|
-
border: 1px solid var(--colors-emulsify-blue-700);
|
|
436
|
-
}
|
|
437
|
-
.sidebar-header button[title="Shortcuts"]:active {
|
|
438
|
-
border: 1px solid var(--colors-emulsify-blue-500);
|
|
439
|
-
}
|
|
440
|
-
.sidebar-header button[title="Shortcuts"]:focus {
|
|
441
|
-
background: transparent;
|
|
442
|
-
}
|
|
443
|
-
#shortcuts {
|
|
444
|
-
border-bottom-color: var(--colors-emulsify-blue-900) !important;
|
|
445
|
-
}
|
|
446
|
-
[role="main"]:not(:nth-child(3)) {
|
|
447
|
-
top: 1rem !important;
|
|
448
|
-
height: calc(100vh - 2rem) !important;
|
|
449
|
-
}
|
|
450
|
-
[role="main"] .os-host .os-content button:hover {
|
|
451
|
-
background: var(--colors-emulsify-blue-100);
|
|
452
|
-
}
|
|
453
|
-
[role="main"] .os-host .os-content button:hover svg {
|
|
454
|
-
color: var(--colors-emulsify-blue-900);
|
|
455
|
-
}
|
|
456
|
-
#panel-tab-content,
|
|
457
|
-
#panel-tab-content>* {
|
|
458
|
-
color: var(--colors-emulsify-blue-100) !important;
|
|
459
|
-
}
|
|
460
|
-
#panel-tab-content a,
|
|
461
|
-
#panel-tab-content a span,
|
|
462
|
-
#panel-tab-content a span svg {
|
|
463
|
-
color: var(--colors-emulsify-blue-800);
|
|
464
|
-
}
|
|
465
|
-
#panel-tab-content>div>div>div>div>div>div {
|
|
466
|
-
background: transparent;
|
|
467
|
-
}
|
|
468
|
-
#panel-tab-content>div>div>div>div>div>div>div {
|
|
469
|
-
color: var(--colors-emulsify-blue-1000) !important;
|
|
470
|
-
}
|
|
471
|
-
</style>
|
|
472
|
-
`;
|
|
225
|
+
const managerStyles = readOptionalHtmlFragment('./manager-head.css');
|
|
226
|
+
const inlineStyles = managerStyles
|
|
227
|
+
? `<style>
|
|
228
|
+
${managerStyles}
|
|
229
|
+
</style>`
|
|
230
|
+
: '';
|
|
473
231
|
const externalManagerHtml = readOptionalHtmlFragment(
|
|
474
232
|
'../../../../config/emulsify-core/storybook/manager-head.html',
|
|
475
233
|
);
|
|
@@ -497,156 +255,7 @@ const baseConfig = {
|
|
|
497
255
|
${externalHtml}`;
|
|
498
256
|
},
|
|
499
257
|
|
|
500
|
-
|
|
501
|
-
* Merges Storybook's generated Vite config with Emulsify's shared Vite config.
|
|
502
|
-
*
|
|
503
|
-
* Storybook supplies a baseline config, but Emulsify still needs to expose
|
|
504
|
-
* the resolved environment, expand filesystem access, and expose the Twig
|
|
505
|
-
* virtual glob module used by the runtime resolver.
|
|
506
|
-
*
|
|
507
|
-
* @param {import('vite').UserConfig} config - Storybook's generated Vite config.
|
|
508
|
-
* @returns {Promise<import('vite').UserConfig>} Final Vite config used by Storybook.
|
|
509
|
-
*/
|
|
510
|
-
async viteFinal(config) {
|
|
511
|
-
const { mergeConfig } = await import('vite');
|
|
512
|
-
/** @type {StorybookEnvironment} */
|
|
513
|
-
const env = resolvedStorybookEnv;
|
|
514
|
-
const storybookBuildConfig = config?.build || {};
|
|
515
|
-
|
|
516
|
-
// Keep using the `serve` branch of the shared Vite config here. Storybook
|
|
517
|
-
// has historically consumed that branch, while `mode` still reflects
|
|
518
|
-
// whether Storybook is running in development or production.
|
|
519
|
-
const mode = config?.mode || 'development';
|
|
520
|
-
const baseViteConfig =
|
|
521
|
-
typeof viteConfig === 'function'
|
|
522
|
-
? await viteConfig({ command: 'serve', mode })
|
|
523
|
-
: viteConfig;
|
|
524
|
-
const existingDefine = (config && config.define) || {};
|
|
525
|
-
const viteDefine = (baseViteConfig && baseViteConfig.define) || {};
|
|
526
|
-
|
|
527
|
-
// Allow Storybook's dev server to read component sources from the project
|
|
528
|
-
// root and any structure override paths used by Emulsify consumers.
|
|
529
|
-
const allowList = new Set([
|
|
530
|
-
...(config?.server?.fs?.allow || []),
|
|
531
|
-
env.projectDir,
|
|
532
|
-
path.resolve(env.projectDir, 'src'),
|
|
533
|
-
path.resolve(env.projectDir, 'components'),
|
|
534
|
-
path.resolve(env.projectDir, 'dist'),
|
|
535
|
-
...(Array.isArray(env.projectStructure?.sourceRoots)
|
|
536
|
-
? env.projectStructure.sourceRoots
|
|
537
|
-
: []),
|
|
538
|
-
...(Array.isArray(env.componentRoots) ? env.componentRoots : []),
|
|
539
|
-
...(Array.isArray(env.structureRoots) ? env.structureRoots : []),
|
|
540
|
-
...(env.namespaceRoots && typeof env.namespaceRoots === 'object'
|
|
541
|
-
? Object.values(env.namespaceRoots)
|
|
542
|
-
: []),
|
|
543
|
-
...(Array.isArray(env.projectStructure?.assetRoots)
|
|
544
|
-
? env.projectStructure.assetRoots
|
|
545
|
-
: []),
|
|
546
|
-
]);
|
|
547
|
-
|
|
548
|
-
// Twig files are loaded through custom resolvers/plugins, so they need to
|
|
549
|
-
// be treated as importable assets by Storybook's Vite pipeline.
|
|
550
|
-
const assetsInclude = Array.from(
|
|
551
|
-
new Set([
|
|
552
|
-
...(config.assetsInclude || []),
|
|
553
|
-
...(baseViteConfig.assetsInclude || []),
|
|
554
|
-
'**/*.twig',
|
|
555
|
-
]),
|
|
556
|
-
);
|
|
557
|
-
const optimizeDepsInclude = mergeReactSingletonOptimizeDeps(
|
|
558
|
-
baseViteConfig?.optimizeDeps?.include,
|
|
559
|
-
config?.optimizeDeps?.include,
|
|
560
|
-
[
|
|
561
|
-
'twig',
|
|
562
|
-
'@emulsify/core/extensions/twig',
|
|
563
|
-
...twigExtensionModuleSpecifiers(env),
|
|
564
|
-
],
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
const mergedConfig = mergeConfig(config, {
|
|
568
|
-
...baseViteConfig,
|
|
569
|
-
resolve: mergeReactSingletonResolve(baseViteConfig, config),
|
|
570
|
-
define: {
|
|
571
|
-
// Preserve shared and Storybook-provided constants, then publish the
|
|
572
|
-
// resolved Emulsify environment to client-side code.
|
|
573
|
-
...viteDefine,
|
|
574
|
-
...existingDefine,
|
|
575
|
-
__EMULSIFY_ENV__: JSON.stringify(env),
|
|
576
|
-
'globalThis.__EMULSIFY_ENV__': JSON.stringify(env),
|
|
577
|
-
},
|
|
578
|
-
server: {
|
|
579
|
-
...(baseViteConfig?.server || {}),
|
|
580
|
-
fs: {
|
|
581
|
-
allow: Array.from(allowList),
|
|
582
|
-
},
|
|
583
|
-
},
|
|
584
|
-
assetsInclude,
|
|
585
|
-
plugins: [...(baseViteConfig?.plugins || [])],
|
|
586
|
-
esbuild: {
|
|
587
|
-
// Some downstream code is authored as `.js` files containing JSX, so
|
|
588
|
-
// keep Storybook's esbuild settings aligned with the shared Vite config.
|
|
589
|
-
jsx: 'automatic',
|
|
590
|
-
loader: 'jsx',
|
|
591
|
-
include: /.*\.jsx?$/,
|
|
592
|
-
exclude: [],
|
|
593
|
-
},
|
|
594
|
-
optimizeDeps: {
|
|
595
|
-
...(baseViteConfig?.optimizeDeps || {}),
|
|
596
|
-
...(config?.optimizeDeps || {}),
|
|
597
|
-
include: optimizeDepsInclude,
|
|
598
|
-
exclude: mergeTwigRuntimeOptimizeDepsExcludes(
|
|
599
|
-
baseViteConfig?.optimizeDeps?.exclude,
|
|
600
|
-
config?.optimizeDeps?.exclude,
|
|
601
|
-
),
|
|
602
|
-
esbuildOptions: {
|
|
603
|
-
...(baseViteConfig?.optimizeDeps?.esbuildOptions || {}),
|
|
604
|
-
...(config?.optimizeDeps?.esbuildOptions || {}),
|
|
605
|
-
plugins: [
|
|
606
|
-
...(baseViteConfig?.optimizeDeps?.esbuildOptions?.plugins || []),
|
|
607
|
-
...(config?.optimizeDeps?.esbuildOptions?.plugins || []),
|
|
608
|
-
makeTwigVirtualModuleOptimizerPlugin(),
|
|
609
|
-
],
|
|
610
|
-
loader: {
|
|
611
|
-
...(baseViteConfig?.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
612
|
-
...(config?.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
613
|
-
// Pre-bundle `.js` dependencies with the JSX loader for packages
|
|
614
|
-
// that ship JSX without a `.jsx` extension.
|
|
615
|
-
'.js': 'jsx',
|
|
616
|
-
},
|
|
617
|
-
},
|
|
618
|
-
},
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
return {
|
|
622
|
-
...mergedConfig,
|
|
623
|
-
build: {
|
|
624
|
-
...(mergedConfig.build || {}),
|
|
625
|
-
...(storybookBuildConfig.outDir
|
|
626
|
-
? { outDir: storybookBuildConfig.outDir }
|
|
627
|
-
: {}),
|
|
628
|
-
assetsDir: storybookViteAssetsDir,
|
|
629
|
-
emptyOutDir: false,
|
|
630
|
-
},
|
|
631
|
-
resolve: mergeReactSingletonResolve(mergedConfig),
|
|
632
|
-
optimizeDeps: {
|
|
633
|
-
...(mergedConfig.optimizeDeps || {}),
|
|
634
|
-
include: mergeReactSingletonOptimizeDeps(
|
|
635
|
-
mergedConfig.optimizeDeps?.include,
|
|
636
|
-
),
|
|
637
|
-
exclude: mergeTwigRuntimeOptimizeDepsExcludes(
|
|
638
|
-
mergedConfig.optimizeDeps?.exclude,
|
|
639
|
-
),
|
|
640
|
-
esbuildOptions: {
|
|
641
|
-
...(mergedConfig.optimizeDeps?.esbuildOptions || {}),
|
|
642
|
-
loader: {
|
|
643
|
-
...(mergedConfig.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
644
|
-
'.js': 'jsx',
|
|
645
|
-
},
|
|
646
|
-
},
|
|
647
|
-
},
|
|
648
|
-
};
|
|
649
|
-
},
|
|
258
|
+
viteFinal: createViteFinal(resolvedStorybookEnv),
|
|
650
259
|
};
|
|
651
260
|
|
|
652
261
|
/**
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--colors-emulsify-blue-100: #e6f5fc;
|
|
3
|
+
--colors-emulsify-blue-200: #ccecfa;
|
|
4
|
+
--colors-emulsify-blue-300: #99d9f4;
|
|
5
|
+
--colors-emulsify-blue-400: #66c5ef;
|
|
6
|
+
--colors-emulsify-blue-500: #33b2e9;
|
|
7
|
+
--colors-emulsify-blue-600: #009fe4;
|
|
8
|
+
--colors-emulsify-blue-700: #007fb6;
|
|
9
|
+
--colors-emulsify-blue-800: #005f89;
|
|
10
|
+
--colors-emulsify-blue-900: #00405b;
|
|
11
|
+
--colors-emulsify-blue-1000: #00202e;
|
|
12
|
+
--colors-purple: #8b1e7e;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.sidebar-container {
|
|
16
|
+
background-color: var(--colors-emulsify-blue-900);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.sidebar-container .sidebar-subheading {
|
|
20
|
+
color: var(--colors-emulsify-blue-200);
|
|
21
|
+
font-size: 13px;
|
|
22
|
+
letter-spacing: 0.15em;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.sidebar-container .sidebar-subheading button:focus {
|
|
26
|
+
color: var(--colors-emulsify-blue-300);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Triangle icon. */
|
|
30
|
+
.sidebar-container .sidebar-subheading button span {
|
|
31
|
+
color: var(--colors-emulsify-blue-300);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.sidebar-container .search-field input {
|
|
35
|
+
border-color: var(--colors-emulsify-blue-700);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.sidebar-container .search-field input:active {
|
|
39
|
+
border-color: var(--colors-emulsify-blue-700);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.sidebar-container .search-result-recentlyOpened,
|
|
43
|
+
.sidebar-container .search-result-back,
|
|
44
|
+
.sidebar-container .search-result-clearHistory {
|
|
45
|
+
color: var(--colors-emulsify-blue-300) !important;
|
|
46
|
+
letter-spacing: 0.15em;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.sidebar-container .search-result-back span,
|
|
50
|
+
.sidebar-container .search-result-back svg,
|
|
51
|
+
.sidebar-container .search-result-clearHistory span,
|
|
52
|
+
.sidebar-container .search-result-clearHistory svg {
|
|
53
|
+
letter-spacing: normal;
|
|
54
|
+
color: white;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sidebar-container .sidebar-item svg {
|
|
58
|
+
margin-top: 1px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.sidebar-container .sidebar-item span {
|
|
62
|
+
margin-top: 4px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.sidebar-container .sidebar-subheading-action svg {
|
|
66
|
+
color: var(--colors-emulsify-blue-400);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.sidebar-container .sidebar-subheading-action:hover svg {
|
|
70
|
+
color: var(--colors-emulsify-blue-300);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.sidebar-header button[title='Shortcuts'] {
|
|
74
|
+
box-shadow: none;
|
|
75
|
+
border: 1px solid var(--colors-emulsify-blue-700);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.sidebar-header button[title='Shortcuts']:active {
|
|
79
|
+
border: 1px solid var(--colors-emulsify-blue-500);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.sidebar-header button[title='Shortcuts']:focus {
|
|
83
|
+
background: transparent;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#shortcuts {
|
|
87
|
+
border-bottom-color: var(--colors-emulsify-blue-900) !important;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[role='main']:not(:nth-child(3)) {
|
|
91
|
+
top: 1rem !important;
|
|
92
|
+
height: calc(100vh - 2rem) !important;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[role='main'] .os-host .os-content button:hover {
|
|
96
|
+
background: var(--colors-emulsify-blue-100);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
[role='main'] .os-host .os-content button:hover svg {
|
|
100
|
+
color: var(--colors-emulsify-blue-900);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#panel-tab-content,
|
|
104
|
+
#panel-tab-content > * {
|
|
105
|
+
color: var(--colors-emulsify-blue-100) !important;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#panel-tab-content a,
|
|
109
|
+
#panel-tab-content a span,
|
|
110
|
+
#panel-tab-content a span svg {
|
|
111
|
+
color: var(--colors-emulsify-blue-800);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#panel-tab-content > div > div > div > div > div > div {
|
|
115
|
+
background: transparent;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#panel-tab-content > div > div > div > div > div > div > div {
|
|
119
|
+
color: var(--colors-emulsify-blue-1000) !important;
|
|
120
|
+
}
|
package/.storybook/preview.js
CHANGED
|
@@ -14,12 +14,9 @@ import {
|
|
|
14
14
|
applyStoryDecorators,
|
|
15
15
|
renderPreviewStory,
|
|
16
16
|
} from '../src/storybook/preview-decorator.js';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
getStorybookPlatformAdapter,
|
|
21
|
-
setupTwig,
|
|
22
|
-
} from './utils.js';
|
|
17
|
+
import { attachStorybookBehaviors } from '../src/storybook/platform-behaviors.js';
|
|
18
|
+
import { setupTwig } from '../src/storybook/twig/setup.js';
|
|
19
|
+
import { fetchCSSFiles, getStorybookPlatformAdapter } from './utils.js';
|
|
23
20
|
|
|
24
21
|
const previewOverrideModules = import.meta.glob(
|
|
25
22
|
[
|
package/.storybook/utils.js
CHANGED
|
@@ -89,7 +89,7 @@ export function getProjectMachineName() {
|
|
|
89
89
|
: undefined;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// Keep these
|
|
92
|
+
// Keep these compatibility exports for downstream preview overrides.
|
|
93
93
|
export {
|
|
94
94
|
attachStorybookBehaviors,
|
|
95
95
|
fetchCSSFiles,
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ See [Version Evolution](docs/version-evolution.md) for more release history.
|
|
|
34
34
|
|
|
35
35
|
Twig and React are equally valid ways to build component libraries with Emulsify Core. The right authoring model depends on the consuming project:
|
|
36
36
|
|
|
37
|
-
- Use Twig for CMS themes and server-rendered template systems. Drupal has a
|
|
37
|
+
- Use Twig for CMS themes and server-rendered template systems. Drupal has a Drupal-specific adapter, and WordPress/Timber projects can use the intentionally neutral `wordpress` adapter. WordPress runtime integration belongs in `emulsify-wordpress-theme`.
|
|
38
38
|
- Use React for standalone UI libraries, application components, or projects that already use React.
|
|
39
39
|
- Use mixed Twig and React when a design system needs to document both CMS-rendered and JavaScript-rendered components in the same Storybook instance.
|
|
40
40
|
|
|
@@ -85,7 +85,7 @@ The documentation is split by task:
|
|
|
85
85
|
| [Component Authoring](docs/component-authoring.md) | Choosing Twig, React, or mixed Storybook authoring and comparing component examples. |
|
|
86
86
|
| [Storybook](docs/storybook.md) | Rendering Twig stories, using `renderTwig()`, understanding Twig runtime helpers, and mixing Twig with React stories. |
|
|
87
87
|
| [Project Structure And Output](docs/project-structure.md) | Configuring `src/components`, root `./components`, `variant.structureImplementations`, and expected output paths. |
|
|
88
|
-
| [Platform Adapters](docs/platform-adapters.md) | Understanding `none`, `drupal`, platform resolution order, and Drupal SDC behavior.
|
|
88
|
+
| [Platform Adapters](docs/platform-adapters.md) | Understanding `none`, `wordpress`, `drupal`, platform resolution order, and Drupal SDC behavior. |
|
|
89
89
|
| [Extension Points](docs/extension-points.md) | Adding Vite plugins, Tailwind CSS, Storybook preview overrides, and other framework tooling. |
|
|
90
90
|
| [Performance](docs/performance.md) | Understanding sourcemaps, eager Twig imports, Tailwind scanning, copied files, and fixture validation. |
|
|
91
91
|
| [Native Twig Extensions](docs/native-twig-extensions.md) | Using `bem()`, `add_attributes()`, and `switch/case/default/endswitch` in Twig.js. |
|
|
@@ -94,24 +94,25 @@ The documentation is split by task:
|
|
|
94
94
|
|
|
95
95
|
## Known Limitations
|
|
96
96
|
|
|
97
|
-
- Implemented platform adapters are
|
|
97
|
+
- Implemented platform adapters are `none`, `wordpress`, and `drupal`. The `wordpress` adapter is intentionally neutral: it supports Core Twig authoring, Storybook, Vite, `bem()`, `add_attributes()`, `include()`, and `source()`, but it does not emulate WordPress or Timber PHP runtime behavior. Runtime integration belongs in `emulsify-wordpress-theme`. See [Platform Adapters](docs/platform-adapters.md).
|
|
98
98
|
- Storybook's Twig resolver eagerly imports Twig modules and raw Twig source. This is reliable for `include()` and `source()`, but large Twig libraries should keep Storybook source roots intentional. See [Performance](docs/performance.md).
|
|
99
99
|
- Production sourcemaps are enabled by default unless a project overrides Vite config through `config/emulsify-core/vite/plugins.*`. See [Performance](docs/performance.md).
|
|
100
100
|
- Project extensions use the public `config/emulsify-core` directory: `config/emulsify-core/vite/plugins.*` for Vite, `config/emulsify-core/storybook/...` for Storybook, and `config/emulsify-core/a11y.config.js` for a11y. See [Extension Points](docs/extension-points.md).
|
|
101
101
|
- Webpack-specific customizations must be migrated manually to Vite plugins or `extendConfig()`. See [Migration](docs/migration-4x.md).
|
|
102
|
-
- Drupal SDC mirroring only applies when the Drupal adapter and SDC settings are enabled. `none` projects should expect output to remain in `dist/`. See [Platform Adapters](docs/platform-adapters.md).
|
|
102
|
+
- Drupal SDC mirroring only applies when the Drupal adapter and SDC settings are enabled. `none` and `wordpress` projects should expect output to remain in `dist/`. See [Platform Adapters](docs/platform-adapters.md).
|
|
103
103
|
|
|
104
104
|
## Supported Project Shapes
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
Core supports these project shapes:
|
|
107
107
|
|
|
108
108
|
- Drupal SDC projects using `src/components`.
|
|
109
109
|
- `none` platform Twig projects using `src/components`.
|
|
110
|
+
- `wordpress` platform Twig projects using `src/components`.
|
|
110
111
|
- Root `./components` projects.
|
|
111
112
|
- Projects using multiple `variant.structureImplementations`.
|
|
112
113
|
- Mixed Twig + React Storybook projects.
|
|
113
114
|
|
|
114
|
-
WordPress and Timber projects should
|
|
115
|
+
WordPress and Timber projects should use `platform: "wordpress"` when they want Core's neutral WordPress adapter. The adapter keeps output in `dist/`, loads Storybook CSS from `dist/**/*.css`, and leaves WordPress runtime behavior to `emulsify-wordpress-theme`.
|
|
115
116
|
|
|
116
117
|
## Public Imports
|
|
117
118
|
|
|
@@ -125,7 +126,7 @@ import { defineReactExtension } from '@emulsify/core/extensions/react';
|
|
|
125
126
|
|
|
126
127
|
`defineReactExtension` is reserved for future React extension support. It currently returns the input unchanged. Adopting the import path is safe; the runtime is intentionally a no-op until the registry lands. See [Extension Points](docs/extension-points.md#public-imports).
|
|
127
128
|
|
|
128
|
-
Vite consumers can import the shared config from `@emulsify/core/vite
|
|
129
|
+
Vite consumers can import the shared config from `@emulsify/core/vite`, public Vite plugin helpers from `@emulsify/core/vite/plugins`, and platform adapter helpers from `@emulsify/core/vite/platforms`.
|
|
129
130
|
|
|
130
131
|
## Contributing
|
|
131
132
|
|