@emulsify/core 3.1.1 → 3.3.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.
@@ -1,26 +1,99 @@
1
1
  /**
2
- * @fileoverview Webpack configuration entry file.
3
- * This file generates Webpack entries for JS, SCSS, and SVG assets.
2
+ * @fileoverview Build Webpack entries and export the configuration.
3
+ * - Discovers JS/SCSS assets (base + component) via glob patterns
4
+ * - Shapes output paths based on platform and SDC (singleDirectoryComponents)
5
+ * - Wires up loaders, plugins, and optimizations
4
6
  */
5
7
 
6
- import { resolve, dirname } from 'path';
8
+ import { posix as path } from 'node:path';
7
9
  import { sync as globSync } from 'glob';
8
10
  import fs from 'fs-extra';
11
+
9
12
  import loaders from './loaders.js';
10
13
  import plugins from './plugins.js';
11
14
  import resolves from './resolves.js';
12
15
  import optimizers from './optimizers.js';
13
16
  import emulsifyConfig from '../../../../../project.emulsify.json' with { type: 'json' };
14
17
 
15
- // Create __filename from import.meta.url without fileURLToPath
18
+ /**
19
+ * Resolve the directory of this file (without fileURLToPath).
20
+ * @type {string}
21
+ */
16
22
  let _filename = decodeURIComponent(new URL(import.meta.url).pathname);
17
-
18
- // On Windows, remove the leading slash (e.g. "/C:/path" -> "C:/path")
19
23
  if (process.platform === 'win32' && _filename.startsWith('/')) {
20
24
  _filename = _filename.slice(1);
21
25
  }
26
+ const _dirname = path.dirname(_filename);
27
+
28
+ /** @type {string} Absolute project root (five levels up from this file). */
29
+ const projectDir = path.resolve(_dirname, '../../../../..');
30
+
31
+ /** @type {boolean} True when a "src/" directory exists (WP layout). */
32
+ const hasSrc = fs.pathExistsSync(path.resolve(projectDir, 'src'));
33
+
34
+ /** @type {string} The canonical source directory ("src" if present, else "components"). */
35
+ const srcDir = hasSrc
36
+ ? path.resolve(projectDir, 'src')
37
+ : path.resolve(projectDir, 'components');
38
+
39
+ /** @type {boolean} True when platform is Drupal (affects component output root). */
40
+ const isDrupal = emulsifyConfig?.project?.platform === 'drupal';
41
+
42
+ /** @type {boolean} Respect SDC (single-directory-components) layout if explicitly true. */
43
+ const SDC = Boolean(emulsifyConfig?.project?.singleDirectoryComponents);
44
+
45
+ /** @type {string} Output base for "global" assets. */
46
+ const globalOutBase = hasSrc ? 'dist/global' : 'dist';
47
+
48
+ /**
49
+ * Create a path under the component output root.
50
+ * - In Drupal + src layout, components resolve to "components/…"
51
+ * - Otherwise, they resolve to "dist/components/…"
52
+ * @param {string} subpath - Component-local subpath (no extension).
53
+ * @returns {string} Component output path segment.
54
+ */
55
+ const componentOutPath = (subpath) =>
56
+ (isDrupal && hasSrc ? 'components' : 'dist/components') + '/' + subpath;
57
+
58
+ /**
59
+ * Join segments with POSIX semantics (forward slashes), trimming empties.
60
+ * @param {...string} segs - Path segments.
61
+ * @returns {string} POSIX-joined path.
62
+ */
63
+ const pj = (...segs) => path.join(...segs.filter(Boolean));
22
64
 
23
- const _dirname = dirname(_filename);
65
+ /**
66
+ * Compute the “dist subpath” for a non-component asset.
67
+ * Inserts a type folder ("js" or "css") when SDC = false.
68
+ * Drops the original file extension.
69
+ * @param {string} absFile - Absolute file path.
70
+ * @param {'js'|'css'} type - Asset type.
71
+ * @returns {string} Subpath under the global output base (no extension).
72
+ */
73
+ const distSubpathForBase = (absFile, type) => {
74
+ const rel = path.relative(srcDir, absFile);
75
+ const dir = path.dirname(rel);
76
+ const name = path.basename(rel, '.' + type);
77
+ return SDC ? pj(dir, name) : pj(dir, type, name);
78
+ };
79
+
80
+ /**
81
+ * Compute the “dist subpath” for a component asset located under "…/components".
82
+ * Inserts a type folder ("js" or "css") when SDC = false.
83
+ * Drops the original file extension.
84
+ * @param {string} absFile - Absolute file path.
85
+ * @param {'js'|'scss'} type - Source type (scss maps to 'css').
86
+ * @returns {string} Component-local subpath (no extension).
87
+ */
88
+ const distSubpathForComponent = (absFile, type) => {
89
+ const relFromComponents = path.relative(pj(srcDir, 'components'), absFile);
90
+ const dir = path.dirname(relFromComponents);
91
+ const isStyle = type === 'scss';
92
+ const outTypeDir = isStyle ? 'css' : 'js';
93
+ const ext = isStyle ? '.scss' : '.js';
94
+ const name = path.basename(relFromComponents, ext);
95
+ return SDC ? pj(dir, name) : pj(dir, outTypeDir, name);
96
+ };
24
97
 
25
98
  /**
26
99
  * Sanitize a file path by removing unwanted characters.
@@ -30,170 +103,134 @@ const _dirname = dirname(_filename);
30
103
  */
31
104
  const sanitizePath = (inputPath) => inputPath.replace(/[^a-zA-Z0-9/_-]/g, '');
32
105
 
33
- // Get directories for file contexts.
34
- const projectDir = resolve(_dirname, '../../../../..');
106
+ /**
107
+ * Reject keys that could touch object internals even after sanitization.
108
+ * @param {string} k
109
+ * @returns {boolean}
110
+ */
111
+ const isDangerousKey = (k) =>
112
+ k.includes('__proto__') || k.includes('prototype') || k === 'constructor';
113
+
114
+ /**
115
+ * Add a file under an entry key; if the key exists, merge to an array.
116
+ * Keeps JS before SCSS for deterministic order.
117
+ *
118
+ * @param {Map<string, string | string[]>} map
119
+ * @param {string} key
120
+ * @param {string} file
121
+ * @returns {void}
122
+ */
123
+ const addEntry = (map, key, file) => {
124
+ const safeKey = sanitizePath(String(key));
125
+ if (!safeKey || isDangerousKey(safeKey)) return;
35
126
 
36
- const srcPath = resolve(projectDir, 'src');
37
- const isSrcExists = fs.pathExistsSync(srcPath);
38
- const srcDir = isSrcExists ? srcPath : resolve(projectDir, 'components');
39
- const isDrupal = emulsifyConfig.project.platform === 'drupal';
127
+ const current = map.get(safeKey);
40
128
 
41
- // Glob pattern for SCSS files that ignore file names prefixed with underscore.
42
- const BaseScssPattern = fs.pathExistsSync(resolve(projectDir, 'src'))
43
- ? resolve(srcDir, '!(components|util)/**/!(_*|cl-*|sb-*).scss')
44
- : '';
45
- const ComponentScssPattern = fs.pathExistsSync(resolve(projectDir, 'src'))
46
- ? resolve(srcDir, 'components/**/!(_*|cl-*|sb-*).scss')
47
- : resolve(srcDir, '**/!(_*|cl-*|sb-*).scss');
48
- const ComponentLibraryScssPattern = resolve(srcDir, '**/*{cl-*,sb-*}.scss');
49
-
50
- // Glob pattern for JS files.
51
- const BaseJsPattern = fs.pathExistsSync(resolve(projectDir, 'src'))
52
- ? resolve(
53
- srcDir,
54
- '!(components|util)/**/!(*.stories|*.component|*.min|*.test).js',
55
- )
56
- : '';
57
- const ComponentJsPattern = fs.pathExistsSync(resolve(projectDir, 'src'))
58
- ? resolve(srcDir, 'components/**/!(*.stories|*.component|*.min|*.test).js')
59
- : resolve(srcDir, '**/!(*.stories|*.component|*.min|*.test).js');
129
+ if (!current) {
130
+ map.set(safeKey, file);
131
+ return;
132
+ }
60
133
 
61
- // Glob pattern for SVG sprite config.
62
- const spritePattern = resolve(projectDir, 'assets/icons/**/*.svg');
134
+ const arr = Array.isArray(current) ? current : [current];
135
+ if (!arr.includes(file)) arr.push(file);
136
+
137
+ // Optional: ensure JS comes before SCSS
138
+ arr.sort((a, b) => {
139
+ const ax = a.endsWith('.js') ? 0 : 1;
140
+ const bx = b.endsWith('.js') ? 0 : 1;
141
+ return ax - bx || a.localeCompare(b);
142
+ });
143
+
144
+ map.set(safeKey, arr);
145
+ };
63
146
 
64
147
  /**
65
- * Replace the last occurrence of a slash in a string with a replacement.
66
- *
67
- * @param {string} str - The original string.
68
- * @param {string} replacement - The string to replace the last slash with.
69
- * @returns {string} The modified string.
148
+ * Safe glob wrapper: returns [] if the pattern is falsy.
149
+ * @param {string} pattern - Glob pattern.
150
+ * @returns {string[]} Matching file paths.
70
151
  */
71
- function replaceLastSlash(str, replacement) {
72
- const lastSlashIndex = str.lastIndexOf('/');
73
- if (lastSlashIndex === -1) {
74
- return str;
75
- }
76
- return (
77
- str.slice(0, lastSlashIndex) + replacement + str.slice(lastSlashIndex + 1)
78
- );
79
- }
152
+ const glob = (pattern) => (pattern ? globSync(pattern) : []);
153
+
154
+ /* -------------------------------------------------------------------------- */
155
+ /* GLOBS */
156
+ /* -------------------------------------------------------------------------- */
157
+
158
+ const BaseScssPattern = hasSrc
159
+ ? pj(srcDir, '!(components|util)/**/!(_*|cl-*|sb-*).scss')
160
+ : '';
161
+
162
+ const ComponentScssPattern = hasSrc
163
+ ? pj(srcDir, 'components/**/!(_*|cl-*|sb-*).scss')
164
+ : pj(srcDir, '**/!(_*|cl-*|sb-*).scss');
165
+
166
+ const ComponentLibraryScssPattern = pj(srcDir, '**/*{cl-*,sb-*}.scss');
167
+
168
+ const BaseJsPattern = hasSrc
169
+ ? pj(srcDir, '!(components|util)/**/!(*.stories|*.component|*.min|*.test).js')
170
+ : '';
171
+
172
+ const ComponentJsPattern = hasSrc
173
+ ? pj(srcDir, 'components/**/!(*.stories|*.component|*.min|*.test).js')
174
+ : pj(srcDir, '**/!(*.stories|*.component|*.min|*.test).js');
175
+
176
+ /* -------------------------------------------------------------------------- */
177
+ /* ENTRY BUILD */
178
+ /* -------------------------------------------------------------------------- */
80
179
 
81
180
  /**
82
- * Generate Webpack entries for JS, SCSS, and SVG files.
83
- *
84
- * @param {string} BaseJsMatcher - Glob pattern for base JS files.
85
- * @param {string} jsMatcher - Glob pattern for component JS files.
86
- * @param {string} BaseScssMatcher - Glob pattern for base SCSS files.
87
- * @param {string} ComponentScssMatcher - Glob pattern for component SCSS files.
88
- * @param {string} ComponentLibraryScssMatcher - Glob pattern for component library SCSS files.
89
- * @param {string} spriteMatcher - Glob pattern for SVG sprite configuration.
90
- * @returns {Object} An object containing the Webpack entries.
181
+ * Build the complete Webpack entries map.
182
+ * @returns {Record<string,string>} Webpack entries.
91
183
  */
92
- function getEntries(
93
- BaseJsMatcher,
94
- jsMatcher,
95
- BaseScssMatcher,
96
- ComponentScssMatcher,
97
- ComponentLibraryScssMatcher,
98
- spriteMatcher,
99
- ) {
100
- const entries = {};
101
-
102
- /**
103
- * Add an entry to the entries object after sanitizing the key.
104
- *
105
- * @param {string} key - The key for the entry.
106
- * @param {string} file - The file path to associate with the entry.
107
- */
108
- const addEntry = (key, file) => {
109
- const sanitizedKey = sanitizePath(key);
110
- if (
111
- sanitizedKey &&
112
- !Object.prototype.hasOwnProperty.call(entries, sanitizedKey)
113
- ) {
114
- // eslint-disable-next-line security/detect-object-injection
115
- entries[sanitizedKey] = file;
116
- }
117
- };
118
-
119
- // Non-component or global JS entries.
120
- globSync(BaseJsMatcher).forEach((file) => {
121
- const filePath = file.split(`${srcDir}/`)[1];
122
- const pathParts = filePath.split('/');
123
- const filePathDist = `${pathParts.slice(0, -1).join('/')}/js/${pathParts
124
- .at(-1)
125
- .replace('.js', '')}`;
126
- const newFilePath = fs.pathExistsSync(resolve(projectDir, 'src'))
127
- ? `dist/global/${filePathDist}`
128
- : `dist/js/${filePathDist}`;
129
- addEntry(newFilePath, file);
130
- });
184
+ const buildEntries = () => {
185
+ /** @type {Map<string, string | string[]>} */
186
+ const entries = new Map();
131
187
 
132
- // Component JS entries.
133
- globSync(jsMatcher).forEach((file) => {
134
- if (!file.includes('dist/')) {
135
- const filePath = file.split(`${srcDir}/components/`)[1];
136
- const filePathDistRaw = replaceLastSlash(filePath, '/js/');
137
- const filePathDist = filePathDistRaw.replace(/\.js$/, '');
138
- const prefix = isDrupal && isSrcExists ? 'components' : 'dist/components';
139
- const newFilePath = `${prefix}/${filePathDist}`;
140
- addEntry(newFilePath, file);
141
- }
142
- });
188
+ /* ----------------------------- Base / Global JS ----------------------------- */
189
+ for (const file of glob(BaseJsPattern)) {
190
+ const sub = distSubpathForBase(file, 'js');
191
+ // If no "src/", legacy layout puts global JS directly under "dist/js".
192
+ const outRoot = hasSrc ? pj(globalOutBase) : pj('dist', 'js');
193
+ addEntry(entries, pj(outRoot, sub), file);
194
+ }
143
195
 
144
- // Non-component or global SCSS entries.
145
- globSync(BaseScssMatcher).forEach((file) => {
146
- const filePath = file.split(`${srcDir}/`)[1];
147
- const pathParts = filePath.split('/');
148
- const filePathDist = `${pathParts.slice(0, -1).join('/')}/css/${pathParts
149
- .at(-1)
150
- .replace('.scss', '')}`;
151
- const newFilePath = fs.pathExistsSync(resolve(projectDir, 'src'))
152
- ? `dist/global/${filePathDist}`
153
- : `dist/css/${filePathDist}`;
154
- addEntry(newFilePath, file);
155
- });
196
+ /* --------------------------- Component JS (no dist) -------------------------- */
197
+ for (const file of glob(ComponentJsPattern)) {
198
+ if (file.includes('/dist/')) continue; // guard against accidental recursion
199
+ const sub = distSubpathForComponent(file, 'js');
200
+ addEntry(entries, componentOutPath(sub), file);
201
+ }
156
202
 
157
- // Component SCSS entries.
158
- globSync(ComponentScssMatcher).forEach((file) => {
159
- const filePath = file.split(`${srcDir}/components/`)[1];
160
- const filePathDistRaw = replaceLastSlash(filePath, '/css/');
161
- const filePathDist = filePathDistRaw.replace(/\.scss$/, '');
162
- const prefix = isDrupal && isSrcExists ? 'components' : 'dist/components';
163
- const newFilePath = `${prefix}/${filePathDist}`;
164
- addEntry(newFilePath, file);
165
- });
203
+ /* ------------------------------ Base / Global CSS --------------------------- */
204
+ for (const file of glob(BaseScssPattern)) {
205
+ const sub = distSubpathForBase(file, 'css');
206
+ // If no "src/", legacy layout puts global CSS directly under "dist/css".
207
+ const outRoot = hasSrc ? pj(globalOutBase) : pj('dist', 'css');
208
+ addEntry(entries, pj(outRoot, sub), file);
209
+ }
166
210
 
167
- // Component Library SCSS entries.
168
- globSync(ComponentLibraryScssMatcher).forEach((file) => {
169
- const filePath = file.split(`${srcDir}/`)[1];
170
- const newFilePath = `dist/storybook/${filePath.replace('.scss', '')}`;
171
- addEntry(newFilePath, file);
172
- });
211
+ /* ---------------------------- Component CSS (SCSS) --------------------------- */
212
+ for (const file of glob(ComponentScssPattern)) {
213
+ const sub = distSubpathForComponent(file, 'scss'); // maps to css
214
+ addEntry(entries, componentOutPath(sub), file);
215
+ }
173
216
 
174
- // SVG sprite config entries.
175
- globSync(spriteMatcher).forEach((file) => {
176
- const filePath = file.split('/assets/')[1];
177
- const newEntry = `dist/${filePath}`;
178
- addEntry(newEntry, file);
179
- });
217
+ /* -------------------------- Component Library (Storybook) -------------------- */
218
+ for (const file of glob(ComponentLibraryScssPattern)) {
219
+ const rel = path.relative(srcDir, file).replace(/\.scss$/, '');
220
+ addEntry(entries, pj('dist', 'storybook', rel), file);
221
+ }
180
222
 
181
- return entries;
182
- }
223
+ return Object.fromEntries(entries);
224
+ };
225
+
226
+ /* -------------------------------------------------------------------------- */
227
+ /* WEBPACK CONFIG EXPORT */
228
+ /* -------------------------------------------------------------------------- */
183
229
 
184
230
  export default {
185
231
  target: 'web',
186
- stats: {
187
- errorDetails: true,
188
- },
189
- entry: getEntries(
190
- BaseJsPattern,
191
- ComponentJsPattern,
192
- BaseScssPattern,
193
- ComponentScssPattern,
194
- ComponentLibraryScssPattern,
195
- spritePattern,
196
- ),
232
+ stats: { errorDetails: true },
233
+ entry: buildEntries(),
197
234
  module: {
198
235
  rules: [
199
236
  loaders.CSSLoader,
@@ -204,22 +241,26 @@ export default {
204
241
  ],
205
242
  },
206
243
  plugins: [
244
+ plugins.RemoveEmptyJS,
207
245
  plugins.MiniCssExtractPlugin,
208
246
  plugins.ImageminPlugin,
209
- plugins.SpriteLoaderPlugin,
247
+ plugins.SpritePlugin,
210
248
  plugins.ProgressPlugin,
211
249
  plugins.CopyTwigPlugin,
250
+ plugins.CopyComponentAssetsPlugin,
251
+ ...(plugins.CopyGlobalAssetsPlugin ? [plugins.CopyGlobalAssetsPlugin] : []),
212
252
  plugins.CleanWebpackPlugin,
213
253
  ],
214
254
  output: {
215
- path: `${projectDir}`,
255
+ path: projectDir,
216
256
  filename: '[name].js',
217
257
  },
218
258
  resolve: resolves.TwigResolve,
219
259
  optimization: optimizers,
260
+ // Quiet deprecation noise from Sass @import warnings
220
261
  ignoreWarnings: [
221
262
  (warning) =>
222
- warning.message &&
263
+ Boolean(warning?.message) &&
223
264
  /Sass @import rules are deprecated/.test(warning.message),
224
265
  ],
225
266
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emulsify/core",
3
- "version": "3.1.1",
3
+ "version": "3.3.0",
4
4
  "description": "Bundled tooling for Storybook development + Webpack Build",
5
5
  "keywords": [
6
6
  "component library",
@@ -50,11 +50,11 @@
50
50
  "twatch": "jest --no-coverage --watch --verbose"
51
51
  },
52
52
  "dependencies": {
53
- "@babel/core": "^7.27.7",
54
- "@babel/eslint-parser": "^7.27.5",
55
- "@babel/preset-env": "^7.27.2",
53
+ "@babel/core": "^7.28.4",
54
+ "@babel/eslint-parser": "^7.28.4",
55
+ "@babel/preset-env": "^7.28.3",
56
56
  "@emulsify/cli": "^1.11.4",
57
- "@eslint/js": "^9.30.0",
57
+ "@eslint/js": "^9.35.0",
58
58
  "@storybook/addon-a11y": "^8.6.14",
59
59
  "@storybook/addon-actions": "^8.6.14",
60
60
  "@storybook/addon-essentials": "^8.6.14",
@@ -72,61 +72,60 @@
72
72
  "babel-preset-minify": "^0.5.2",
73
73
  "bem-twig-extension": "^0.1.1",
74
74
  "breakpoint-sass": "^3.0.0",
75
- "chalk": "^5.4.1",
76
75
  "clean-webpack-plugin": "^4.0.0",
77
- "concurrently": "^9.2.0",
78
- "copy-webpack-plugin": "^13.0.0",
76
+ "concurrently": "^9.2.1",
77
+ "copy-webpack-plugin": "^13.0.1",
79
78
  "css-loader": "^7.1.1",
80
- "eslint": "^9.30.0",
81
- "eslint-config-prettier": "^10.1.5",
79
+ "eslint": "^9.35.0",
80
+ "eslint-config-prettier": "^10.1.8",
82
81
  "eslint-plugin-import": "^2.32.0",
83
82
  "eslint-plugin-jest": "^29.0.1",
84
- "eslint-plugin-prettier": "^5.5.1",
83
+ "eslint-plugin-prettier": "^5.5.4",
85
84
  "eslint-plugin-security": "^3.0.1",
86
85
  "eslint-plugin-storybook": "^0.12.0",
87
86
  "eslint-webpack-plugin": "^5.0.2",
88
87
  "file-loader": "^6.2.0",
89
- "fs-extra": "^11.3.0",
88
+ "fs-extra": "^11.3.1",
90
89
  "glob": "^11.0.3",
91
90
  "graceful-fs": "^4.2.11",
92
- "html-webpack-plugin": "^5.6.3",
93
- "image-minimizer-webpack-plugin": "^4.1.3",
91
+ "html-webpack-plugin": "^5.6.4",
92
+ "image-minimizer-webpack-plugin": "^4.1.4",
94
93
  "imagemin": "^9.0.1",
95
- "imagemin-gifsicle": "^7.0.0",
96
94
  "imagemin-jpegtran": "^8.0.0",
97
95
  "imagemin-optipng": "^8.0.0",
98
- "jest": "^30.0.3",
99
- "jest-environment-jsdom": "^30.0.2",
96
+ "jest": "^30.1.3",
97
+ "jest-environment-jsdom": "^30.1.2",
100
98
  "js-yaml": "^4.1.0",
101
99
  "js-yaml-loader": "^1.2.2",
102
- "mini-css-extract-plugin": "^2.9.2",
100
+ "mini-css-extract-plugin": "^2.9.4",
103
101
  "node-sass-glob-importer": "^5.3.3",
104
102
  "normalize.css": "^8.0.1",
105
103
  "open-cli": "^8.0.0",
106
104
  "pa11y": "^9.0.0",
107
105
  "postcss": "^8.5.6",
108
- "postcss-loader": "^8.1.1",
106
+ "postcss-loader": "^8.2.0",
109
107
  "postcss-scss": "^4.0.9",
110
108
  "ramda": "^0.31.3",
111
109
  "regenerator-runtime": "^0.14.1",
112
- "sass": "^1.89.2",
110
+ "sass": "^1.92.1",
113
111
  "sass-loader": "^16.0.5",
114
112
  "storybook": "^8.6.14",
115
- "style-dictionary": "^4.4.0",
116
- "stylelint": "^16.21.0",
113
+ "style-dictionary": "^5.0.4",
114
+ "stylelint": "^16.24.0",
117
115
  "stylelint-config-standard-scss": "^15.0.1",
118
116
  "stylelint-prettier": "^5.0.3",
119
117
  "stylelint-selector-bem-pattern": "^4.0.1",
120
118
  "stylelint-webpack-plugin": "^5.0.1",
121
- "svg-sprite-loader": "^6.0.11",
119
+ "svg-spritemap-webpack-plugin": "^5.0.1",
122
120
  "token-transformer": "^0.0.33",
123
121
  "twig-drupal-filters": "^3.2.0",
124
122
  "twig-testing-library": "^1.2.0",
125
123
  "twigjs-loader": "^1.0.3",
126
- "webpack": "^5.99.9",
124
+ "webpack": "^5.101.3",
127
125
  "webpack-cli": "^6.0.1",
128
126
  "webpack-merge": "^6.0.1",
129
- "yaml": "^2.8.0"
127
+ "webpack-remove-empty-scripts": "^1.1.1",
128
+ "yaml": "^2.8.1"
130
129
  },
131
130
  "devDependencies": {
132
131
  "@commitlint/cli": "^19.8.1",
@@ -134,12 +133,12 @@
134
133
  "@semantic-release/changelog": "^6.0.2",
135
134
  "@semantic-release/commit-analyzer": "^13.0.1",
136
135
  "@semantic-release/git": "^10.0.1",
137
- "@semantic-release/github": "^11.0.3",
138
- "@semantic-release/release-notes-generator": "^14.0.3",
136
+ "@semantic-release/github": "^11.0.5",
137
+ "@semantic-release/release-notes-generator": "^14.1.0",
139
138
  "all-contributors-cli": "^6.26.1",
140
139
  "husky": "^9.1.7",
141
- "lint-staged": "^16.1.2",
142
- "semantic-release": "^24.2.5"
140
+ "lint-staged": "^16.1.6",
141
+ "semantic-release": "^24.2.7"
143
142
  },
144
143
  "overrides": {
145
144
  "inflight": "^1.0.7",