@plaudit/webpack-extensions 3.16.0 → 3.18.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +88 -1
  2. package/README.md +22 -2
  3. package/USER-GUIDE.md +21 -16
  4. package/dist/plugins/AbstractBiPhasicGroupAndEntryPlugin.d.ts +1 -1
  5. package/dist/plugins/AbstractBiPhasicGroupPlugin.d.ts +3 -3
  6. package/dist/plugins/EnhancedBlockJSONPlugin.d.ts +1 -0
  7. package/dist/plugins/EnhancedBlockJSONPlugin.js +36 -25
  8. package/dist/plugins/MiniCSSExtractPluginErrorCleaner.d.ts +2 -3
  9. package/dist/plugins/PackageConfigSanityChecker.d.ts +2 -3
  10. package/dist/plugins/PlainEntrypointsConfigFileGeneratorPlugin.js +22 -9
  11. package/dist/plugins/VariablesJSMonitorPlugin.d.ts +2 -3
  12. package/dist/plugins/WPMLConfigBuilder.d.ts +2 -3
  13. package/dist/plugins/dependency-extraction-webpack-plugin-config-builder.d.ts +3 -6
  14. package/dist/plugins/dependency-extraction-webpack-plugin-config-builder.js +8 -21
  15. package/dist/shared.d.ts +24 -24
  16. package/dist/shared.js +41 -54
  17. package/dist/utils/common-config-helpers.d.ts +2 -2
  18. package/dist/utils/common-config-helpers.js +21 -13
  19. package/dist/utils/css-writer.js +3 -3
  20. package/dist/utils/entrypoint-resolution-logic.d.ts +5 -2
  21. package/dist/utils/location-encoding-filename-parser.d.ts +0 -1
  22. package/dist/utils/location-encoding-filename-parser.js +4 -3
  23. package/dist/utils/path-query-and-related-helpers.d.ts +0 -1
  24. package/dist/utils/path-query-and-related-helpers.js +0 -4
  25. package/dist/wordpress-ports/dependency-extraction-webpack-plugin/index.d.ts +72 -0
  26. package/dist/wordpress-ports/dependency-extraction-webpack-plugin/index.js +522 -0
  27. package/dist/wordpress-ports/dependency-extraction-webpack-plugin/util.d.ts +56 -0
  28. package/dist/wordpress-ports/dependency-extraction-webpack-plugin/util.js +150 -0
  29. package/dist/wordpress-scripts-wrapper.js +151 -89
  30. package/package.json +32 -8
  31. package/dist/plugins/BrowserSyncPlugin.d.ts +0 -25
  32. package/dist/plugins/BrowserSyncPlugin.js +0 -212
package/CHANGELOG.md CHANGED
@@ -5,6 +5,51 @@ 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.18.0] - 2026-08-18
9
+ ### Added
10
+ - David's improvements to build pipeline efficiency (Credit: David)
11
+ - This comes from version `2.97.0`
12
+ - Support for David's `@plaudit/dev-webpack-plugin` (Credit: David)
13
+ - This comes from version `2.97.0`
14
+
15
+ ### Changed
16
+ - To Webpack's standard plugin interface (`WebpackPluginInstance`)
17
+ - This comes from version `2.97.0`
18
+
19
+ ### Removed
20
+ - Browsersync and its tests (Credit: David)
21
+ - This comes from version `2.97.0`
22
+
23
+ ## [3.17.0] - 2026-08-18
24
+ ### Added
25
+ - A port of WordPress's Dependency Extraction Webpack Plugin because they keep breaking it
26
+ - This comes from version `2.96.0`
27
+ - Warnings and additional info for entrypoints that are lazy-loaded and assigned to actual locations
28
+ - This comes from version `2.96.0`
29
+ - An `externalize` that is not a string, an array, an object, or false is now rejected rather than silently ignored
30
+ - This matches with what webpack's `output.library` config supports
31
+
32
+ ### Changed
33
+ - `handle` callbacks are now passed the un-prefixed nice name rather than the fully-prefixed handle
34
+ - 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`
35
+ - Nice names taken from a filename are now kebab-cased, the way the basename-derived handle they replace already was
36
+ - `view.MyThing.ts` is `<prefix>/my-thing` rather than `<prefix>/MyThing`
37
+ - This is for web-safety
38
+ - `pathQueryParameters` to be an explicitly internal property instead of just asking people to not set it directly
39
+ - This fixes a few potential "bugs" that could happen if somebody set it anyway
40
+
41
+ ### Fixed
42
+ - `targetNamespace` being derived eagerly for every config, which failed any build run from a directory with no adjacent `composer.json` *or* `style.css`
43
+ - Only the `lazyLoader` emitters read it, so it now falls back to an empty namespace (this matches the pre-`3.10.0` behavior)
44
+ - A missing `style.css` also no longer surfaces as a raw `ENOENT`
45
+
46
+ ### Removed
47
+ - `processTranslationConfigs` because it should not be used and didn't actually do anything
48
+ - This comes from version `2.96.2`
49
+
50
+ ### Internals
51
+ - Added more integration tests
52
+
8
53
  ## [3.16.0] - 2026-08-04
9
54
  ### Added
10
55
  - Logic so that files and directories with names equal to "example" or "examples" or that include "example" as a name segment are skipped
@@ -230,6 +275,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
230
275
  - Legacy PostCSS features that have been integrated into modern CSS
231
276
  - `@extends` support
232
277
 
278
+ ## [2.97.0] - 2026-08-18
279
+ ### Added
280
+ - David's improvements to build pipeline efficiency
281
+ - Support for David's `@plaudit/dev-webpack-plugin`
282
+
283
+ ### Changed
284
+ - To Webpack's standard plugin interface (`WebpackPluginInstance`)
285
+
286
+ ### Removed
287
+ - Browsersync tests
288
+
289
+ ## [2.96.3] - 2026-08-11
290
+ ### Added
291
+ - A few more validation checks
292
+
293
+ ## [2.96.2] - 2026-08-11
294
+ ### Removed
295
+ - `processTranslationConfigs` because it should not be used and didn't actually do anything
296
+
297
+ ### Internals
298
+ - Added more integration tests
299
+
300
+ ## [2.96.1] - 2026-08-10
301
+ ### Fixed
302
+ - An error in how externalized dependencies were resolved that caused jquery to never be emitted as a dependency
303
+
304
+ ## [2.96.0] - 2026-08-07
305
+ ### Added
306
+ - A port of WordPress's Dependency Extraction Webpack Plugin because they keep breaking it
307
+ - Warnings and additional info for entrypoints that are lazy-loaded and assigned to actual locations
308
+
309
+ ### Removed
310
+ - "combineAssetMetadata" - it has never actually been a valid prop
311
+
312
+ ### Fixed
313
+ - Path-query handle handling
314
+ - Double emission of CSS from entrypoint.json files when module processing is enabled
315
+ - A bug that could result in writes outside the webpack context root
316
+
317
+ ### Changed
318
+ - Backported the hook_name fix from the 3.x branch
319
+
233
320
  ## [2.95.0] - 2026-08-04
234
321
  ### Added
235
322
  - Logic so that files and directories with names equal to "example" or "examples" or that include "example" as a name segment are skipped
@@ -465,7 +552,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
465
552
 
466
553
  ## [2.72.1] - 2025-10-29
467
554
  ### Fixed
468
- - The wrapper being unable to find the `DependencyExtractionWebpackPlugin` due to version desyncs
555
+ - The wrapper being unable to find the `EnhancedDependencyExtractionWebpackPlugin` due to version desyncs
469
556
  - This isn't so much a "fix" as it is a "mitigation" - version desyncs are not fixable at the library level
470
557
 
471
558
  ## [2.72.0] - 2025-10-13
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Plaudit Webpack Extensions
2
- A webpack configuration and plugin suite designed specifically for WordPress development workflows. This package extends `@wordpress/scripts` with custom webpack plugins, PostCSS configurations, and build optimizations tailored for Plaudit's WordPress projects. It includes specialized handling for Gutenberg blocks, asset management, internationalization (WPML), and development tooling like BrowserSync integration.
2
+ A webpack configuration and plugin suite designed specifically for WordPress development workflows. This package extends `@wordpress/scripts` with custom webpack plugins, PostCSS configurations, and build optimizations tailored for Plaudit's WordPress projects. It includes specialized handling for Gutenberg blocks, asset management, internationalization (WPML), and a same-origin CSS-inject / live-reload client for `wp-scripts start`.
3
3
 
4
4
  # Requirements
5
5
  - All block- and extension-related capabilities require that the `plaudit/plaudit-common` composer library be installed.
@@ -23,7 +23,27 @@ This suite generates loader code that uses the most efficient mechanisms availab
23
23
  The same file structure (both in the webpack.config.js file and the actual directory tree) can be used in plugins and themes.
24
24
 
25
25
  # Usage
26
- Check out the [User Guide](USER-GUIDE.md) file.
26
+ Config, entry types, and handles: [User Guide](USER-GUIDE.md).
27
+
28
+ ## `dist` vs `dist-dev`
29
+
30
+ With the unified loader (site `standard`), `outputDir` is `dist`. When `SERVER_MODE` is `development` (the default if unset on the host) and `omitDistDev` is off, webpack writes **`dist-dev`**. WordPress in development loads that folder via `dist/unified-loader.php` when `dist-dev/unified-loader.php` exists. Production WordPress always uses **`dist`**.
31
+
32
+ `SERVER_MODE` chooses the folder. The compiler (`mode`, minify, webpack cache) is always production for `dist`. For `dist-dev` it follows `NODE_ENV`: `wp-scripts start` is development (no minify, cache on); `wp-scripts build` is production (minify, cache off) still written to `dist-dev`.
33
+
34
+ `dev-reload.js` / overlay run only for `pnpm run dev` (`BROWSER_SYNC` + watch) and only under `dist-dev`. Pass `--no-dev-reload` after `--` (or `DEV_RELOAD=0`) to skip the overlay and live reload: `pnpm run dev -- --no-dev-reload`. Docker still starts and the browser still opens. The `--` is required so webpack-cli does not treat the flag as an unknown option.
35
+
36
+ | Command | Folder | Compiler | Overlay |
37
+ |-----------------------------------------|------------|------------------------------------------------------------------|---------|
38
+ | `pnpm run dev` | `dist-dev` | development (no minify, cache on) | yes |
39
+ | `pnpm run dev -- --no-dev-reload` | `dist-dev` | development (no minify, cache on) | no |
40
+ | `pnpm run watch` | `dist-dev` | development (no minify, cache on) | no |
41
+ | `pnpm run build` | `dist-dev` | production (minify, cache off) | no |
42
+ | `SERVER_MODE=production pnpm run build` | `dist` | same production compiler as `pnpm run build` (minify, cache off) | no |
43
+
44
+ The last row is the same compile as a local `build`; only the folder changes. That is what ships: `@plaudit/toolbox` (`toolbox init`) installs a **pre-commit** hook that runs `pnpm … build` with `SERVER_MODE=production` and stages `wp-content/**/dist/**`. A plain `pnpm run build` on your machine does **not** update `dist` unless you set `SERVER_MODE` yourself. `SKIP_BUILD=1` skips the hook build.
45
+
46
+ See [Plaudit - Dev Webpack Plugin](https://github.com/plaudit/dev-webpack-plugin#readme) for more details
27
47
 
28
48
  # Development
29
49
 
package/USER-GUIDE.md CHANGED
@@ -109,7 +109,7 @@ However, if you need to do something unusual or just want to know what everythin
109
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)
110
110
 
111
111
  ### Examples
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
+ - 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`)
113
113
 
114
114
  ---
115
115
 
@@ -190,11 +190,13 @@ This file automatically:
190
190
  - Sets up blocks and extensions
191
191
 
192
192
  ### 4. Run Build Commands
193
- While you *can* manually run these commands, using `theapp pack dev/watch/build` is preferred
193
+ While you *can* manually run these commands, using `theapp pack dev/watch/build` is preferred.
194
+
195
+ Typical theme/plugin scripts are `watch`, `dev`, and `build`. Which folder they write (`dist` vs `dist-dev`) and whether the compiler minifies is in the package [README](README.md#dist-vs-dist-dev) — a local `pnpm run build` is a production compile into `dist-dev`, not into `dist`.
194
196
 
195
197
  ```bash
196
- pnpm run watch # Development mode (watches for changes)
197
- pnpm run build # Production build
198
+ pnpm run watch # Watch; writes dist-dev
199
+ pnpm run build # One-shot production compile into dist-dev
198
200
  ```
199
201
 
200
202
  ---
@@ -205,8 +207,11 @@ After building, each asset gets a handle you can reference in PHP.
205
207
 
206
208
  ### Handle Format
207
209
 
208
- **Plain:** `{prefix}.{filename}`
209
- - Example: `acme-my-plugin.admin-editor`
210
+ **Plain:** `{prefix}/{filename}`
211
+ - Example: `acme-my-plugin/admin-editor`
212
+ - 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
213
+ - `view.MyThing.ts` is `acme-my-plugin/my-thing`, not `acme-my-plugin/view-my-thing`
214
+ - Because the location is dropped, two files that differ only by location cannot share a nice name
210
215
 
211
216
  **Blocks:** `{prefix}_blocks_{block-name}-{type}`
212
217
  - Example: `acme-my-plugin_blocks_hero-banner-editor-script`
@@ -231,14 +236,14 @@ After building, each asset gets a handle you can reference in PHP.
231
236
  add_action('admin_enqueue_scripts', function($hook) {
232
237
  if ($hook === 'post.php') {
233
238
  // Localize data first (optional)
234
- wp_localize_script('acme-my-plugin.admin-editor', 'myData', [
239
+ wp_localize_script('acme-my-plugin/admin-editor', 'myData', [
235
240
  'ajaxUrl' => admin_url('admin-ajax.php'),
236
241
  'nonce' => wp_create_nonce('my_ajax_action'),
237
242
  ]);
238
243
 
239
244
  // Then enqueue
240
- wp_enqueue_script('acme-my-plugin.admin-editor');
241
- wp_enqueue_style('acme-my-plugin.admin-editor');
245
+ wp_enqueue_script('acme-my-plugin/admin-editor');
246
+ wp_enqueue_style('acme-my-plugin/admin-editor');
242
247
  }
243
248
  });
244
249
  ```
@@ -277,7 +282,7 @@ src: {
277
282
  // In your plugin
278
283
  add_action('admin_enqueue_scripts', function($hook) {
279
284
  if ($hook === 'post.php') {
280
- wp_enqueue_script('acme-my-plugin.post-editor');
285
+ wp_enqueue_script('acme-my-plugin/post-editor');
281
286
  }
282
287
  });
283
288
  ```
@@ -315,7 +320,7 @@ src: {
315
320
  | `srcDir` | `string` | `""` | Source directory (**Required** on new sites) |
316
321
  | `outputDir` | `string` | `""` | Output directory (**Required** on new sites) |
317
322
  | `useUnifiedLoader` | `boolean` | `false` | Generate unified-loader.php (always set to `true` on new sites!) |
318
- | `useWebpackResourceFiltering` | `boolean` | `false` | Generates versioned copies of images, fonts, etc (always set to `true` on new sites!) |
323
+ | `useWebpackResourceFiltering` | `boolean` | `true` | Generates versioned copies of images, fonts, etc (always set to `true` on new sites!) |
319
324
  | `plainEntrypointsVersion` | `1\|2` | `1` | Use 2 for new sites |
320
325
  | `extensionsVersion` | `1\|2\|3` | `1` | Use 3 for new sites |
321
326
  | `verbose` | `boolean` | `false` | Verbose logging |
@@ -571,11 +576,11 @@ This is the form that *should* be used. It has two categories of keys: control f
571
576
 
572
577
  **Control Flags:**
573
578
 
574
- | Key | Type | Default | Description |
575
- |----------------------|--------------------------------------------------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------|
576
- | `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 |
577
- | `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 |
578
- | `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}` |
579
+ | Key | Type | Default | Description |
580
+ |----------------------|--------------------------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
581
+ | `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 |
582
+ | `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 |
583
+ | `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}` |
579
584
 
580
585
  **Location Identifiers:**
581
586
 
@@ -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;
@@ -1,7 +1,7 @@
1
- import { Compilation, Compiler } from "webpack";
1
+ import { Compilation, Compiler, type WebpackPluginInstance } from "webpack";
2
2
  import { PseudoSemaphore } from "../utils/pseduo-semaphore";
3
3
  import type { VerifiedPlauditWordpressWebpackConfig } from "../utils/common-config-helpers";
4
- import { type ParsedAssetJsonProvider, WebpackPlugin } from "../shared";
4
+ import { type ParsedAssetJsonProvider } from "../shared";
5
5
  type TapWithAssetsJsonOptions = {
6
6
  name?: string;
7
7
  stage?: (typeof Compilation.PROCESS_ASSETS_STAGE_REPORT) | (typeof Compilation.PROCESS_ASSETS_STAGE_ANALYSE);
@@ -9,7 +9,7 @@ type TapWithAssetsJsonOptions = {
9
9
  };
10
10
  type TapWithAssetsJsonFn = Parameters<Compilation['hooks']['processAssets']['tapPromise']>[1];
11
11
  type TapWithParsedAssetsJsonFn = (parsedAssetJsonProvider: ParsedAssetJsonProvider, assets: Parameters<TapWithAssetsJsonFn>[0]) => Promise<void> | void;
12
- export declare abstract class AbstractBiPhasicGroupPlugin implements WebpackPlugin {
12
+ export declare abstract class AbstractBiPhasicGroupPlugin implements WebpackPluginInstance {
13
13
  protected readonly config: VerifiedPlauditWordpressWebpackConfig;
14
14
  readonly group: string;
15
15
  private readonly semaphores;
@@ -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 {
@@ -1,5 +1,4 @@
1
- import type { Compiler } from "webpack";
2
- import type { WebpackPlugin } from "../shared";
3
- export declare class MiniCSSExtractPluginErrorCleaner implements WebpackPlugin {
1
+ import type { Compiler, WebpackPluginInstance } from "webpack";
2
+ export declare class MiniCSSExtractPluginErrorCleaner implements WebpackPluginInstance {
4
3
  apply(compiler: Compiler): void;
5
4
  }
@@ -1,5 +1,4 @@
1
- import type { Compiler } from "webpack";
2
- import type { WebpackPlugin } from "../shared";
3
- export declare class PackageConfigSanityChecker implements WebpackPlugin {
1
+ import type { Compiler, WebpackPluginInstance } from "webpack";
2
+ export declare class PackageConfigSanityChecker implements WebpackPluginInstance {
4
3
  apply(compiler: Compiler): void;
5
4
  }
@@ -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,6 @@
1
1
  import type { VerifiedPlauditWordpressWebpackConfig } from "../utils/common-config-helpers";
2
- import type { Compiler } from "webpack";
3
- import type { WebpackPlugin } from "../shared";
4
- export declare class VariablesJSMonitorPlugin implements WebpackPlugin {
2
+ import type { Compiler, WebpackPluginInstance } from "webpack";
3
+ export declare class VariablesJSMonitorPlugin implements WebpackPluginInstance {
5
4
  private readonly config;
6
5
  constructor(config: VerifiedPlauditWordpressWebpackConfig);
7
6
  apply(compiler: Compiler): void;
@@ -1,7 +1,6 @@
1
- import { type WebpackPlugin } from "../shared";
2
1
  import { AbstractBiPhasicGroupAndEntryPlugin } from "./AbstractBiPhasicGroupAndEntryPlugin";
3
- import { type Compiler } from "webpack";
4
- export declare class WPMLConfigBuilderPlugin implements WebpackPlugin {
2
+ import { type Compiler, type WebpackPluginInstance } from "webpack";
3
+ export declare class WPMLConfigBuilderPlugin implements WebpackPluginInstance {
5
4
  private readonly destDir;
6
5
  private readonly referencePlugin;
7
6
  private static jsdomInstance?;
@@ -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
  }