@plaudit/webpack-extensions 3.20.1 → 3.21.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/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.21.0] - 2026-09-03
9
+ ### Added
10
+ - Logic to associate inline scripts with non-inlined scripts that have the same handle for Plain entrypoints
11
+ - This is intended to make using the "inlined script that does early prep for a deferred script" pattern easier
12
+
13
+ ## [3.20.2] - 2026-09-01
14
+ ### Removed
15
+ - The duplicated `isDistDevOutputPath` logic around the initialization of the `@plaudit/dev-webpack-plugin`
16
+ - This comes from version `2.99.2`
17
+
8
18
  ## [3.20.1] - 2026-08-26
9
19
  ### Internals
10
20
  - Updated to the 34.x line of `@wordpress/scripts`
@@ -298,6 +308,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
298
308
  - Legacy PostCSS features that have been integrated into modern CSS
299
309
  - `@extends` support
300
310
 
311
+ ## [2.99.2] - 2026-09-01
312
+ ### Removed
313
+ - The duplicated `isDistDevOutputPath` logic around the initialization of the `@plaudit/dev-webpack-plugin`
314
+
301
315
  ## [2.99.1] - 2026-08-26
302
316
  ### Internals
303
317
  - Updated to the 34.x line of `@wordpress/scripts`
@@ -10,6 +10,8 @@ export declare class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractB
10
10
  private static readonly semaphore;
11
11
  constructor(config: VerifiedPlauditWordpressWebpackConfig, buildRoot: string, outputDir: string, dest: VerifiedAdvancedOutputConfig, useUnifiedLoader: boolean, context: string, entry: EntryProvider<BlockEntrypointInfo>);
12
12
  private generatePlainEntrypointsLoader;
13
+ private static writeHandleItem;
14
+ private static collatePotentiallyMergeableAssets;
13
15
  private static addHandlesToHandleLists;
14
16
  private static appendEnqueuingHandleLists;
15
17
  private static separateHandleListByPriority;
@@ -32,29 +32,12 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
32
32
  inline: [],
33
33
  ...Object.fromEntries((0, shared_1.constantKeys)(shared_1.standardLocationNamesMeta).map(sln => [sln, []]))
34
34
  };
35
- const usedHandleNames = { script: {}, script_module: {}, style: {} };
36
- const plainEntrypointsConfig = { scriptHandles: {}, script_moduleHandles: {}, styleHandles: {} };
37
- for (const { handles, handlePrefix } of assets) {
38
- for (const { src, rest, locations, type, handleName, lazyLoader, ...otherProps } of handles) {
39
- const cleanBasename = (0, node_path_1.basename)(src).replace(/_(?:script(?:-\d+)?\.js|style(?:-\d+)?\.css)$|(?<!_(script|style))\.(js|css)$/, "");
40
- let finalHandleName;
41
- const handleNameMap = usedHandleNames[type];
42
- const baseFinalHandleName = finalHandleName = (0, shared_1.convertUsageLocationsHandleToEmittableHandle)({ niceName: (0, shared_1.kebabCase)(cleanBasename) }, handlePrefix, cleanBasename);
43
- for (let count = 0; finalHandleName in handleNameMap && handleNameMap[finalHandleName] !== src;) {
44
- finalHandleName = `${baseFinalHandleName}-${++count}`;
45
- }
46
- if (handleName !== undefined) {
47
- finalHandleName = (0, shared_1.convertUsageLocationsHandleToEmittableHandle)(handleName, handlePrefix, finalHandleName.substring(handlePrefix.length + 1));
48
- }
49
- handleNameMap[finalHandleName] = src;
50
- plainEntrypointsConfig[`${type}Handles`][finalHandleName] = { src, rest, locations, type, lazyLoader, ...otherProps };
51
- }
52
- }
53
- PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'script', Object.entries(plainEntrypointsConfig.scriptHandles)
35
+ const plainEntrypointsConfig = PlainEntrypointsConfigFileGeneratorPlugin.collatePotentiallyMergeableAssets(assets);
36
+ PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'script', plainEntrypointsConfig.scriptHandles
54
37
  .toSorted(([a], [b]) => a.localeCompare(b)), handleLists);
55
- PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'script_module', Object.entries(plainEntrypointsConfig.script_moduleHandles)
38
+ PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'script_module', plainEntrypointsConfig.script_moduleHandles
56
39
  .toSorted(([a], [b]) => a.localeCompare(b)), handleLists);
57
- const sortedStyleHandles = Object.entries(plainEntrypointsConfig.styleHandles)
40
+ const sortedStyleHandles = plainEntrypointsConfig.styleHandles
58
41
  .toSorted(([a], [b]) => a.localeCompare(b));
59
42
  PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'style', sortedStyleHandles, handleLists);
60
43
  const callItemSorter = (a, b) => a.handle.localeCompare(b.handle);
@@ -71,13 +54,12 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
71
54
  for (const [priority, prioritizedHandleList] of PlainEntrypointsConfigFileGeneratorPlugin.separateHandleListByPriority(handleLists.register)) {
72
55
  writer.action("init", writer => {
73
56
  writer.call("plaudit_webpack_extensions__resolve_base_uri", [expressions_1.Constants.__DIR__], { assignTo: baseUriVar });
74
- for (const { handle, type, data } of prioritizedHandleList) {
75
- if (data.inlinedAsset !== undefined) {
76
- writer.call(`wp_register_${type}`, [handle, false, ...data.rest]);
77
- (0, shared_1.appendAddInlineAssetCall)(compilation, writer, handle, type, data);
78
- }
79
- else {
80
- writer.call(`wp_register_${type}`, [handle, expressions_1.Op.concat(baseUriVar, (0, node_path_1.relative)(emitDir, data.src)), ...data.rest]);
57
+ for (const handleItem of prioritizedHandleList) {
58
+ PlainEntrypointsConfigFileGeneratorPlugin.writeHandleItem(compilation, handleItem, { baseUriVar, emitDir, writer });
59
+ if (handleItem.data.associatedInlinedHandles !== undefined) {
60
+ for (const associatedHandle of handleItem.data.associatedInlinedHandles) {
61
+ PlainEntrypointsConfigFileGeneratorPlugin.writeHandleItem(compilation, { data: associatedHandle, handle: handleItem.handle, type: associatedHandle.type }, { baseUriVar, emitDir, writer });
62
+ }
81
63
  }
82
64
  }
83
65
  }, { priority, accountForAlreadyDoing: this.config.includePostInitFallback });
@@ -138,6 +120,79 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
138
120
  }
139
121
  (0, shared_1.emitPHPWriterAsAsset)(file, compilation, "plain-entrypoints-loader.php");
140
122
  }
123
+ static writeHandleItem(compilation, { data, handle, type }, { baseUriVar, emitDir, writer }) {
124
+ if (data.inlinedAsset !== undefined) {
125
+ if (!data.inlinedAsset.omitRegistration) {
126
+ writer.call(`wp_register_${type}`, [handle, false, ...data.rest]);
127
+ }
128
+ (0, shared_1.appendAddInlineAssetCall)(compilation, writer, handle, type, data);
129
+ }
130
+ else {
131
+ writer.call(`wp_register_${type}`, [handle, expressions_1.Op.concat(baseUriVar, (0, node_path_1.relative)(emitDir, data.src)), ...data.rest]);
132
+ }
133
+ }
134
+ static collatePotentiallyMergeableAssets(assets) {
135
+ const collatedHandles = { scriptHandles: {}, script_moduleHandles: {}, styleHandles: {} };
136
+ for (const { handles, handlePrefix } of assets) {
137
+ for (const { src, rest, locations, type, handleName, lazyLoader, ...otherProps } of handles) {
138
+ const cleanBasename = (0, node_path_1.basename)(src).replace(/_(?:script(?:-\d+)?\.js|style(?:-\d+)?\.css)$|(?<!_(script|style))\.(js|css)$/, "");
139
+ let finalHandleName = (0, shared_1.convertUsageLocationsHandleToEmittableHandle)({ niceName: (0, shared_1.kebabCase)(cleanBasename) }, handlePrefix, cleanBasename);
140
+ if (handleName !== undefined) {
141
+ finalHandleName = (0, shared_1.convertUsageLocationsHandleToEmittableHandle)(handleName, handlePrefix, finalHandleName.substring(handlePrefix.length + 1));
142
+ }
143
+ (collatedHandles[`${type}Handles`][finalHandleName] ??= []).push({ src, rest, locations, type, lazyLoader, ...otherProps });
144
+ }
145
+ }
146
+ const plainEntrypointsConfig = { scriptHandles: [], script_moduleHandles: [], styleHandles: [] };
147
+ for (const key of ['scriptHandles', 'script_moduleHandles', 'styleHandles']) {
148
+ for (const [baseFinalHandleName, handles] of Object.entries(collatedHandles[key])) {
149
+ if ((0, shared_1.arrayIsLength)(handles, 1)) {
150
+ plainEntrypointsConfig[key].push([baseFinalHandleName, handles[0]]);
151
+ }
152
+ else if ((0, shared_1.hasAtLeastOneItem)(handles)) {
153
+ const inlinedHandles = handles.filter(item => item.inlinedAsset !== undefined);
154
+ if (inlinedHandles.length === 0) {
155
+ plainEntrypointsConfig[key].push([baseFinalHandleName, handles[0]]);
156
+ let count = 1;
157
+ for (const handle of handles.slice(1)) {
158
+ plainEntrypointsConfig[key].push([`${baseFinalHandleName}-${count++}`, handle]);
159
+ }
160
+ }
161
+ else {
162
+ const emittedHandles = handles.filter(item => item.inlinedAsset === undefined);
163
+ inlinedHandlesLoop: for (const inlinedHandle of inlinedHandles) {
164
+ for (const emittedHandle of emittedHandles) {
165
+ const canMerge = (0, shared_1.canMergeHandles)(emittedHandle, inlinedHandle);
166
+ if (!canMerge) {
167
+ continue;
168
+ }
169
+ if ((0, shared_1.hasDeferringStrategy)(emittedHandle)) {
170
+ inlinedHandle.inlinedAsset.position = 'before';
171
+ }
172
+ if (canMerge === 'dependent') {
173
+ emittedHandle.rest[0].push(`${baseFinalHandleName}-${emittedHandles.length}`);
174
+ emittedHandles.push(inlinedHandle);
175
+ }
176
+ else {
177
+ (emittedHandle.associatedInlinedHandles ??= []).push(inlinedHandle);
178
+ }
179
+ continue inlinedHandlesLoop;
180
+ }
181
+ emittedHandles.push(inlinedHandle);
182
+ }
183
+ if ((0, shared_1.hasAtLeastOneItem)(emittedHandles)) {
184
+ plainEntrypointsConfig[key].push([baseFinalHandleName, emittedHandles[0]]);
185
+ let count = 1;
186
+ for (const handle of emittedHandles.slice(1)) {
187
+ plainEntrypointsConfig[key].push([`${baseFinalHandleName}-${count++}`, handle]);
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+ return plainEntrypointsConfig;
195
+ }
141
196
  static addHandlesToHandleLists(compilation, type, handles, handleLists) {
142
197
  const defaultPriority = 10;
143
198
  for (const [handle, data] of handles) {
package/dist/shared.d.ts CHANGED
@@ -35,6 +35,7 @@ export type ScriptArgsObject = {
35
35
  };
36
36
  export type InlinedAsset = {
37
37
  position?: 'before' | 'after';
38
+ omitRegistration?: boolean;
38
39
  };
39
40
  export type IAHandleData = BaseHandleData & AssetMetaSegment;
40
41
  export type HandleData = BaseHandleData & (AssetMetaSegment | {
@@ -98,6 +99,20 @@ export type NormalizedUsageLocations = Omit<UsageLocations, 'registerScriptArgs'
98
99
  registerScriptArgs?: InputRegisterScriptArgs;
99
100
  };
100
101
  export declare function isNormalizedUsageLocations(usageLocations: UsageLocations): usageLocations is NormalizedUsageLocations;
102
+ export type EnqueuedAssetType = 'script' | 'script_module' | 'style';
103
+ export type ExtendedHandleData = HandleData & {
104
+ locations: UsageLocations;
105
+ handleName?: UsageLocations['handle'];
106
+ lazyLoader: string | undefined;
107
+ type: EnqueuedAssetType;
108
+ associatedInlinedHandles?: ExtendedHandleData[];
109
+ };
110
+ export declare function convertHookLocationToNamesAndPriority(hookLocation: NonNullable<Exclude<UsageLocations[keyof typeof standardLocationNamesMeta], false>>): [string[], number];
111
+ export declare function hasDeferringStrategy(handle: HandleData): boolean;
112
+ /**
113
+ * @return {@code true} -> "can share the same handle", {@code false} -> "cannot be merged", {@code 'dependent'} -> "one handle can be listed as a dependency of the other"
114
+ */
115
+ export declare function canMergeHandles(handleA: ExtendedHandleData, handleB: ExtendedHandleData): boolean | 'dependent';
101
116
  export declare function constantKeys<K extends string, V>(object: {
102
117
  [k in K]?: V;
103
118
  }): K[];
@@ -273,7 +288,7 @@ export declare function resolveLegacyBlockScriptsInFolder(folder: string): strin
273
288
  * @param type 'script_module' is NOT supported at this time; however, it is *handled* in this function
274
289
  * @param handleData
275
290
  */
276
- export declare function appendAddInlineAssetCall(compilation: Compilation, writer: PHPWriter, handle: string, type: 'script' | 'style' | 'script_module', handleData: IAHandleData): void;
291
+ export declare function appendAddInlineAssetCall(compilation: Compilation, writer: PHPWriter, handle: string, type: EnqueuedAssetType, handleData: IAHandleData): void;
277
292
  export declare function wrapInlineAssetContents(compilation: Compilation, wrapperIdPrefix: string, handle: string, contents: string): HereOrNowDoc;
278
293
  export declare function synchronousDirIterator(dirPath: string): Generator<import("node:fs").Dirent<string>, void, unknown>;
279
294
  /**
package/dist/shared.js CHANGED
@@ -22,6 +22,9 @@ exports.isParsedAssetsJson = isParsedAssetsJson;
22
22
  exports.isStandardLocationName = isStandardLocationName;
23
23
  exports.isRegisterScriptArgsShorthandName = isRegisterScriptArgsShorthandName;
24
24
  exports.isNormalizedUsageLocations = isNormalizedUsageLocations;
25
+ exports.convertHookLocationToNamesAndPriority = convertHookLocationToNamesAndPriority;
26
+ exports.hasDeferringStrategy = hasDeferringStrategy;
27
+ exports.canMergeHandles = canMergeHandles;
25
28
  exports.constantKeys = constantKeys;
26
29
  exports.constantEntries = constantEntries;
27
30
  exports.determineCurrentSourceType = determineCurrentSourceType;
@@ -93,6 +96,95 @@ function isRegisterScriptArgsShorthandName(name) {
93
96
  function isNormalizedUsageLocations(usageLocations) {
94
97
  return typeof usageLocations.registerScriptArgs !== 'string';
95
98
  }
99
+ function convertHookLocationToNamesAndPriority(hookLocation) {
100
+ if (typeof hookLocation === 'string') {
101
+ return [[hookLocation], 10];
102
+ }
103
+ if (typeof hookLocation === 'number') {
104
+ return [[], hookLocation];
105
+ }
106
+ if (typeof hookLocation === 'boolean') {
107
+ return [[], 10];
108
+ }
109
+ if (Array.isArray(hookLocation)) {
110
+ return [hookLocation, 10];
111
+ }
112
+ // if it's in object-form
113
+ return [typeof hookLocation.hook_name === 'string' ? [hookLocation.hook_name] : hookLocation.hook_name ?? [], hookLocation.priority ?? 10];
114
+ }
115
+ function hasDeferringStrategy(handle) {
116
+ return handle.rest[2]?.strategy === 'async' || handle.rest[2]?.strategy === 'defer';
117
+ }
118
+ /**
119
+ * @return {@code true} -> "can share the same handle", {@code false} -> "cannot be merged", {@code 'dependent'} -> "one handle can be listed as a dependency of the other"
120
+ */
121
+ function canMergeHandles(handleA, handleB) {
122
+ if (handleA.inlinedAsset === undefined && handleB.inlinedAsset === undefined) {
123
+ return false; // We cannot merge handles for two non-inlined assets
124
+ }
125
+ if (handleA.lazyLoader !== handleB.lazyLoader) {
126
+ return false; // We cannot merge handles for assets that will be loaded under separate conditions
127
+ }
128
+ if (node_path_1.default.dirname(handleA.src) !== node_path_1.default.dirname(handleB.src)) {
129
+ return false; // We don't want assets from different locations to be able to have their handles overlap
130
+ }
131
+ const additionalArgsA = handleA.rest.length === 3 ? handleA.rest[2] : {};
132
+ const additionalArgsB = handleB.rest.length === 3 ? handleB.rest[2] : {};
133
+ if (additionalArgsA.module_dependencies !== additionalArgsB.module_dependencies) {
134
+ if (additionalArgsA.module_dependencies === undefined) {
135
+ return false;
136
+ }
137
+ if (additionalArgsB.module_dependencies === undefined) {
138
+ return false;
139
+ }
140
+ if (additionalArgsA.module_dependencies.length !== additionalArgsB.module_dependencies.length) {
141
+ return false;
142
+ }
143
+ for (const modDep of additionalArgsA.module_dependencies) {
144
+ if (!additionalArgsB.module_dependencies.includes(modDep)) {
145
+ return false;
146
+ }
147
+ }
148
+ }
149
+ if (additionalArgsA.in_footer !== additionalArgsB.in_footer) {
150
+ return 'dependent';
151
+ }
152
+ if (hasDeferringStrategy(handleA) && handleB.inlinedAsset?.position === 'after') {
153
+ return false;
154
+ }
155
+ if (hasDeferringStrategy(handleB) && handleA.inlinedAsset?.position === 'after') {
156
+ return false;
157
+ }
158
+ if (handleA.locations.register !== handleB.locations.register) {
159
+ return false; //TODO: Maybe make this more granular?
160
+ }
161
+ for (const standardLocation of constantKeys(exports.standardLocationNamesMeta)) {
162
+ const locA = handleA.locations[standardLocation];
163
+ const locB = handleB.locations[standardLocation];
164
+ if (locA === locB) {
165
+ continue;
166
+ }
167
+ if (locA === undefined || locA === false || locB === undefined || locB === false) {
168
+ return false;
169
+ }
170
+ const [locAHookNames, locAPriority] = convertHookLocationToNamesAndPriority(locA);
171
+ const [locBHookNames, locBPriority] = convertHookLocationToNamesAndPriority(locB);
172
+ if (locAHookNames.length !== locBHookNames.length) {
173
+ return false;
174
+ }
175
+ if (locAHookNames.length > 0) {
176
+ for (const hookName of locAHookNames) {
177
+ if (!locBHookNames.includes(hookName)) {
178
+ return false;
179
+ }
180
+ }
181
+ }
182
+ if (locAPriority !== locBPriority) {
183
+ return 'dependent'; // We cannot merge hooks across priority boundaries, but we can have one require the other
184
+ }
185
+ }
186
+ return true;
187
+ }
96
188
  function constantKeys(object) {
97
189
  return Object.keys(object);
98
190
  }
@@ -458,13 +458,11 @@ function buildCommonPluginConfig(webpackConfig, config, bundleAnalyzer, assumeGl
458
458
  }
459
459
  const builtDependencyExtractionWebpackPlugin = (0, dependency_extraction_webpack_plugin_config_builder_1.makeDependencyExtractionPlugin)(config.externals, assumeGlobalizedPlauditLibraries, externalize);
460
460
  plugins[dependencyExtractionPluginIndex] = builtDependencyExtractionWebpackPlugin;
461
- if ((0, shared_1.isDistDevOutputPath)(config.outputDir)) {
462
- try {
463
- const { PlauditDevPlugin } = require("@plaudit/dev-webpack-plugin");
464
- plugins.push(new PlauditDevPlugin(config));
465
- }
466
- catch { }
461
+ try {
462
+ const { PlauditDevPlugin } = require("@plaudit/dev-webpack-plugin");
463
+ plugins.push(new PlauditDevPlugin(config));
467
464
  }
465
+ catch { }
468
466
  return {
469
467
  plugins, removeEmptyScriptsPlugin,
470
468
  makeAdditionalDependencyInjectorPlugin(referencePlugin) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaudit/webpack-extensions",
3
- "version": "3.20.1",
3
+ "version": "3.21.0",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "repository": {
6
6
  "type": "git",