@plaudit/webpack-extensions 3.15.1 → 3.17.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,47 @@ 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.17.0] - 2026-08-18
9
+ ### Added
10
+ - A port of WordPress's Dependency Extraction Webpack Plugin because they keep breaking it
11
+ - This comes from version `2.96.0`
12
+ - Warnings and additional info for entrypoints that are lazy-loaded and assigned to actual locations
13
+ - This comes from version `2.96.0`
14
+ - An `externalize` that is not a string, an array, an object, or false is now rejected rather than silently ignored
15
+ - This matches with what webpack's `output.library` config supports
16
+
17
+ ### Changed
18
+ - `handle` callbacks are now passed the un-prefixed nice name rather than the fully-prefixed handle
19
+ - The `{basename}` token in the string form has always substituted the un-prefixed name, so the two spellings of the option disagreed. A callback like ``name => `acme.cb-${name}` `` was producing `acme.cb-my-theme/thing`
20
+ - Nice names taken from a filename are now kebab-cased, the way the basename-derived handle they replace already was
21
+ - `view.MyThing.ts` is `<prefix>/my-thing` rather than `<prefix>/MyThing`
22
+ - This is for web-safety
23
+ - `pathQueryParameters` to be an explicitly internal property instead of just asking people to not set it directly
24
+ - This fixes a few potential "bugs" that could happen if somebody set it anyway
25
+
26
+ ### Fixed
27
+ - `targetNamespace` being derived eagerly for every config, which failed any build run from a directory with no adjacent `composer.json` *or* `style.css`
28
+ - Only the `lazyLoader` emitters read it, so it now falls back to an empty namespace (this matches the pre-`3.10.0` behavior)
29
+ - A missing `style.css` also no longer surfaces as a raw `ENOENT`
30
+
31
+ ### Removed
32
+ - `processTranslationConfigs` because it should not be used and didn't actually do anything
33
+ - This comes from version `2.96.2`
34
+
35
+ ### Internals
36
+ - Added more integration tests
37
+
38
+ ## [3.16.0] - 2026-08-04
39
+ ### Added
40
+ - Logic so that files and directories with names equal to "example" or "examples" or that include "example" as a name segment are skipped
41
+ - This is done to allow us to include example files
42
+ - This comes from version `2.95.0`
43
+ - Updated the user guide with this information
44
+ - This comes from version `2.95.0`
45
+
46
+ ### Fixed
47
+ - The TOC in the user guide being out of date
48
+
8
49
  ## [3.15.1] - 2026-07-20
9
50
  ### Fixed
10
51
  - A bug that caused all styles bound for editor use to be emitted as inline CSS
@@ -219,6 +260,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
219
260
  - Legacy PostCSS features that have been integrated into modern CSS
220
261
  - `@extends` support
221
262
 
263
+ ## [2.96.3] - 2026-08-11
264
+ ### Added
265
+ - A few more validation checks
266
+
267
+ ## [2.96.2] - 2026-08-11
268
+ ### Removed
269
+ - `processTranslationConfigs` because it should not be used and didn't actually do anything
270
+
271
+ ### Internals
272
+ - Added more integration tests
273
+
274
+ ## [2.96.1] - 2026-08-10
275
+ ### Fixed
276
+ - An error in how externalized dependencies were resolved that caused jquery to never be emitted as a dependency
277
+
278
+ ## [2.96.0] - 2026-08-07
279
+ ### Added
280
+ - A port of WordPress's Dependency Extraction Webpack Plugin because they keep breaking it
281
+ - Warnings and additional info for entrypoints that are lazy-loaded and assigned to actual locations
282
+
283
+ ### Removed
284
+ - "combineAssetMetadata" - it has never actually been a valid prop
285
+
286
+ ### Fixed
287
+ - Path-query handle handling
288
+ - Double emission of CSS from entrypoint.json files when module processing is enabled
289
+ - A bug that could result in writes outside the webpack context root
290
+
291
+ ### Changed
292
+ - Backported the hook_name fix from the 3.x branch
293
+
294
+ ## [2.95.0] - 2026-08-04
295
+ ### Added
296
+ - Logic so that files and directories with names equal to "example" or "examples" or that include "example" as a name segment are skipped
297
+ - This is done to allow us to include example files
298
+ - Updated the user guide with this information
299
+
222
300
  ## [2.94.1] - 2026-07-20
223
301
  ### Fixed
224
302
  - A bug that caused all styles bound for editor use to be emitted as inline CSS
@@ -448,7 +526,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
448
526
 
449
527
  ## [2.72.1] - 2025-10-29
450
528
  ### Fixed
451
- - The wrapper being unable to find the `DependencyExtractionWebpackPlugin` due to version desyncs
529
+ - The wrapper being unable to find the `EnhancedDependencyExtractionWebpackPlugin` due to version desyncs
452
530
  - This isn't so much a "fix" as it is a "mitigation" - version desyncs are not fixable at the library level
453
531
 
454
532
  ## [2.72.0] - 2025-10-13
package/USER-GUIDE.md CHANGED
@@ -8,6 +8,7 @@
8
8
  * [Examples](#examples)
9
9
  * [Best Practices](#best-practices)
10
10
  * [Folder Structure](#folder-structure)
11
+ * [Enforced Naming Conventions](#enforced-naming-conventions)
11
12
  * [Blocks](#blocks)
12
13
  * [Extensions](#extensions)
13
14
  * [Plain Assets](#plain-assets)
@@ -104,11 +105,11 @@ However, if you need to do something unusual or just want to know what everythin
104
105
  - Any time that `dist` is referenced in a path, it should be replaced with the value of `outputDir` in your WebPack file
105
106
  - As with `src`, the value of `outputDir` *should be* `dist`; however, this might not be the case in some inherited or older code
106
107
  - Keys in the `src` *object* are all assumed to be paths relative to `srcDir` if it is specified or the folder in which the `webpack.config.js` file is located if it is not specified
107
- - Paths in values in the `src` *object* are all assumed to be paths relative to `outputDir` if it is specified or the folder in which the `webpack.config.js` file is located if it is not specified
108
+ - Paths in *values* in the `src` object are all assumed to be paths relative to `outputDir` if it is specified or the folder in which the `webpack.config.js` file is located if it is not specified
108
109
  - All file names are informational placeholders. They can be whatever you want provided that they are web-safe (basically, don't do things like calling the email address block "✉️" and you'll be fine)
109
110
 
110
111
  ### Examples
111
- - Any uses of `acme-my-plugin` should be replaced with your plugin or theme's kebab-case name (ex: `acme-my-plugin.wp-admin` => `plaudit-base.wp-admin`)
112
+ - Any uses of `acme-my-plugin` should be replaced with your plugin or theme's kebab-case name (ex: `acme-my-plugin/wp-admin` => `plaudit-base/wp-admin`)
112
113
 
113
114
  ---
114
115
 
@@ -117,6 +118,10 @@ However, if you need to do something unusual or just want to know what everythin
117
118
  - All folder and file names should be in kebab-case
118
119
  - Assets *should not* be in the root of the `src` folder. Assets that aren't block- or extension-bound should be in the `site` folder (or in a folder named after where they'll be used like, `admin` or `map-page`)
119
120
 
121
+ ### Enforced Naming Conventions
122
+ - Directory and file names that start with a "~" are interpreted as "backup code" and are, therefore, not built.
123
+ - File and directory names that are equal to "example" or "examples", or have "example" as a name segment (see [Location-Encoding Filenames](#location-encoding-filenames) for details on what a name-segment is) are treated as example code and are also not built
124
+
120
125
  ### Blocks
121
126
  - Block folder names *should* match the local portion of their name (ex: `plaudit-query-loop-maps/address-wrapper` should be stored in `address-wrapper`)
122
127
 
@@ -200,8 +205,11 @@ After building, each asset gets a handle you can reference in PHP.
200
205
 
201
206
  ### Handle Format
202
207
 
203
- **Plain:** `{prefix}.{filename}`
204
- - Example: `acme-my-plugin.admin-editor`
208
+ **Plain:** `{prefix}/{filename}`
209
+ - Example: `acme-my-plugin/admin-editor`
210
+ - If the filename carries a nice-name segment, that segment *replaces* the location-derived name rather than being appended to it, and it is kebab-cased on the way in
211
+ - `view.MyThing.ts` is `acme-my-plugin/my-thing`, not `acme-my-plugin/view-my-thing`
212
+ - Because the location is dropped, two files that differ only by location cannot share a nice name
205
213
 
206
214
  **Blocks:** `{prefix}_blocks_{block-name}-{type}`
207
215
  - Example: `acme-my-plugin_blocks_hero-banner-editor-script`
@@ -226,14 +234,14 @@ After building, each asset gets a handle you can reference in PHP.
226
234
  add_action('admin_enqueue_scripts', function($hook) {
227
235
  if ($hook === 'post.php') {
228
236
  // Localize data first (optional)
229
- wp_localize_script('acme-my-plugin.admin-editor', 'myData', [
237
+ wp_localize_script('acme-my-plugin/admin-editor', 'myData', [
230
238
  'ajaxUrl' => admin_url('admin-ajax.php'),
231
239
  'nonce' => wp_create_nonce('my_ajax_action'),
232
240
  ]);
233
241
 
234
242
  // Then enqueue
235
- wp_enqueue_script('acme-my-plugin.admin-editor');
236
- wp_enqueue_style('acme-my-plugin.admin-editor');
243
+ wp_enqueue_script('acme-my-plugin/admin-editor');
244
+ wp_enqueue_style('acme-my-plugin/admin-editor');
237
245
  }
238
246
  });
239
247
  ```
@@ -272,7 +280,7 @@ src: {
272
280
  // In your plugin
273
281
  add_action('admin_enqueue_scripts', function($hook) {
274
282
  if ($hook === 'post.php') {
275
- wp_enqueue_script('acme-my-plugin.post-editor');
283
+ wp_enqueue_script('acme-my-plugin/post-editor');
276
284
  }
277
285
  });
278
286
  ```
@@ -310,7 +318,7 @@ src: {
310
318
  | `srcDir` | `string` | `""` | Source directory (**Required** on new sites) |
311
319
  | `outputDir` | `string` | `""` | Output directory (**Required** on new sites) |
312
320
  | `useUnifiedLoader` | `boolean` | `false` | Generate unified-loader.php (always set to `true` on new sites!) |
313
- | `useWebpackResourceFiltering` | `boolean` | `false` | Generates versioned copies of images, fonts, etc (always set to `true` on new sites!) |
321
+ | `useWebpackResourceFiltering` | `boolean` | `true` | Generates versioned copies of images, fonts, etc (always set to `true` on new sites!) |
314
322
  | `plainEntrypointsVersion` | `1\|2` | `1` | Use 2 for new sites |
315
323
  | `extensionsVersion` | `1\|2\|3` | `1` | Use 3 for new sites |
316
324
  | `verbose` | `boolean` | `false` | Verbose logging |
@@ -354,16 +362,17 @@ Notes on how to read the following table:
354
362
  - `(-|=)` means "this can be either a dash or an equal sign"
355
363
  - Pipe-separated items in parentheses means "any one of these items can go here"
356
364
 
357
- | Name | Token | Usage |
358
- |--------------------|-------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
359
- | Enqueuing Location | `(enqueuing-location)[(-\|=)priority]` | This is a special section that is actually a collection of values. See the [Enqueuing Location](#enqueuing-location) section for more details |
360
- | Type Override | `script`, `style`, `script-module` | Can be used to override the detected of the asset. Must be the first token in the filename; if used directly, it is shorthand for `both-(Type Override)` |
361
- | Inline | `inline[(-\|=)position]`, `strategy(-\|=)inline[-position]` | Indicates that the script should be inlined. `position` can be either `before` or `after` |
362
- | Strategy | `lazy\|eager`, `strategy(-\|=)(lazy\|eager\|defer\|async)` | Controls the loading strategy. Lazy and eager have shorthand while the remaining values must be prefixed with `strategy` |
363
- | Fetch Priority | `fetchpriority(-\|=)(low\|high\|auto)` | Controls the fetchpriority property; `auto` is the default |
364
- | In Footer | `in-footer`, `in_footer` | Sets the `in_footer` value to `true`; incompatible with `in-header` |
365
- | In Header | `in-header`, `in_header` | Sets the `in_footer` value to `false`; incompatible with `in-footer` |
366
- | Extension | `([ps]?c\|sa)ss`, `m?[jt]sx?` | This determines the type unless it is overridden. This **MUST** be the last token in the name (it is the file extension) |
365
+ | Name | Token | Usage |
366
+ |---------------------|-------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
367
+ | Enqueuing Location | `(enqueuing-location)[(-\|=)priority]` | This is a special section that is actually a collection of values. See the [Enqueuing Location](#enqueuing-location) section for more details |
368
+ | Type Override | `script`, `style`, `script-module` | Can be used to override the detected of the asset. Must be the first token in the filename; if used directly, it is shorthand for `both-(Type Override)` |
369
+ | Inline | `inline[(-\|=)position]`, `strategy(-\|=)inline[-position]` | Indicates that the script should be inlined. `position` can be either `before` or `after` |
370
+ | Strategy | `lazy\|eager`, `strategy(-\|=)(lazy\|eager\|defer\|async)` | Controls the loading strategy. Lazy and eager have shorthand while the remaining values must be prefixed with `strategy` |
371
+ | Fetch Priority | `fetchpriority(-\|=)(low\|high\|auto)` | Controls the fetchpriority property; `auto` is the default |
372
+ | In Footer | `in-footer`, `in_footer` | Sets the `in_footer` value to `true`; incompatible with `in-header` |
373
+ | In Header | `in-header`, `in_header` | Sets the `in_footer` value to `false`; incompatible with `in-footer` |
374
+ | Extension | `([ps]?c\|sa)ss`, `m?[jt]sx?` | This determines the type unless it is overridden. This **MUST** be the last token in the name (it is the file extension) |
375
+ | Example Code Marker | `example` | This informs the build system that the file contains example code and, therefore, should not be build |
367
376
 
368
377
  ### Enqueuing Location
369
378
  If this is the first token, it can be suffixed with `-script`, `-style`, or `-script-module` to override the type.
@@ -565,11 +574,11 @@ This is the form that *should* be used. It has two categories of keys: control f
565
574
 
566
575
  **Control Flags:**
567
576
 
568
- | Key | Type | Default | Description |
569
- |----------------------|--------------------------------------------------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------|
570
- | `handle` | `string \| fn(string) => string` | Auto | The handle by which the entrypoint will be referenced. The function form is passed the default handle for manual tweaking |
571
- | `register` | `boolean \| number` | `true` | **[Advanced]** Whether the entrypoint should be registered at all or, if a number, the priority of the hook in which the registration will occur |
572
- | `registerScriptArgs` | `boolean` \| `'lazy'` \| [`WordPressScriptArgs`](#wordpress-script-args) | Auto | The loading flags (strategy, in_footer, and fetchpriority) for the script.<br>'lazy' is an alias for `{strategy: 'defer', in_footer: true}` |
577
+ | Key | Type | Default | Description |
578
+ |----------------------|--------------------------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
579
+ | `handle` | `string \| fn(string) => string` | Auto | The handle by which the entrypoint will be referenced. The function form is passed the default handle for manual tweaking, and may return nothing to accept the default |
580
+ | `register` | `boolean \| number` | `true` | **[Advanced]** Whether the entrypoint should be registered at all or, if a number, the priority of the hook in which the registration will occur |
581
+ | `registerScriptArgs` | `boolean` \| `'lazy'` \| [`WordPressScriptArgs`](#wordpress-script-args) | Auto | The loading flags (strategy, in_footer, and fetchpriority) for the script.<br>'lazy' is an alias for `{strategy: 'defer', in_footer: true}` |
573
582
 
574
583
  **Location Identifiers:**
575
584
 
@@ -2,7 +2,7 @@ import { MinimumViableMetadata, ParsedAssetJsonProvider, ParsedAssetsJson } from
2
2
  import type { ExtensibleEntryObject, VerifiedPlauditWordpressWebpackConfig } from "../utils/common-config-helpers";
3
3
  import { PseudoSemaphore } from "../utils/pseduo-semaphore";
4
4
  import { AbstractBiPhasicGroupPlugin } from "./AbstractBiPhasicGroupPlugin";
5
- import { Compilation, Compiler, Entrypoint } from "webpack";
5
+ import { type Compilation, type Compiler, type Entrypoint } from "webpack";
6
6
  export type EntryProvider<M extends MinimumViableMetadata = MinimumViableMetadata> = () => ExtensibleEntryObject<M> | Promise<ExtensibleEntryObject<M>>;
7
7
  export declare abstract class AbstractBiPhasicGroupAndEntryPlugin<M extends MinimumViableMetadata = MinimumViableMetadata> extends AbstractBiPhasicGroupPlugin {
8
8
  private readonly context;
@@ -24,6 +24,7 @@ export declare class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEnt
24
24
  constructor(config: VerifiedPlauditWordpressWebpackConfig, dest: VerifiedAdvancedOutputConfig, webpackRemoveEmptyScriptsPlugin: WebpackRemoveEmptyScriptsPlugin, context: string, entry: EntryProvider<BlockEntrypointInfo>);
25
25
  protected processAssets(compilation: Compilation, parsedAssetJsonProvider: ParsedAssetJsonProvider): Promise<void>;
26
26
  private emitBlockLoaderFile;
27
+ private static readonly THEME_JSON_FLAG_PROPS;
27
28
  private static pickOutFlagPropsFromBlockSupports;
28
29
  private callRegisterFunction;
29
30
  private transformBlocks;
@@ -226,37 +226,48 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
226
226
  compilation.emitAsset((0, node_path_1.join)(this.dest.destination, "blockdir.config.php"), new webpack_1.sources.RawSource((0, shared_1.makeEmittableConfigPHP)(blockDirConfig, true)));
227
227
  }
228
228
  }
229
- // This isn't strictly necessary, but it allows us to trivially eliminate commonly-used block supports flags that aren't used in theme.json
230
- // private static readonly IGNORED_THEME_JSON_FLAG_PROPS = ['align', 'alignWide', 'anchor', 'contentRole', 'inserter', 'interactivity', 'listView', 'plaudit'];
229
+ /*
230
+ * The block supports that have a boolean counterpart under theme.json's settings, as
231
+ * `settings key -> {support prop: settings prop}`. This is an allow-list rather than an ignore-list
232
+ * because most supports (align, alignWide, anchor, contentRole, inserter, interactivity, listView,
233
+ * plaudit, ...) have no theme.json settings equivalent at all, and the ones that do don't always
234
+ * share a name: typography's fontSize is settings.typography.customFontSize, and Gutenberg still
235
+ * ships several of these under an __experimental prefix.
236
+ */
237
+ static THEME_JSON_FLAG_PROPS = {
238
+ color: { background: 'background', link: 'link', text: 'text' },
239
+ dimensions: { aspectRatio: 'aspectRatio', minHeight: 'minHeight' },
240
+ spacing: { blockGap: 'blockGap', margin: 'margin', padding: 'padding' },
241
+ typography: {
242
+ fontSize: 'customFontSize',
243
+ lineHeight: 'lineHeight',
244
+ textAlign: 'textAlign',
245
+ fontStyle: 'fontStyle', __experimentalFontStyle: 'fontStyle',
246
+ fontWeight: 'fontWeight', __experimentalFontWeight: 'fontWeight',
247
+ letterSpacing: 'letterSpacing', __experimentalLetterSpacing: 'letterSpacing',
248
+ textDecoration: 'textDecoration', __experimentalTextDecoration: 'textDecoration',
249
+ textTransform: 'textTransform', __experimentalTextTransform: 'textTransform',
250
+ writingMode: 'writingMode', __experimentalWritingMode: 'writingMode'
251
+ }
252
+ };
231
253
  static pickOutFlagPropsFromBlockSupports(supports) {
232
254
  function isRecord(value) {
233
255
  return typeof value === 'object' && !!value && !Array.isArray(value);
234
256
  }
235
257
  let addedProp = false;
236
258
  const res = {};
237
- if ('color' in supports) {
238
- const color = supports.color;
239
- if (isRecord(color)) {
240
- const colorFlags = {};
241
- for (const prop of ['background', 'link', 'text']) {
242
- if (supports[prop]) {
243
- colorFlags[prop] = true;
244
- res.color = colorFlags;
245
- addedProp = true;
246
- }
247
- }
259
+ for (const [settingsKey, props] of Object.entries(EnhancedBlockJSONPlugin.THEME_JSON_FLAG_PROPS)) {
260
+ const support = supports[settingsKey];
261
+ if (!isRecord(support)) {
262
+ continue;
248
263
  }
249
- }
250
- if ('dimensions' in supports) {
251
- const dimensions = supports.dimensions;
252
- if (isRecord(dimensions)) {
253
- const dimensionsFlags = {};
254
- for (const prop of ['aspectRatio']) {
255
- if (supports[prop]) {
256
- dimensionsFlags[prop] = true;
257
- res.color = dimensionsFlags;
258
- addedProp = true;
259
- }
264
+ const flags = {};
265
+ for (const [supportProp, settingsProp] of Object.entries(props)) {
266
+ // A sided support such as `"margin": ["top", "bottom"]` is still just "on" as far as theme.json is concerned
267
+ if (support[supportProp]) {
268
+ flags[settingsProp] = true;
269
+ res[settingsKey] = flags;
270
+ addedProp = true;
260
271
  }
261
272
  }
262
273
  }
@@ -536,7 +547,7 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
536
547
  }
537
548
  delete blockJson[invalidTemplateLocation];
538
549
  if (renderTemplate.length > 1) {
539
- compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Encountered a block with multiple possible render files. Please set the render key in block.json to the proper one", (0, node_path_1.join)(sourceDir, 'block.json')));
550
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Encountered a block with multiple possible render files. Please set the render key in block.json to the intended one", (0, node_path_1.join)(sourceDir, 'block.json')));
540
551
  blockJson[validTemplateLocation] = renderTemplate.find(p => p.endsWith(".php")) ?? renderTemplate[0];
541
552
  }
542
553
  else {
@@ -52,7 +52,7 @@ class ExtensionsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAndEntryP
52
52
  compilation.contextDependencies.add(srcRoot);
53
53
  }
54
54
  for await (const dirent of await (0, promises_1.opendir)(srcRoot)) {
55
- if (dirent.name.startsWith("~")) {
55
+ if ((0, shared_1.shouldBeSkipped)(dirent.name)) {
56
56
  continue;
57
57
  }
58
58
  if (dirent.isFile()) {
@@ -41,20 +41,20 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
41
41
  for (let count = 0; finalHandleName in handleNameMap && handleNameMap[finalHandleName] !== src;) {
42
42
  finalHandleName = `${baseFinalHandleName}-${++count}`;
43
43
  }
44
- if (typeof handleName === 'string' || typeof handleName === 'function' || (handleName && 'niceName' in handleName)) {
44
+ if (handleName !== undefined) {
45
45
  finalHandleName = (0, shared_1.convertUsageLocationsHandleToEmittableHandle)(handleName, handlePrefix, finalHandleName.substring(handlePrefix.length + 1));
46
46
  }
47
47
  handleNameMap[finalHandleName] = src;
48
48
  plainEntrypointsConfig[`${type}Handles`][finalHandleName] = { src, rest, locations, type, lazyLoader, ...otherProps };
49
49
  }
50
50
  }
51
- PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists('script', Object.entries(plainEntrypointsConfig.scriptHandles)
51
+ PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'script', Object.entries(plainEntrypointsConfig.scriptHandles)
52
52
  .toSorted(([a], [b]) => a.localeCompare(b)), handleLists);
53
- PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists('script_module', Object.entries(plainEntrypointsConfig.script_moduleHandles)
53
+ PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'script_module', Object.entries(plainEntrypointsConfig.script_moduleHandles)
54
54
  .toSorted(([a], [b]) => a.localeCompare(b)), handleLists);
55
55
  const sortedStyleHandles = Object.entries(plainEntrypointsConfig.styleHandles)
56
56
  .toSorted(([a], [b]) => a.localeCompare(b));
57
- PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists('style', sortedStyleHandles, handleLists);
57
+ PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists(compilation, 'style', sortedStyleHandles, handleLists);
58
58
  const callItemSorter = (a, b) => a.handle.localeCompare(b.handle);
59
59
  for (const list of Object.values(handleLists)) {
60
60
  list.sort(callItemSorter);
@@ -113,8 +113,10 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
113
113
  writer.return(editorSettings);
114
114
  }, { functionArgParameters: [editorSettings], accountForAlreadyDoing: false });
115
115
  }
116
- const lazyLoadedHandles = Object.values(handleLists)
117
- .flatMap(handleList => handleList.map(handle => [handle.data.lazyLoader, handle]))
116
+ // Only the register and inline lists, which are mutually exclusive, so each handle is collected
117
+ // exactly once. Flattening every list instead would collect a handle once per location it declares.
118
+ const lazyLoadedHandles = [...handleLists.register, ...handleLists.inline]
119
+ .map(handle => [handle.data.lazyLoader, handle])
118
120
  .filter((entry) => entry[0] !== undefined)
119
121
  .sort((a, b) => a[0].localeCompare(b[0]) || a[1].priority - b[1].priority);
120
122
  if (lazyLoadedHandles.length > 0) {
@@ -134,7 +136,7 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
134
136
  }
135
137
  (0, shared_1.emitPHPWriterAsAsset)(file, compilation, "plain-entrypoints-loader.php");
136
138
  }
137
- static addHandlesToHandleLists(type, handles, handleLists) {
139
+ static addHandlesToHandleLists(compilation, type, handles, handleLists) {
138
140
  const defaultPriority = 10;
139
141
  for (const [handle, data] of handles) {
140
142
  if (data.locations.register !== false) {
@@ -143,6 +145,19 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
143
145
  else if (data.inlinedAsset !== undefined) {
144
146
  handleLists.inline.push({ handle, type, data, priority: typeof data.locations.inline === 'number' ? data.locations.inline : 10 });
145
147
  }
148
+ if (data.lazyLoader !== undefined) {
149
+ // A lazily loaded handle is enqueued by its generated function and never by an action, so it
150
+ // is deliberately kept out of the location lists. Those lists are also what the lazy function
151
+ // is built from, and a handle present in several of them would be enqueued once per list.
152
+ const declaredLocations = (0, shared_1.constantKeys)(shared_1.standardLocationNamesMeta)
153
+ .filter(location => data.locations[location] || typeof data.locations[location] === 'number');
154
+ if (declaredLocations.length > 0) {
155
+ const locText = declaredLocations.length === 1 ? "location" : "locations";
156
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)(`The handle "${handle}" declares both a lazyLoader (${data.lazyLoader}) and the ${declaredLocations.join(", ")} ${locText}.`
157
+ + ` It will only be enqueued by ${data.lazyLoader}(); remove either the ${locText} or the lazyLoader.`, data.src));
158
+ }
159
+ continue;
160
+ }
146
161
  for (const location of (0, shared_1.constantKeys)(shared_1.standardLocationNamesMeta)) {
147
162
  let priority, hook_name;
148
163
  const dataLocation = data.locations[location];
@@ -186,7 +201,6 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
186
201
  }
187
202
  }
188
203
  static appendEnqueuingHandleLists(writer, handleLists) {
189
- const lazyLoadableHandles = {};
190
204
  for (const [sln, { action, supports_hook_name, additional_action_code }] of (0, shared_1.constantEntries)(shared_1.standardLocationNamesMeta)) {
191
205
  const handleList = handleLists[sln];
192
206
  if (handleList.length > 0) {
@@ -226,7 +240,6 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
226
240
  }
227
241
  }
228
242
  }
229
- return lazyLoadableHandles;
230
243
  }
231
244
  static separateHandleListByPriority(handleItems) {
232
245
  const lists = new Map();
@@ -1,7 +1,4 @@
1
- import DependencyExtractionWebpackPlugin from "@wordpress/dependency-extraction-webpack-plugin";
2
- import { Externals } from "../shared";
1
+ import type { Externals } from "../shared";
2
+ import { EnhancedDependencyExtractionWebpackPlugin } from "../wordpress-ports/dependency-extraction-webpack-plugin";
3
3
  import type { Configuration } from "webpack";
4
- export declare function makeDependencyExtractionPlugin(externals: Externals | undefined, assumeGlobalizedPlauditLibraries: boolean, wantsGroupedDepData: boolean, externalizing: Required<Configuration>['output']['library']): {
5
- instance: DependencyExtractionWebpackPlugin;
6
- addExternalizedDep: (dep: string) => void;
7
- };
4
+ export declare function makeDependencyExtractionPlugin(externals: Externals | undefined, assumeGlobalizedPlauditLibraries: boolean, externalizing: Required<Configuration>['output']['library']): EnhancedDependencyExtractionWebpackPlugin;
@@ -1,19 +1,10 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.makeDependencyExtractionPlugin = makeDependencyExtractionPlugin;
7
- const dependency_extraction_webpack_plugin_1 = __importDefault(require("@wordpress/dependency-extraction-webpack-plugin"));
8
- function makeDependencyExtractionPlugin(externals, assumeGlobalizedPlauditLibraries, wantsGroupedDepData, externalizing) {
9
- const baseProps = makeDependencyExtractionPluginProps(externals, assumeGlobalizedPlauditLibraries, externalizing);
10
- const instance = new dependency_extraction_webpack_plugin_1.default(wantsGroupedDepData ? { ...baseProps, outputFormat: "json", combineAssets: true } : baseProps);
11
- return {
12
- instance,
13
- addExternalizedDep: (dep) => {
14
- instance.externalizedDeps.add(dep);
15
- }
16
- };
4
+ const dependency_extraction_webpack_plugin_1 = require("../wordpress-ports/dependency-extraction-webpack-plugin");
5
+ const util_1 = require("../wordpress-ports/dependency-extraction-webpack-plugin/util");
6
+ function makeDependencyExtractionPlugin(externals, assumeGlobalizedPlauditLibraries, externalizing) {
7
+ return new dependency_extraction_webpack_plugin_1.EnhancedDependencyExtractionWebpackPlugin(makeDependencyExtractionPluginProps(externals, assumeGlobalizedPlauditLibraries, externalizing));
17
8
  }
18
9
  function makeDependencyExtractionPluginProps(externals, assumeGlobalizedPlauditLibraries, externalizing) {
19
10
  if (!externals) {
@@ -93,7 +84,7 @@ const EXTERNALIZABLE_PLAUDIT_LIBRARIES = {
93
84
  ...Object.fromEntries(['@plaudit/frontend-apis', '@plaudit/library-extensions', '@plaudit/block-supports', '@plaudit/gutenberg-api-extensions']
94
85
  .map(req => {
95
86
  return [req, {
96
- import: ['plaudit', camelCaseDash(req.substring(PLAUDIT_NAMESPACE.length))],
87
+ import: ['plaudit', (0, util_1.camelCaseDash)(req.substring(PLAUDIT_NAMESPACE.length))],
97
88
  handle: `plaudit-${req.substring(PLAUDIT_NAMESPACE.length)}`
98
89
  }];
99
90
  })),
@@ -116,12 +107,6 @@ function normalizeRequest(request) {
116
107
  function curryRequestHandlers(primary, secondary) {
117
108
  return (...args) => primary(...args) ?? secondary(...args);
118
109
  }
119
- /**
120
- * Copied from @wordpress/dependency-extraction-webpack-plugin for consistency
121
- */
122
- function camelCaseDash(string) {
123
- return string.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
124
- }
125
110
  function handleExternalizingEqualsPossibleExternal(possibleExternal, ext) {
126
111
  if (!ext || !possibleExternal) {
127
112
  return possibleExternal;
@@ -151,7 +136,9 @@ function handleExternalizingEqualsPossibleExternal(possibleExternal, ext) {
151
136
  }
152
137
  else {
153
138
  for (const key of ['amd', 'commonjs', 'root']) {
154
- if (key in ext && handleExternalizingEqualsPossibleExternal(possibleExternal, ext[key])) {
139
+ // The recursion returns the external when it should be kept and undefined when it matched, so
140
+ // only an undefined result means this umd name is the thing being built and must not externalize.
141
+ if (key in ext && handleExternalizingEqualsPossibleExternal(possibleExternal, ext[key]) === undefined) {
155
142
  return undefined;
156
143
  }
157
144
  }
package/dist/shared.d.ts CHANGED
@@ -5,17 +5,20 @@ import { type AssetInfo, type Compilation, type Configuration, type Entrypoint,
5
5
  import { SourceType } from "./utils/entrypoint-resolution-logic";
6
6
  import type { NormalizedEnqueuingControlFlags } from "./utils/path-query-and-related-helpers";
7
7
  export * from "./utils/entrypoint-resolution-logic";
8
- export type WPModuleDependency = {
8
+ export type WPModuleDependency = string | {
9
9
  id: string;
10
10
  import?: string;
11
11
  };
12
- export type ParsedAssetsJson = Record<string, {
13
- dependencies: string[];
12
+ export type ParsedAssetJson = {
13
+ dependencies: WPModuleDependency[];
14
+ module_dependencies?: WPModuleDependency[];
14
15
  version: string;
15
- module_dependencies?: (string | WPModuleDependency)[];
16
- }>;
16
+ type?: 'module';
17
+ handle?: string;
18
+ };
19
+ export type ParsedAssetsJson = Record<string, ParsedAssetJson>;
17
20
  export declare function isParsedAssetsJson(thing: any): thing is ParsedAssetsJson;
18
- type BaseRestType = [/* dependencies: */ string[], /* version: */ string];
21
+ type BaseRestType = [/* dependencies: */ WPModuleDependency[], /* version: */ string];
19
22
  export type AssetMetaSegment = {
20
23
  inlinedAsset: InlinedAsset;
21
24
  assetContents: string;
@@ -28,7 +31,7 @@ export type ScriptArgsObject = {
28
31
  strategy?: 'defer' | 'async';
29
32
  in_footer?: boolean;
30
33
  fetchpriority?: 'auto' | 'low' | 'high';
31
- module_dependencies?: (string | WPModuleDependency)[];
34
+ module_dependencies?: WPModuleDependency[];
32
35
  };
33
36
  export type InlinedAsset = {
34
37
  position?: 'before' | 'after';
@@ -120,16 +123,16 @@ export type AdvancedOutputConfig = {
120
123
  directoryLayout?: SourceType;
121
124
  assumeGlobalizedPlauditLibraries?: boolean;
122
125
  externalize?: Required<Configuration>['output']['library'];
123
- bundleAnalyzer?: boolean;
126
+ bundleAnalyzer?: boolean | 'no-auto-open';
124
127
  locations?: UsageLocations | UsageLocations['handle'];
125
128
  lazyLoader?: string;
126
- pathQueryParameters?: PathQueryParameters;
127
129
  enqueuingFlags?: NormalizedEnqueuingControlFlags;
128
130
  };
129
- type OptionalCfgFields = 'directoryLayout' | 'externalize' | 'lazyLoader' | 'pathQueryParameters';
131
+ type OptionalCfgFields = 'directoryLayout' | 'externalize' | 'lazyLoader';
130
132
  export type VerifiedAdvancedOutputConfig = Required<Omit<AdvancedOutputConfig, 'locations' | 'enqueuingFlags' | OptionalCfgFields>> & Pick<AdvancedOutputConfig, OptionalCfgFields> & {
131
133
  enqueuingFlags: NormalizedEnqueuingControlFlags | undefined;
132
134
  locations: NormalizedUsageLocations;
135
+ pathQueryParameters?: PathQueryParameters;
133
136
  };
134
137
  export type SourcesObject = Record<string, string | AdvancedOutputConfig | boolean>;
135
138
  export type PlauditWordpressWebpackConfig = {
@@ -144,8 +147,6 @@ export type PlauditWordpressWebpackConfig = {
144
147
  };
145
148
  externals?: Externals;
146
149
  assumeGlobalizedPlauditLibraries?: boolean;
147
- processTranslationConfigs?: boolean;
148
- combineAssetMetadata?: boolean;
149
150
  useWebpackResourceFiltering?: boolean;
150
151
  outputDir?: string;
151
152
  extensionsVersion?: 1 | 2 | 3;
@@ -172,8 +173,6 @@ export declare function applyStandards(config: PlauditWordpressWebpackConfig): {
172
173
  };
173
174
  readonly externals?: Externals;
174
175
  readonly assumeGlobalizedPlauditLibraries?: boolean;
175
- readonly processTranslationConfigs?: boolean;
176
- readonly combineAssetMetadata?: boolean;
177
176
  useWebpackResourceFiltering?: boolean;
178
177
  outputDir?: string;
179
178
  extensionsVersion?: 1 | 2 | 3;
@@ -219,8 +218,7 @@ export type MinimumViableMetadata = {
219
218
  dest: VerifiedAdvancedOutputConfig;
220
219
  absoluteSrc: string;
221
220
  };
222
- export declare function convertUsageLocationsHandleToEmittableHandle(handle: UsageLocations['handle'], handlePrefix: string, generatedNiceName: string): string;
223
- export declare function convertUsageLocationsHandleToEmittableHandleSuffix(handle: UsageLocations['handle'], generatedNiceName: string): string;
221
+ export declare function convertUsageLocationsHandleToEmittableHandle(handle: UsageLocations['handle'], handlePrefix: string, handleSuffix: string): string;
224
222
  export declare function makeEmittableConfigPHP(data: any, asFullFile: boolean, parentIndent?: string): string;
225
223
  export declare const entrypointFields: readonly ["editorStyle", "viewStyle", "style", "editorScript", "viewScript", "script", "viewScriptModule"];
226
224
  export type EntrypointFields = (typeof entrypointFields[number])[];
@@ -233,7 +231,7 @@ export declare const styleExtension: RegExp;
233
231
  export declare function scriptOrStyleTest(entryPath: string, scriptExtension: RegExp): "script" | "style" | "";
234
232
  export declare function isStyleField(field: string): field is 'style' | 'viewStyle' | 'editorStyle';
235
233
  export declare function isScriptModuleField(field: string): field is 'viewScriptModule';
236
- export declare function mergeModuleDependencyArrays(a: (string | WPModuleDependency)[] | undefined, b: (string | WPModuleDependency)[] | undefined): (string | WPModuleDependency)[] | undefined;
234
+ export declare function mergeModuleDependencyArrays(a: WPModuleDependency[] | undefined, b: WPModuleDependency[] | undefined): WPModuleDependency[] | undefined;
237
235
  export declare function getHandleGroup(field: string): 'styleHandles' | 'scriptHandles' | 'scriptModuleHandles';
238
236
  export type StripFirstTwoItems<A extends any[]> = A extends [any, any, ...rest: infer R] ? R : never;
239
237
  export declare function hasAtLeastOneItem<T>(list: T[]): list is [T, ...T[]];
@@ -260,7 +258,7 @@ export declare function getParsedAssetsJsonDataForEntrypoint(compilation: Compil
260
258
  assetData: ParsedAssetsJson[string];
261
259
  } | undefined;
262
260
  export declare function emitPHPWriterAsAsset(writer: PHPWriter | PHPFile, compilation: Compilation, file: string, assetInfo?: AssetInfo): void;
263
- export declare function dedent(text: TemplateStringsArray): string;
261
+ export declare function dedent(text: TemplateStringsArray, ...values: unknown[]): string;
264
262
  export declare function resolveLegacyBlockScriptsInFolder(folder: string): string[];
265
263
  /**
266
264
  * @param compilation
@@ -278,3 +276,7 @@ export declare function synchronousDirIterator(dirPath: string): Generator<impor
278
276
  * @link https://dev.to/jdbar
279
277
  */
280
278
  export declare function instanceOfNodeError<T extends new (...args: any) => Error>(value: unknown, errorType: T): value is InstanceType<T> & NodeJS.ErrnoException;
279
+ /**
280
+ * This tests if the name starts with a "~", is equal to "example" or "examples", or includes "example" as a name segment
281
+ */
282
+ export declare function shouldBeSkipped(name: string): boolean;