@emulsify/core 4.0.0 → 4.0.2
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/.cli/init.js +3 -3
- package/.storybook/_drupal.js +0 -3
- package/.storybook/main.js +39 -2
- package/config/eslint.config.js +7 -1
- package/config/vite/entries.js +1 -1
- package/config/vite/plugins/mirror-components.js +0 -12
- package/config/vite/plugins/source-file-index.js +0 -1
- package/config/vite/plugins/svg-sprite.js +0 -1
- package/config/vite/plugins/twig-module.js +0 -6
- package/package.json +2 -1
- package/scripts/a11y.js +0 -4
- package/src/extensions/twig/register.js +43 -0
- package/src/storybook/preview-parameters.js +0 -2
- package/src/storybook/twig/resolver.js +0 -3
package/.cli/init.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import
|
|
10
|
+
import { dump as dumpYaml, load as loadYaml } from 'js-yaml';
|
|
11
11
|
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
13
|
|
|
@@ -116,8 +116,8 @@ const applyToYmlFile = (filePath, functor) => {
|
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
const file =
|
|
120
|
-
fs.writeFileSync(filePath,
|
|
119
|
+
const file = loadYaml(fs.readFileSync(filePath, 'utf8'));
|
|
120
|
+
fs.writeFileSync(filePath, dumpYaml(functor(file)));
|
|
121
121
|
};
|
|
122
122
|
|
|
123
123
|
const main = () => {
|
package/.storybook/_drupal.js
CHANGED
|
@@ -65,7 +65,6 @@ function mergeDrupalSettings(defaults, overrides) {
|
|
|
65
65
|
|
|
66
66
|
for (const [key, value] of Object.entries(overrides || {})) {
|
|
67
67
|
// Drupal settings keys are project/module-defined by design.
|
|
68
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
69
68
|
const defaultValue = merged[key];
|
|
70
69
|
const nextValue =
|
|
71
70
|
isPlainObject(defaultValue) && isPlainObject(value)
|
|
@@ -73,7 +72,6 @@ function mergeDrupalSettings(defaults, overrides) {
|
|
|
73
72
|
: value;
|
|
74
73
|
|
|
75
74
|
// Drupal settings keys are project/module-defined by design.
|
|
76
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
77
75
|
merged[key] = nextValue;
|
|
78
76
|
}
|
|
79
77
|
|
|
@@ -156,7 +154,6 @@ window.drupalSettings = mergeDrupalSettings(
|
|
|
156
154
|
// Attach each registered behavior while isolating individual failures.
|
|
157
155
|
Object.keys(behaviors).forEach(function (behaviorName) {
|
|
158
156
|
// Drupal behavior names are project/module-defined by design.
|
|
159
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
160
157
|
const behavior = behaviors[behaviorName];
|
|
161
158
|
if (typeof behavior.attach === 'function') {
|
|
162
159
|
try {
|
package/.storybook/main.js
CHANGED
|
@@ -70,6 +70,27 @@ const _filename = fileURLToPath(import.meta.url);
|
|
|
70
70
|
*/
|
|
71
71
|
const _dirname = path.dirname(_filename);
|
|
72
72
|
|
|
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
|
+
|
|
73
94
|
/**
|
|
74
95
|
* Reads an optional HTML fragment relative to this config file.
|
|
75
96
|
*
|
|
@@ -247,10 +268,17 @@ const baseConfig = {
|
|
|
247
268
|
staticDirs: [
|
|
248
269
|
...existingStaticDirs([
|
|
249
270
|
{
|
|
250
|
-
from: path.resolve(
|
|
271
|
+
from: path.resolve(projectRoot, 'assets'),
|
|
272
|
+
to: '/assets',
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
from: path.resolve(projectRoot, 'dist/assets'),
|
|
251
276
|
to: '/assets',
|
|
252
277
|
},
|
|
253
|
-
|
|
278
|
+
{
|
|
279
|
+
from: path.resolve(projectRoot, 'dist'),
|
|
280
|
+
to: '/dist',
|
|
281
|
+
},
|
|
254
282
|
]),
|
|
255
283
|
],
|
|
256
284
|
|
|
@@ -452,6 +480,7 @@ const baseConfig = {
|
|
|
452
480
|
const { mergeConfig } = await import('vite');
|
|
453
481
|
/** @type {StorybookEnvironment} */
|
|
454
482
|
const env = resolvedStorybookEnv;
|
|
483
|
+
const storybookBuildConfig = config?.build || {};
|
|
455
484
|
|
|
456
485
|
// Keep using the `serve` branch of the shared Vite config here. Storybook
|
|
457
486
|
// has historically consumed that branch, while `mode` still reflects
|
|
@@ -560,6 +589,14 @@ const baseConfig = {
|
|
|
560
589
|
|
|
561
590
|
return {
|
|
562
591
|
...mergedConfig,
|
|
592
|
+
build: {
|
|
593
|
+
...(mergedConfig.build || {}),
|
|
594
|
+
...(storybookBuildConfig.outDir
|
|
595
|
+
? { outDir: storybookBuildConfig.outDir }
|
|
596
|
+
: {}),
|
|
597
|
+
assetsDir: storybookViteAssetsDir,
|
|
598
|
+
emptyOutDir: false,
|
|
599
|
+
},
|
|
563
600
|
resolve: mergeReactSingletonResolve(mergedConfig),
|
|
564
601
|
optimizeDeps: {
|
|
565
602
|
...(mergedConfig.optimizeDeps || {}),
|
package/config/eslint.config.js
CHANGED
|
@@ -41,9 +41,15 @@ export default [
|
|
|
41
41
|
ignores: ['**/*.min.js', '**/node_modules/**/*'],
|
|
42
42
|
|
|
43
43
|
rules: {
|
|
44
|
-
// Keep historical project conventions while
|
|
44
|
+
// Keep historical project conventions while tuning noisy security
|
|
45
|
+
// heuristics that flag intentional file-generation and source-scanning
|
|
46
|
+
// patterns throughout this tooling package.
|
|
45
47
|
strict: 0,
|
|
46
48
|
'consistent-return': 'off',
|
|
49
|
+
'security/detect-non-literal-fs-filename': 'off',
|
|
50
|
+
'security/detect-non-literal-regexp': 'off',
|
|
51
|
+
'security/detect-object-injection': 'off',
|
|
52
|
+
'security/detect-unsafe-regex': 'off',
|
|
47
53
|
'no-underscore-dangle': 'off',
|
|
48
54
|
'max-nested-callbacks': ['warn', 3],
|
|
49
55
|
'import/extensions': 'off',
|
package/config/vite/entries.js
CHANGED
|
@@ -57,7 +57,7 @@ export const sanitizePath = (s) => s.replace(/[^a-zA-Z0-9/_-]/g, '');
|
|
|
57
57
|
function safeSetKey(map, key, value) {
|
|
58
58
|
const forbidden = ['__proto__', 'prototype', 'constructor'];
|
|
59
59
|
if (!key || forbidden.some((bad) => key.includes(bad))) return;
|
|
60
|
-
map[key] = value;
|
|
60
|
+
map[key] = value;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/** Return an absolute path from a source index entry or string. */
|
|
@@ -68,7 +68,6 @@ const pruneEmptyDirsUpTo = (startDir, stopAtDir) => {
|
|
|
68
68
|
|
|
69
69
|
const isEmpty = (dir) => {
|
|
70
70
|
try {
|
|
71
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
72
71
|
return readdirSync(dir).length === 0;
|
|
73
72
|
} catch {
|
|
74
73
|
return false;
|
|
@@ -79,7 +78,6 @@ const pruneEmptyDirsUpTo = (startDir, stopAtDir) => {
|
|
|
79
78
|
if (!isEmpty(cursor)) break;
|
|
80
79
|
|
|
81
80
|
try {
|
|
82
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
83
81
|
rmdirSync(cursor);
|
|
84
82
|
} catch {
|
|
85
83
|
// Stop at the first directory that cannot be removed.
|
|
@@ -103,9 +101,7 @@ const pruneEmptyDirsUpTo = (startDir, stopAtDir) => {
|
|
|
103
101
|
*/
|
|
104
102
|
export const filesHaveSameBytes = (sourceFile, destinationFile) => {
|
|
105
103
|
try {
|
|
106
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
107
104
|
const sourceStats = statSync(sourceFile);
|
|
108
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
109
105
|
const destinationStats = statSync(destinationFile);
|
|
110
106
|
if (!destinationStats.isFile()) return false;
|
|
111
107
|
if (sourceStats.size !== destinationStats.size) return false;
|
|
@@ -117,10 +113,8 @@ export const filesHaveSameBytes = (sourceFile, destinationFile) => {
|
|
|
117
113
|
|
|
118
114
|
const sourceBuffer = Buffer.allocUnsafe(FILE_COMPARE_CHUNK_SIZE);
|
|
119
115
|
const destinationBuffer = Buffer.allocUnsafe(FILE_COMPARE_CHUNK_SIZE);
|
|
120
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
121
116
|
const sourceHandle = openSync(sourceFile, 'r');
|
|
122
117
|
try {
|
|
123
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
124
118
|
const destinationHandle = openSync(destinationFile, 'r');
|
|
125
119
|
try {
|
|
126
120
|
let position = 0;
|
|
@@ -175,7 +169,6 @@ export const filesHaveSameBytes = (sourceFile, destinationFile) => {
|
|
|
175
169
|
*/
|
|
176
170
|
const isSymlink = (filePath) => {
|
|
177
171
|
try {
|
|
178
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
179
172
|
return lstatSync(filePath).isSymbolicLink();
|
|
180
173
|
} catch {
|
|
181
174
|
return false;
|
|
@@ -189,7 +182,6 @@ const isSymlink = (filePath) => {
|
|
|
189
182
|
*/
|
|
190
183
|
const removeSourceFile = (sourceFile) => {
|
|
191
184
|
try {
|
|
192
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
193
185
|
unlinkSync(sourceFile);
|
|
194
186
|
} catch (error) {
|
|
195
187
|
if (error?.code !== 'ENOENT') throw error;
|
|
@@ -221,12 +213,10 @@ const copyFileIntoPlace = (sourceFile, destinationFile) => {
|
|
|
221
213
|
|
|
222
214
|
try {
|
|
223
215
|
copyFileSync(sourceFile, tempDestination);
|
|
224
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
225
216
|
renameSync(tempDestination, destinationFile);
|
|
226
217
|
removeSourceFile(sourceFile);
|
|
227
218
|
} catch (error) {
|
|
228
219
|
try {
|
|
229
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
230
220
|
unlinkSync(tempDestination);
|
|
231
221
|
} catch {
|
|
232
222
|
/* noop */
|
|
@@ -255,7 +245,6 @@ const moveFileIntoPlace = (sourceFile, destinationFile) => {
|
|
|
255
245
|
}
|
|
256
246
|
|
|
257
247
|
try {
|
|
258
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
259
248
|
renameSync(sourceFile, destinationFile);
|
|
260
249
|
} catch (error) {
|
|
261
250
|
if (error?.code !== 'EXDEV') throw error;
|
|
@@ -282,7 +271,6 @@ const readMirrorState = (markerFile) => {
|
|
|
282
271
|
*/
|
|
283
272
|
const writeMirrorState = (markerFile, state) => {
|
|
284
273
|
mkdirSync(dirname(markerFile), { recursive: true });
|
|
285
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
286
274
|
writeFileSync(markerFile, `${JSON.stringify(state, null, 2)}\n`);
|
|
287
275
|
};
|
|
288
276
|
|
|
@@ -332,7 +332,6 @@ const buildTemplateFileCandidates = (baseDir, templatePath) => {
|
|
|
332
332
|
const findExistingTemplateFile = (paths) =>
|
|
333
333
|
paths.filter(Boolean).find((filePath) => {
|
|
334
334
|
try {
|
|
335
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
336
335
|
return fs.statSync(filePath).isFile();
|
|
337
336
|
} catch {
|
|
338
337
|
return false;
|
|
@@ -503,7 +502,6 @@ const parseTwigNamespaceReference = (templatePath, namespaces = {}) => {
|
|
|
503
502
|
return {
|
|
504
503
|
namespace: slashNamespace,
|
|
505
504
|
// Namespace names come from the normalized Twig namespace map.
|
|
506
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
507
505
|
root: namespaces[slashNamespace],
|
|
508
506
|
path: templatePath.slice(slashNamespace.length + 1),
|
|
509
507
|
};
|
|
@@ -523,7 +521,6 @@ const componentGroupRoots = (componentRoot) => {
|
|
|
523
521
|
|
|
524
522
|
try {
|
|
525
523
|
// Component group roots come from a configured project directory.
|
|
526
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
527
524
|
return fs
|
|
528
525
|
.readdirSync(componentRoot, { withFileTypes: true })
|
|
529
526
|
.filter((entry) => entry.isDirectory())
|
|
@@ -707,7 +704,6 @@ const invalidateKnownResolutionCacheEntries = (filePath) => {
|
|
|
707
704
|
const compileTwigTemplate = (filePath, options, cache = compileCache) => {
|
|
708
705
|
const absoluteFilePath = resolve(filePath);
|
|
709
706
|
knownTwigFiles.add(absoluteFilePath);
|
|
710
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
711
707
|
const { mtimeMs } = fs.statSync(absoluteFilePath);
|
|
712
708
|
const cached = cache.get(absoluteFilePath);
|
|
713
709
|
if (cached?.mtimeMs === mtimeMs) {
|
|
@@ -718,7 +714,6 @@ const compileTwigTemplate = (filePath, options, cache = compileCache) => {
|
|
|
718
714
|
registerTwigExtensions(compilerTwig);
|
|
719
715
|
registerConfiguredTwigExtensions(compilerTwig, options);
|
|
720
716
|
|
|
721
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
722
717
|
const source = fs.readFileSync(absoluteFilePath, 'utf8');
|
|
723
718
|
const compileOptions = {
|
|
724
719
|
allowInlineIncludes: true,
|
|
@@ -1070,7 +1065,6 @@ export function emulsifyTwigModulePlugin(options) {
|
|
|
1070
1065
|
const absoluteSourcePath = resolve(sourcePath);
|
|
1071
1066
|
addDependencyImporter(absoluteSourcePath, sourceFilePath);
|
|
1072
1067
|
this.addWatchFile(absoluteSourcePath);
|
|
1073
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
1074
1068
|
const sourceText = fs.readFileSync(absoluteSourcePath, 'utf8');
|
|
1075
1069
|
|
|
1076
1070
|
return unique([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emulsify/core",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Bundled tooling for Storybook development + Vite Build",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"component library",
|
|
@@ -169,6 +169,7 @@
|
|
|
169
169
|
"@storybook/react-vite": "^10.1.4",
|
|
170
170
|
"@vituum/vite-plugin-twig": "^1.1.0",
|
|
171
171
|
"autoprefixer": "^10.4.21",
|
|
172
|
+
"axe-core": "^4.11.4",
|
|
172
173
|
"babel-preset-minify": "^0.5.2",
|
|
173
174
|
"concurrently": "^9.2.1",
|
|
174
175
|
"eslint": "^9.39.4",
|
package/scripts/a11y.js
CHANGED
|
@@ -62,7 +62,6 @@ const applyProjectA11yConfig = (config = {}) => {
|
|
|
62
62
|
* @returns {void}
|
|
63
63
|
*/
|
|
64
64
|
const printHelp = () => {
|
|
65
|
-
// eslint-disable-next-line no-console
|
|
66
65
|
console.log(
|
|
67
66
|
[
|
|
68
67
|
'Usage: node scripts/a11y.js [options]',
|
|
@@ -128,7 +127,6 @@ const logIssue = ({ type: severity, message, context, selector }) => {
|
|
|
128
127
|
`selector: ${selector}`,
|
|
129
128
|
'',
|
|
130
129
|
];
|
|
131
|
-
// eslint-disable-next-line no-console
|
|
132
130
|
console.log(lines.join('\n'));
|
|
133
131
|
};
|
|
134
132
|
|
|
@@ -142,11 +140,9 @@ const logReport = ({ issues, pageUrl }) => {
|
|
|
142
140
|
const hasIssues = validIssues.length > 0;
|
|
143
141
|
|
|
144
142
|
if (hasIssues) {
|
|
145
|
-
// eslint-disable-next-line no-console
|
|
146
143
|
console.log(`Issues found in component: ${pageUrl}`);
|
|
147
144
|
validIssues.forEach(logIssue);
|
|
148
145
|
} else {
|
|
149
|
-
// eslint-disable-next-line no-console
|
|
150
146
|
console.log(`No issues found in component: ${pageUrl}`);
|
|
151
147
|
}
|
|
152
148
|
|
|
@@ -13,6 +13,47 @@ import { getTwigTagDefinitions } from './tag-map.js';
|
|
|
13
13
|
*/
|
|
14
14
|
const registeredTwigInstances = new WeakSet();
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* PHP-style boolean conversion used by Twig.js truthiness checks.
|
|
18
|
+
*
|
|
19
|
+
* Twig.js normally provides this through `Twig.lib.boolval`, but dependency
|
|
20
|
+
* optimizer interop can drop the helper while leaving the rest of Twig usable.
|
|
21
|
+
*
|
|
22
|
+
* @param {*} value - Value to coerce using PHP/Twig truthiness rules.
|
|
23
|
+
* @returns {boolean} TRUE when the value should be truthy in Twig.
|
|
24
|
+
*/
|
|
25
|
+
function phpBoolval(value) {
|
|
26
|
+
return (
|
|
27
|
+
value !== false &&
|
|
28
|
+
value !== 0 &&
|
|
29
|
+
value !== '' &&
|
|
30
|
+
value !== '0' &&
|
|
31
|
+
!(Array.isArray(value) && value.length === 0) &&
|
|
32
|
+
value !== null &&
|
|
33
|
+
typeof value !== 'undefined'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ensure Twig.js has the internal boolean helper required by conditionals.
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} Twig - Twig.js module or compatible extension target.
|
|
41
|
+
* @returns {Object} The same Twig instance after compatibility patching.
|
|
42
|
+
*/
|
|
43
|
+
function ensureTwigBoolval(Twig) {
|
|
44
|
+
Twig.extend((InternalTwig) => {
|
|
45
|
+
if (!InternalTwig.lib) {
|
|
46
|
+
InternalTwig.lib = {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof InternalTwig.lib.boolval !== 'function') {
|
|
50
|
+
InternalTwig.lib.boolval = phpBoolval;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return Twig;
|
|
55
|
+
}
|
|
56
|
+
|
|
16
57
|
/**
|
|
17
58
|
* Register native Emulsify Twig functions and logic tags with Twig.js.
|
|
18
59
|
*
|
|
@@ -32,6 +73,8 @@ export function registerTwigExtensions(Twig) {
|
|
|
32
73
|
);
|
|
33
74
|
}
|
|
34
75
|
|
|
76
|
+
ensureTwigBoolval(Twig);
|
|
77
|
+
|
|
35
78
|
if (registeredTwigInstances.has(Twig)) {
|
|
36
79
|
return Twig;
|
|
37
80
|
}
|
|
@@ -33,7 +33,6 @@ export function mergePreviewParameters(defaults = {}, overrides = {}) {
|
|
|
33
33
|
if (value === undefined) continue;
|
|
34
34
|
|
|
35
35
|
// Storybook parameter keys are intentionally dynamic.
|
|
36
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
37
36
|
const current = merged[key];
|
|
38
37
|
const nextValue =
|
|
39
38
|
isPlainObject(current) && isPlainObject(value)
|
|
@@ -41,7 +40,6 @@ export function mergePreviewParameters(defaults = {}, overrides = {}) {
|
|
|
41
40
|
: value;
|
|
42
41
|
|
|
43
42
|
// Storybook parameter keys are intentionally dynamic.
|
|
44
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
45
43
|
merged[key] = nextValue;
|
|
46
44
|
}
|
|
47
45
|
|
|
@@ -30,7 +30,6 @@ const ENV = (typeof __EMULSIFY_ENV__ !== 'undefined' && __EMULSIFY_ENV__) || {};
|
|
|
30
30
|
function resolveFromMap(map, candidates) {
|
|
31
31
|
for (const key of candidates) {
|
|
32
32
|
// Vite glob map keys are generated from static Storybook patterns.
|
|
33
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
34
33
|
const value = map[key];
|
|
35
34
|
if (value) {
|
|
36
35
|
return value.default ?? value;
|
|
@@ -114,7 +113,6 @@ function findGlobEntry(map, candidates) {
|
|
|
114
113
|
for (const key of candidates) {
|
|
115
114
|
if (Object.hasOwnProperty.call(map, key)) {
|
|
116
115
|
// Vite glob map keys are generated from static Storybook patterns.
|
|
117
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
118
116
|
return { key, value: map[key] };
|
|
119
117
|
}
|
|
120
118
|
}
|
|
@@ -240,7 +238,6 @@ export function createTwigResolver({
|
|
|
240
238
|
candidateKeysForReference: (name) => candidateKeysForReference(name, env),
|
|
241
239
|
resolveTemplate(name) {
|
|
242
240
|
// Direct lookups support callers that already resolved a Vite glob key.
|
|
243
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
244
241
|
const direct = modules[name];
|
|
245
242
|
if (direct) {
|
|
246
243
|
return direct.default ?? direct;
|