@pixolith/webpack-sw6-config 11.0.8 → 12.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pixolith/webpack-sw6-config",
3
3
  "public": true,
4
- "version": "11.0.8",
4
+ "version": "12.0.0",
5
5
  "description": "",
6
6
  "main": "src/index.js",
7
7
  "scripts": {},
@@ -21,7 +21,7 @@
21
21
  "url": "https://github.com/pixolith/webpack-plugins/issues"
22
22
  },
23
23
  "homepage": "https://github.com/pixolith/webpack-plugins/tree/master/packages/webpack-hook-plugin/#readme",
24
- "gitHead": "318c5c1605244bf6835032ec86e6ee130da6aba3",
24
+ "gitHead": "5c29b0a4c1b2e0876f25a05f517100c83fc9a36c",
25
25
  "dependencies": {
26
26
  "@babel/cli": "7.25.9",
27
27
  "@babel/core": "^7.26.0",
@@ -29,15 +29,15 @@
29
29
  "@babel/plugin-proposal-decorators": "7.25.9",
30
30
  "@babel/preset-env": "7.26.0",
31
31
  "@babel/preset-typescript": "7.26.0",
32
- "@pixolith/eslint-config-sw6": "^11.0.0",
33
- "@pixolith/external-svg-sprite-loader": "^11.0.0",
34
- "@pixolith/stylelint-config-standard": "^11.0.0",
35
- "@pixolith/webpack-assets-copy-plugin": "^11.0.0",
36
- "@pixolith/webpack-filename-linter-plugin": "^11.0.0",
37
- "@pixolith/webpack-hook-plugin": "^11.0.0",
38
- "@pixolith/webpack-sw6-plugin-map-emitter": "^11.0.0",
39
- "@pixolith/webpack-twig-assets-emitter-plugin": "^11.0.8",
40
- "@pixolith/webpack-watcher": "^11.0.5",
32
+ "@pixolith/eslint-config-sw6": "^12.0.0",
33
+ "@pixolith/external-svg-sprite-loader": "^12.0.0",
34
+ "@pixolith/stylelint-config-standard": "^12.0.0",
35
+ "@pixolith/webpack-assets-copy-plugin": "^12.0.0",
36
+ "@pixolith/webpack-filename-linter-plugin": "^12.0.0",
37
+ "@pixolith/webpack-hook-plugin": "^12.0.0",
38
+ "@pixolith/webpack-sw6-plugin-map-emitter": "^12.0.0",
39
+ "@pixolith/webpack-twig-assets-emitter-plugin": "^12.0.0",
40
+ "@pixolith/webpack-watcher": "^12.0.0",
41
41
  "@swc/core": "^1.9.3",
42
42
  "autoprefixer": "^10.4.20",
43
43
  "babel-loader": "^9.2.1",
package/src/config.js CHANGED
@@ -6,11 +6,22 @@ const config = {
6
6
  isProd: process.env.NODE_ENV === 'production',
7
7
  isDebug: !!process.env.DEBUG || false,
8
8
  shopwareMode: process.env.SHOPWARE_MODE,
9
- shopwareVersion: process.env.SHOPWARE_VERSION || '6.6',
10
9
 
11
10
  assetUrl: process.env.ASSET_URL || '/',
12
11
  pluginPrefixes: process.env.PLUGIN_PREFIXES || 'Pxsw',
13
12
 
13
+ // Multi-theme build configuration (mandatory in v12+)
14
+ themeNames: process.env.THEME_NAMES
15
+ ? process.env.THEME_NAMES.split(',')
16
+ .map((s) => s.trim())
17
+ .filter(Boolean)
18
+ : [],
19
+ skipPlugins: process.env.SKIP_PLUGINS
20
+ ? process.env.SKIP_PLUGINS.split(',')
21
+ .map((s) => s.trim())
22
+ .filter(Boolean)
23
+ : [],
24
+
14
25
  pxSharedPath: process.env.SHARED_SCSS_PATH || '../../shared',
15
26
  scssFolder: process.env.SCSS_FOLDER || 'scss',
16
27
  jsFolder: process.env.JS_FOLDER || 'js',
@@ -32,11 +43,35 @@ const config = {
32
43
  shopwareVendorPath: Path.join(process.cwd(), 'vendor/shopware/storefront/Resources/app/storefront/vendor'),
33
44
  shopwarePluginPath: Path.join(process.cwd(), 'vendor/shopware/storefront/Resources/app/storefront/src'),
34
45
 
35
- allowedExtensions: ['.ts', '.js', '.scss', '.css', '.svg']
46
+ allowedExtensions: ['.ts', '.js', '.scss', '.css', '.svg'],
47
+ };
48
+
49
+ // PICKED_THEME: optional filter to build/watch a single theme from THEME_NAMES
50
+ let pickedTheme = process.env.PICKED_THEME
51
+ ? process.env.PICKED_THEME.trim()
52
+ : '';
53
+
54
+ if (pickedTheme && config.themeNames.indexOf(pickedTheme) === -1) {
55
+ process.stderr.write(
56
+ `PICKED_THEME "${pickedTheme}" is not in THEME_NAMES [${config.themeNames.join(', ')}].\n`,
57
+ );
58
+ process.exit(1);
36
59
  }
37
60
 
38
- const pxEntryPath = process.env.PX_ENTRY_PATH || (process.env.SHOPWARE_MODE === 'storefront' ? 'src/Resources/app/storefront/private' : 'src/Resources/app/administration/src');
39
- const pxRouteSplitPath = process.env.PX_ROUTE_SPLIT_PATH || (process.env.SHOPWARE_MODE === 'storefront' ? 'src/Resources/app/storefront/private/scss-route-split/*' : '');
61
+ // buildThemes: the subset of themeNames that actually get compiled
62
+ // themeNames stays as the full list (used for _global_resources exclusion)
63
+ config.buildThemes = pickedTheme ? [pickedTheme] : config.themeNames;
64
+
65
+ const pxEntryPath =
66
+ process.env.PX_ENTRY_PATH ||
67
+ (process.env.SHOPWARE_MODE === 'storefront'
68
+ ? 'src/Resources/app/storefront/private'
69
+ : 'src/Resources/app/administration/src');
70
+ const pxRouteSplitPath =
71
+ process.env.PX_ROUTE_SPLIT_PATH ||
72
+ (process.env.SHOPWARE_MODE === 'storefront'
73
+ ? 'src/Resources/app/storefront/private/scss-route-split/*'
74
+ : '');
40
75
 
41
76
  // Create a glob regex to match the plugin prefixes
42
77
  let prefixes = config.pluginPrefixes.split(',').map(p => `${p}*`).join('|');
@@ -49,6 +84,7 @@ const routeSplitMatch = new RegExp(`/scss-route-split\/([\\w-]*)`);
49
84
 
50
85
  module.exports = {
51
86
  ...config,
87
+
52
88
  pluginSrcPath: Path.join(pluginSrcPath, pxEntryPath),
53
89
  pluginScssPath: Path.join(pluginSrcPath, pxEntryPath, config.scssFolder),
54
90
  pluginJsPath: Path.join(pluginSrcPath, pxEntryPath, config.jsFolder),
@@ -70,5 +106,9 @@ module.exports = {
70
106
  pluginRouteSplitPath: Path.join(pluginSrcPath, pxRouteSplitPath),
71
107
  vendorRouteSplitPath: Path.join(vendorSrcPath, pxRouteSplitPath),
72
108
 
73
- resourcesPath: process.env.RESOURCES_PATHS
74
- }
109
+ // Raw glob base paths for multi-theme resource resolution
110
+ pluginGlobBase: pluginSrcPath,
111
+ vendorGlobBase: vendorSrcPath,
112
+ pxEntryPath: pxEntryPath,
113
+ resourcesPath: process.env.RESOURCES_PATHS,
114
+ };
package/src/index.js CHANGED
@@ -1,15 +1,20 @@
1
+ const Fs = require('fs');
2
+ const Glob = require('glob');
1
3
  const config = require('./config');
2
4
  const production = require('./webpack.config.production');
3
- const storefront = require('./webpack.config.storefront');
4
- const dev = require('./webpack.config.dev');
5
- const administration = require('./webpack.config.administration');
5
+ const createStorefrontConfig = require('./webpack.config.storefront');
6
+ const createDevConfig = require('./webpack.config.dev');
7
+ const createAdministrationConfig = require('./webpack.config.administration');
6
8
  const pkg = require('./../package.json');
7
9
  const watcher = require('@pixolith/webpack-watcher');
8
10
  const { merge } = require('webpack-merge');
11
+ const ChangeCase = require('change-case');
9
12
 
10
13
  const setup = () => {
11
14
  watcher.clean(config);
12
- watcher.run(null, config);
15
+ config.buildThemes.forEach((themeName) => {
16
+ watcher.compileTheme(themeName, config);
17
+ });
13
18
 
14
19
  if (config.isDebug) {
15
20
  console.table({
@@ -29,14 +34,207 @@ const setup = () => {
29
34
  isProd: config.isProd,
30
35
  shopwareMode: config.shopwareMode,
31
36
  assetUrl: config.assetUrl,
37
+ themeNames: config.themeNames.join(', ') + (config.buildThemes.length < config.themeNames.length ? ' (building: ' + config.buildThemes.join(', ') + ')' : ''),
32
38
  version: pkg.version,
33
39
  });
34
40
  }
35
41
  };
36
42
 
37
- setup();
43
+ /**
44
+ * Build per-theme resource paths for sass-resources-loader.
45
+ *
46
+ * Each theme gets:
47
+ * 1. uses.scss (shared vendor utilities)
48
+ * 2. vendor global resources (vendor/pxsw/...)
49
+ * 3. all non-theme plugin _global_resources (custom/plugins + custom/static-plugins
50
+ * that are NOT in THEME_NAMES)
51
+ * 4. this theme's _global_resources (overrides everything)
52
+ *
53
+ * Other themes' global resources are excluded to prevent style bleeding.
54
+ *
55
+ * @param {string} themeName - Theme name (e.g. 'PxswEbertTheme')
56
+ * @param {Object} options
57
+ * @param {string[]} options.uses - Paths to shared vendor uses.scss
58
+ * @param {string[]} options.sharedVendorResourcePaths - Paths to shared vendor _global_resources
59
+ * @returns {string[]} Array of SCSS resource paths
60
+ */
61
+ const getResourcesPaths = (themeName, options) => {
62
+ let uses = options.uses || [];
63
+ let sharedVendorResourcePaths = options.sharedVendorResourcePaths || [];
64
+ let paths = [].concat(uses, sharedVendorResourcePaths);
65
+ let globalResourcesSuffix = '/src/Resources/app/_global_resources';
66
+ let scssGlob = '/**/*.scss';
67
+
68
+ // Collect base plugin global resources (plugins NOT in THEME_NAMES)
69
+ let pluginDirs = ['custom/plugins', 'custom/static-plugins'];
70
+ pluginDirs.forEach((dir) => {
71
+ if (!Fs.existsSync(dir)) {
72
+ return;
73
+ }
74
+
75
+ let entries = Fs.readdirSync(dir).filter((name) => {
76
+ // Must match plugin prefix pattern
77
+ let prefixes = (config.pluginPrefixes || 'Pxsw').split(',');
78
+ let matchesPrefix = prefixes.some((p) =>
79
+ name.startsWith(p.trim()),
80
+ );
81
+ if (!matchesPrefix) {
82
+ return false;
83
+ }
84
+ // Exclude ALL themes from base pool — prevents style bleeding
85
+ let isTheme = config.themeNames.some(
86
+ (t) => name.indexOf(t) !== -1,
87
+ );
88
+ return !isTheme;
89
+ });
90
+
91
+ entries.forEach((name) => {
92
+ let glob = dir + '/' + name + globalResourcesSuffix + scssGlob;
93
+ if (Glob.sync(glob).length) {
94
+ paths.push(glob);
95
+ }
96
+ });
97
+ });
98
+
99
+ // Add this theme's global resources LAST (overrides base)
100
+ pluginDirs.forEach((dir) => {
101
+ if (!Fs.existsSync(dir)) {
102
+ return;
103
+ }
104
+
105
+ let themeGlob =
106
+ dir + '/*' + themeName + '*' + globalResourcesSuffix + scssGlob;
107
+ if (Glob.sync(themeGlob).length) {
108
+ paths.push(themeGlob);
109
+ }
110
+ });
111
+
112
+ return paths;
113
+ };
114
+
115
+ /**
116
+ * Create an array of webpack configs, one per theme.
117
+ * Each theme gets its own dev config (with per-theme resourcesPaths for
118
+ * sass-resources-loader) and its own storefront config (with per-theme entry).
119
+ *
120
+ * @param {Object} options
121
+ * @param {string[]} options.uses - Paths to shared vendor uses.scss
122
+ * @param {string[]} options.sharedVendorResourcePaths - Paths to shared vendor _global_resources
123
+ * @returns {Object[]} Array of webpack config objects (one per theme)
124
+ */
125
+ const createThemeConfigs = (options) => {
126
+ setup();
127
+
128
+ return config.buildThemes.map((themeName, index) => {
129
+ let resourcesPaths = getResourcesPaths(themeName, options);
130
+ let themeSlug = ChangeCase.kebabCase(themeName);
131
+ let themeOptions = {
132
+ themeName: themeName,
133
+ resourcesPaths: resourcesPaths,
134
+ };
135
+
136
+ let devCfg = createDevConfig(themeOptions);
137
+ let storefrontCfg = createStorefrontConfig(themeOptions);
138
+ let merged = merge(
139
+ devCfg,
140
+ storefrontCfg,
141
+ config.isProd ? production : {},
142
+ );
143
+
144
+ // Only the first compiler should have devServer (webpack multi-compiler limitation)
145
+ if (index > 0) {
146
+ delete merged.devServer;
147
+ }
148
+
149
+ // Per-theme filesystem cache to avoid collisions between compilers
150
+ merged.cache = {
151
+ type: 'filesystem',
152
+ name: themeSlug,
153
+ buildDependencies: {
154
+ config: [__filename],
155
+ },
156
+ };
157
+
158
+ return merged;
159
+ });
160
+ };
161
+
162
+ const setupAdministration = () => {
163
+ watcher.clean(config);
164
+ config.buildThemes.forEach((themeName) => {
165
+ watcher.compileAdministration(themeName, config);
166
+ });
167
+
168
+ if (config.isDebug) {
169
+ console.table({
170
+ ...config,
171
+
172
+ version: pkg.version,
173
+
174
+ pluginMatch: config.pluginMatch.toString(),
175
+ vendorMatch: config.vendorMatch.toString(),
176
+ allowedExtensions: config.allowedExtensions.toString(),
177
+ });
178
+ } else {
179
+ console.table({
180
+ isProd: config.isProd,
181
+ shopwareMode: config.shopwareMode,
182
+ assetUrl: config.assetUrl,
183
+ themeNames: config.themeNames.join(', ') + (config.buildThemes.length < config.themeNames.length ? ' (building: ' + config.buildThemes.join(', ') + ')' : ''),
184
+ version: pkg.version,
185
+ });
186
+ }
187
+ };
188
+
189
+ /**
190
+ * Create an array of webpack configs for administration, one per theme.
191
+ * Each theme gets its own dev config (with per-theme resourcesPaths for
192
+ * sass-resources-loader) and its own administration config (with per-theme
193
+ * SCSS entries for theme-specific styling).
194
+ *
195
+ * @param {Object} options
196
+ * @param {string[]} options.uses - Paths to shared vendor uses.scss
197
+ * @param {string[]} options.sharedVendorResourcePaths - Paths to shared vendor _global_resources
198
+ * @returns {Object[]} Array of webpack config objects (one per theme)
199
+ */
200
+ const createAdminConfigs = (options) => {
201
+ setupAdministration();
202
+
203
+ return config.buildThemes.map((themeName, index) => {
204
+ let resourcesPaths = getResourcesPaths(themeName, options);
205
+ let themeSlug = ChangeCase.kebabCase(themeName);
206
+ let themeOptions = {
207
+ themeName: themeName,
208
+ resourcesPaths: resourcesPaths,
209
+ };
210
+
211
+ let devCfg = createDevConfig(themeOptions);
212
+ let adminCfg = createAdministrationConfig(themeOptions);
213
+ let merged = merge(
214
+ devCfg,
215
+ adminCfg,
216
+ config.isProd ? production : {},
217
+ );
218
+
219
+ // Only the first compiler should have devServer (webpack multi-compiler limitation)
220
+ if (index > 0) {
221
+ delete merged.devServer;
222
+ }
223
+
224
+ // Per-theme filesystem cache to avoid collisions between compilers
225
+ merged.cache = {
226
+ type: 'filesystem',
227
+ name: themeSlug + '-admin',
228
+ buildDependencies: {
229
+ config: [__filename],
230
+ },
231
+ };
232
+
233
+ return merged;
234
+ });
235
+ };
38
236
 
39
237
  module.exports = {
40
- storefront: merge(dev, storefront, config.isProd ? production : {}),
41
- administration: merge(dev, administration, config.isProd ? production : {}),
238
+ createThemeConfigs: createThemeConfigs,
239
+ createAdminConfigs: createAdminConfigs,
42
240
  };
@@ -5,178 +5,110 @@ const Path = require('path'),
5
5
  Entry = require('webpack-glob-entry'),
6
6
  ChangeCase = require('change-case'),
7
7
  MiniCssExtractPlugin = require('mini-css-extract-plugin'),
8
- Consola = require('consola'),
9
- AssetsCopyPlugin = require('@pixolith/webpack-assets-copy-plugin'),
10
- SvgStorePlugin = require('@pixolith/external-svg-sprite-loader'),
11
- TwigAssetEmitterPlugin = require('@pixolith/webpack-twig-assets-emitter-plugin'),
12
- outputConfig = {
8
+ Consola = require('consola');
9
+
10
+ module.exports = function createAdministrationConfig(themeOptions) {
11
+ let themeName = themeOptions && themeOptions.themeName;
12
+ let themeSlug = themeName
13
+ ? ChangeCase.kebabCase(themeName)
14
+ : 'administration';
15
+
16
+ let outputConfig = {
13
17
  path: config.outputPath,
14
- publicPath: config.shopwareVersion === '6.6' ? '/' : config.assetUrl,
15
- filename: (chunkData) => {
16
- let pluginName = chunkData.chunk.name.toLowerCase().replace('pxsw-pxsw-', 'pxsw-');
17
- pluginName = config.shopwareVersion === '6.6' ? pluginName.replace('vendor-', '') : pluginName;
18
- return config.shopwareVersion === '6.6' ?
19
- `${pluginName.replace(/-/g, '',)}/administration/js/${pluginName}.js` :
20
- `js/${chunkData.chunk.name.toLowerCase()}${
21
- config.isProd ? '.admin.[contenthash]' : ''
22
- }.js`;
23
- }
24
- },
25
- miniCssChunksConfig = {
18
+ publicPath: config.assetUrl,
26
19
  filename: (chunkData) => {
27
- let pluginName = chunkData.chunk.name.toLowerCase().replace('pxsw-pxsw-', 'pxsw-');
28
- pluginName = config.shopwareVersion === '6.6' ? pluginName.replace('vendor-', '') : pluginName;
29
- return config.shopwareVersion === '6.6' ?
30
- `${pluginName.replace(/-/g, '',)}/administration/css/${pluginName}.css` :
31
- `css/[name]${
32
- config.isProd ? '.admin.[contenthash]' : ''
33
- }.css`;
34
- }
35
- }
20
+ return `js/${chunkData.chunk.name.toLowerCase()}${
21
+ config.isProd ? '.admin.[contenthash]' : ''
22
+ }.js`;
23
+ },
24
+ uniqueName: themeSlug + '-admin',
25
+ };
36
26
 
37
- module.exports = {
38
- entry: () => {
39
- let entriesPlugins = Entry(
40
- (filePath) =>
41
- ChangeCase.kebabCase(
42
- filePath.match(config.pluginMatch)[1],
43
- ),
44
- Path.join(config.pluginSrcPath, 'index.js'),
45
- );
27
+ let miniCssChunksConfig = {
28
+ filename: (chunkData) => {
29
+ return `css/[name]${
30
+ config.isProd ? '.admin.[contenthash]' : ''
31
+ }.css`;
32
+ },
33
+ };
46
34
 
47
- let entriesVendor = Entry(
48
- (filePath) =>
49
- ChangeCase.kebabCase(
50
- filePath.match(config.vendorMatch)[1],
51
- ),
52
- Path.resolve(config.vendorSrcPath, 'index.js'),
53
- );
35
+ return {
36
+ name: themeSlug + '-admin',
37
+ entry: () => {
38
+ let entriesPlugins = Entry(
39
+ (filePath) =>
40
+ ChangeCase.kebabCase(filePath.match(config.pluginMatch)[2]),
41
+ Path.join(config.pluginSrcPath, 'index.js'),
42
+ );
54
43
 
55
- if (process.env.DEBUG) {
56
- Consola.info('[DEBUG]: Webpack entry points:');
57
- console.table({...entriesPlugins, ...entriesVendor });
58
- }
44
+ let entriesVendor = Entry(
45
+ (filePath) =>
46
+ ChangeCase.kebabCase(filePath.match(config.vendorMatch)[1]),
47
+ Path.resolve(config.vendorSrcPath, 'index.js'),
48
+ );
59
49
 
60
- return {...entriesPlugins, ...entriesVendor };
61
- },
62
- module: {
63
- rules: [
64
- {
65
- test: /\.js$/,
66
- exclude: (file) => /node_modules/.test(file),
67
- loader: 'babel-loader',
68
- options: {
69
- configFile: Path.resolve(__dirname, 'babel.config.js'),
70
- },
71
- },
72
- {
73
- test: /\.(jpe?g|png|gif|ico)(\?v=\d+\.\d+\.\d+)?$/,
74
- type: 'asset/resource',
75
- generator: {
76
- filename: config.shopwareVersion === '6.6' ? '../img/[name][ext]' : 'img/[name][ext]'
77
- }
78
- },
79
- {
80
- test: /\.(eot|ttf|woff2?)(\?v=\d+\.\d+\.\d+)?$/,
81
- type: 'asset/resource',
82
- generator: {
83
- filename: config.shopwareVersion === '6.6' ? '../fonts/[name][ext]' : 'fonts/[name][ext]'
84
- }
85
- },
86
- {
87
- test: /\.svg$/,
88
- use: [
89
- {
90
- loader: SvgStorePlugin.loader,
91
- options: {
92
- name: 'sprite/sprite.svg',
93
- iconName: '[name]',
94
- overrideOrder: config.spriteOrder,
95
- ignoreIconsByName: config.ignoreIcons,
96
- onlySymbols: true,
97
- },
98
- },
99
- {
100
- loader: 'svgo-loader',
101
- options: {
102
- plugins: [
103
- 'cleanupAttrs',
104
- 'removeDoctype',
105
- 'removeXMLProcInst',
106
- 'cleanupEnableBackground',
107
- 'convertStyleToAttrs',
108
- 'convertPathData',
109
- 'cleanupIds',
110
- 'minifyStyles',
111
- 'removeUselessDefs',
112
- 'convertShapeToPath',
113
- 'removeUnusedNS',
114
- 'removeDimensions',
115
- 'convertTransform',
116
- 'collapseGroups',
117
- 'removeComments',
118
- 'removeEditorsNSData',
119
- 'removeUnknownsAndDefaults',
120
- ],
121
- },
122
- },
123
- ],
124
- },
125
- ],
126
- },
127
- output: outputConfig,
128
- plugins: [
129
- new SvgStorePlugin(),
130
- new MiniCssExtractPlugin(miniCssChunksConfig),
131
- ].concat(
132
- config.isProd && config.shopwareVersion === '6.6' ?
133
- new AssetsCopyPlugin({
134
- includes: ['js', 'css'],
135
- ignoreFiles: [/[-\w.]*.hot-update.js/],
136
- files: [
137
- {
138
- from: config.outputPath,
139
- to: '$pluginPath/$plugin/src/Resources/public',
140
- replace: async (fromPath, toPath) => {
141
- let composerPluginName = Path.basename(fromPath).replace(
142
- Path.extname(fromPath),
143
- '',
144
- ).replace('pxsw-', ''),
145
- pluginName = 'Pxsw' + ChangeCase.pascalCase(composerPluginName);
50
+ // Discover the consolidated admin SCSS entry from .theme-entries/{theme-slug}/
51
+ let scssEntries = {};
52
+ let scssEntryDir = Path.join(
53
+ config.outputPath,
54
+ '.theme-entries',
55
+ themeSlug,
56
+ );
57
+ let scssEntryFile = Path.join(
58
+ scssEntryDir,
59
+ themeSlug + '-admin.scss',
60
+ );
146
61
 
147
- let isPlugin = await Fs.existsSync(`custom/plugins/${pluginName}/src`),
148
- isStaticPlugin = await Fs.existsSync(`custom/static-plugins/${pluginName}/src`);
62
+ if (Fs.existsSync(scssEntryFile)) {
63
+ scssEntries[themeSlug + '-admin-scss'] = scssEntryFile;
64
+ }
149
65
 
150
- let pluginFolder = isPlugin ? 'custom/plugins' : (isStaticPlugin ? 'custom/static-plugins' : 'vendor/pxsw');
66
+ let allEntries = {
67
+ ...entriesPlugins,
68
+ ...entriesVendor,
69
+ ...scssEntries,
70
+ };
151
71
 
152
- toPath = toPath.replace('$pluginPath', pluginFolder);
153
- if (!isPlugin && !isStaticPlugin) {
154
- let isDoublePxswPlugin = await Fs.existsSync(`vendor/pxsw/pxsw-${composerPluginName}/src`);
155
- toPath = toPath.replace('$plugin', isDoublePxswPlugin ? `pxsw-${composerPluginName}` : composerPluginName);
156
- } else {
157
- toPath = toPath.replace('$plugin', pluginName);
158
- }
72
+ if (config.isDebug) {
73
+ Consola.info(
74
+ `[${themeName || themeSlug}] Administration webpack entry points:`,
75
+ );
76
+ console.table(allEntries);
77
+ }
159
78
 
160
- return toPath;
161
- },
79
+ return allEntries;
80
+ },
81
+ module: {
82
+ rules: [
83
+ {
84
+ test: /\.js$/,
85
+ exclude: (file) => /node_modules/.test(file),
86
+ loader: 'babel-loader',
87
+ options: {
88
+ configFile: Path.resolve(__dirname, 'babel.config.js'),
162
89
  },
163
- ],
164
- }) : [],
165
- ).concat(
166
- config.isProd && config.shopwareVersion !== '6.6' ?
167
- new TwigAssetEmitterPlugin({
168
- includes: ['js', 'css'],
169
- ignoreFiles: [/.*icons.*\.js/],
170
- template: {
171
- admin: {
172
- assetUrl: config.assetUrl,
173
- filename: 'index.html.twig',
90
+ },
91
+ {
92
+ test: /\.(jpe?g|png|gif|ico)(\?v=\d+\.\d+\.\d+)?$/,
93
+ type: 'asset/resource',
94
+ generator: {
95
+ filename: 'img/[name][ext]',
96
+ },
97
+ },
98
+ {
99
+ test: /\.(eot|ttf|woff2?)(\?v=\d+\.\d+\.\d+)?$/,
100
+ type: 'asset/resource',
101
+ generator: {
102
+ filename: 'fonts/[name][ext]',
174
103
  },
175
104
  },
176
- }) : [],
177
- ),
105
+ ],
106
+ },
107
+ output: outputConfig,
108
+ plugins: [new MiniCssExtractPlugin(miniCssChunksConfig)],
178
109
 
179
- optimization: {
180
- splitChunks: false,
181
- },
110
+ optimization: {
111
+ splitChunks: false,
112
+ },
113
+ };
182
114
  };
@@ -13,188 +13,183 @@ const webpack = require('webpack'),
13
13
  Sass = require('sass'),
14
14
  TimeFixPlugin = require('time-fix-plugin');
15
15
 
16
- module.exports = {
17
- target: 'web',
18
- mode: 'development',
19
- resolve: {
20
- modules: [
21
- 'node_modules',
22
- Path.resolve(config.shopwareVendorPath),
23
- ],
24
- alias: {
25
- src: Path.resolve(config.shopwarePluginPath),
26
- vendor: Path.resolve(config.shopwareVendorPath),
16
+ module.exports = function createDevConfig(themeOptions) {
17
+ let resourcesPaths =
18
+ themeOptions && themeOptions.resourcesPaths
19
+ ? themeOptions.resourcesPaths
20
+ : JSON.parse(process.env.RESOURCES_PATHS || '[]');
21
+
22
+ let resolvedResources = resourcesPaths.filter((path) => {
23
+ return Glob.sync(path).length > 0;
24
+ });
25
+
26
+ return {
27
+ target: 'web',
28
+ mode: 'development',
29
+ resolve: {
30
+ modules: ['node_modules', Path.resolve(config.shopwareVendorPath)],
31
+ alias: {
32
+ src: Path.resolve(config.shopwarePluginPath),
33
+ vendor: Path.resolve(config.shopwareVendorPath),
34
+ },
27
35
  },
28
- },
29
- devtool: 'inline-cheap-module-source-map',
30
- module: {
31
- rules: [
32
- {
33
- test: /(\.scss|\.css)$/,
34
- use: [
35
- config.isProd ? MiniCssExtractPlugin.loader : 'style-loader',
36
- {
37
- loader: 'css-loader',
38
- options: {
39
- importLoaders: 1,
40
- sourceMap: !config.isProd,
36
+ devtool: 'inline-cheap-module-source-map',
37
+ module: {
38
+ rules: [
39
+ {
40
+ test: /(\.scss|\.css)$/,
41
+ use: [
42
+ config.isProd
43
+ ? MiniCssExtractPlugin.loader
44
+ : 'style-loader',
45
+ {
46
+ loader: 'css-loader',
47
+ options: {
48
+ importLoaders: 1,
49
+ sourceMap: !config.isProd,
50
+ },
41
51
  },
42
- },
43
- config.isProd && config.mediaQueries ? MediaQueryPlugin.loader : '',
44
- {
45
- loader: 'postcss-loader',
46
- options: {
47
- sourceMap: !config.isProd,
48
- postcssOptions: {
49
- config: Path.resolve(__dirname, 'postcss.config.js'),
52
+ config.isProd && config.mediaQueries
53
+ ? MediaQueryPlugin.loader
54
+ : '',
55
+ {
56
+ loader: 'postcss-loader',
57
+ options: {
58
+ sourceMap: !config.isProd,
59
+ postcssOptions: {
60
+ config: Path.resolve(
61
+ __dirname,
62
+ 'postcss.config.js',
63
+ ),
64
+ },
50
65
  },
51
66
  },
52
- },
53
- {
54
- loader: 'sass-loader',
55
- options: {
56
- sourceMap: !config.isProd,
57
- additionalData: `$asset_url: '${config.assetUrl}';`,
58
- sassOptions: {
59
- quietDeps: true,
60
- logger: Sass.Logger.silent,
67
+ {
68
+ loader: 'sass-loader',
69
+ options: {
70
+ sourceMap: !config.isProd,
71
+ additionalData: `$asset_url: '${config.assetUrl}';`,
72
+ sassOptions: {
73
+ quietDeps: true,
74
+ logger: Sass.Logger.silent,
75
+ },
61
76
  },
62
77
  },
63
- },
64
- {
65
- loader: 'sass-resources-loader',
66
- options: {
67
- resources: JSON.parse(
68
- process.env.RESOURCES_PATHS,
69
- ).filter((path) => {
70
- return Glob.sync(path).length > 0;
71
- }),
72
- hoistUseStatements: true
78
+ {
79
+ loader: 'sass-resources-loader',
80
+ options: {
81
+ resources: resolvedResources,
82
+ hoistUseStatements: true,
83
+ },
73
84
  },
74
- },
75
- ],
76
- },
77
- {
78
- test: /\.(html|twig)$/,
79
- use: [
80
- {
81
- loader: 'html-loader',
82
- },
83
- ],
85
+ ],
86
+ },
87
+ {
88
+ test: /\.(html|twig)$/,
89
+ use: [
90
+ {
91
+ loader: 'html-loader',
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ },
97
+ stats: 'errors-warnings',
98
+ devServer: {
99
+ allowedHosts: 'all',
100
+ client: {
101
+ webSocketURL: {
102
+ hostname: 'node.px-staging.de',
103
+ protocol: 'wss',
104
+ port:
105
+ process.env.SHOPWARE_MODE === 'administration'
106
+ ? 8080
107
+ : 8081,
108
+ },
109
+ overlay: {
110
+ warnings: false,
111
+ errors: true,
112
+ },
84
113
  },
85
- ],
86
- },
87
- stats: 'errors-warnings',
88
- devServer: {
89
- allowedHosts: 'all',
90
- client: {
91
- webSocketURL: {
92
- hostname: 'node.px-staging.de',
93
- protocol: 'wss',
94
- port: process.env.SHOPWARE_MODE === 'administration' ? 8080 : 8081,
114
+ headers: {
115
+ 'Access-Control-Allow-Origin': '*',
116
+ 'Access-Control-Allow-Methods':
117
+ 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
118
+ 'Access-Control-Allow-Headers':
119
+ 'X-Requested-With, content-type, Authorization',
95
120
  },
96
- overlay: {
97
- warnings: false,
98
- errors: true,
121
+ port: process.env.SHOPWARE_MODE === 'administration' ? 8080 : 8081,
122
+ server: !config.isProd
123
+ ? {
124
+ type: 'https',
125
+ options: {
126
+ ca: Fs.readFileSync(
127
+ Path.join(
128
+ process.cwd() +
129
+ '/.ddev/ssl/_wildcard.px-staging.de+1-client.pem',
130
+ ),
131
+ ),
132
+ key: Fs.readFileSync(
133
+ Path.join(
134
+ process.cwd() +
135
+ '/.ddev/ssl/_wildcard.px-staging.de+1-key.pem',
136
+ ),
137
+ ),
138
+ cert: Fs.readFileSync(
139
+ Path.join(
140
+ process.cwd() +
141
+ '/.ddev/ssl/_wildcard.px-staging.de+1.pem',
142
+ ),
143
+ ),
144
+ },
145
+ }
146
+ : 'http',
147
+ devMiddleware: {
148
+ writeToDisk: true,
99
149
  },
100
- },
101
- headers: {
102
- 'Access-Control-Allow-Origin': '*',
103
- 'Access-Control-Allow-Methods':
104
- 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
105
- 'Access-Control-Allow-Headers':
106
- 'X-Requested-With, content-type, Authorization',
107
- },
108
- port: process.env.SHOPWARE_MODE === 'administration' ? 8080 : 8081,
109
- server: !config.isProd
110
- ? {
111
- type: 'https',
112
- options: {
113
- ca: Fs.readFileSync(
114
- Path.join(
115
- process.cwd() +
116
- '/.ddev/ssl/_wildcard.px-staging.de+1-client.pem',
117
- ),
118
- ),
119
- key: Fs.readFileSync(
120
- Path.join(
121
- process.cwd() +
122
- '/.ddev/ssl/_wildcard.px-staging.de+1-key.pem',
123
- ),
124
- ),
125
- cert: Fs.readFileSync(
126
- Path.join(
127
- process.cwd() +
128
- '/.ddev/ssl/_wildcard.px-staging.de+1.pem',
129
- ),
130
- ),
150
+ setupMiddlewares: (middlewares, devServer) => {
151
+ if (!devServer) {
152
+ throw new Error('webpack-dev-server is not defined');
131
153
  }
132
- }
133
- : 'http',
134
- devMiddleware: {
135
- writeToDisk: true,
136
- },
137
- setupMiddlewares: (middlewares, devServer) => {
138
- if (!devServer) {
139
- throw new Error("webpack-dev-server is not defined")
140
- }
141
-
142
- Consola.success(
143
- `Starting webpack in [${process.env.NODE_ENV}] with [${process.env.SHOPWARE_MODE}]`,
144
- );
145
-
146
- watcher.watch(config);
147
154
 
148
- return middlewares;
149
- },
150
- },
151
- plugins: [
152
- new ESLintPlugin({
153
- exclude: [
154
- '**/node_modules/**',
155
- 'vendor'
156
- ]
157
- }),
158
- new HookPlugin({
159
- beforeCompile(compiler, callback) {
160
- let path = Path.join(config.outputPath, 'sprite'),
161
- filename = 'sprite.svg',
162
- exists = Fs.existsSync(path);
155
+ Consola.success(
156
+ `Starting webpack in [${process.env.NODE_ENV}] with [${process.env.SHOPWARE_MODE}]`,
157
+ );
163
158
 
164
- if (!exists) {
165
- Fs.mkdirSync(path, {
166
- recursive: true,
167
- });
168
- Fs.appendFile(Path.join(path, filename), '#', (err) => {
169
- if (err) {
170
- throw err;
171
- }
172
- });
173
- }
159
+ watcher.watch(config);
174
160
 
175
- callback();
161
+ return middlewares;
176
162
  },
177
- failed() {
178
- watcher.run(null, config);
179
- },
180
- }),
163
+ },
164
+ plugins: [
165
+ new ESLintPlugin({
166
+ exclude: ['**/node_modules/**', 'vendor'],
167
+ }),
168
+ new HookPlugin({
169
+ failed() {
170
+ watcher.onFileChange(null, config);
171
+ },
172
+ }),
181
173
 
182
- new TimeFixPlugin(),
174
+ new TimeFixPlugin(),
183
175
 
184
- new webpack.DefinePlugin({
185
- 'process.env.NODE_ENV': JSON.stringify(
186
- process.env.NODE_ENV || 'development',
187
- ),
188
- 'process.env.ASSET_URL': JSON.stringify(config.assetUrl),
189
- 'process.env.RESOURCES_PATHS': JSON.stringify(process.env.RESOURCES_PATHS || '[]'),
190
- }),
191
- ].concat(
192
- config.isProd && config.mediaQueries
193
- ? new MediaQueryPlugin({
194
- include: true,
195
- queries: JSON.parse(config.mediaQueries)
196
- })
197
- : []
198
- ),
199
- watch: false,
176
+ new webpack.DefinePlugin({
177
+ 'process.env.NODE_ENV': JSON.stringify(
178
+ process.env.NODE_ENV || 'development',
179
+ ),
180
+ 'process.env.ASSET_URL': JSON.stringify(config.assetUrl),
181
+ 'process.env.RESOURCES_PATHS': JSON.stringify(
182
+ JSON.stringify(resourcesPaths),
183
+ ),
184
+ }),
185
+ ].concat(
186
+ config.isProd && config.mediaQueries
187
+ ? new MediaQueryPlugin({
188
+ include: true,
189
+ queries: JSON.parse(config.mediaQueries),
190
+ })
191
+ : [],
192
+ ),
193
+ watch: false,
194
+ };
200
195
  };
@@ -1,166 +1,207 @@
1
1
  const config = require('./config');
2
2
 
3
3
  const Path = require('path'),
4
+ Fs = require('fs'),
4
5
  MiniCssExtractPlugin = require('mini-css-extract-plugin'),
5
6
  ChangeCase = require('change-case'),
6
7
  Consola = require('consola'),
7
8
  Sw6PluginMapEmitterPlugin = require('@pixolith/webpack-sw6-plugin-map-emitter'),
8
- Entry = require('webpack-glob-entry'),
9
- SvgStorePlugin = require('@pixolith/external-svg-sprite-loader'),
10
- outputConfig = {
9
+ SvgStorePlugin = require('@pixolith/external-svg-sprite-loader');
10
+
11
+ module.exports = function createStorefrontConfig(themeOptions) {
12
+ let themeName = themeOptions && themeOptions.themeName;
13
+ let themeSlug = themeName ? ChangeCase.kebabCase(themeName) : 'storefront';
14
+
15
+ let outputConfig = {
11
16
  path: config.outputPath,
12
17
  publicPath: config.assetUrl,
13
18
  chunkFilename: (chunkData) => {
14
- return `js/chunk[name]${
15
- config.isProd ? '.[contenthash]' : ''
16
- }.js`;
19
+ return `js/chunk[name]${config.isProd ? '.[contenthash]' : ''}.js`;
17
20
  },
18
21
  filename: (chunkData) => {
19
22
  return `js/${chunkData.chunk.name.toLowerCase()}${
20
23
  config.isProd ? `.[contenthash]` : ''
21
24
  }.js`;
22
25
  },
23
- },
24
- miniCssChunksConfig = {
25
- filename: `css/[name]${
26
- config.isProd ? '.[contenthash]' : ''
27
- }.css`,
26
+ uniqueName: themeSlug,
27
+ };
28
+
29
+ let miniCssChunksConfig = {
30
+ filename: `css/[name]${config.isProd ? '.[contenthash]' : ''}.css`,
28
31
  chunkFilename: `css/[name].vendor${
29
32
  config.isProd ? '.[contenthash]' : ''
30
- }.css`
33
+ }.css`,
31
34
  };
32
35
 
33
- module.exports = {
34
- entry: () => {
35
- let entriesPlugins = Entry(
36
- (filePath) =>
37
- ChangeCase.kebabCase(
38
- filePath.match(config.pluginMatch)[1],
39
- ),
40
- Path.resolve(config.pluginSrcPath, 'index.js'),
41
- );
36
+ // Per-theme sprite path to avoid collisions between compilers
37
+ let spritePath = `sprite/${themeSlug}-sprite.svg`;
42
38
 
43
- let entriesVendor = Entry(
44
- (filePath) =>
45
- ChangeCase.kebabCase(
46
- filePath.match(config.vendorMatch)[1],
47
- ),
48
- Path.resolve(config.vendorSrcPath, 'index.js'),
39
+ let entryFn = () => {
40
+ let entryDir = Path.join(
41
+ config.outputPath,
42
+ '.theme-entries',
43
+ themeSlug,
49
44
  );
50
45
 
51
- let routeSplitEntriesPlugins = Entry(
52
- (filePath) => filePath.split('/').pop().replace('.index.scss', '').replace('.', '_'),
53
- Path.resolve(config.pluginRouteSplitPath, '*index.scss'),
54
- );
55
- let routeSplitEntriesVendor = Entry(
56
- (filePath) => filePath.split('/').pop().replace('.index.scss', '').replace('.', '_'),
57
- Path.resolve(config.vendorRouteSplitPath, '*index.scss'),
58
- );
46
+ let entries = {};
47
+
48
+ // Main theme entry
49
+ let mainEntry = Path.join(entryDir, 'index.js');
50
+ if (Fs.existsSync(mainEntry)) {
51
+ entries[themeSlug] = mainEntry;
52
+ }
53
+
54
+ // Route-split SCSS entries
55
+ let routeSplitFiles = Fs.existsSync(entryDir)
56
+ ? Fs.readdirSync(entryDir).filter(
57
+ (f) => f.endsWith('.index.scss') && f !== 'index.scss',
58
+ )
59
+ : [];
60
+
61
+ routeSplitFiles.forEach((file) => {
62
+ let routeName = file.replace('.index.scss', '');
63
+ let entryName = themeSlug + '_' + routeName;
64
+ entries[entryName] = Path.join(entryDir, file);
65
+ });
59
66
 
60
67
  if (config.isDebug) {
61
- Consola.info('[DEBUG]: Webpack entry points:');
62
- console.table({ ...entriesPlugins, ...entriesVendor, ...routeSplitEntriesPlugins, ...routeSplitEntriesVendor });
68
+ Consola.info(`[${themeName || themeSlug}] Webpack entry points:`);
69
+ console.table(entries);
63
70
  }
64
71
 
65
- return { ...entriesPlugins, ...entriesVendor, ...routeSplitEntriesPlugins, ...routeSplitEntriesVendor };
66
- },
67
- module: {
68
- rules: [
69
- {
70
- test: /\.js$/,
71
- exclude: (file) => {
72
- return /node_modules/.test(file);
73
- },
74
- use: [
75
- {
76
- loader: 'swc-loader',
77
- options: {
78
- env: {
79
- mode: 'entry',
80
- coreJs: '3.34.0',
81
- // .browserlist settings are not found by swc-loader, so we load it manually: https://github.com/swc-project/swc/issues/3365
82
- targets: require('browserslist').loadConfig({
83
- config: './package.json',
84
- }),
72
+ return entries;
73
+ };
74
+
75
+ return {
76
+ name: themeSlug,
77
+ entry: entryFn,
78
+ // -.- hardcoded fix for zbar-wasm and issue https://github.com/webpack/webpack/issues/16878
79
+ resolve: {
80
+ conditionNames: [
81
+ 'zbar-inlined',
82
+ 'browser',
83
+ 'import',
84
+ 'require',
85
+ 'default',
86
+ ],
87
+ },
88
+
89
+ module: {
90
+ rules: [
91
+ {
92
+ // -.- hardcoded fix for zbar-wasm and issue https://github.com/webpack/webpack/issues/16878
93
+ test: /\.m?js$/,
94
+ include: /node_modules[\\/]@undecaf[\\/]zbar-wasm/,
95
+ enforce: 'pre',
96
+ use: [
97
+ {
98
+ loader: 'string-replace-loader',
99
+ options: {
100
+ search: 'new URL("./",import.meta.url)',
101
+ replace: '"/"',
85
102
  },
86
- jsc: {
87
- parser: {
88
- syntax: 'typescript',
103
+ },
104
+ ],
105
+ },
106
+ {
107
+ test: /\.js$/,
108
+ exclude: (file) => {
109
+ return /node_modules/.test(file);
110
+ },
111
+ use: [
112
+ {
113
+ loader: 'swc-loader',
114
+ options: {
115
+ env: {
116
+ mode: 'entry',
117
+ coreJs: '3.34.0',
118
+ // .browserlist settings are not found by swc-loader, so we load it manually: https://github.com/swc-project/swc/issues/3365
119
+ targets: require('browserslist').loadConfig(
120
+ {
121
+ config: './package.json',
122
+ },
123
+ ),
89
124
  },
90
- transform: {
91
- // NEXT-30535 - Restore babel option to not use defineProperty for class fields.
92
- // Previously (in v6.5.x) this was done by `@babel/preset-typescript` automatically.
93
- useDefineForClassFields: false,
125
+ jsc: {
126
+ parser: {
127
+ syntax: 'typescript',
128
+ },
129
+ transform: {
130
+ // NEXT-30535 - Restore babel option to not use defineProperty for class fields.
131
+ // Previously (in v6.5.x) this was done by `@babel/preset-typescript` automatically.
132
+ useDefineForClassFields: false,
133
+ },
94
134
  },
95
135
  },
96
136
  },
137
+ ],
138
+ },
139
+ {
140
+ test: /\.(jpe?g|png|gif|ico)(\?v=\d+\.\d+\.\d+)?$/,
141
+ type: 'asset/resource',
142
+ generator: {
143
+ filename: 'img/[name][ext]',
97
144
  },
98
- ],
99
- },
100
- {
101
- test: /\.(jpe?g|png|gif|ico)(\?v=\d+\.\d+\.\d+)?$/,
102
- type: 'asset/resource',
103
- generator: {
104
- filename: 'img/[name][ext]'
105
- }
106
- },
107
- {
108
- test: /\.(eot|ttf|woff2?)(\?v=\d+\.\d+\.\d+)?$/,
109
- type: 'asset/resource',
110
- generator: {
111
- filename: 'fonts/[name][ext]'
112
- }
113
- },
114
- {
115
- test: /\.svg$/,
116
- use: [
117
- {
118
- loader: SvgStorePlugin.loader,
119
- options: {
120
- name: 'sprite/sprite.svg',
121
- iconName: '[name]',
122
- overrideOrder: config.spriteOrder,
123
- ignoreIconsByName: config.ignoreIcons,
124
- onlySymbols: true,
125
- },
145
+ },
146
+ {
147
+ test: /\.(eot|ttf|woff2?)(\?v=\d+\.\d+\.\d+)?$/,
148
+ type: 'asset/resource',
149
+ generator: {
150
+ filename: 'fonts/[name][ext]',
126
151
  },
127
- {
128
- loader: 'svgo-loader',
129
- options: {
130
- plugins: [
131
- 'cleanupAttrs',
132
- 'removeDoctype',
133
- 'removeXMLProcInst',
134
- 'cleanupEnableBackground',
135
- 'convertStyleToAttrs',
136
- 'convertPathData',
137
- 'cleanupIds',
138
- 'minifyStyles',
139
- 'removeUselessDefs',
140
- 'convertShapeToPath',
141
- 'removeUnusedNS',
142
- 'removeDimensions',
143
- 'convertTransform',
144
- 'collapseGroups',
145
- 'removeComments',
146
- 'removeEditorsNSData',
147
- 'removeUnknownsAndDefaults',
148
- ],
152
+ },
153
+ {
154
+ test: /\.svg$/,
155
+ use: [
156
+ {
157
+ loader: SvgStorePlugin.loader,
158
+ options: {
159
+ name: spritePath,
160
+ iconName: '[name]',
161
+ overrideOrder: config.spriteOrder,
162
+ ignoreIconsByName: config.ignoreIcons,
163
+ onlySymbols: true,
164
+ },
149
165
  },
150
- },
151
- ],
152
- },
166
+ {
167
+ loader: 'svgo-loader',
168
+ options: {
169
+ plugins: [
170
+ 'cleanupAttrs',
171
+ 'removeDoctype',
172
+ 'removeXMLProcInst',
173
+ 'cleanupEnableBackground',
174
+ 'convertStyleToAttrs',
175
+ 'convertPathData',
176
+ 'cleanupIds',
177
+ 'minifyStyles',
178
+ 'removeUselessDefs',
179
+ 'convertShapeToPath',
180
+ 'removeUnusedNS',
181
+ 'removeDimensions',
182
+ 'convertTransform',
183
+ 'collapseGroups',
184
+ 'removeComments',
185
+ 'removeEditorsNSData',
186
+ 'removeUnknownsAndDefaults',
187
+ ],
188
+ },
189
+ },
190
+ ],
191
+ },
192
+ ],
193
+ },
194
+ output: outputConfig,
195
+ plugins: [
196
+ new Sw6PluginMapEmitterPlugin({
197
+ includes: ['js', 'css'],
198
+ ignoreFiles: [/.*icons.*\.js/, /.*chunk.*\.js/],
199
+ filename: `var/px_plugins_${themeSlug}.json`,
200
+ }),
201
+
202
+ new SvgStorePlugin(),
203
+
204
+ new MiniCssExtractPlugin(miniCssChunksConfig),
153
205
  ],
154
- },
155
- output: outputConfig,
156
- plugins: [
157
- new Sw6PluginMapEmitterPlugin({
158
- includes: ['js', 'css'],
159
- ignoreFiles: [/.*icons.*\.js/, /.*chunk.*\.js/],
160
- }),
161
-
162
- new SvgStorePlugin(),
163
-
164
- new MiniCssExtractPlugin(miniCssChunksConfig),
165
- ],
206
+ };
166
207
  };