@emulsify/core 3.4.1 → 4.0.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.
Files changed (110) hide show
  1. package/.cli/init.js +40 -31
  2. package/.storybook/_drupal.js +129 -8
  3. package/.storybook/css-components.js +13 -0
  4. package/.storybook/css-dist.js +5 -0
  5. package/.storybook/emulsifyTheme.js +10 -7
  6. package/.storybook/main.js +417 -65
  7. package/.storybook/manager.js +11 -18
  8. package/.storybook/preview.js +93 -37
  9. package/.storybook/utils.js +70 -69
  10. package/README.md +110 -59
  11. package/config/.stylelintrc.json +2 -6
  12. package/config/a11y.config.js +9 -5
  13. package/config/babel.config.js +5 -0
  14. package/config/eslint.config.js +6 -3
  15. package/config/postcss.config.js +5 -0
  16. package/config/vite/entries.js +227 -0
  17. package/config/vite/environment.js +39 -0
  18. package/config/vite/platforms.js +70 -0
  19. package/config/vite/plugins/copy-src-assets.js +76 -0
  20. package/config/vite/plugins/copy-twig-files.js +84 -0
  21. package/config/vite/plugins/css-asset-relativizer.js +40 -0
  22. package/config/vite/plugins/index.js +105 -0
  23. package/config/vite/plugins/mirror-components.js +358 -0
  24. package/config/vite/plugins/require-context.js +311 -0
  25. package/config/vite/plugins/source-file-index.js +184 -0
  26. package/config/vite/plugins/svg-sprite.js +117 -0
  27. package/config/vite/plugins/twig-extension-installers.js +36 -0
  28. package/config/vite/plugins/twig-module.js +1251 -0
  29. package/config/vite/plugins/virtual-twig-asset-sources.js +404 -0
  30. package/config/vite/plugins/virtual-twig-globs.js +136 -0
  31. package/config/vite/plugins/vituum-patch.js +167 -0
  32. package/config/vite/plugins/yaml-module.js +133 -0
  33. package/config/vite/plugins.js +12 -0
  34. package/config/vite/project-config.js +192 -0
  35. package/config/vite/project-extensions.js +177 -0
  36. package/config/vite/project-structure.js +447 -0
  37. package/config/vite/twig-extensions.js +109 -0
  38. package/config/vite/utils/fs-safe.js +66 -0
  39. package/config/vite/utils/paths.js +40 -0
  40. package/config/vite/utils/react-singleton.js +85 -0
  41. package/config/vite/utils/unique.js +36 -0
  42. package/config/vite/vite.config.js +161 -0
  43. package/package.json +168 -88
  44. package/scripts/a11y.js +70 -16
  45. package/scripts/audit-twig-stories.js +378 -0
  46. package/scripts/audit.js +1602 -0
  47. package/scripts/check-node-version.js +18 -0
  48. package/scripts/loadYaml.js +5 -1
  49. package/src/extensions/index.js +8 -0
  50. package/src/extensions/react/index.js +12 -0
  51. package/src/extensions/react/register.js +45 -0
  52. package/src/extensions/shared/attributes.js +308 -0
  53. package/src/extensions/shared/html.js +41 -0
  54. package/src/extensions/shared/lists.js +38 -0
  55. package/src/extensions/shared/object.js +22 -0
  56. package/src/extensions/twig/function-map.js +20 -0
  57. package/src/extensions/twig/functions/add-attributes.js +39 -0
  58. package/src/extensions/twig/functions/bem.js +166 -0
  59. package/src/extensions/twig/index.js +13 -0
  60. package/src/extensions/twig/register.js +52 -0
  61. package/src/extensions/twig/tag-map.js +16 -0
  62. package/src/extensions/twig/tags/switch.js +266 -0
  63. package/src/storybook/index.js +14 -0
  64. package/src/storybook/main-config.js +132 -0
  65. package/src/storybook/platform-behaviors.js +60 -0
  66. package/src/storybook/preview-parameters.js +81 -0
  67. package/src/storybook/render-twig.js +295 -0
  68. package/src/storybook/twig/drupal-filters.js +7 -0
  69. package/src/storybook/twig/include-function.js +109 -0
  70. package/src/storybook/twig/include.js +28 -0
  71. package/src/storybook/twig/reference-paths.js +294 -0
  72. package/src/storybook/twig/resolver.js +318 -0
  73. package/src/storybook/twig/setup.js +39 -0
  74. package/src/storybook/twig/source-events.js +5 -0
  75. package/src/storybook/twig/source-extensions.js +24 -0
  76. package/src/storybook/twig/source-function.js +239 -0
  77. package/src/storybook/twig/source.js +39 -0
  78. package/.all-contributorsrc +0 -45
  79. package/.editorconfig +0 -5
  80. package/.github/ISSUE_TEMPLATE/BUG_REPORT_TEMPLATE.md +0 -18
  81. package/.github/ISSUE_TEMPLATE/FEATURE_REQUEST_TEMPLATE.md +0 -11
  82. package/.github/PULL_REQUEST_TEMPLATE.md +0 -19
  83. package/.github/dependabot.yml +0 -6
  84. package/.github/workflows/addtoprojects.yml +0 -21
  85. package/.github/workflows/contributors.yml +0 -37
  86. package/.github/workflows/lint.yml +0 -22
  87. package/.github/workflows/semantic-release.yml +0 -24
  88. package/.husky/commit-msg +0 -2
  89. package/.husky/pre-commit +0 -2
  90. package/.nvmrc +0 -1
  91. package/.prettierignore +0 -4
  92. package/.storybook/polyfills/twig-include.js +0 -36
  93. package/.storybook/polyfills/twig-resolver.js +0 -68
  94. package/.storybook/polyfills/twig-source.js +0 -54
  95. package/.storybook/webpack.config.js +0 -193
  96. package/CODE_OF_CONDUCT.md +0 -56
  97. package/commitlint.config.js +0 -5
  98. package/config/jest.config.js +0 -19
  99. package/config/webpack/app.js +0 -1
  100. package/config/webpack/loaders.js +0 -167
  101. package/config/webpack/optimizers.js +0 -17
  102. package/config/webpack/plugins.js +0 -283
  103. package/config/webpack/resolves.js +0 -157
  104. package/config/webpack/sdc-loader.js +0 -16
  105. package/config/webpack/webpack.common.js +0 -268
  106. package/config/webpack/webpack.dev.js +0 -41
  107. package/config/webpack/webpack.prod.js +0 -6
  108. package/release.config.cjs +0 -30
  109. package/scripts/a11y.test.js +0 -172
  110. package/scripts/loadYaml.test.js +0 -30
@@ -0,0 +1,239 @@
1
+ /**
2
+ * @file Twig source() function factory shared by Storybook Twig renderers.
3
+ */
4
+
5
+ import {
6
+ coversAssetPath,
7
+ getAssetText,
8
+ isAssetTextLoading,
9
+ whenAssetTextLoaded,
10
+ } from 'virtual:emulsify-twig-asset-sources';
11
+ import { IMAGE_ASSET_EXTS, INLINE_ASSET_EXTS } from './source-extensions.js';
12
+ import { TWIG_SOURCE_LOADED_EVENT } from './source-events.js';
13
+
14
+ const DEFAULT_ENV =
15
+ (typeof __EMULSIFY_ENV__ !== 'undefined' && __EMULSIFY_ENV__) || {};
16
+ const missingTemplateSourceResolver = () => undefined;
17
+
18
+ function getRuntimeEnv() {
19
+ return globalThis.__EMULSIFY_ENV__ || DEFAULT_ENV;
20
+ }
21
+
22
+ // GitHub Pages serves static assets from a repository-prefixed base path.
23
+ const PUBLIC_ASSET_BASE =
24
+ typeof window !== 'undefined' &&
25
+ window.location &&
26
+ window.location.hostname &&
27
+ window.location.hostname.endsWith('github.io')
28
+ ? `/${getRuntimeEnv().machineName || ''}/assets/`
29
+ : '/assets/';
30
+
31
+ const pendingSourceLoads = new Set();
32
+ const warnedAssetSources = new Set();
33
+
34
+ function allowSyncXhrSource() {
35
+ const adapter = getRuntimeEnv().platformAdapter || {};
36
+ return Boolean(
37
+ adapter.storybook?.allowSyncXhrSource || adapter.allowSyncXhrSource,
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Normalize an `@assets` reference to a public asset path.
43
+ *
44
+ * @param {string} assetPath - Twig asset reference.
45
+ * @returns {string} Asset path below the public asset base.
46
+ */
47
+ function normalizeAssetPath(assetPath) {
48
+ return assetPath.replace(/^@assets\//, '').replace(/^assets\//, '');
49
+ }
50
+
51
+ /**
52
+ * Read a text asset from Storybook's static server.
53
+ *
54
+ * @param {string} relPath - Public asset path below `/assets`.
55
+ * @returns {string|undefined} Fetched text when available.
56
+ */
57
+ function fetchTextAsset(relPath) {
58
+ try {
59
+ const xhr = new XMLHttpRequest();
60
+ xhr.open('GET', `${PUBLIC_ASSET_BASE}${relPath}`, false);
61
+ xhr.send(null);
62
+ if (xhr.status >= 200 && xhr.status < 300) {
63
+ return xhr.responseText;
64
+ }
65
+
66
+ console.error(`source(): ${xhr.status} while fetching ${relPath}`);
67
+ } catch (error) {
68
+ console.error(`source(): failed to fetch ${relPath}`, error);
69
+ }
70
+
71
+ return undefined;
72
+ }
73
+
74
+ /**
75
+ * Warn once when a text asset cannot use the lazy virtual source map.
76
+ *
77
+ * @param {string} relPath - Public asset path below `/assets`.
78
+ * @param {string} reason - Short explanation of the missing source.
79
+ */
80
+ function warnTextAssetSource(relPath, reason) {
81
+ if (warnedAssetSources.has(relPath)) {
82
+ return;
83
+ }
84
+
85
+ warnedAssetSources.add(relPath);
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.`,
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Notify Storybook renderers when any lazy source import is available.
93
+ *
94
+ * @param {Promise} sourceLoad - Lazy source import promise.
95
+ * @param {object} detail - Event detail payload.
96
+ */
97
+ function scheduleSourceLoadedEvent(sourceLoad, detail) {
98
+ if (!sourceLoad || typeof sourceLoad.then !== 'function') {
99
+ return;
100
+ }
101
+ if (pendingSourceLoads.has(sourceLoad)) {
102
+ return;
103
+ }
104
+
105
+ pendingSourceLoads.add(sourceLoad);
106
+ sourceLoad
107
+ .then((sourceText) => {
108
+ if (
109
+ typeof sourceText !== 'string' ||
110
+ typeof window === 'undefined' ||
111
+ typeof window.dispatchEvent !== 'function'
112
+ ) {
113
+ return;
114
+ }
115
+
116
+ window.dispatchEvent(
117
+ new CustomEvent(TWIG_SOURCE_LOADED_EVENT, { detail }),
118
+ );
119
+ })
120
+ .catch(() => {})
121
+ .finally(() => {
122
+ pendingSourceLoads.delete(sourceLoad);
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Resolve an `@assets` reference for Storybook.
128
+ *
129
+ * @param {string} assetPath - Twig asset reference.
130
+ * @param {{ ignoreMissing?: boolean }} [options={}] - Optional source() flags.
131
+ * @returns {string|undefined} Raw text, image markup, URL, or undefined while lazy text loads.
132
+ */
133
+ export function resolveAssetSource(assetPath, { ignoreMissing = false } = {}) {
134
+ const relPath = normalizeAssetPath(assetPath);
135
+ const extension = relPath.split('.').pop().toLowerCase();
136
+
137
+ if (INLINE_ASSET_EXTS.has(extension)) {
138
+ const text = getAssetText(assetPath);
139
+ if (typeof text === 'string') {
140
+ return text;
141
+ }
142
+
143
+ if (isAssetTextLoading(assetPath)) {
144
+ scheduleSourceLoadedEvent(whenAssetTextLoaded(assetPath), {
145
+ assetPath,
146
+ });
147
+ return undefined;
148
+ }
149
+
150
+ if (coversAssetPath(assetPath)) {
151
+ if (!ignoreMissing) {
152
+ warnTextAssetSource(
153
+ relPath,
154
+ 'no build-time text asset source was found',
155
+ );
156
+ }
157
+ return undefined;
158
+ }
159
+
160
+ if (allowSyncXhrSource()) {
161
+ return fetchTextAsset(relPath);
162
+ }
163
+
164
+ if (!ignoreMissing) {
165
+ warnTextAssetSource(
166
+ relPath,
167
+ 'no configured build-time asset root covers',
168
+ );
169
+ }
170
+ return undefined;
171
+ }
172
+
173
+ if (IMAGE_ASSET_EXTS.has(extension)) {
174
+ return `<img src="${PUBLIC_ASSET_BASE}${relPath}" alt="" role="img">`;
175
+ }
176
+
177
+ return `${PUBLIC_ASSET_BASE}${relPath}`;
178
+ }
179
+
180
+ /**
181
+ * Notify Storybook renderers when a lazy Twig template source import is available.
182
+ *
183
+ * @param {Function} templateSourceResolver - Twig template source resolver.
184
+ * @param {string} templateName - Twig template reference.
185
+ */
186
+ function scheduleTemplateSourceLoadedEvent(
187
+ templateSourceResolver,
188
+ templateName,
189
+ ) {
190
+ const sourceLoad =
191
+ typeof templateSourceResolver.whenTemplateSourceLoaded === 'function'
192
+ ? templateSourceResolver.whenTemplateSourceLoaded(templateName)
193
+ : undefined;
194
+
195
+ scheduleSourceLoadedEvent(sourceLoad, { templateName });
196
+ }
197
+
198
+ /**
199
+ * Create a Twig.js `source()` function for Storybook rendering.
200
+ *
201
+ * Lazy Twig source loaders return an empty string on first render while the raw
202
+ * source import resolves. The source text is cached by the resolver, and the
203
+ * Storybook Twig renderer re-renders when the load completes.
204
+ *
205
+ * @param {Function} templateSourceResolver - Twig template source resolver.
206
+ * @returns {Function} Twig.js function implementation.
207
+ */
208
+ export function createTwigSourceFunction(
209
+ templateSourceResolver = missingTemplateSourceResolver,
210
+ ) {
211
+ return function source(templateName, ignoreMissing = false) {
212
+ if (typeof templateName !== 'string') return '';
213
+
214
+ const templateSource = templateSourceResolver(templateName);
215
+ if (typeof templateSource === 'string') {
216
+ return templateSource;
217
+ }
218
+ if (
219
+ typeof templateSourceResolver.isTemplateSourceLoading === 'function' &&
220
+ templateSourceResolver.isTemplateSourceLoading(templateName)
221
+ ) {
222
+ scheduleTemplateSourceLoadedEvent(templateSourceResolver, templateName);
223
+ return '';
224
+ }
225
+
226
+ if (
227
+ templateName.startsWith('@assets/') ||
228
+ templateName.startsWith('assets/')
229
+ ) {
230
+ return resolveAssetSource(templateName, { ignoreMissing }) || '';
231
+ }
232
+
233
+ if (!ignoreMissing) {
234
+ console.error(`source(): cannot resolve ${templateName}`);
235
+ }
236
+
237
+ return '';
238
+ };
239
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @file Twig source() runtime helper for Storybook-rendered templates.
3
+ */
4
+
5
+ import { resolveTemplateSource } from './resolver.js';
6
+ import {
7
+ createTwigSourceFunction as createSourceFunction,
8
+ resolveAssetSource,
9
+ } from './source-function.js';
10
+
11
+ export { resolveAssetSource };
12
+
13
+ /**
14
+ * Create a Twig.js `source()` function for Storybook rendering.
15
+ *
16
+ * Lazy Twig source loaders return an empty string on first render while the raw
17
+ * source import resolves. The source text is cached by the resolver, and the
18
+ * Storybook Twig renderer re-renders when the load completes.
19
+ *
20
+ * @param {Function} templateSourceResolver - Twig template source resolver.
21
+ * @returns {Function} Twig.js function implementation.
22
+ */
23
+ export function createTwigSourceFunction(
24
+ templateSourceResolver = resolveTemplateSource,
25
+ ) {
26
+ return createSourceFunction(templateSourceResolver);
27
+ }
28
+
29
+ /**
30
+ * Twig `source()` runtime helper.
31
+ *
32
+ * @param {Object} Twig - Twig.js module.
33
+ * @returns {undefined}
34
+ */
35
+ function twigSource(Twig) {
36
+ Twig.extendFunction('source', createTwigSourceFunction());
37
+ }
38
+
39
+ export default twigSource;
@@ -1,45 +0,0 @@
1
- {
2
- "projectName": "emulsify-core",
3
- "projectOwner": "fourkitchens",
4
- "repoType": "github",
5
- "repoHost": "https://github.com",
6
- "files": [
7
- "README.md"
8
- ],
9
- "imageSize": 100,
10
- "commit": true,
11
- "commitConvention": "angular",
12
- "contributors": [
13
- {
14
- "login": "callinmullaney",
15
- "name": "Callin Mullaney",
16
- "avatar_url": "https://avatars.githubusercontent.com/u/369018?v=4",
17
- "profile": "https://github.com/callinmullaney",
18
- "contributions": ["code", "doc"]
19
- },
20
- {
21
- "login": "amazingrando",
22
- "name": "Randy Oest",
23
- "avatar_url": "https://avatars.githubusercontent.com/u/409903?v=4",
24
- "profile": "https://github.com/amazingrando",
25
- "contributions": ["code", "doc"]
26
- },
27
- {
28
- "login": "robherba",
29
- "name": "Roberto Hernandez",
30
- "avatar_url": "https://avatars.githubusercontent.com/u/9342274?v=4",
31
- "profile": "https://github.com/robherba",
32
- "contributions": ["code"]
33
- },
34
- {
35
- "login": "dependabot",
36
- "name": "Dependabot",
37
- "avatar_url": "https://avatars.githubusercontent.com/u/49699333?v=4",
38
- "profile": "https://github.com/dependabot",
39
- "contributions": ["maintenance"]
40
- }
41
- ],
42
-
43
- "contributorsPerLine": 6,
44
- "skipCi": true
45
- }
package/.editorconfig DELETED
@@ -1,5 +0,0 @@
1
- # Unix-style newlines with a newline ending every file
2
- [*]
3
- indent_style = space
4
- indent_size = 2
5
- insert_final_newline = true
@@ -1,18 +0,0 @@
1
- ### Describe the bug
2
- _A clear and concise description of what the bug is._
3
-
4
- ### Steps to reproduce the bug
5
- Steps to reproduce the behavior:
6
- 1. Go to '...'
7
- 2. Click on '....'
8
- 3. Scroll down to '....'
9
- 4. See error
10
-
11
- ### Expected behavior
12
- A clear and concise description of what you expected to happen.
13
-
14
- ### Suggested solution or approach:
15
- _(optional) Describe a solution or approach to your request that may help with implementation._
16
-
17
- ### Additional context
18
- _(optional) Add any other context or screenshots about the feature request here._
@@ -1,11 +0,0 @@
1
- ### Emulsify Core Version:
2
- - [1.0.0](https://github.com/emulsify-ds/emulsify-core/releases)):
3
-
4
- ### Description of the feature request:
5
- _Please give as much information as possible_
6
-
7
- ### Suggested solution or approach:
8
- _(optional) Describe a solution or approach to your request that may help with implementation._
9
-
10
- ### Additional context:
11
- _(optional) Add any other context or screenshots about the feature request here._
@@ -1,19 +0,0 @@
1
- **This PR does the following:**
2
- - Adds functionality bullet item
3
- - Fixes this or that bullet item
4
-
5
- ### Related Issue(s)
6
- - [Title of the issue](https://github.com/emulsify-ds/emulsify-core/issues/1)
7
-
8
- ### Notes:
9
- - (optional) Document any intentionally unfinished parts or known issues within this PR
10
-
11
- ### Functional Testing:
12
- - [ ] Document steps that allow someone to fully test your code changes. Include screenshot and links when appropriate.
13
-
14
- ### Security
15
- _Security checks that should be reviewed_
16
-
17
- ### Accessibility
18
- _Should this be checked for this feature?_
19
-
@@ -1,6 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: npm
4
- directory: "/"
5
- schedule:
6
- interval: weekly
@@ -1,21 +0,0 @@
1
- name: Add to projects
2
-
3
- on:
4
- issues:
5
- types:
6
- - opened
7
- pull_request:
8
- types:
9
- - opened
10
-
11
- jobs:
12
- add-to-project:
13
- name: Add issue to project
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/add-to-project@v0.3.0
17
- with:
18
- # You can target a repository in a different organization
19
- # to the issue
20
- project-url: https://github.com/emulsify-ds/emulsify-core
21
- github-token: ${{ secrets.GH_TOKEN }}
@@ -1,37 +0,0 @@
1
- name: Add contributors
2
- on:
3
- push:
4
- branches:
5
- - main
6
- - develop
7
- workflow_dispatch:
8
-
9
- jobs:
10
- add-contributors:
11
- runs-on: ubuntu-latest
12
- permissions:
13
- contents: write
14
- steps:
15
- - uses: actions/checkout@v4
16
- with:
17
- fetch-depth: 0
18
- token: ${{ secrets.CONTRIBUTORS_TOKEN }}
19
-
20
- - name: Setup Node.js
21
- uses: actions/setup-node@v4
22
- with:
23
- node-version: "24"
24
-
25
- - name: Install all-contributors-cli
26
- run: npm install -g all-contributors-cli
27
-
28
- - name: Generate contributors
29
- run: |
30
- all-contributors generate
31
- git config --global user.name 'GitHub Actions'
32
- git config --global user.email 'github-actions@github.com'
33
- git add README.md
34
- git commit -m "docs: update contributors" || exit 0
35
- git push
36
- env:
37
- GITHUB_TOKEN: ${{ secrets.CONTRIBUTORS_TOKEN }}
@@ -1,22 +0,0 @@
1
- name: Lint
2
- on:
3
- push:
4
- branches: [develop, main]
5
- pull_request:
6
- branches: [develop, main]
7
- jobs:
8
- build:
9
- runs-on: ubuntu-latest
10
- steps:
11
- - name: Checkout
12
- uses: actions/checkout@v4
13
- with:
14
- fetch-depth: 0
15
- - name: Install Node.js
16
- uses: actions/setup-node@v4
17
- with:
18
- node-version: "24"
19
- - name: Install
20
- run: npm install
21
- - name: Lint
22
- run: npm run lint
@@ -1,24 +0,0 @@
1
- name: Semantic Release on Merge
2
- on:
3
- push:
4
- branches: [main]
5
- jobs:
6
- release:
7
- name: Attempt Semantic Release
8
- runs-on: ubuntu-latest
9
- steps:
10
- - name: Checkout
11
- uses: actions/checkout@v4
12
- with:
13
- fetch-depth: 0
14
- - name: Install Node.js
15
- uses: actions/setup-node@v4
16
- with:
17
- node-version: "24.x"
18
- - name: Install
19
- run: npm install
20
- - name: Release
21
- env:
22
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
24
- run: npm run semantic-release
package/.husky/commit-msg DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/sh
2
- npm run husky:commit-msg
package/.husky/pre-commit DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/sh
2
- npm run husky:pre-commit
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- 24
package/.prettierignore DELETED
@@ -1,4 +0,0 @@
1
- dist
2
- .out
3
- .coverage
4
- *.min.js
@@ -1,36 +0,0 @@
1
-
2
- import resolveTemplate from './twig-resolver.js';
3
-
4
- /**
5
- * Twig `include()` polyfill.
6
- * Mirrors Drupal behaviour inside Storybook.
7
- * @param {string} templateName
8
- * @param {Object} [variables]
9
- * @param {boolean} [withContext=false]
10
- * @return {string}
11
- */
12
- function twigInclude(Twig) {
13
- Twig.extendFunction('include', (...args) => {
14
- let [templateName, variables = {}, withContext = false] = args;
15
- if (typeof withContext !== 'boolean' && variables && typeof variables.with_context !== 'undefined') {
16
- withContext = variables.with_context;
17
- delete variables.with_context;
18
- }
19
-
20
- try {
21
- const templateFn = resolveTemplate(templateName);
22
- if (!templateFn) return '';
23
-
24
- const finalContext = withContext && typeof this === 'object'
25
- ? { ...(this.context || {}), ...variables }
26
- : variables;
27
-
28
- return templateFn(finalContext);
29
- } catch (err) {
30
- console.error(`Twig include() failed for: ${templateName}`, err);
31
- return '';
32
- }
33
- });
34
- };
35
-
36
- export default twigInclude;
@@ -1,68 +0,0 @@
1
- import { getProjectMachineName } from '../utils';
2
-
3
- const namespace = getProjectMachineName();
4
-
5
- const twigComponents = require.context(
6
- '../../../../../src/components/',
7
- true,
8
- /\.twig$/
9
- );
10
-
11
- /**
12
- * Resolve template identifier to compiled Twig function.
13
- * Supports: @component.twig, namespace:component, @namespace/component, namespace/component
14
- * @param {string} name Template identifier
15
- * @returns {Function|undefined} Compiled function or noop
16
- */
17
- function resolveTemplate(name) {
18
- // namespace:icon, @namespace/icon.twig
19
- if (name.startsWith(`${namespace}:`) || name.startsWith(`@${namespace}/`)) {
20
- const part = name.startsWith(`${namespace}:`) ? name.split(':')[1] : name.replace(`${namespace}/`, '').replace('.twig', '');
21
- const path = `./${part}/${part}.twig`;
22
- try {
23
- {
24
- const mod = twigComponents(path);
25
- return mod && mod.default ? mod.default : mod;
26
- }
27
- } catch (e) {
28
- // eslint-disable-next-line no-console
29
- console.error(`Cannot resolve Twig component for '${name}' at '${path}'`);
30
- }
31
- }
32
-
33
- // @icon.twig → icon/icon.twig
34
- if (name.startsWith('@') && name.endsWith('.twig')) {
35
- const part = name.slice(1, -5); // remove leading @ and trailing .twig
36
- const path = `./${part}/${part}.twig`;
37
- try {
38
- return twigComponents(path).default || twigComponents(path);
39
- } catch (e) {
40
- console.error(`Cannot resolve Twig shorthand template '${name}' at '${path}'`);
41
- }
42
- }
43
-
44
- // namespace/icon.twig via webpack alias
45
- if (name.startsWith(`${namespace}/`)) {
46
- const part = name.replace(new RegExp(`^${namespace}/`), '').replace('.twig', '');
47
- const path = `./${part}/${part}.twig`;
48
- try {
49
- return twigComponents(path).default || twigComponents(path);
50
- } catch (e) {
51
- console.error(`Cannot resolve Twig alias template '${name}' at '${path}'`);
52
- }
53
- }
54
-
55
- try {
56
- {
57
- // eslint-disable-next-line import/no-dynamic-require, global-require
58
- const mod = require(name);
59
- return mod && mod.default ? mod.default : mod;
60
- }
61
- } catch (e) {
62
- // eslint-disable-next-line no-console
63
- console.error(`Cannot resolve Twig template '${name}'`, e);
64
- return () => '';
65
- }
66
- };
67
-
68
- export default resolveTemplate;
@@ -1,54 +0,0 @@
1
- import { getProjectMachineName } from '../utils';
2
-
3
- const namespace = getProjectMachineName();
4
-
5
- // Constants used by the `source()` polyfill.
6
- const PUBLIC_ASSET_BASE = (typeof window !== 'undefined' && window.location && window.location.hostname && window.location.hostname.endsWith('github.io'))
7
- ? `/${namespace}/assets/`
8
- : '/assets/';
9
-
10
- const INLINE_ASSET_EXTS = new Set(['svg', 'html', 'twig', 'css', 'js', 'json', 'txt', 'md']);
11
- const IMAGE_ASSET_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'avif']);
12
-
13
- /**
14
- * Twig `source()` polyfill.
15
- * Returns an <img> tag or URL for @assets paths.
16
- * @param {string} assetPath
17
- * @return {string}
18
- */
19
- function twigSource(Twig) {
20
- Twig.extendFunction('source', (assetPath) => {
21
- if (typeof assetPath !== 'string') return '';
22
-
23
- // Strip Drupal-style alias and extract file extension.
24
- const relPath = assetPath.replace(/^@assets\//, '');
25
- const extension = relPath.split('.').pop().toLowerCase();
26
-
27
- // Inline raw content for textual assets.
28
- if (INLINE_ASSET_EXTS.has(extension)) {
29
- try {
30
- const xhr = new XMLHttpRequest();
31
- xhr.open('GET', `${PUBLIC_ASSET_BASE}${relPath}`, false); // synchronous
32
- xhr.send(null);
33
- if (xhr.status >= 200 && xhr.status < 300) {
34
- return xhr.responseText;
35
- }
36
- // eslint-disable-next-line no-console
37
- console.error(`source(): ${xhr.status} while fetching ${relPath}`);
38
- } catch (err) {
39
- // eslint-disable-next-line no-console
40
- console.error(`source(): failed to fetch ${relPath}`, err);
41
- }
42
- }
43
-
44
- // Auto-render raster images.
45
- if (IMAGE_ASSET_EXTS.has(extension)) {
46
- return `<img src="${PUBLIC_ASSET_BASE}${relPath}" alt="" role="img">`;
47
- }
48
-
49
- // Fallback: return public URL.
50
- return `${PUBLIC_ASSET_BASE}${relPath}`;
51
- });
52
- };
53
-
54
- export default twigSource;