@plaudit/webpack-extensions 2.84.0 → 2.85.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.
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAssetFileContents = getAssetFileContents;
4
+ exports.unpackPotentiallyPrefixedFilePath = unpackPotentiallyPrefixedFilePath;
5
+ exports.unpackPathQuery = unpackPathQuery;
6
+ exports.isValidLoadingStrategy = isValidLoadingStrategy;
7
+ exports.newInvalidLoadingStrategyError = newInvalidLoadingStrategyError;
8
+ exports.isValidInFooter = isValidInFooter;
9
+ exports.newInvalidInFooterError = newInvalidInFooterError;
10
+ exports.isValidFetchPriority = isValidFetchPriority;
11
+ exports.newInvalidFetchPriorityError = newInvalidFetchPriorityError;
12
+ exports.isValidPositionForInlineStrategy = isValidPositionForInlineStrategy;
13
+ exports.newInvalidPositionForInlineStrategyError = newInvalidPositionForInlineStrategyError;
14
+ exports.mergeInPathQueryParameters = mergeInPathQueryParameters;
15
+ exports.parseScriptArgsObjectFromPathQueryParameters = parseScriptArgsObjectFromPathQueryParameters;
16
+ const shared_1 = require("../shared");
17
+ const node_path_1 = require("node:path");
18
+ function getAssetFileContents(compilation, name) {
19
+ const asset = compilation.getAsset(name);
20
+ if (!asset) {
21
+ throw new Error(`${name} is unexpectedly missing`);
22
+ }
23
+ const assetSource = asset.source.source();
24
+ return typeof assetSource === 'string' ? assetSource : assetSource?.toString('utf-8');
25
+ }
26
+ function unpackPotentiallyPrefixedFilePath(filePath) {
27
+ if (filePath.startsWith("file:")) {
28
+ filePath = filePath.substring(5);
29
+ }
30
+ const queryStart = filePath.indexOf("?");
31
+ if (queryStart === -1) {
32
+ return [filePath, undefined];
33
+ }
34
+ else {
35
+ return [filePath.substring(0, queryStart), unpackPathQuery(filePath.substring(queryStart + 1))];
36
+ }
37
+ }
38
+ /**
39
+ * The following substitutions are performed:
40
+ * <ul>
41
+ * <li>parameters without an assigned value -> a boolean parameter set to true</li>
42
+ * <li>true and false -> their boolean equivalents</li>
43
+ * <li>numeric values -> their number equivalents</li>
44
+ * </ul>
45
+ *
46
+ * @param pathQuery the part after the "?" in a webpack inclusion path
47
+ * @return the query as an object with common string -> JS type conversions already performed
48
+ */
49
+ function unpackPathQuery(pathQuery) {
50
+ if (!pathQuery) {
51
+ return undefined;
52
+ }
53
+ return new URLSearchParams(pathQuery.replaceAll(/((?:[&?]|^)\w+)(&|$)/g, "$1=true$2")).entries()
54
+ .map(([key, value]) => {
55
+ if (value === 'true') {
56
+ return [key, true];
57
+ }
58
+ if (value === 'false') {
59
+ return [key, false];
60
+ }
61
+ if (Number.isFinite(+value)) {
62
+ return [key, +value];
63
+ }
64
+ return [key, value];
65
+ })
66
+ .reduce((collector, [key, value]) => {
67
+ if (collector[key] instanceof Array) {
68
+ collector[key].push(value);
69
+ }
70
+ else if (key in collector) {
71
+ collector[key] = [collector[key], value];
72
+ }
73
+ else {
74
+ collector[key] = value;
75
+ }
76
+ return collector;
77
+ }, {});
78
+ }
79
+ const namedStrategies = ['defer', 'async', 'eager', 'lazy', 'inline'];
80
+ function isValidLoadingStrategy(strategy) {
81
+ return strategy === undefined || (typeof strategy === 'string' && namedStrategies.includes(strategy));
82
+ }
83
+ function newInvalidLoadingStrategyError(strategy, file) {
84
+ return (0, shared_1.newWebpackErrorForFile)(`The strategy was invalid. Received: ${strategy}, Expected: undefined|boolean|'${namedStrategies.join("'|'")}'`, file);
85
+ }
86
+ function isValidInFooter(in_footer) {
87
+ return typeof in_footer === 'boolean';
88
+ }
89
+ function newInvalidInFooterError(in_footer, file) {
90
+ return (0, shared_1.newWebpackErrorForFile)(`The in_footer value was invalid. Received: ${in_footer}, Expected: undefined|boolean`, file);
91
+ }
92
+ function isValidFetchPriority(fetchpriority) {
93
+ return typeof fetchpriority === 'string' && ["auto", "low", "high"].includes(fetchpriority);
94
+ }
95
+ function newInvalidFetchPriorityError(fetchpriority, file) {
96
+ return (0, shared_1.newWebpackErrorForFile)(`The fetchpriority value was invalid. Received: ${fetchpriority}, Expected: undefined|'auto'|'low'|'high'`, file);
97
+ }
98
+ function isValidPositionForInlineStrategy(position) {
99
+ return typeof position === 'string' && ["before", "after"].includes(position);
100
+ }
101
+ function newInvalidPositionForInlineStrategyError(position, file) {
102
+ return (0, shared_1.newWebpackErrorForFile)(`The position value for the inlined asset was invalid. Received: ${position}, Expected: undefined|'before'|'after'`, file);
103
+ }
104
+ function mergeInPathQueryParameters(file, registerScriptArgs, pathQueryParameters) {
105
+ const baseArgs = unpackRegisterScriptArgsFromPathQueryParameters(typeof registerScriptArgs === 'object' ? registerScriptArgs : { strategy: registerScriptArgs }, file, 'registerScriptArgs');
106
+ const pathQueryArgs = unpackRegisterScriptArgsFromPathQueryParameters(pathQueryParameters, file, 'path query parameters');
107
+ if (baseArgs === undefined) {
108
+ return pathQueryArgs;
109
+ }
110
+ if (pathQueryArgs === undefined) {
111
+ return baseArgs;
112
+ }
113
+ for (const key of ['strategy', 'in_footer', 'fetchpriority', 'inline', 'position']) {
114
+ if (key in baseArgs) {
115
+ if (key in pathQueryArgs && pathQueryArgs[key] !== baseArgs[key]) {
116
+ throw (0, shared_1.newWebpackErrorForFile)(`The ${key} values in the registerScriptArgs and the pathQueryParameters for the file conflict`, file);
117
+ }
118
+ }
119
+ else if (key in pathQueryArgs) {
120
+ baseArgs[key] = pathQueryArgs[key];
121
+ }
122
+ }
123
+ return baseArgs;
124
+ }
125
+ function unpackRegisterScriptArgsFromPathQueryParameters(pathQueryParameters, file, sourceType) {
126
+ if (pathQueryParameters === undefined) {
127
+ return undefined;
128
+ }
129
+ let hasAnyKeys = false;
130
+ let baseArgs;
131
+ const strategy = pathQueryParameters['strategy'];
132
+ switch (strategy) {
133
+ case 'eager':
134
+ hasAnyKeys = true;
135
+ baseArgs = { strategy: 'eager' };
136
+ break;
137
+ case 'lazy':
138
+ hasAnyKeys = true;
139
+ baseArgs = { strategy: 'defer', in_footer: true };
140
+ break;
141
+ case 'inline':
142
+ hasAnyKeys = true;
143
+ baseArgs = { inline: true };
144
+ break;
145
+ case true:
146
+ case false:
147
+ hasAnyKeys = true;
148
+ baseArgs = { in_footer: strategy };
149
+ break;
150
+ case null:
151
+ case undefined:
152
+ baseArgs = {};
153
+ break;
154
+ default:
155
+ throw newInvalidLoadingStrategyError(strategy, file);
156
+ }
157
+ if (baseArgs.inline === undefined && pathQueryParameters['inline'] !== undefined) {
158
+ baseArgs.inline = !!pathQueryParameters['inline'];
159
+ }
160
+ const in_footer = pathQueryParameters['in_footer'] ?? pathQueryParameters['in-footer'];
161
+ if (in_footer !== undefined) {
162
+ if (!isValidInFooter(in_footer)) {
163
+ throw newInvalidInFooterError(in_footer, file);
164
+ }
165
+ hasAnyKeys = true;
166
+ baseArgs.in_footer = in_footer;
167
+ }
168
+ if (pathQueryParameters['fetchpriority'] !== undefined) {
169
+ if (!isValidFetchPriority(pathQueryParameters['fetchpriority'])) {
170
+ throw newInvalidFetchPriorityError(pathQueryParameters['fetchpriority'], file);
171
+ }
172
+ if (baseArgs.fetchpriority !== undefined && baseArgs.fetchpriority !== pathQueryParameters['fetchpriority']) {
173
+ throw (0, shared_1.newWebpackErrorForFile)(`The strategy and fetchpriority values in the ${sourceType} for the file conflict. Got ${strategy} and ${pathQueryParameters['fetchpriority']}`, file);
174
+ }
175
+ hasAnyKeys = true;
176
+ baseArgs.fetchpriority = pathQueryParameters['fetchpriority'];
177
+ }
178
+ if (pathQueryParameters['inline'] !== undefined) {
179
+ }
180
+ return hasAnyKeys ? baseArgs : undefined;
181
+ }
182
+ /**
183
+ * This function does a few things:
184
+ * <ol>
185
+ * <li>It extracts strategy, in_footer, and fetchpriority from pathQueryParameters</li>
186
+ * <li>If the strategy is, 'inline', it records the contents of the asset for later injection inline and removes the asset from the compilation output</li>
187
+ * </ol>
188
+ * @param compilation
189
+ * @param file
190
+ * @param pathQueryParameters
191
+ */
192
+ function parseScriptArgsObjectFromPathQueryParameters(compilation, file, pathQueryParameters) {
193
+ if (pathQueryParameters === undefined) {
194
+ return {};
195
+ }
196
+ const scriptArgsObject = {};
197
+ let inlinedAsset = undefined;
198
+ let hasAnyKeys = false;
199
+ const strategy = pathQueryParameters['strategy'];
200
+ switch (strategy) {
201
+ case 'async':
202
+ case 'defer':
203
+ hasAnyKeys = true;
204
+ scriptArgsObject.strategy = strategy;
205
+ break;
206
+ case 'eager':
207
+ hasAnyKeys = true;
208
+ scriptArgsObject.in_footer = false;
209
+ break;
210
+ case 'lazy':
211
+ hasAnyKeys = true;
212
+ scriptArgsObject.strategy = 'defer';
213
+ scriptArgsObject.in_footer = true;
214
+ break;
215
+ case 'inline':
216
+ inlinedAsset = { contents: getAssetFileContents(compilation, file) };
217
+ removeFileAndAssetPHP(compilation, file);
218
+ if ('inline' in pathQueryParameters) {
219
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("The inline parameter should not be set when setting strategy to 'inline'", file));
220
+ }
221
+ break;
222
+ case true:
223
+ case false:
224
+ hasAnyKeys = true;
225
+ scriptArgsObject.in_footer = strategy;
226
+ break;
227
+ case null:
228
+ case undefined:
229
+ // This is included because are effectively validating the strategy value with this switch statement, and "not defined" is a valid state
230
+ break;
231
+ default:
232
+ throw newInvalidLoadingStrategyError(strategy, file);
233
+ }
234
+ const in_footer = pathQueryParameters['in_footer'] ?? pathQueryParameters['in-footer'];
235
+ if (in_footer !== undefined) {
236
+ if (!isValidInFooter(in_footer)) {
237
+ throw newInvalidInFooterError(in_footer, file);
238
+ }
239
+ hasAnyKeys = true;
240
+ scriptArgsObject.in_footer = in_footer;
241
+ }
242
+ if (inlinedAsset === undefined && pathQueryParameters['inline']) {
243
+ inlinedAsset = { contents: getAssetFileContents(compilation, file) };
244
+ removeFileAndAssetPHP(compilation, file);
245
+ }
246
+ if (inlinedAsset !== undefined) {
247
+ if (pathQueryParameters['position'] !== undefined) {
248
+ if (!isValidPositionForInlineStrategy(pathQueryParameters['position'])) {
249
+ throw newInvalidPositionForInlineStrategyError(pathQueryParameters['position'], file);
250
+ }
251
+ inlinedAsset.position = pathQueryParameters['position'];
252
+ }
253
+ if (inlinedAsset.position !== 'before' && scriptArgsObject.strategy !== undefined) {
254
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Deferred and async scripts that have inlined JS attached in the 'after' position will cause WordPress to convert all of their dependencies to eager scripts at runtime", file));
255
+ }
256
+ if (scriptArgsObject.in_footer === undefined) {
257
+ scriptArgsObject.in_footer = true;
258
+ }
259
+ }
260
+ const fetchpriority = pathQueryParameters['fetchpriority'];
261
+ if (fetchpriority !== undefined) {
262
+ if (!isValidFetchPriority(fetchpriority)) {
263
+ throw newInvalidFetchPriorityError(fetchpriority, file);
264
+ }
265
+ if (inlinedAsset !== undefined) {
266
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Fetchpriority has no effect on inlined assets", file));
267
+ }
268
+ hasAnyKeys = true;
269
+ scriptArgsObject.fetchpriority = fetchpriority;
270
+ }
271
+ return hasAnyKeys ? { scriptArgsObject, inlinedAsset } : { inlinedAsset };
272
+ }
273
+ function removeFileAndAssetPHP(compilation, file) {
274
+ const pathParts = (0, node_path_1.parse)(file);
275
+ compilation.deleteAsset((0, node_path_1.join)(pathParts.dir, pathParts.name) + ".asset.php");
276
+ compilation.deleteAsset(file);
277
+ }
@@ -25,6 +25,7 @@ const UnifiedLoaderGenerator_1 = require("./plugins/UnifiedLoaderGenerator");
25
25
  const copy_webpack_plugin_1 = __importDefault(require("copy-webpack-plugin"));
26
26
  const fork_ts_checker_webpack_plugin_1 = __importDefault(require("fork-ts-checker-webpack-plugin"));
27
27
  const webpack_remove_empty_scripts_1 = __importDefault(require("webpack-remove-empty-scripts"));
28
+ const path_query_and_related_helpers_1 = require("./utils/path-query-and-related-helpers");
28
29
  function testForDuplicatedEntryPaths(sources) {
29
30
  const seenPaths = (0, common_config_helpers_1.groupEntrypointsByAssetFile)(Array.isArray(sources)
30
31
  ? sources.map(s => typeof s === 'string' ? s : s[1].destination)
@@ -188,15 +189,16 @@ function buildVerifiedConfig(config) {
188
189
  throw new Error("Plain Entrypoints V2 and higher require 'outputDir' to be set");
189
190
  }
190
191
  }
191
- const normalizeSrcAndDestination = ([src, dest]) => {
192
+ const normalizeSrcAndDestination = ([rawSrc, dest]) => {
193
+ let [src, pathQueryParameters] = (0, path_query_and_related_helpers_1.unpackPotentiallyPrefixedFilePath)(rawSrc);
192
194
  if (srcDir) {
193
195
  src = (0, node_path_1.join)(srcDir, src);
194
196
  }
195
197
  src = (0, node_path_1.isAbsolute)(src) ? (0, node_path_1.relative)(process.cwd(), src) : src;
196
198
  if (dest.destination !== undefined && (0, node_path_1.isAbsolute)(dest.destination)) {
197
- dest = { ...dest, destination: (0, node_path_1.relative)(process.cwd(), dest.destination) };
199
+ return [src, { ...dest, destination: (0, node_path_1.relative)(process.cwd(), dest.destination), pathQueryParameters }];
198
200
  }
199
- return [src, dest];
201
+ return [src, { ...dest, pathQueryParameters }];
200
202
  };
201
203
  const rawSources = Array.isArray(config.src)
202
204
  ? config.src.map(s => normalizeSrcAndDestination([s, { destination: s }]))
@@ -221,29 +223,22 @@ function buildVerifiedConfig(config) {
221
223
  // Destination -> source map
222
224
  const allocatedDestinations = {};
223
225
  const partiallyVerifiedSources = rawSources.map(rawSource => {
224
- const { destination, additionalDependencies = [], assumeGlobalizedPlauditLibraries = cfg.assumeGlobalizedPlauditLibraries, bundleAnalyzer = false, directoryLayout, externalize, withLegacyBlocksIn = false, lazyLoader } = rawSource[1];
226
+ const { destination, additionalDependencies = [], assumeGlobalizedPlauditLibraries = cfg.assumeGlobalizedPlauditLibraries, bundleAnalyzer = false, directoryLayout, externalize, withLegacyBlocksIn = false, lazyLoader, pathQueryParameters } = rawSource[1];
225
227
  const normalizedParts = { additionalDependencies, assumeGlobalizedPlauditLibraries, bundleAnalyzer, directoryLayout, externalize, withLegacyBlocksIn, lazyLoader };
226
228
  const locations = typeof rawSource[1].locations === 'string' || typeof rawSource[1].locations === 'function'
227
229
  ? { handle: rawSource[1].locations } : rawSource[1].locations ?? {};
228
- let normalizedLocations;
229
- if ((0, shared_1.isNormalizedUsageLocations)(locations)) {
230
- normalizedLocations = locations;
231
- }
232
- else if (locations.registerScriptArgs === 'lazy') {
233
- normalizedLocations = { ...locations, registerScriptArgs: { strategy: 'defer', in_footer: true } };
234
- }
235
- else {
236
- throw new ReferenceError("The only supported string value for registerScript args is 'lazy'");
230
+ if (typeof locations.registerScriptArgs === 'string' && !['lazy', 'eager', 'inline'].includes(locations.registerScriptArgs)) {
231
+ throw new ReferenceError("The only supported string values for registerScriptArgs are 'lazy', 'eager', and 'inline'");
237
232
  }
238
233
  if (destination !== undefined) {
239
234
  const effectiveDestination = toEffectiveWebpackDestination(destination);
240
235
  allocatedDestinations[effectiveDestination] = rawSource[0]; // We need to pre-populate the allocatedDestinations map with statically-declared destinations
241
- return [rawSource[0], { ...normalizedParts, locations: normalizedLocations, destination, effectiveDestination, staticallyDeclaredDestination: true }];
236
+ return [rawSource[0], { ...normalizedParts, locations, destination, effectiveDestination, staticallyDeclaredDestination: true, pathQueryParameters }];
242
237
  }
243
238
  else {
244
239
  const naiveDestination = deriveNaiveDestinationFromUnverifiedSourceEntry(rawSource, srcPrefixes);
245
240
  const effectiveDestination = toEffectiveWebpackDestination(naiveDestination);
246
- return [rawSource[0], { ...normalizedParts, locations: normalizedLocations, destination: naiveDestination, effectiveDestination, staticallyDeclaredDestination: false }];
241
+ return [rawSource[0], { ...normalizedParts, locations, destination: naiveDestination, effectiveDestination, staticallyDeclaredDestination: false, pathQueryParameters }];
247
242
  }
248
243
  });
249
244
  const dynamicEffectiveDestinationsWithExpectedNaiveDuplicates = partiallyVerifiedSources
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@plaudit/webpack-extensions",
3
- "version": "2.84.0",
3
+ "version": "2.85.0",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "files": [
6
6
  "/build",
7
7
  "README.md",
8
8
  "LICENSE.md",
9
- "CHANGELOG.md"
9
+ "CHANGELOG.md",
10
+ "USER-GUIDE.md"
10
11
  ],
11
12
  "exports": {
12
13
  "./wordpress-scripts-wrapper": "./build/wordpress-scripts-wrapper.js",
@@ -23,12 +24,11 @@
23
24
  }
24
25
  },
25
26
  "devDependencies": {
26
- "@plaudit/gutenberg-api-extensions": "^2.84.3",
27
+ "@plaudit/gutenberg-api-extensions": "^2.87.0",
27
28
  "@types/browser-sync-webpack-plugin": "^2.2.5",
28
- "@types/node": "^25.1.0",
29
+ "@types/node": "^25.3.4",
29
30
  "@types/postcss-functions": "^4.0.4",
30
31
  "@types/tapable": "^2.3.0",
31
- "@types/webpack": "^5.28.5",
32
32
  "@types/webpack-sources": "^3.2.3",
33
33
  "postcss-load-config": "^4.0.2",
34
34
  "postcss-loader": "^7.3.4",
@@ -36,22 +36,22 @@
36
36
  "webpack-bundle-analyzer": "^4.10.2"
37
37
  },
38
38
  "dependencies": {
39
- "@plaudit/php-writer": "^1.3.3",
39
+ "@plaudit/php-writer": "^1.4.1",
40
40
  "@plaudit/postcss-color-function": "^5.0.0",
41
41
  "@plaudit/postcss-legacy-shorthand": "^1.0.0",
42
42
  "@plaudit/postcss-silent-extend": "^3.0.0",
43
43
  "@plaudit/postcss-strip-units": "^3.0.0",
44
44
  "@plaudit/postcss-variables": "^1.1.0",
45
- "@wordpress/dependency-extraction-webpack-plugin": "^6.39.0",
46
- "@wordpress/scripts": "^31.4.0",
47
- "autoprefixer": "^10.4.24",
45
+ "@wordpress/dependency-extraction-webpack-plugin": "^6.41.0",
46
+ "@wordpress/scripts": "^31.6.0",
47
+ "autoprefixer": "^10.4.27",
48
48
  "browser-sync": "^3.0.4",
49
49
  "copy-webpack-plugin": "10.2.4",
50
50
  "css-minimizer-webpack-plugin": "^6.0.0",
51
51
  "fork-ts-checker-webpack-plugin": "^9.1.0",
52
52
  "http-proxy-middleware": "^3.0.5",
53
53
  "json2php": "^0.0.12",
54
- "postcss": "^8.5.6",
54
+ "postcss": "^8.5.8",
55
55
  "postcss-calc": "^9.0.1",
56
56
  "postcss-discard-comments": "^6.0.2",
57
57
  "postcss-functions": "^4.0.2",
@@ -63,7 +63,7 @@
63
63
  "postcss-reporter": "^7.1.0",
64
64
  "postcss-simple-vars": "^7.0.1",
65
65
  "postcss-url": "^10.1.3",
66
- "webpack": "^5.104.1",
66
+ "webpack": "^5.105.4",
67
67
  "webpack-remove-empty-scripts": "^1.1.1",
68
68
  "xml-formatter": "^3.6.7"
69
69
  },