@emulsify/core 4.1.0 → 4.1.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/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/config/vite/plugins/virtual-twig-asset-sources.js +29 -3
- package/package.json +4 -1
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* dist/components so the mirrored component CSS is not loaded twice.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
// Load mirrored component output first so Drupal adapter stories match theme roots.
|
|
9
10
|
import.meta.glob('../../../../components/**/*.css', { eager: true });
|
|
11
|
+
|
|
12
|
+
// Keep shared dist CSS while avoiding duplicate generated component CSS.
|
|
10
13
|
import.meta.glob(
|
|
11
14
|
['../../../../dist/**/*.css', '!../../../../dist/components/**/*.css'],
|
|
12
15
|
{ eager: true },
|
package/.storybook/css-dist.js
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const projectRoot = process.cwd();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Keeps Storybook static directory config aligned to the consuming project.
|
|
8
|
+
*
|
|
9
|
+
* @param {Array<string|{from: string, to: string}>} staticDirs - Static directory entries.
|
|
10
|
+
* @returns {Array<string|{from: string, to: string}>} Existing static directory entries.
|
|
11
|
+
*/
|
|
12
|
+
function existingStaticDirs(staticDirs) {
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
const existing = [];
|
|
15
|
+
|
|
16
|
+
for (const staticDir of staticDirs) {
|
|
17
|
+
const directory =
|
|
18
|
+
typeof staticDir === 'string' ? staticDir : staticDir.from;
|
|
19
|
+
|
|
20
|
+
if (!directory || !fs.existsSync(directory)) continue;
|
|
21
|
+
|
|
22
|
+
const key =
|
|
23
|
+
typeof staticDir === 'string'
|
|
24
|
+
? staticDir
|
|
25
|
+
: `${staticDir.from || ''}\0${staticDir.to || ''}`;
|
|
26
|
+
if (seen.has(key)) continue;
|
|
27
|
+
|
|
28
|
+
seen.add(key);
|
|
29
|
+
existing.push(staticDir);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return existing;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Build static directory mounts for normalized project asset roots.
|
|
37
|
+
*
|
|
38
|
+
* @param {object} env - Resolved project paths used by Storybook.
|
|
39
|
+
* @returns {Array<string|{from: string, to: string}>} Static directory entries.
|
|
40
|
+
*/
|
|
41
|
+
export function buildAssetStaticDirs(env) {
|
|
42
|
+
const configuredAssetRoots = Array.isArray(env.projectStructure?.assetRoots)
|
|
43
|
+
? env.projectStructure.assetRoots
|
|
44
|
+
: [];
|
|
45
|
+
const assetRoots = [
|
|
46
|
+
...configuredAssetRoots,
|
|
47
|
+
path.resolve(projectRoot, 'assets'),
|
|
48
|
+
path.resolve(projectRoot, 'src/assets'),
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
return existingStaticDirs([
|
|
52
|
+
...assetRoots.map((root) => ({
|
|
53
|
+
from: root,
|
|
54
|
+
to: '/assets',
|
|
55
|
+
})),
|
|
56
|
+
{
|
|
57
|
+
from: path.resolve(projectRoot, 'dist/assets'),
|
|
58
|
+
to: '/assets',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
from: path.resolve(projectRoot, 'dist/assets'),
|
|
62
|
+
to: '/',
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Checks whether a resolved file path stays inside an expected directory.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} filePath - Resolved candidate file path.
|
|
71
|
+
* @param {string} directory - Resolved directory that must contain the file.
|
|
72
|
+
* @returns {boolean} Whether the file path is inside the directory.
|
|
73
|
+
*/
|
|
74
|
+
function isWithinDirectory(filePath, directory) {
|
|
75
|
+
// `path.relative()` exposes traversal attempts as `..` or absolute paths.
|
|
76
|
+
const relativePath = path.relative(directory, filePath);
|
|
77
|
+
return Boolean(
|
|
78
|
+
relativePath &&
|
|
79
|
+
!relativePath.startsWith('..') &&
|
|
80
|
+
!path.isAbsolute(relativePath),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns a browser content type for generated files served by Storybook.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} filePath - Resolved file path being served.
|
|
88
|
+
* @returns {string} HTTP content type header value.
|
|
89
|
+
*/
|
|
90
|
+
function contentTypeForFile(filePath) {
|
|
91
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
92
|
+
// Keep this map small; unknown generated files can still download as binary.
|
|
93
|
+
const types = {
|
|
94
|
+
'.css': 'text/css; charset=utf-8',
|
|
95
|
+
'.gif': 'image/gif',
|
|
96
|
+
'.jpg': 'image/jpeg',
|
|
97
|
+
'.jpeg': 'image/jpeg',
|
|
98
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
99
|
+
'.json': 'application/json; charset=utf-8',
|
|
100
|
+
'.png': 'image/png',
|
|
101
|
+
'.svg': 'image/svg+xml; charset=utf-8',
|
|
102
|
+
'.webp': 'image/webp',
|
|
103
|
+
'.woff': 'font/woff',
|
|
104
|
+
'.woff2': 'font/woff2',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return types[extension] || 'application/octet-stream';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Serves generated dist files that may not exist when `staticDirs` is built.
|
|
112
|
+
*
|
|
113
|
+
* Storybook validates static directories during config load, but Emulsify
|
|
114
|
+
* projects often generate `dist` after Storybook starts. This middleware keeps
|
|
115
|
+
* those generated asset URLs available without replacing Vite's CSS HMR.
|
|
116
|
+
*
|
|
117
|
+
* @param {import('http').IncomingMessage} req - Vite dev server request.
|
|
118
|
+
* @param {import('http').ServerResponse} res - Vite dev server response.
|
|
119
|
+
* @param {Function} next - Next middleware callback.
|
|
120
|
+
* @returns {void}
|
|
121
|
+
*/
|
|
122
|
+
function serveGeneratedDistFile(req, res, next) {
|
|
123
|
+
const method = req.method || 'GET';
|
|
124
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
125
|
+
next();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let pathname = '';
|
|
130
|
+
try {
|
|
131
|
+
// Malformed URLs should fall through to Storybook's normal Vite server.
|
|
132
|
+
pathname = decodeURIComponent(
|
|
133
|
+
new URL(req.url || '/', 'http://localhost').pathname,
|
|
134
|
+
);
|
|
135
|
+
} catch {
|
|
136
|
+
next();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// These URL shapes match Emulsify's compiled CSS and sprite references.
|
|
141
|
+
const routes = [
|
|
142
|
+
{
|
|
143
|
+
pathname: '/icons.svg',
|
|
144
|
+
file: path.resolve(projectRoot, 'dist/assets/icons.svg'),
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
prefix: '/assets/',
|
|
148
|
+
directory: path.resolve(projectRoot, 'dist/assets'),
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
prefix: '/dist/',
|
|
152
|
+
directory: path.resolve(projectRoot, 'dist'),
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
const route = routes.find(({ prefix, pathname: routePathname }) =>
|
|
156
|
+
routePathname ? pathname === routePathname : pathname.startsWith(prefix),
|
|
157
|
+
);
|
|
158
|
+
if (!route) {
|
|
159
|
+
next();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const filePath = route.file
|
|
164
|
+
? route.file
|
|
165
|
+
: path.resolve(route.directory, pathname.slice(route.prefix.length));
|
|
166
|
+
// Resolve from known roots only, then reject traversal before reading.
|
|
167
|
+
if (route.directory && !isWithinDirectory(filePath, route.directory)) {
|
|
168
|
+
next();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const stats = fs.statSync(filePath);
|
|
174
|
+
if (!stats.isFile()) {
|
|
175
|
+
next();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (path.extname(filePath).toLowerCase() === '.css') {
|
|
179
|
+
next();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
res.statusCode = 200;
|
|
184
|
+
res.setHeader('Content-Type', contentTypeForFile(filePath));
|
|
185
|
+
res.setHeader('Content-Length', stats.size);
|
|
186
|
+
if (method === 'HEAD') {
|
|
187
|
+
res.end();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
res.end(fs.readFileSync(filePath));
|
|
191
|
+
} catch {
|
|
192
|
+
next();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Adds Vite dev-server access to generated dist files.
|
|
198
|
+
*
|
|
199
|
+
* CSS itself is still imported through native `import.meta.glob()` calls in the
|
|
200
|
+
* preview runtime; this plugin only fills the static-file gap for late-created
|
|
201
|
+
* dist assets.
|
|
202
|
+
*
|
|
203
|
+
* @returns {import('vite').Plugin} Vite middleware plugin.
|
|
204
|
+
*/
|
|
205
|
+
export function makeGeneratedDistFilesPlugin() {
|
|
206
|
+
return {
|
|
207
|
+
name: 'emulsify-generated-dist-files',
|
|
208
|
+
configureServer(server) {
|
|
209
|
+
server.middlewares.use(serveGeneratedDistFile);
|
|
210
|
+
// Watch generated assets so Vite notices files created after startup.
|
|
211
|
+
server.watcher.add([
|
|
212
|
+
path.join(projectRoot, 'dist/**/*.css'),
|
|
213
|
+
path.join(projectRoot, 'dist/assets/**/*'),
|
|
214
|
+
]);
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import viteConfig from '../config/vite/vite.config.js';
|
|
3
|
+
import { twigExtensionModuleSpecifiers } from '../config/vite/twig-extensions.js';
|
|
4
|
+
import {
|
|
5
|
+
mergeReactSingletonOptimizeDeps,
|
|
6
|
+
mergeReactSingletonResolve,
|
|
7
|
+
} from '../config/vite/utils/react-singleton.js';
|
|
8
|
+
import { makeGeneratedDistFilesPlugin } from './main-static-assets.js';
|
|
9
|
+
|
|
10
|
+
// Twig glob maps are provided by config/vite/plugins/virtual-twig-globs.js.
|
|
11
|
+
const twigVirtualModuleIds = [
|
|
12
|
+
'virtual:emulsify-twig-globs',
|
|
13
|
+
'virtual:emulsify-twig-asset-sources',
|
|
14
|
+
'virtual:emulsify-twig-extension-installers',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const twigRuntimeOptimizeDepsExclude = [
|
|
18
|
+
...twigVirtualModuleIds,
|
|
19
|
+
'@emulsify/core/storybook/twig/source-function',
|
|
20
|
+
'@emulsify/core/storybook/twig/source',
|
|
21
|
+
'@emulsify/core/storybook/twig/resolver',
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Vite-generated Storybook chunks should not share `/assets` with project
|
|
26
|
+
* static files. Storybook copies staticDirs while the preview build runs, so
|
|
27
|
+
* keeping generated chunks in a separate folder avoids concurrent writers in
|
|
28
|
+
* `.out/assets`.
|
|
29
|
+
*
|
|
30
|
+
* @type {string}
|
|
31
|
+
*/
|
|
32
|
+
const storybookViteAssetsDir = 'storybook-assets';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Merge Storybook and project optimizeDeps excludes with Core Twig runtime IDs.
|
|
36
|
+
*
|
|
37
|
+
* Storybook's dependency optimizer runs before normal Vite virtual module
|
|
38
|
+
* resolution. Core Twig runtime modules import virtual IDs that must stay in
|
|
39
|
+
* the Vite module graph so Emulsify's virtual plugins can resolve them.
|
|
40
|
+
*
|
|
41
|
+
* @param {...string[]} excludeLists - Existing optimizeDeps exclude arrays.
|
|
42
|
+
* @returns {string[]} Merged exclude list.
|
|
43
|
+
*/
|
|
44
|
+
function mergeTwigRuntimeOptimizeDepsExcludes(...excludeLists) {
|
|
45
|
+
return Array.from(
|
|
46
|
+
new Set([
|
|
47
|
+
...excludeLists.flatMap((excludeList) =>
|
|
48
|
+
Array.isArray(excludeList) ? excludeList : [],
|
|
49
|
+
),
|
|
50
|
+
...twigRuntimeOptimizeDepsExclude,
|
|
51
|
+
]),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Keep Emulsify Twig virtual imports out of Storybook dependency prebundles.
|
|
57
|
+
*
|
|
58
|
+
* @returns {import('esbuild').Plugin} Esbuild plugin for optimizeDeps.
|
|
59
|
+
*/
|
|
60
|
+
function makeTwigVirtualModuleOptimizerPlugin() {
|
|
61
|
+
return {
|
|
62
|
+
name: 'emulsify-twig-virtual-modules',
|
|
63
|
+
setup(build) {
|
|
64
|
+
build.onResolve(
|
|
65
|
+
{ filter: /^virtual:emulsify-twig-(?:globs|asset-sources)$/ },
|
|
66
|
+
(args) => ({
|
|
67
|
+
path: args.path,
|
|
68
|
+
external: true,
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Builds the Storybook Vite config merger.
|
|
77
|
+
*
|
|
78
|
+
* @param {object} resolvedStorybookEnv - Resolved project paths used by Storybook.
|
|
79
|
+
* @returns {Function} Storybook `viteFinal` callback.
|
|
80
|
+
*/
|
|
81
|
+
export function createViteFinal(resolvedStorybookEnv) {
|
|
82
|
+
return async function viteFinal(config) {
|
|
83
|
+
const { mergeConfig } = await import('vite');
|
|
84
|
+
const env = resolvedStorybookEnv;
|
|
85
|
+
const storybookBuildConfig = config?.build || {};
|
|
86
|
+
|
|
87
|
+
// Keep using the `serve` branch of the shared Vite config here. Storybook
|
|
88
|
+
// has historically consumed that branch, while `mode` still reflects
|
|
89
|
+
// whether Storybook is running in development or production.
|
|
90
|
+
const mode = config?.mode || 'development';
|
|
91
|
+
const baseViteConfig =
|
|
92
|
+
typeof viteConfig === 'function'
|
|
93
|
+
? await viteConfig({ command: 'serve', mode })
|
|
94
|
+
: viteConfig;
|
|
95
|
+
const existingDefine = (config && config.define) || {};
|
|
96
|
+
const viteDefine = (baseViteConfig && baseViteConfig.define) || {};
|
|
97
|
+
|
|
98
|
+
// Allow Storybook's dev server to read component sources from the project
|
|
99
|
+
// root and any structure override paths used by Emulsify consumers.
|
|
100
|
+
const allowList = new Set([
|
|
101
|
+
...(config?.server?.fs?.allow || []),
|
|
102
|
+
env.projectDir,
|
|
103
|
+
path.resolve(env.projectDir, 'src'),
|
|
104
|
+
path.resolve(env.projectDir, 'components'),
|
|
105
|
+
path.resolve(env.projectDir, 'dist'),
|
|
106
|
+
...(Array.isArray(env.projectStructure?.sourceRoots)
|
|
107
|
+
? env.projectStructure.sourceRoots
|
|
108
|
+
: []),
|
|
109
|
+
...(Array.isArray(env.componentRoots) ? env.componentRoots : []),
|
|
110
|
+
...(Array.isArray(env.structureRoots) ? env.structureRoots : []),
|
|
111
|
+
...(env.namespaceRoots && typeof env.namespaceRoots === 'object'
|
|
112
|
+
? Object.values(env.namespaceRoots)
|
|
113
|
+
: []),
|
|
114
|
+
...(Array.isArray(env.projectStructure?.assetRoots)
|
|
115
|
+
? env.projectStructure.assetRoots
|
|
116
|
+
: []),
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
// Twig files are loaded through custom resolvers/plugins, so they need to
|
|
120
|
+
// be treated as importable assets by Storybook's Vite pipeline.
|
|
121
|
+
const assetsInclude = Array.from(
|
|
122
|
+
new Set([
|
|
123
|
+
...(config.assetsInclude || []),
|
|
124
|
+
...(baseViteConfig.assetsInclude || []),
|
|
125
|
+
'**/*.twig',
|
|
126
|
+
]),
|
|
127
|
+
);
|
|
128
|
+
const optimizeDepsInclude = mergeReactSingletonOptimizeDeps(
|
|
129
|
+
baseViteConfig?.optimizeDeps?.include,
|
|
130
|
+
config?.optimizeDeps?.include,
|
|
131
|
+
[
|
|
132
|
+
'twig',
|
|
133
|
+
'@emulsify/core/extensions/twig',
|
|
134
|
+
...twigExtensionModuleSpecifiers(env),
|
|
135
|
+
],
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const mergedConfig = mergeConfig(config, {
|
|
139
|
+
...baseViteConfig,
|
|
140
|
+
resolve: mergeReactSingletonResolve(baseViteConfig, config),
|
|
141
|
+
define: {
|
|
142
|
+
// Preserve shared and Storybook-provided constants, then publish the
|
|
143
|
+
// resolved Emulsify environment to client-side code.
|
|
144
|
+
...viteDefine,
|
|
145
|
+
...existingDefine,
|
|
146
|
+
__EMULSIFY_ENV__: JSON.stringify(env),
|
|
147
|
+
'globalThis.__EMULSIFY_ENV__': JSON.stringify(env),
|
|
148
|
+
},
|
|
149
|
+
server: {
|
|
150
|
+
...(baseViteConfig?.server || {}),
|
|
151
|
+
fs: {
|
|
152
|
+
allow: Array.from(allowList),
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
assetsInclude,
|
|
156
|
+
plugins: [
|
|
157
|
+
...(baseViteConfig?.plugins || []),
|
|
158
|
+
makeGeneratedDistFilesPlugin(),
|
|
159
|
+
],
|
|
160
|
+
esbuild: {
|
|
161
|
+
// Some downstream code is authored as `.js` files containing JSX, so
|
|
162
|
+
// keep Storybook's esbuild settings aligned with the shared Vite config.
|
|
163
|
+
jsx: 'automatic',
|
|
164
|
+
loader: 'jsx',
|
|
165
|
+
include: /.*\.jsx?$/,
|
|
166
|
+
exclude: [],
|
|
167
|
+
},
|
|
168
|
+
optimizeDeps: {
|
|
169
|
+
...(baseViteConfig?.optimizeDeps || {}),
|
|
170
|
+
...(config?.optimizeDeps || {}),
|
|
171
|
+
include: optimizeDepsInclude,
|
|
172
|
+
exclude: mergeTwigRuntimeOptimizeDepsExcludes(
|
|
173
|
+
baseViteConfig?.optimizeDeps?.exclude,
|
|
174
|
+
config?.optimizeDeps?.exclude,
|
|
175
|
+
),
|
|
176
|
+
esbuildOptions: {
|
|
177
|
+
...(baseViteConfig?.optimizeDeps?.esbuildOptions || {}),
|
|
178
|
+
...(config?.optimizeDeps?.esbuildOptions || {}),
|
|
179
|
+
plugins: [
|
|
180
|
+
...(baseViteConfig?.optimizeDeps?.esbuildOptions?.plugins || []),
|
|
181
|
+
...(config?.optimizeDeps?.esbuildOptions?.plugins || []),
|
|
182
|
+
makeTwigVirtualModuleOptimizerPlugin(),
|
|
183
|
+
],
|
|
184
|
+
loader: {
|
|
185
|
+
...(baseViteConfig?.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
186
|
+
...(config?.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
187
|
+
// Pre-bundle `.js` dependencies with the JSX loader for packages
|
|
188
|
+
// that ship JSX without a `.jsx` extension.
|
|
189
|
+
'.js': 'jsx',
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
...mergedConfig,
|
|
197
|
+
build: {
|
|
198
|
+
...(mergedConfig.build || {}),
|
|
199
|
+
...(storybookBuildConfig.outDir
|
|
200
|
+
? { outDir: storybookBuildConfig.outDir }
|
|
201
|
+
: {}),
|
|
202
|
+
assetsDir: storybookViteAssetsDir,
|
|
203
|
+
emptyOutDir: false,
|
|
204
|
+
},
|
|
205
|
+
resolve: mergeReactSingletonResolve(mergedConfig),
|
|
206
|
+
optimizeDeps: {
|
|
207
|
+
...(mergedConfig.optimizeDeps || {}),
|
|
208
|
+
include: mergeReactSingletonOptimizeDeps(
|
|
209
|
+
mergedConfig.optimizeDeps?.include,
|
|
210
|
+
),
|
|
211
|
+
exclude: mergeTwigRuntimeOptimizeDepsExcludes(
|
|
212
|
+
mergedConfig.optimizeDeps?.exclude,
|
|
213
|
+
),
|
|
214
|
+
esbuildOptions: {
|
|
215
|
+
...(mergedConfig.optimizeDeps?.esbuildOptions || {}),
|
|
216
|
+
loader: {
|
|
217
|
+
...(mergedConfig.optimizeDeps?.esbuildOptions?.loader || {}),
|
|
218
|
+
'.js': 'jsx',
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
}
|
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,
|
|
@@ -16,6 +16,7 @@ export const VIRTUAL_TWIG_ASSET_SOURCES_ID =
|
|
|
16
16
|
'virtual:emulsify-twig-asset-sources';
|
|
17
17
|
const RESOLVED_VIRTUAL_TWIG_ASSET_SOURCES_ID = `\0${VIRTUAL_TWIG_ASSET_SOURCES_ID}`;
|
|
18
18
|
const GENERATED_ASSET_ALIASES = new Set(['icons.svg']);
|
|
19
|
+
const GENERATED_ASSET_ROOTS = ['/dist/assets'];
|
|
19
20
|
const PUBLIC_ASSET_ROOTS = new Map([
|
|
20
21
|
['/assets', '/assets'],
|
|
21
22
|
['/dist/assets', '/assets'],
|
|
@@ -116,14 +117,21 @@ export function assetSourceRoots(env) {
|
|
|
116
117
|
*/
|
|
117
118
|
export function generatedAssetSourceRoots(env) {
|
|
118
119
|
return unique(
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
GENERATED_ASSET_ROOTS.map((root) =>
|
|
121
|
+
toAbsoluteAssetRoot(env?.projectDir, root),
|
|
122
|
+
)
|
|
121
123
|
.filter((root) => root && safeExists(root))
|
|
122
124
|
.map((root) => toRootRelativePath(env?.projectDir, root))
|
|
123
125
|
.filter(Boolean),
|
|
124
126
|
);
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
function generatedAssetRootPrefixes() {
|
|
130
|
+
return GENERATED_ASSET_ROOTS.map((root) =>
|
|
131
|
+
`${root}/`.replace(/\/{2,}/g, '/'),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
127
135
|
/**
|
|
128
136
|
* Build Vite glob patterns from text asset roots.
|
|
129
137
|
*
|
|
@@ -216,6 +224,20 @@ export function publicAssetSourceEntries(env) {
|
|
|
216
224
|
}
|
|
217
225
|
}
|
|
218
226
|
|
|
227
|
+
for (const root of generatedAssetRootPrefixes()) {
|
|
228
|
+
const publicBase = publicAssetBaseForRoot(root);
|
|
229
|
+
if (!publicBase) continue;
|
|
230
|
+
|
|
231
|
+
for (const alias of GENERATED_ASSET_ALIASES) {
|
|
232
|
+
const key = `${root.replace(/\/+$/, '')}/${alias}`.replace(
|
|
233
|
+
/\/{2,}/g,
|
|
234
|
+
'/',
|
|
235
|
+
);
|
|
236
|
+
const url = `${publicBase}${alias}`.replace(/\/{2,}/g, '/');
|
|
237
|
+
entries.set(key, { key, url });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
219
241
|
return Array.from(entries.values());
|
|
220
242
|
}
|
|
221
243
|
|
|
@@ -232,6 +254,10 @@ export function generateVirtualTwigAssetSourcesModule(env) {
|
|
|
232
254
|
const generatedRootPrefixes = generatedAssetSourceRoots(env).map((root) =>
|
|
233
255
|
`${root === '/' ? '' : root}/`.replace(/\/{2,}/g, '/'),
|
|
234
256
|
);
|
|
257
|
+
const allGeneratedRootPrefixes = unique([
|
|
258
|
+
...generatedRootPrefixes,
|
|
259
|
+
...generatedAssetRootPrefixes(),
|
|
260
|
+
]);
|
|
235
261
|
const patterns = assetSourceGlobPatterns(env);
|
|
236
262
|
const globEntries = patterns.length
|
|
237
263
|
? patterns
|
|
@@ -255,7 +281,7 @@ export function generateVirtualTwigAssetSourcesModule(env) {
|
|
|
255
281
|
*/
|
|
256
282
|
|
|
257
283
|
export const assetRootPrefixes = ${JSON.stringify(rootPrefixes)};
|
|
258
|
-
export const generatedAssetRootPrefixes = ${JSON.stringify(
|
|
284
|
+
export const generatedAssetRootPrefixes = ${JSON.stringify(allGeneratedRootPrefixes)};
|
|
259
285
|
export const generatedAssetAliases = ${JSON.stringify(
|
|
260
286
|
Array.from(GENERATED_ASSET_ALIASES),
|
|
261
287
|
)};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emulsify/core",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.1",
|
|
4
4
|
"description": "Bundled tooling for Storybook development + Vite Build",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"component library",
|
|
@@ -38,7 +38,10 @@
|
|
|
38
38
|
".storybook/css-components.js",
|
|
39
39
|
".storybook/css-dist.js",
|
|
40
40
|
".storybook/emulsifyTheme.js",
|
|
41
|
+
".storybook/main-static-assets.js",
|
|
42
|
+
".storybook/main-vite.js",
|
|
41
43
|
".storybook/main.js",
|
|
44
|
+
".storybook/manager-head.css",
|
|
42
45
|
".storybook/manager.js",
|
|
43
46
|
".storybook/preview.js",
|
|
44
47
|
".storybook/utils.js",
|