@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.
@@ -5,7 +5,21 @@
5
5
  * decisions can be used by Node-side Vite config and Storybook browser code.
6
6
  */
7
7
 
8
- const noneAdapter = {
8
+ /**
9
+ * Freeze adapter definition data while preserving cloneable plain objects.
10
+ *
11
+ * @param {object} adapter - Adapter definition.
12
+ * @returns {object} Frozen adapter definition.
13
+ */
14
+ function freezeAdapter(adapter) {
15
+ return Object.freeze({
16
+ ...adapter,
17
+ storybook: Object.freeze({ ...adapter.storybook }),
18
+ build: Object.freeze({ ...adapter.build }),
19
+ });
20
+ }
21
+
22
+ const noneAdapter = freezeAdapter({
9
23
  name: 'none',
10
24
  outputStrategy: 'dist',
11
25
  storybook: {
@@ -18,9 +32,9 @@ const noneAdapter = {
18
32
  build: {
19
33
  mirrorDistComponentsToRoot: false,
20
34
  },
21
- };
35
+ });
22
36
 
23
- const drupalAdapter = {
37
+ const drupalAdapter = freezeAdapter({
24
38
  name: 'drupal',
25
39
  outputStrategy: 'drupal-sdc',
26
40
  storybook: {
@@ -33,13 +47,29 @@ const drupalAdapter = {
33
47
  build: {
34
48
  mirrorDistComponentsToRoot: true,
35
49
  },
36
- };
50
+ });
37
51
 
38
- const adapters = {
52
+ const wordpressAdapter = freezeAdapter({
53
+ name: 'wordpress',
54
+ outputStrategy: 'dist',
55
+ storybook: {
56
+ loadDrupalBehaviorShim: false,
57
+ attachDrupalBehaviors: false,
58
+ registerDrupalTwigFilters: false,
59
+ loadMirroredComponentCss: false,
60
+ allowSyncXhrSource: false,
61
+ },
62
+ build: {
63
+ mirrorDistComponentsToRoot: false,
64
+ },
65
+ });
66
+
67
+ const adapters = Object.freeze({
39
68
  none: noneAdapter,
40
69
  generic: noneAdapter,
41
70
  drupal: drupalAdapter,
42
- };
71
+ wordpress: wordpressAdapter,
72
+ });
43
73
 
44
74
  /**
45
75
  * Deep-clone an adapter so callers can safely serialize or extend it.
@@ -80,10 +110,7 @@ export function normalizePlatformName(platform = 'none') {
80
110
  */
81
111
  export function getPlatformAdapter(platform = 'none') {
82
112
  const key = normalizePlatformName(platform);
83
- if (key === 'drupal') {
84
- return cloneAdapter(drupalAdapter);
85
- }
86
- return cloneAdapter(noneAdapter);
113
+ return cloneAdapter(adapters[key] || noneAdapter);
87
114
  }
88
115
 
89
116
  export { adapters };
@@ -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
- ['/dist/assets']
120
- .map((root) => toAbsoluteAssetRoot(env?.projectDir, root))
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(generatedRootPrefixes)};
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.0",
3
+ "version": "4.2.0",
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",
@@ -124,6 +127,7 @@
124
127
  "./storybook/twig/source": "./src/storybook/twig/source.js",
125
128
  "./vite": "./config/vite/vite.config.js",
126
129
  "./vite/plugins": "./config/vite/plugins.js",
130
+ "./vite/platforms": "./config/vite/platforms.js",
127
131
  "./package.json": "./package.json"
128
132
  },
129
133
  "publishConfig": {
@@ -150,6 +154,7 @@
150
154
  "prettier": "npm run check-node-version && prettier --check --config config/.prettierrc.json --ignore-unknown \"**/*.{js,mjs,cjs,jsx,json,yml,yaml,scss,md,twig}\"",
151
155
  "prettier-fix": "npm run check-node-version && prettier --config config/.prettierrc.json --write --ignore-unknown \"**/*.{js,mjs,cjs,jsx,json,yml,yaml,scss,md,twig}\"",
152
156
  "semantic-release": "npm run check-node-version && semantic-release --config ./release.config.cjs",
157
+ "smoke:pack": "npm run check-node-version && node scripts/smoke-pack.js",
153
158
  "version:develop": "npm run check-node-version && node scripts/bump-version-from-commits.js",
154
159
  "storybook": "npm run check-node-version && NODE_OPTIONS=--no-deprecation storybook dev -p 6006 --no-open --exact-port",
155
160
  "storybook-build": "npm run check-node-version && storybook build -o .out",
@@ -161,7 +166,7 @@
161
166
  "@babel/core": "^7.29.7",
162
167
  "@babel/eslint-parser": "^7.28.6",
163
168
  "@babel/preset-env": "^7.28.3",
164
- "@emulsify/cli": "^1.11.4",
169
+ "@emulsify/cli": "^2.2.0",
165
170
  "@eslint/js": "^9.39.4",
166
171
  "@storybook/addon-a11y": "^10.1.4",
167
172
  "@storybook/addon-links": "^10.1.4",
@@ -169,31 +174,27 @@
169
174
  "@storybook/react": "^10.1.4",
170
175
  "@storybook/react-vite": "^10.1.4",
171
176
  "@vituum/vite-plugin-twig": "^1.1.0",
172
- "autoprefixer": "^10.4.21",
177
+ "autoprefixer": "^10.5.2",
173
178
  "axe-core": "^4.11.4",
174
179
  "babel-preset-minify": "^0.5.2",
175
180
  "concurrently": "^9.2.3",
176
181
  "eslint": "^9.39.4",
177
182
  "eslint-config-prettier": "^10.1.8",
178
183
  "eslint-plugin-import": "^2.32.0",
179
- "eslint-plugin-jest": "^29.0.1",
184
+ "eslint-plugin-jest": "^29.15.4",
180
185
  "eslint-plugin-prettier": "^5.5.4",
181
- "eslint-plugin-security": "^4.0.0",
182
- "eslint-plugin-storybook": "^10.1.4",
183
- "fs-extra": "^11.3.1",
186
+ "eslint-plugin-security": "^4.0.1",
187
+ "eslint-plugin-storybook": "^10.4.6",
184
188
  "glob": "^13.0.6",
185
- "graceful-fs": "^4.2.11",
186
189
  "jest": "^30.2.0",
187
190
  "jest-environment-jsdom": "^30.2.0",
188
191
  "js-yaml": "^4.1.0",
189
192
  "normalize.css": "^8.0.1",
190
193
  "open-cli": "^9.0.0",
191
194
  "pa11y": "^9.0.1",
192
- "postcss": "^8.5.4",
195
+ "postcss": "^8.5.16",
193
196
  "postcss-scss": "^4.0.9",
194
- "ramda": "^0.32.0",
195
- "regenerator-runtime": "^0.14.1",
196
- "sass": "^1.93.2",
197
+ "sass": "^1.101.0",
197
198
  "storybook": "^10.1.4",
198
199
  "stylelint": "^17.12.0",
199
200
  "stylelint-config-standard-scss": "^17.0.0",
@@ -208,18 +209,18 @@
208
209
  "yaml": "^2.8.1"
209
210
  },
210
211
  "devDependencies": {
211
- "@commitlint/cli": "^21.0.1",
212
- "@commitlint/config-conventional": "^21.0.1",
212
+ "@commitlint/cli": "^21.2.0",
213
+ "@commitlint/config-conventional": "^21.2.0",
213
214
  "@semantic-release/changelog": "^6.0.2",
214
215
  "@semantic-release/commit-analyzer": "^13.0.1",
215
216
  "@semantic-release/git": "^10.0.1",
216
- "@semantic-release/github": "^12.0.8",
217
+ "@semantic-release/github": "^12.0.9",
217
218
  "@semantic-release/npm": "^13.1.5",
218
219
  "@semantic-release/release-notes-generator": "^14.1.0",
219
220
  "husky": "^9.1.7",
220
- "lint-staged": "^17.0.5",
221
- "react": "^19.2.0",
222
- "react-dom": "^19.2.0",
221
+ "lint-staged": "^17.0.8",
222
+ "react": "^19.2.7",
223
+ "react-dom": "^19.2.7",
223
224
  "semantic-release": "^25.0.3"
224
225
  },
225
226
  "peerDependencies": {
package/scripts/a11y.js CHANGED
@@ -8,7 +8,6 @@
8
8
  import { existsSync, readFileSync } from 'fs';
9
9
  import path from 'path';
10
10
  import { fileURLToPath, pathToFileURL } from 'url';
11
- import * as R from 'ramda';
12
11
  import pa11y from 'pa11y';
13
12
 
14
13
  import a11yConfig from '../config/a11y.config.js';
@@ -210,11 +209,12 @@ const resolvePa11yStoryIds = ({
210
209
  * @param {'error'|'warning'|'notice'} severity
211
210
  * @returns {'red'|'yellow'|'blue'|undefined}
212
211
  */
213
- const severityToColor = R.cond([
214
- [R.equals('error'), R.always('red')],
215
- [R.equals('warning'), R.always('yellow')],
216
- [R.equals('notice'), R.always('blue')],
217
- ]);
212
+ const severityToColor = (severity) =>
213
+ ({
214
+ error: 'red',
215
+ warning: 'yellow',
216
+ notice: 'blue',
217
+ })[severity];
218
218
 
219
219
  /**
220
220
  * @typedef {Object} Pa11yIssue
@@ -297,25 +297,20 @@ const lintComponent = async (name) =>
297
297
  * @param {string[]} names - List of Storybook story IDs.
298
298
  * @returns {Promise<void>}
299
299
  */
300
- const lintReportAndExit = R.pipe(
301
- /** @param {string[]} list */
302
- (list) => list.map(lintComponent),
303
- (promises) => Promise.all(promises),
304
- R.andThen(
305
- R.pipe(
306
- /** @param {Array<{issues: Pa11yIssue[], pageUrl: string}>} results */
307
- (results) => results.map(logReport),
308
- R.reject(R.equals(false)),
309
- R.unless(R.isEmpty, () => process.exit(1)),
310
- ),
311
- ),
312
- );
300
+ const lintReportAndExit = async (names) => {
301
+ const results = await Promise.all(names.map(lintComponent));
302
+ const hasIssues = results.map(logReport).some(Boolean);
303
+
304
+ if (hasIssues) {
305
+ process.exit(1);
306
+ }
307
+ };
313
308
 
314
309
  // Only perform linting/reporting when instructed via "-r".
315
310
  /* istanbul ignore next */
316
- if (R.includes(process.argv[2], ['-h', '--help'])) {
311
+ if (['-h', '--help'].includes(process.argv[2])) {
317
312
  printHelp();
318
- } else if (R.pathEq(['argv', 2], '-r')(process)) {
313
+ } else if (process.argv[2] === '-r') {
319
314
  loadProjectA11yConfig().then((projectConfig) => {
320
315
  applyProjectA11yConfig(projectConfig);
321
316
  return lintReportAndExit(resolvePa11yStoryIds());
@@ -84,7 +84,7 @@ function warnTextAssetSource(relPath, reason) {
84
84
 
85
85
  warnedAssetSources.add(relPath);
86
86
  console.warn(
87
- `source(): ${reason} for @assets/${relPath}. Synchronous XHR fallback is disabled by default because it blocks Storybook rendering. Move the asset under a configured asset root such as src/assets or assets, or temporarily enable platformAdapter.storybook.allowSyncXhrSource. The sync-XHR fallback is deprecated and will be removed in 4.2.`,
87
+ `source(): ${reason} for @assets/${relPath}. Synchronous XHR fallback is disabled by default because it blocks Storybook rendering. Move the asset under a configured asset root such as src/assets or assets, or temporarily enable platformAdapter.storybook.allowSyncXhrSource. The sync-XHR fallback is deprecated and scheduled for removal in a future major release.`,
88
88
  );
89
89
  }
90
90