@plaudit/webpack-extensions 2.85.1 → 2.86.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,19 @@ 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
+ ## [2.86.0] - 2026-03-16
9
+ ### Added
10
+ - Support for `Location-Encoding Filenames`
11
+ - Support for standardized flag sets
12
+
13
+ ## [2.85.3] - 2026-03-06
14
+ ### Fixed
15
+ - `defer` and `async` not being supported strategies in some pipelines
16
+
17
+ ## [2.85.2] - 2026-03-06
18
+ ### Fixed
19
+ - Issues caused by trying to save a couple of cycles by using `hasAnyKeys` in place of checking the actual keys
20
+
8
21
  ## [2.85.1] - 2026-03-06
9
22
  ### Fixed
10
23
  - Inlined scripts without any other path query parameters not being placed in the footer by default
package/USER-GUIDE.md CHANGED
@@ -28,7 +28,13 @@
28
28
  * [Pattern 4: Block Editor Styles](#pattern-4-block-editor-styles)
29
29
  * [Reference](#reference)
30
30
  * [Root Options](#root-options)
31
+ * [Standard Flag Sets](#standard-flag-sets)
32
+ * [Location-Encoding Filenames](#location-encoding-filenames)
33
+ * [Enqueuing Location](#enqueuing-location)
34
+ * [Examples](#examples-1)
35
+ * [Block- and Plain-compatible](#block--and-plain-compatible)
31
36
  * [Per-Entrypoint Options](#per-entrypoint-options)
37
+ * [Path Queries](#path-queries)
32
38
  * [Boolean-form](#boolean-form)
33
39
  * [String-form](#string-form)
34
40
  * [Object-form](#object-form)
@@ -137,85 +143,26 @@ The config tells webpack **what** to build and **where** to load it in WordPress
137
143
 
138
144
  ```javascript
139
145
  module.exports = require("@plaudit/webpack-extensions/wordpress-scripts-wrapper")({
140
- src: {
141
- // Build all blocks in src/blocks/
142
- "blocks": true,
143
-
144
- // Build block extensions from src/extensions/
145
- "extensions": {directoryLayout: 'extensions'},
146
-
147
- // Auto-load in the head on frontend
148
- "site/index-header.ts": {
149
- locations: {
150
- clientView: true,
151
- registerScriptArgs: false
152
- }
153
- },
154
-
155
- // Auto-load at the bottom of the body on frontend
156
- "site/index-footer.ts": {
157
- locations: {
158
- clientView: true,
159
- registerScriptArgs: 'lazy'
160
- }
161
- },
162
- // Auto-load in admin
163
- "site/wp-admin.ts": {
164
- locations: {
165
- admin: true
166
- }
167
- },
168
- // Auto-load in admin
169
- "site/wp-admin.pcss": {
170
- locations: {
171
- admin: true
172
- }
173
- },
174
- // Auto-load in the block editor
175
- "site/block-editor.pcss": {
176
- locations: {
177
- clientEditor: true
178
- }
179
- },
180
- // Auto-load in the head on frontend
181
- "site/public.pcss": {
182
- locations: {
183
- clientView: true
184
- }
185
- },
186
- // A script that can be enqueued via its handle, but is not autoloaded
187
- "site/manually-loaded.ts": {
188
- locations: "plaudit-theme/manually-loaded-script"
189
- },
190
- // A script that will be inlined into the footer
191
- "site/inlined-script.ts?inline=true": {
192
- locations: "plaudit-theme/inlined-script"
193
- }
194
- },
195
- useWebpackResourceFiltering: true, // Always use this!
196
- extensionsVersion: 3,
197
- plainEntrypointsVersion: 2,
198
- srcDir: "src", // Always use this!
199
- outputDir: "dist", // Always use this!
200
- useUnifiedLoader: true, // Always use this!
146
+ standard: '2026-03-16',
201
147
  variables: {
202
148
  font_size: 16 // Required for pxAsRem to work; replace the 16 with your site's base font size
203
149
  }
204
150
  });
205
151
  ```
152
+ The `standard` key is shorthand to the standard set of flags as of the given date. See the [Standard Flag Sets](#standard-flag-sets) section for details.
206
153
 
207
154
  ### 2. Set Up Your File Structure
208
155
 
209
156
  **Blocks** (subdirectories with block.json):
210
- - **Config:** `'blocks': true`
157
+ - **Config:** optional so long as the directory name is `blocks`; `'<dirname>': true` otherwise
211
158
  - **File Structure:** [See Here](#file-structure)
212
159
 
213
160
  **Extensions** (flat files that modify existing blocks):
214
- - **Config:** `'extensions': {directoryLayout: 'extensions'}`
161
+ - **Config:** optional so long as the directory name is `extensions`; `'<dirname>': {directoryLayout: 'extensions'}` otherwise
215
162
  - **File Structure:** [See Here](#file-structure-1)
216
163
 
217
164
  **Other Files** (scripts, styles):
218
- - **Config:** List each file individually in `src` object
165
+ - **Config:** Optional so long as you are using [Location-Encoding Filenames](#location-encoding-filenames); List each file that does not encode its own location in `src` object
219
166
  - **File Structure:** [See Here](#file-structure-2)
220
167
 
221
168
  ### 3. Include unified-loader.php in Your Plugin
@@ -269,7 +216,7 @@ After building, each asset gets a handle you can reference in PHP.
269
216
  **Best way:** Check the generated loader files after building:
270
217
  - `dist/blocks/blocks-loader.php`
271
218
  - `dist/extensions/extensions-loader.php`
272
- - The first argument of the `wp_register_script` and `wp_register_style` calls in `dist/plain-entrypoints-loader.php`
219
+ - The first argument of the `wp_register_script` and `wp_register_style` calls in `dist/plain-entrypoints-loader.php`
273
220
 
274
221
  ### Example: Manual Enqueue with Localization
275
222
 
@@ -356,21 +303,93 @@ src: {
356
303
  # Reference
357
304
 
358
305
  ## Root Options
359
- | Option | Type | Default | Description |
360
- |------------------------------------|-----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
361
- | `src` | `object` | **Required** | Files/directories to process (see the next section for what you can put in here) |
362
- | `srcDir` | `string` | `""` | Source directory (**Required** on new sites) |
363
- | `outputDir` | `string` | `""` | Output directory (**Required** on new sites) |
364
- | `useUnifiedLoader` | `boolean` | `false` | Generate unified-loader.php (always set to `true` on new sites!) |
365
- | `useWebpackResourceFiltering` | `boolean` | `false` | Generates versioned copies of images, fonts, etc (always set to `true` on new sites!) |
366
- | `plainEntrypointsVersion` | `1\|2` | `1` | Use 2 for new sites |
367
- | `extensionsVersion` | `1\|2\|3` | `1` | Use 3 for new sites |
368
- | `verbose` | `boolean` | `false` | Verbose logging |
369
- | `variables` | `object` | Auto | CSS/JS variables. If a variables.js file is present, this will automatically load from that file |
370
- | `onlyRunPostCSSOnPCSS` | `boolean` | `false` | If true, the PostCSS processor will not be run on CSS files. This is true for all new sites and can be safely enabled on most sites if desired. |
371
- | `assumeGlobalizedPlauditLibraries` | `boolean` | `true` | **[Advanced]** When `false`, normally-externalized plaudit libraries will be included.<br>**DO NOT USE THIS. IF YOU SEE IT BEING USED, CONTACT JOSH** |
372
- | `externals` | `object` | - | **[Advanced]** Allows dependencies to be marked as external. This prevents them from being included in the bundle under the assumption that they will be provided via an alternate mechanism |
373
- | `targetHandlePrefix` | `string` | Auto | **[Legacy]** Prefix for handles. Auto-detected from composer.json if omitted. Do not set this unless absolutely necessary |
306
+ | Option | Type | Default | Description |
307
+ |------------------------------------|-----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
308
+ | `standard` | `enum` | - | An identifier for the standard set of flags as of a given date. See the [Standards Flag Sets](#standard-flag-sets) section for values |
309
+ | `src` | `object` | `{}` | Files/directories to process (see the next section for what you can put in here) |
310
+ | `srcDir` | `string` | `""` | Source directory (**Required** on new sites) |
311
+ | `outputDir` | `string` | `""` | Output directory (**Required** on new sites) |
312
+ | `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!) |
314
+ | `plainEntrypointsVersion` | `1\|2` | `1` | Use 2 for new sites |
315
+ | `extensionsVersion` | `1\|2\|3` | `1` | Use 3 for new sites |
316
+ | `verbose` | `boolean` | `false` | Verbose logging |
317
+ | `variables` | `object` | Auto | CSS/JS variables. If a variables.js file is present, this will automatically load from that file |
318
+ | `onlyRunPostCSSOnPCSS` | `boolean` | `false` | If true, the PostCSS processor will not be run on CSS files. This is true for all new sites and can be safely enabled on most sites if desired. |
319
+ | `assumeGlobalizedPlauditLibraries` | `boolean` | `true` | **[Advanced]** When `false`, normally-externalized plaudit libraries will be included.<br>**DO NOT USE THIS. IF YOU SEE IT BEING USED, CONTACT JOSH** |
320
+ | `externals` | `object` | - | **[Advanced]** Allows dependencies to be marked as external. This prevents them from being included in the bundle under the assumption that they will be provided via an alternate mechanism |
321
+ | `targetHandlePrefix` | `string` | Auto | **[Legacy]** Prefix for handles. Auto-detected from composer.json if omitted. Do not set this unless absolutely necessary |
322
+
323
+ ### Standard Flag Sets
324
+ Each flag set is identified by the date that it was added to the standards set. Individual values can be overridden by specifying them in the config file
325
+
326
+ **2026-03-13**
327
+ ```javascript
328
+ {
329
+ useWebpackResourceFiltering: true,
330
+ extensionsVersion: 3,
331
+ plainEntrypointsVersion: 2,
332
+ srcDir: "src",
333
+ outputDir: "dist",
334
+ useUnifiedLoader: true,
335
+ onlyRunPostCSSOnPCSS: true,
336
+ }
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Location-Encoding Filenames
342
+ This section covers how asset emission locations can be encoded in filenames.
343
+ While every common flag is accounted for in this schema, there are some things that cannot be encoded in filenames that will require enumeration in the `webpack.config.js` file's `src` object.
344
+
345
+ Location-encoding filenames are composed of two or more `.`-separated segments (the file extension being one of those segments).
346
+ The following is a brief list of notes followed by a breakdown of the individual segments:
347
+ - The only required segments are the location and type segments (the type segment is the file extension).
348
+ - So long as there is at least one location and type segment (the type segment MUST be last), "invalid" segments are allowed (this is for the purposes of giving human-readable names to files)
349
+ - Each file can enumerate any combination enqueuing locations
350
+ - The first item is special in that, if it encodes a location, in can optionally be suffixed with a type override (see the [Enqueuing Location](#enqueuing-location) section for more details)
351
+
352
+ Notes on how to read the following table:
353
+ - Brackets in the token column mean "optional section"
354
+ - `(-|=)` means "this can be either a dash or an equal sign"
355
+ - Pipe-separated items in parentheses means "any one of these items can go here"
356
+
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) |
367
+
368
+ ### Enqueuing Location
369
+ If this is the first token, it can be suffixed with `-script`, `-style`, or `-script-module` to override the type.
370
+ Specifying the type override alone is interpreted as `both-(Type Override)` for compatibility with how blocks expect assets to be named.
371
+
372
+ Blocks do not support anything other than `view`, `editor`, and `both` (typically written as `script` or `style`).
373
+
374
+ **Valid Values**:
375
+
376
+ | Token | Meaning |
377
+ |-------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
378
+ | `view`, `client-view`, `clientView` | Enqueue the asset on the frontend (the part of the site seen by visitors) |
379
+ | `editor`, `client-editor`, `clientEditor` | Enqueue the asset in the *display* portion of the block editor |
380
+ | `both` | Shorthand for `view.editor` |
381
+ | `block-assets`, `block-assets` | Enqueue the asset in the *wrapper* portion of the block editor |
382
+ | `admin` | Enqueue the asset in the admin area |
383
+ | `login` | Enqueue the asset on the standard login page |
384
+ | `customizer` | Enqueue the asset in the legacy customizer interface |
385
+ | `analytics` | Enqueue the asset when analytics scripts have been accepted by the visitor (this "location" is unique to Plaudit's systems) |
386
+ | `register` | Register the asset *without* enqueuing it anywhere (this is primarily useful when localizing the asset) |
387
+
388
+ ### Examples
389
+ #### Block- and Plain-compatible
390
+ - **A view script**: `view.ts`
391
+ - **An editor script**: `editor.ts`
392
+ - **A view and editor script**: `script.ts`
374
393
 
375
394
  ---
376
395
 
@@ -379,6 +398,7 @@ This section covers the shared options for entrypoints. See the [Entrypoint Type
379
398
 
380
399
  ### Path Queries
381
400
  - These are URL-style queries added to the end of entrypoint *source* paths and are used to set entrypoint-specific properties in Plain and Block contexts.
401
+ - If you find yourself using these, consider switching to [Location-Encoding Filenames](#location-encoding-filenames)
382
402
  - At present, this is only being used to configure inlining, but expansion to other systems is being considered
383
403
  - The available options are: `strategy`, `inline`, `in_footer`, `fetchpriority`, and `position`
384
404
 
@@ -468,7 +488,8 @@ src/blocks/
468
488
  #### Notes
469
489
  - Blocks **should** be created with `pnpm @plaudit/scaffold create block`
470
490
  - template.php and setup.php files are automatically loaded; however, they can be manually specified if desired
471
- - Script and Style files **must** be specified using the `file:./<path here>` syntax in the appropriate keys. See [WordPress' `block.json` reference](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/) for further details
491
+ - Script and Style files **must** be specified using the `file:./<path here>[?path-query-here]` syntax in the appropriate keys. See [WordPress' `block.json` reference](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/) for further details
492
+ - The `[?path-query-here]` component is something that we added. You can see more details in its section [here](#path-queries)
472
493
 
473
494
  #### Per-Entrypoint Options
474
495
  The `blocks` entrypoint type doesn't have any.
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.EnhancedBlockJSONPlugin = void 0;
7
7
  const node_crypto_1 = __importDefault(require("node:crypto"));
8
- const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const promises_1 = require("node:fs/promises");
9
9
  const node_fs_1 = __importDefault(require("node:fs"));
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  const AbstractBiPhasicGroupAndEntryPlugin_1 = require("./AbstractBiPhasicGroupAndEntryPlugin");
@@ -37,8 +37,7 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
37
37
  if (asset) {
38
38
  const epBlockJson = entrypoint.name + ".json";
39
39
  if (!applicableBlockJsonFiles[epBlockJson]) {
40
- const blockJsonText = EnhancedBlockJSONPlugin.extractAssetSource(compilation, epBlockJson)
41
- ?? await promises_1.default.readFile(srcPath, 'utf-8');
40
+ const blockJsonText = EnhancedBlockJSONPlugin.extractAssetSource(compilation, epBlockJson) ?? await (0, promises_1.readFile)(srcPath, 'utf-8');
42
41
  if (!blockJsonText) {
43
42
  compilation.errors.push((0, shared_1.newWebpackErrorForFile)(`Unable to extract the source for ${epBlockJson}`, srcPath));
44
43
  continue;
@@ -66,10 +65,8 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
66
65
  .map(file => [file, (0, shared_1.scriptOrStyleTest)(file, shared_1.scriptExtension)])
67
66
  .filter((item) => item[1] !== '')
68
67
  .map(([file, assetType]) => {
69
- const pathQueryParameters = metadata.pathQueryParameters !== undefined || this.dest.pathQueryParameters !== undefined
70
- ? { ...metadata.pathQueryParameters, ...this.dest.pathQueryParameters }
71
- : undefined;
72
- const { scriptArgsObject, inlinedAsset } = (0, path_query_and_related_helpers_1.parseScriptArgsObjectFromPathQueryParameters)(compilation, file, pathQueryParameters);
68
+ const enqueuingFlags = (0, path_query_and_related_helpers_1.mergeTwoScriptEnqueuingControlFlagSets)(file, metadata.enqueuingFlags, this.dest.enqueuingFlags);
69
+ const { scriptArgsObject, inlinedAsset } = (0, path_query_and_related_helpers_1.convertEnqueuingControlFlagsToScriptArgsObject)(compilation, file, enqueuingFlags);
73
70
  const wasOriginallyAStyleField = (0, shared_1.isStyleField)(metadata.entrypointField);
74
71
  const outputPath = this.stripOffBlocksDestPrefix(file);
75
72
  if (wasOriginallyAStyleField !== (assetType === "style")) { // This means that the file is extracted
@@ -302,7 +302,8 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
302
302
  const isScript = type !== 'style';
303
303
  const dependencies = isScript === entrypointChunkIsScript ? assetData.dependencies : [];
304
304
  const { lazyLoader, locations } = this.dest;
305
- const { inlinedAsset, scriptArgsObject } = (0, path_query_and_related_helpers_1.parseScriptArgsObjectFromPathQueryParameters)(compilation, file, (0, path_query_and_related_helpers_1.mergeInPathQueryParameters)(file, locations.registerScriptArgs, this.dest.pathQueryParameters));
305
+ const { flags } = (0, path_query_and_related_helpers_1.unpackEnqueuingControlFlagsFromPathQueryParameters)(typeof locations.registerScriptArgs === 'object' ? locations.registerScriptArgs : { strategy: locations.registerScriptArgs }, file, "registerScriptArgs");
306
+ const { inlinedAsset, scriptArgsObject } = (0, path_query_and_related_helpers_1.convertEnqueuingControlFlagsToScriptArgsObject)(compilation, file, (0, path_query_and_related_helpers_1.mergeTwoScriptEnqueuingControlFlagSets)(file, flags, this.dest.enqueuingFlags));
306
307
  const rest = isScript && scriptArgsObject !== undefined ? [dependencies, assetData.version, scriptArgsObject] : [dependencies, assetData.version];
307
308
  const destPath = node_path_1.default.join(compilation.outputOptions.path, file);
308
309
  handles.push({
package/build/shared.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { PHPWriter } from "@plaudit/php-writer";
2
2
  import type { Options as PostcssFunctionsOptions } from "postcss-functions";
3
3
  import { AssetInfo, Compilation, Configuration, Entrypoint, WebpackError, Compiler } from "webpack";
4
+ import type { NormalizedEnqueuingControlFlags } from "./utils/path-query-and-related-helpers";
4
5
  export type ParsedAssetsJson = Record<string, {
5
6
  dependencies: string[];
6
7
  version: string;
@@ -54,16 +55,19 @@ export type StandardLocationNameMeta = {
54
55
  };
55
56
  };
56
57
  export type StandardLocationNames = keyof typeof standardLocationNamesMeta;
58
+ export declare function isStandardLocationName(name: string): name is StandardLocationNames;
57
59
  type LocationWithHookNameSupport = boolean | number | string | string[] | {
58
60
  hook_name?: string | string[];
59
61
  priority?: number;
60
62
  };
61
63
  type InputRegisterScriptArgs = ScriptArgsObject | boolean | 'lazy' | 'eager' | 'inline';
62
- export type UsageLocations = {
64
+ export declare function isRegisterScriptArgsShorthandName(name: string): name is Extract<InputRegisterScriptArgs, string>;
65
+ export type RealUsageLocations = {
63
66
  [K in StandardLocationNames]?: typeof standardLocationNamesMeta[K] extends {
64
67
  supports_hook_name: true;
65
68
  } ? LocationWithHookNameSupport : boolean | number;
66
- } & {
69
+ };
70
+ export type UsageLocations = RealUsageLocations & {
67
71
  register?: boolean | number;
68
72
  inline?: number;
69
73
  handle?: string | ((generatedHandle: string) => string);
@@ -74,7 +78,7 @@ export type NormalizedUsageLocations = Omit<UsageLocations, 'registerScriptArgs'
74
78
  };
75
79
  export declare function isNormalizedUsageLocations(usageLocations: UsageLocations): usageLocations is NormalizedUsageLocations;
76
80
  export declare function constantKeys<K extends string, V>(object: {
77
- [k in K]: V;
81
+ [k in K]?: V;
78
82
  }): K[];
79
83
  export declare function constantEntries<K extends string, V>(object: {
80
84
  [k in K]: V;
@@ -84,6 +88,7 @@ export declare const enum SourceType {
84
88
  extensions = "extensions",
85
89
  plain = "plain"
86
90
  }
91
+ export declare function isSourceType(str: string): str is SourceType;
87
92
  export declare function determineCurrentSourceType(dest: string | AdvancedOutputConfig, srcIsDirectory: boolean): SourceType;
88
93
  export interface WebpackPlugin {
89
94
  apply(compiler: Compiler): void;
@@ -107,17 +112,20 @@ export type AdvancedOutputConfig = {
107
112
  locations?: UsageLocations | UsageLocations['handle'];
108
113
  lazyLoader?: string;
109
114
  pathQueryParameters?: PathQueryParameters;
115
+ enqueuingFlags?: NormalizedEnqueuingControlFlags;
110
116
  };
111
117
  type OptionalCfgFields = 'directoryLayout' | 'externalize' | 'lazyLoader' | 'pathQueryParameters';
112
- export type VerifiedAdvancedOutputConfig = Required<Omit<AdvancedOutputConfig, 'locations' | OptionalCfgFields>> & Pick<AdvancedOutputConfig, OptionalCfgFields> & {
118
+ export type VerifiedAdvancedOutputConfig = Required<Omit<AdvancedOutputConfig, 'locations' | 'enqueuingFlags' | OptionalCfgFields>> & Pick<AdvancedOutputConfig, OptionalCfgFields> & {
119
+ enqueuingFlags: NormalizedEnqueuingControlFlags | undefined;
113
120
  locations: NormalizedUsageLocations;
114
121
  };
115
122
  export type SourcesObject = Record<string, string | AdvancedOutputConfig | boolean>;
116
123
  export type PlauditWordpressWebpackConfig = {
124
+ standard?: '2026-03-13';
117
125
  standaloneBlocks?: boolean;
118
126
  variables?: Record<string, any>;
119
127
  verbose?: boolean;
120
- src: string[] | SourcesObject;
128
+ src?: string[] | SourcesObject;
121
129
  stats?: Configuration['stats'];
122
130
  postcss?: {
123
131
  functions?: (variables: (name: string) => unknown) => PostcssFunctionsOptions['functions'];
@@ -148,6 +156,7 @@ export type FileSegmentBlockEntrypointInfo = {
148
156
  dest: VerifiedAdvancedOutputConfig;
149
157
  absoluteSrc: string;
150
158
  pathQueryParameters: PathQueryParameters | undefined;
159
+ enqueuingFlags: NormalizedEnqueuingControlFlags | undefined;
151
160
  };
152
161
  export type PathQueryParameters = Record<string, unknown | [unknown, ...unknown[]]>;
153
162
  export type BlockEntrypointInfo = FileSegmentBlockEntrypointInfo | {
package/build/shared.js CHANGED
@@ -5,9 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.styleExtension = exports.scriptWithModuleExtension = exports.scriptWithoutModuleExtension = exports.scriptExtension = exports.entrypointFields = exports.standardLocationNamesMeta = void 0;
7
7
  exports.isParsedAssetsJson = isParsedAssetsJson;
8
+ exports.isStandardLocationName = isStandardLocationName;
9
+ exports.isRegisterScriptArgsShorthandName = isRegisterScriptArgsShorthandName;
8
10
  exports.isNormalizedUsageLocations = isNormalizedUsageLocations;
9
11
  exports.constantKeys = constantKeys;
10
12
  exports.constantEntries = constantEntries;
13
+ exports.isSourceType = isSourceType;
11
14
  exports.determineCurrentSourceType = determineCurrentSourceType;
12
15
  exports.convertUsageLocationsHandleToEmittableHandle = convertUsageLocationsHandleToEmittableHandle;
13
16
  exports.makeEmittableConfigPHP = makeEmittableConfigPHP;
@@ -30,13 +33,13 @@ exports.emitPHPWriterAsAsset = emitPHPWriterAsAsset;
30
33
  exports.dedent = dedent;
31
34
  exports.resolveLegacyBlockScriptsInFolder = resolveLegacyBlockScriptsInFolder;
32
35
  exports.appendAddInlineAssetCall = appendAddInlineAssetCall;
36
+ const node_crypto_1 = require("node:crypto");
33
37
  const node_fs_1 = __importDefault(require("node:fs"));
34
38
  const promises_1 = __importDefault(require("node:fs/promises"));
35
39
  const node_path_1 = __importDefault(require("node:path"));
36
40
  const php_writer_1 = require("@plaudit/php-writer");
37
41
  const expressions_1 = require("@plaudit/php-writer/expressions");
38
42
  const webpack_1 = require("webpack");
39
- const node_crypto_1 = require("node:crypto");
40
43
  function isParsedAssetsJson(thing) {
41
44
  if (!thing || typeof thing !== 'object') {
42
45
  return false;
@@ -59,6 +62,12 @@ exports.standardLocationNamesMeta = {
59
62
  customizer: { action: "customize_controls_enqueue_scripts" },
60
63
  analytics: { action: "plaudit_enqueue_analytics" },
61
64
  };
65
+ function isStandardLocationName(name) {
66
+ return name in exports.standardLocationNamesMeta;
67
+ }
68
+ function isRegisterScriptArgsShorthandName(name) {
69
+ return ['lazy', 'eager', 'inline'].includes(name);
70
+ }
62
71
  function isNormalizedUsageLocations(usageLocations) {
63
72
  return typeof usageLocations.registerScriptArgs !== 'string';
64
73
  }
@@ -68,6 +77,9 @@ function constantKeys(object) {
68
77
  function constantEntries(object) {
69
78
  return Object.entries(object);
70
79
  }
80
+ function isSourceType(str) {
81
+ return str === "blocks" /* SourceType.blocks */ || str === "extensions" /* SourceType.extensions */ || str === "plain" /* SourceType.plain */;
82
+ }
71
83
  function determineCurrentSourceType(dest, srcIsDirectory) {
72
84
  if (typeof dest === 'string') {
73
85
  return srcIsDirectory ? "blocks" /* SourceType.blocks */ : "plain" /* SourceType.plain */;
@@ -107,7 +119,7 @@ function leadingSlashIt(pathOrSomething) {
107
119
  exports.scriptExtension = /(?<filename>.+)(?<extension>\.m?[jt]sx?)$/i;
108
120
  exports.scriptWithoutModuleExtension = /(?<filename>.+)(?<extension>\.[jt]sx?)($|\?)/i;
109
121
  exports.scriptWithModuleExtension = /(?<filename>.+)(?<extension>\.m[jt]sx?)($|\?)/i;
110
- exports.styleExtension = /(?<filename>.+)(?<extension>\.(p?c|sa)ss)($|\?)/i;
122
+ exports.styleExtension = /(?<filename>.+)(?<extension>\.([ps]?c|sa)ss)($|\?)/i;
111
123
  function scriptOrStyleTest(entryPath, scriptExtension) {
112
124
  return scriptExtension.test(entryPath) ? "script" : (exports.styleExtension.test(entryPath) ? "style" : "");
113
125
  }
@@ -3,7 +3,7 @@ import type { AdditionalDependencyInjectorPlugin } from "../plugins/AdditionalDe
3
3
  import { EntrypointFields, PlauditWordpressWebpackConfig, BlockEntrypointInfo, VerifiedAdvancedOutputConfig, MinimumViableMetadata, WebpackPlugin } from "../shared";
4
4
  import type { Compiler, Configuration, DynamicEntryPlugin, WebpackPluginInstance } from "webpack";
5
5
  import type WebpackRemoveEmptyScriptsPlugin from "webpack-remove-empty-scripts";
6
- export type VerifiedPlauditWordpressWebpackConfig = Required<Omit<PlauditWordpressWebpackConfig, 'variables' | 'src' | 'externals'>> & {
6
+ export type VerifiedPlauditWordpressWebpackConfig = Required<Omit<PlauditWordpressWebpackConfig, 'variables' | 'src' | 'externals' | 'standard'>> & {
7
7
  variablesFilePath?: string;
8
8
  currentVariables: Record<string, any>;
9
9
  } & Pick<PlauditWordpressWebpackConfig, 'externals'>;
@@ -10,6 +10,7 @@ exports.commonMakeWebpackConfig = commonMakeWebpackConfig;
10
10
  const node_fs_1 = __importDefault(require("node:fs"));
11
11
  const promises_1 = __importDefault(require("node:fs/promises"));
12
12
  const node_path_1 = __importDefault(require("node:path"));
13
+ const location_encoding_filename_parser_1 = require("./location-encoding-filename-parser");
13
14
  const path_query_and_related_helpers_1 = require("./path-query-and-related-helpers");
14
15
  const shared_1 = require("../shared");
15
16
  const css_minimizer_webpack_plugin_1 = __importDefault(require("css-minimizer-webpack-plugin"));
@@ -36,9 +37,10 @@ function mapToRealEntrypoints(entrypoint, dir, supportedExtensions, args) {
36
37
  .map(ep => joinPossiblyAbsolutePaths(dir, mapper(ep)))
37
38
  .filter(ep => supportedExtensions(ep) && node_fs_1.default.statSync(ep, { throwIfNoEntry: false })?.isFile())
38
39
  .map(path_query_and_related_helpers_1.unpackPotentiallyPrefixedFilePath)
39
- .map(([ep, pathQueryParameters]) => {
40
+ .map(([ep, rawPathQueryParameters]) => {
40
41
  const parsedEntrypoint = node_path_1.default.parse(ep);
41
42
  const entrypointField = shared_1.styleExtension.test(ep) ? 'style' : shared_1.scriptWithModuleExtension.test(ep) ? 'viewScriptModule' : 'script';
43
+ const { flags: enqueuingFlags, remainder: pathQueryParameters } = (0, path_query_and_related_helpers_1.unpackEnqueuingControlFlagsFromPathQueryParameters)(rawPathQueryParameters, ep, 'path query parameters');
42
44
  const fakeEntrypointInfo = {
43
45
  blockJsonOrigin: args.entrypointJsonOrigin,
44
46
  entrypointField,
@@ -48,7 +50,8 @@ function mapToRealEntrypoints(entrypoint, dir, supportedExtensions, args) {
48
50
  handleGroup: (0, shared_1.getHandleGroup)(entrypointField),
49
51
  dest,
50
52
  absoluteSrc: ep,
51
- pathQueryParameters
53
+ pathQueryParameters,
54
+ enqueuingFlags,
52
55
  };
53
56
  return [joinPossiblyAbsolutePaths(dest.destination, node_path_1.default.basename(parsedEntrypoint.dir), parsedEntrypoint.name),
54
57
  { import: [ep], plauditMetadata: fakeEntrypointInfo }];
@@ -133,6 +136,7 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
133
136
  const blockJsonOrigin = node_path_1.default.join(dir, 'block.json');
134
137
  const blockJson = JSON.parse(await promises_1.default.readFile(blockJsonOrigin, 'utf8'));
135
138
  const blockJsonChunkName = node_path_1.default.join(dest.destination, node_path_1.default.relative(srcRoot, dir), "block");
139
+ const filesRegisteredByJson = [];
136
140
  const presentEntrypoints = (await Promise.all(entrypointFields
137
141
  .filter(entrypointField => entrypointField in blockJson)
138
142
  .flatMap(entrypointField => {
@@ -140,16 +144,19 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
140
144
  .filter(originalValue => typeof originalValue === 'string')
141
145
  .filter(originalValue => originalValue?.startsWith("file:"))
142
146
  .map(originalValue => {
143
- const [entrypointPath, localPathQueryParameters] = (0, path_query_and_related_helpers_1.unpackPotentiallyPrefixedFilePath)(originalValue);
147
+ const [entrypointPath, rawLocalPathQueryParameters] = (0, path_query_and_related_helpers_1.unpackPotentiallyPrefixedFilePath)(originalValue);
144
148
  const absoluteSrc = node_path_1.default.normalize(node_path_1.default.join(dir, entrypointPath));
145
- const pathQueryParameters = (0, path_query_and_related_helpers_1.mergeInPathQueryParameters)(absoluteSrc, localPathQueryParameters, dest.pathQueryParameters);
149
+ filesRegisteredByJson.push(absoluteSrc);
150
+ const { flags: localEnqueuingFlags, remainder: localPathQueryParameters } = (0, path_query_and_related_helpers_1.unpackEnqueuingControlFlagsFromPathQueryParameters)(rawLocalPathQueryParameters, absoluteSrc, "path query parameters");
151
+ const enqueuingFlags = (0, path_query_and_related_helpers_1.mergeTwoScriptEnqueuingControlFlagSets)(absoluteSrc, localEnqueuingFlags, dest.enqueuingFlags);
152
+ const pathQueryParameters = { ...dest.pathQueryParameters, ...localPathQueryParameters };
146
153
  return promises_1.default.stat(absoluteSrc)
147
154
  .then(stats => {
148
155
  if (stats.isFile()) {
149
156
  const parsedEntrypoint = node_path_1.default.parse(node_path_1.default.normalize(node_path_1.default.join(dest.destination, node_path_1.default.relative(srcRoot, dir), entrypointPath)));
150
157
  const extensionlessExpectedSrc = node_path_1.default.normalize(node_path_1.default.join(parsedEntrypoint.dir, parsedEntrypoint.name));
151
158
  const entrypointName = node_path_1.default.normalize(joinPossiblyAbsolutePaths(parsedEntrypoint.dir, parsedEntrypoint.name));
152
- return { entrypointField, originalValue, entrypointName, extensionlessExpectedSrc, absoluteSrc, pathQueryParameters };
159
+ return { entrypointField, originalValue, entrypointName, extensionlessExpectedSrc, absoluteSrc, pathQueryParameters, enqueuingFlags };
153
160
  }
154
161
  else {
155
162
  return undefined;
@@ -157,6 +164,56 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
157
164
  }, () => undefined);
158
165
  });
159
166
  }))).filter(pe => pe !== undefined);
167
+ for await (const dirent of await promises_1.default.opendir(dir)) {
168
+ if (!dirent.isFile()) {
169
+ continue;
170
+ }
171
+ const absoluteSrc = node_path_1.default.normalize(node_path_1.default.join(dir, dirent.name));
172
+ if (filesRegisteredByJson.includes(absoluteSrc)) {
173
+ continue;
174
+ }
175
+ const parsedFilename = (0, location_encoding_filename_parser_1.parseLocationEncodingFilenameForBlock)(absoluteSrc);
176
+ if (!parsedFilename) {
177
+ continue;
178
+ }
179
+ const { type, locations, flags } = parsedFilename;
180
+ let entrypointField;
181
+ if (Object.keys(locations).length === 2) {
182
+ if (type === 'script-module') {
183
+ console.error(`Blocks cannot have script modules enqueued in editor mode. Found one enqueued in both editor and view in ${dirent.parentPath}. It will be enqueued in view alone.`);
184
+ entrypointField = 'viewScriptModule';
185
+ }
186
+ else {
187
+ entrypointField = type;
188
+ }
189
+ }
190
+ else {
191
+ const location = (0, shared_1.constantKeys)(locations)[0];
192
+ if (type === 'script-module') {
193
+ if (location !== 'clientView') {
194
+ console.error(`Blocks cannot have script modules enqueued in editor mode. Found one in ${dirent.parentPath}. It will be ignored`);
195
+ continue;
196
+ }
197
+ entrypointField = 'viewScriptModule';
198
+ }
199
+ else {
200
+ entrypointField = (location.substring(6).toLowerCase() + type.substring(0, 1).toUpperCase() + type.substring(1));
201
+ }
202
+ }
203
+ const parsedEntrypoint = node_path_1.default.parse(node_path_1.default.normalize(node_path_1.default.join(dest.destination, node_path_1.default.relative(srcRoot, dir), `./${dirent.name}`)));
204
+ const extensionlessExpectedSrc = node_path_1.default.normalize(node_path_1.default.join(parsedEntrypoint.dir, parsedEntrypoint.name));
205
+ const entrypointName = node_path_1.default.normalize(joinPossiblyAbsolutePaths(parsedEntrypoint.dir, parsedEntrypoint.name));
206
+ const enqueuingFlags = (0, path_query_and_related_helpers_1.mergeTwoScriptEnqueuingControlFlagSets)(absoluteSrc, flags, dest.enqueuingFlags);
207
+ presentEntrypoints.push({
208
+ entrypointField,
209
+ originalValue: dirent.name,
210
+ entrypointName,
211
+ extensionlessExpectedSrc,
212
+ absoluteSrc,
213
+ pathQueryParameters: dest.pathQueryParameters,
214
+ enqueuingFlags
215
+ });
216
+ }
160
217
  const entrypointNamesWithEffectiveDuplicates = presentEntrypoints
161
218
  .reduce((a, entry) => {
162
219
  // If it's undefined, then this is the first instance, otherwise, it's not the first instance and, therefore, is expected to be a duplicate
@@ -201,7 +258,7 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
201
258
  }
202
259
  ];
203
260
  }));
204
- rawEntrypoints.push([blockJsonChunkName, { import: [blockJsonOrigin], plauditMetadata: { purpose: "block-json-inclusion-assurance", dest, absoluteSrc: blockJsonOrigin, pathQueryParameters: undefined } }]);
261
+ rawEntrypoints.push([blockJsonChunkName, { import: [blockJsonOrigin], plauditMetadata: { purpose: "block-json-inclusion-assurance", dest, absoluteSrc: blockJsonOrigin } }]);
205
262
  wpmlFiles.push(node_path_1.default.join(dir, 'block.json'));
206
263
  }
207
264
  catch (e) {
@@ -0,0 +1,25 @@
1
+ import { RealUsageLocations } from "../shared";
2
+ import { NormalizedEnqueuingControlFlags } from "./path-query-and-related-helpers";
3
+ export type ParsedLocationEncodingFilename = {
4
+ type: 'script' | 'style' | 'script-module';
5
+ locations: RealUsageLocations & {
6
+ register?: boolean | number;
7
+ };
8
+ flags: NormalizedEnqueuingControlFlags;
9
+ };
10
+ export declare function parseLocationEncodingFilename(filename: string): ParsedLocationEncodingFilename | undefined;
11
+ export declare function parseLocationEncodingFilenameForBlock(filename: string): Omit<ParsedLocationEncodingFilename, 'locations'> & {
12
+ locations: Pick<RealUsageLocations, 'clientEditor' | 'clientView'>;
13
+ } | undefined;
14
+ /**
15
+ * This handles detecting all keys of standardLocationNamesMeta as well as "view", "client-view", "editor", "client-editor", and "block-assets".
16
+ * It MUST be kept in sync with standardLocationNamesMeta
17
+ * @param segment
18
+ */
19
+ export declare function extractLocationFromPropertyEncodingFilenameSegment(segment: string): {
20
+ readonly locationName: "clientView" | "clientEditor" | "blockAssets" | "admin" | "login" | "customizer" | "analytics" | "register";
21
+ readonly priority: number | true;
22
+ } | {
23
+ readonly locationName: false;
24
+ readonly priority?: undefined;
25
+ };
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseLocationEncodingFilename = parseLocationEncodingFilename;
4
+ exports.parseLocationEncodingFilenameForBlock = parseLocationEncodingFilenameForBlock;
5
+ exports.extractLocationFromPropertyEncodingFilenameSegment = extractLocationFromPropertyEncodingFilenameSegment;
6
+ const node_path_1 = require("node:path");
7
+ const shared_1 = require("../shared");
8
+ function parseLocationEncodingFilename(filename) {
9
+ const { name, ext } = (0, node_path_1.parse)(filename);
10
+ if (name.startsWith("~")) {
11
+ return undefined;
12
+ }
13
+ let type = /^\.(?:s[ac]|p?c)ss$/i.test(ext) ? 'style' : /^\.m[jt]sx?/i.test(ext) ? 'script-module' : /^\.[jt]sx?/i.test(ext) ? 'script' : false;
14
+ if (type === false) {
15
+ return undefined;
16
+ }
17
+ const locations = {};
18
+ const flags = {};
19
+ const nameSegments = name.split("."); //Split always returns at least one string
20
+ const typeOverrideMatch = /^(?<enqueuePosition>.+)-(?<typeOverride>script|style|script-module)$|^(?<typeOverride>script|style|script-module)$/.exec(nameSegments[0]);
21
+ if (typeOverrideMatch) {
22
+ nameSegments[0] = typeOverrideMatch.groups?.["enqueuePosition"] ?? "both"; // We override the first segment with the version without
23
+ type = typeOverrideMatch.groups?.["typeOverride"];
24
+ }
25
+ const inlinePattern = /^(?<inline>inline)(?:[-=](?<position>before|after))?$|^strategy[-=](?<inline>inline)(?:-(?<position>before|after))?$/;
26
+ const strategyPattern = /^strategy[-=](?<strategy>eager|lazy|defer|async)$|^(?<strategy>lazy|eager)$/;
27
+ const fetchpriorityPattern = /^fetchpriority[-=](auto|low|high)$/;
28
+ const bothViewAndEditorLocationPattern = /^(?:client-)?(?<both>both)(?:[-=](?<priority>-?\d+))?$/;
29
+ for (const segment of nameSegments) {
30
+ const { locationName, priority } = extractLocationFromPropertyEncodingFilenameSegment(segment);
31
+ if (locationName) {
32
+ locations[locationName] = priority;
33
+ continue;
34
+ }
35
+ const lcSegment = segment.toLowerCase();
36
+ const { both, priority: bothPriority } = bothViewAndEditorLocationPattern.exec(lcSegment)?.groups ?? {};
37
+ if (both) {
38
+ const priority = bothPriority !== undefined ? parseInt(bothPriority) : true;
39
+ locations.clientView = priority;
40
+ locations.clientEditor = priority;
41
+ continue;
42
+ }
43
+ const { inline, position: inlinePosition } = (inlinePattern.exec(lcSegment)?.groups ?? {});
44
+ if (inline) {
45
+ flags.inline = true;
46
+ flags.position = inlinePosition;
47
+ continue;
48
+ }
49
+ const strategy = strategyPattern.exec(lcSegment)?.groups?.['strategy']?.toLowerCase();
50
+ if (strategy) {
51
+ flags.strategy = strategy;
52
+ continue;
53
+ }
54
+ const fetchpriority = fetchpriorityPattern.exec(lcSegment)?.[1];
55
+ if (fetchpriority !== undefined) {
56
+ flags.fetchpriority = fetchpriority;
57
+ continue;
58
+ }
59
+ switch (segment.toLowerCase()) {
60
+ // Loading flags
61
+ case "in-footer":
62
+ case "in_footer":
63
+ flags.in_footer = true;
64
+ break;
65
+ case "in-header":
66
+ case "in_header":
67
+ flags.in_footer = false;
68
+ break;
69
+ default:
70
+ break;
71
+ }
72
+ }
73
+ // This means that it wasn't a location-encoding filename
74
+ if (Object.values(locations).length === 0) {
75
+ return undefined;
76
+ }
77
+ return { type, locations, flags };
78
+ }
79
+ function parseLocationEncodingFilenameForBlock(filename) {
80
+ const parsedFilename = parseLocationEncodingFilename(filename);
81
+ if (parsedFilename !== undefined) {
82
+ if ((0, shared_1.constantKeys)(parsedFilename.locations).some(value => value !== 'clientView' && value !== 'clientEditor')) {
83
+ throw (0, shared_1.newWebpackErrorForFile)(`Block assets cannot be enqueued anywhere other than view, editor, or both. Saw: ${(0, shared_1.constantKeys)(parsedFilename).join(", ")}`, filename);
84
+ }
85
+ if (Object.values(parsedFilename.locations).some(value => typeof value === 'number')) {
86
+ throw (0, shared_1.newWebpackErrorForFile)(`Block assets cannot be enqueued with a priority`, filename);
87
+ }
88
+ }
89
+ return parsedFilename;
90
+ }
91
+ const standardLocationNamesAndPriorityPatternInFilenameSegment = /^(?:(?<view>clientView|client-view|view)|(?<editor>clientEditor|client-editor|editor)|(?<blockAssets>blockAssets|block-assets)|(?<locationName>admin|login|customizer|analytics|register))(?:[-=](?<priority>-?\d+))?$/;
92
+ /**
93
+ * This handles detecting all keys of standardLocationNamesMeta as well as "view", "client-view", "editor", "client-editor", and "block-assets".
94
+ * It MUST be kept in sync with standardLocationNamesMeta
95
+ * @param segment
96
+ */
97
+ function extractLocationFromPropertyEncodingFilenameSegment(segment) {
98
+ const { view, editor, blockAssets, locationName, priority: rawPriority } = standardLocationNamesAndPriorityPatternInFilenameSegment.exec(segment)
99
+ ?.groups ?? {};
100
+ const priority = rawPriority !== undefined ? parseInt(rawPriority) : true;
101
+ if (view) {
102
+ return { locationName: "clientView", priority };
103
+ }
104
+ if (editor) {
105
+ return { locationName: "clientEditor", priority };
106
+ }
107
+ if (blockAssets) {
108
+ return { locationName: "blockAssets", priority };
109
+ }
110
+ if (locationName) {
111
+ return { locationName, priority };
112
+ }
113
+ return { locationName: false };
114
+ }
@@ -1,5 +1,5 @@
1
- import { Compilation } from "webpack";
2
- import { InlinedAsset, PathQueryParameters, ScriptArgsObject, UsageLocations } from "../shared";
1
+ import type { Compilation } from "webpack";
2
+ import { InlinedAsset, PathQueryParameters, ScriptArgsObject } from "../shared";
3
3
  export declare function getAssetFileContents(compilation: Compilation, name: string): string;
4
4
  export declare function unpackPotentiallyPrefixedFilePath(filePath: string): [string, PathQueryParameters | undefined];
5
5
  /**
@@ -26,28 +26,32 @@ export declare function newInvalidFetchPriorityError(fetchpriority: unknown, fil
26
26
  export type PositionForInlineStrategy = 'before' | 'after';
27
27
  export declare function isValidPositionForInlineStrategy(position: unknown): position is PositionForInlineStrategy;
28
28
  export declare function newInvalidPositionForInlineStrategyError(position: unknown, file: string): import("webpack").WebpackError;
29
- export declare function mergeInPathQueryParameters(file: string, registerScriptArgs: UsageLocations['registerScriptArgs'], pathQueryParameters: PathQueryParameters | undefined): NormalizedScriptEnqueuingControlFlags | undefined;
30
29
  /**
31
30
  * This function ensures that the passed pathQueryParameters are normalized for the config-loading step, NOT the emission step
32
31
  */
33
- export type NormalizedScriptEnqueuingControlFlags = {
32
+ export type NormalizedEnqueuingControlFlags = {
34
33
  strategy?: Exclude<LoadingStrategy, 'inline'>;
35
34
  in_footer?: InFooter;
36
35
  inline?: boolean;
37
36
  fetchpriority?: FetchPriority;
38
37
  position?: 'before' | 'after';
39
38
  };
39
+ export declare function mergeTwoScriptEnqueuingControlFlagSets(file: string, baseArgs: NormalizedEnqueuingControlFlags | undefined, ancestorArgs: NormalizedEnqueuingControlFlags | undefined): NormalizedEnqueuingControlFlags | undefined;
40
+ export declare function unpackEnqueuingControlFlagsFromPathQueryParameters(pathQueryParameters: PathQueryParameters | undefined, file: string, sourceType: 'registerScriptArgs' | 'path query parameters'): {
41
+ flags?: NormalizedEnqueuingControlFlags;
42
+ remainder?: PathQueryParameters;
43
+ };
40
44
  /**
41
45
  * This function does a few things:
42
46
  * <ol>
43
- * <li>It extracts strategy, in_footer, and fetchpriority from pathQueryParameters</li>
44
- * <li>If the strategy is, 'inline', it records the contents of the asset for later injection inline and removes the asset from the compilation output</li>
47
+ * <li>It converts the strategy, in_footer, and fetchpriority flags into a valid script args object</li>
48
+ * <li>If inline is true, it records the contents of the asset and the value of the position flag for later injection and removes the asset from the compilation output</li>
45
49
  * </ol>
46
50
  * @param compilation
47
51
  * @param file
48
- * @param pathQueryParameters
52
+ * @param flags
49
53
  */
50
- export declare function parseScriptArgsObjectFromPathQueryParameters(compilation: Compilation, file: string, pathQueryParameters: PathQueryParameters | undefined): {
54
+ export declare function convertEnqueuingControlFlagsToScriptArgsObject(compilation: Compilation, file: string, flags: NormalizedEnqueuingControlFlags | undefined): {
51
55
  scriptArgsObject?: ScriptArgsObject;
52
56
  inlinedAsset?: InlinedAsset;
53
57
  };
@@ -11,10 +11,11 @@ exports.isValidFetchPriority = isValidFetchPriority;
11
11
  exports.newInvalidFetchPriorityError = newInvalidFetchPriorityError;
12
12
  exports.isValidPositionForInlineStrategy = isValidPositionForInlineStrategy;
13
13
  exports.newInvalidPositionForInlineStrategyError = newInvalidPositionForInlineStrategyError;
14
- exports.mergeInPathQueryParameters = mergeInPathQueryParameters;
15
- exports.parseScriptArgsObjectFromPathQueryParameters = parseScriptArgsObjectFromPathQueryParameters;
16
- const shared_1 = require("../shared");
14
+ exports.mergeTwoScriptEnqueuingControlFlagSets = mergeTwoScriptEnqueuingControlFlagSets;
15
+ exports.unpackEnqueuingControlFlagsFromPathQueryParameters = unpackEnqueuingControlFlagsFromPathQueryParameters;
16
+ exports.convertEnqueuingControlFlagsToScriptArgsObject = convertEnqueuingControlFlagsToScriptArgsObject;
17
17
  const node_path_1 = require("node:path");
18
+ const shared_1 = require("../shared");
18
19
  function getAssetFileContents(compilation, name) {
19
20
  const asset = compilation.getAsset(name);
20
21
  if (!asset) {
@@ -101,127 +102,112 @@ function isValidPositionForInlineStrategy(position) {
101
102
  function newInvalidPositionForInlineStrategyError(position, file) {
102
103
  return (0, shared_1.newWebpackErrorForFile)(`The position value for the inlined asset was invalid. Received: ${position}, Expected: undefined|'before'|'after'`, file);
103
104
  }
104
- function mergeInPathQueryParameters(file, registerScriptArgs, pathQueryParameters) {
105
- const baseArgs = unpackRegisterScriptArgsFromPathQueryParameters(typeof registerScriptArgs === 'object' ? registerScriptArgs : { strategy: registerScriptArgs }, file, 'registerScriptArgs');
106
- const pathQueryArgs = unpackRegisterScriptArgsFromPathQueryParameters(pathQueryParameters, file, 'path query parameters');
105
+ function mergeTwoScriptEnqueuingControlFlagSets(file, baseArgs, ancestorArgs) {
107
106
  if (baseArgs === undefined) {
108
- return pathQueryArgs;
107
+ return ancestorArgs;
109
108
  }
110
- if (pathQueryArgs === undefined) {
109
+ if (ancestorArgs === undefined) {
111
110
  return baseArgs;
112
111
  }
112
+ // This must be kept in sync with NormalizedScriptEnqueuingControlFlags
113
113
  for (const key of ['strategy', 'in_footer', 'fetchpriority', 'inline', 'position']) {
114
114
  if (key in baseArgs) {
115
- if (key in pathQueryArgs && pathQueryArgs[key] !== baseArgs[key]) {
115
+ if (key in ancestorArgs && ancestorArgs[key] !== baseArgs[key]) {
116
116
  throw (0, shared_1.newWebpackErrorForFile)(`The ${key} values in the registerScriptArgs and the pathQueryParameters for the file conflict`, file);
117
117
  }
118
118
  }
119
- else if (key in pathQueryArgs) {
120
- baseArgs[key] = pathQueryArgs[key];
119
+ else if (key in ancestorArgs) {
120
+ baseArgs[key] = ancestorArgs[key];
121
121
  }
122
122
  }
123
123
  return baseArgs;
124
124
  }
125
- function unpackRegisterScriptArgsFromPathQueryParameters(pathQueryParameters, file, sourceType) {
125
+ function unpackEnqueuingControlFlagsFromPathQueryParameters(pathQueryParameters, file, sourceType) {
126
126
  if (pathQueryParameters === undefined) {
127
- return undefined;
127
+ return {};
128
128
  }
129
- let hasAnyKeys = false;
130
- let baseArgs;
131
- const strategy = pathQueryParameters['strategy'];
129
+ const { strategy, in_footer: normalizedInFooter, 'in-footer': alternateInFooter, fetchpriority, position, ...remainder } = pathQueryParameters;
130
+ let flags;
132
131
  switch (strategy) {
132
+ case 'defer':
133
+ case 'async':
134
+ flags = { strategy };
135
+ break;
133
136
  case 'eager':
134
- hasAnyKeys = true;
135
- baseArgs = { strategy: 'eager' };
137
+ flags = { strategy: 'eager' };
136
138
  break;
137
139
  case 'lazy':
138
- hasAnyKeys = true;
139
- baseArgs = { strategy: 'defer', in_footer: true };
140
+ flags = { strategy: 'defer', in_footer: true };
140
141
  break;
141
142
  case 'inline':
142
- hasAnyKeys = true;
143
- baseArgs = { inline: true };
143
+ flags = { inline: true };
144
144
  break;
145
145
  case true:
146
146
  case false:
147
- hasAnyKeys = true;
148
- baseArgs = { in_footer: strategy };
147
+ flags = { in_footer: strategy };
149
148
  break;
150
149
  case null:
151
150
  case undefined:
152
- baseArgs = {};
151
+ flags = {};
153
152
  break;
154
153
  default:
155
154
  throw newInvalidLoadingStrategyError(strategy, file);
156
155
  }
157
- if (baseArgs.inline === undefined && pathQueryParameters['inline'] !== undefined) {
158
- baseArgs.inline = !!pathQueryParameters['inline'];
156
+ if (flags.inline === undefined && pathQueryParameters['inline'] !== undefined) {
157
+ flags.inline = !!pathQueryParameters['inline'];
159
158
  }
160
- const in_footer = pathQueryParameters['in_footer'] ?? pathQueryParameters['in-footer'];
159
+ const in_footer = normalizedInFooter ?? alternateInFooter;
161
160
  if (in_footer !== undefined) {
162
161
  if (!isValidInFooter(in_footer)) {
163
162
  throw newInvalidInFooterError(in_footer, file);
164
163
  }
165
- hasAnyKeys = true;
166
- baseArgs.in_footer = in_footer;
164
+ flags.in_footer = in_footer;
167
165
  }
168
166
  if (pathQueryParameters['fetchpriority'] !== undefined) {
169
167
  if (!isValidFetchPriority(pathQueryParameters['fetchpriority'])) {
170
168
  throw newInvalidFetchPriorityError(pathQueryParameters['fetchpriority'], file);
171
169
  }
172
- if (baseArgs.fetchpriority !== undefined && baseArgs.fetchpriority !== pathQueryParameters['fetchpriority']) {
170
+ if (flags.fetchpriority !== undefined && flags.fetchpriority !== pathQueryParameters['fetchpriority']) {
173
171
  throw (0, shared_1.newWebpackErrorForFile)(`The strategy and fetchpriority values in the ${sourceType} for the file conflict. Got ${strategy} and ${pathQueryParameters['fetchpriority']}`, file);
174
172
  }
175
- hasAnyKeys = true;
176
- baseArgs.fetchpriority = pathQueryParameters['fetchpriority'];
173
+ flags.fetchpriority = pathQueryParameters['fetchpriority'];
177
174
  }
178
- if (pathQueryParameters['inline'] !== undefined) {
175
+ if (flags.inline && (pathQueryParameters['position'] === 'before' || pathQueryParameters['position'] === 'after')) {
176
+ flags.position = pathQueryParameters['position'];
179
177
  }
180
- return hasAnyKeys ? baseArgs : undefined;
178
+ return Object.keys(flags).length > 0 ? { flags, remainder } : { remainder };
181
179
  }
182
180
  /**
183
181
  * This function does a few things:
184
182
  * <ol>
185
- * <li>It extracts strategy, in_footer, and fetchpriority from pathQueryParameters</li>
186
- * <li>If the strategy is, 'inline', it records the contents of the asset for later injection inline and removes the asset from the compilation output</li>
183
+ * <li>It converts the strategy, in_footer, and fetchpriority flags into a valid script args object</li>
184
+ * <li>If inline is true, it records the contents of the asset and the value of the position flag for later injection and removes the asset from the compilation output</li>
187
185
  * </ol>
188
186
  * @param compilation
189
187
  * @param file
190
- * @param pathQueryParameters
188
+ * @param flags
191
189
  */
192
- function parseScriptArgsObjectFromPathQueryParameters(compilation, file, pathQueryParameters) {
193
- if (pathQueryParameters === undefined) {
190
+ function convertEnqueuingControlFlagsToScriptArgsObject(compilation, file, flags) {
191
+ if (flags === undefined) {
194
192
  return {};
195
193
  }
194
+ const { strategy, in_footer, inline, position, fetchpriority } = flags;
196
195
  const scriptArgsObject = {};
197
196
  let inlinedAsset = undefined;
198
- let hasAnyKeys = false;
199
- const strategy = pathQueryParameters['strategy'];
200
197
  switch (strategy) {
201
198
  case 'async':
202
199
  case 'defer':
203
- hasAnyKeys = true;
204
200
  scriptArgsObject.strategy = strategy;
205
201
  break;
206
202
  case 'eager':
207
- hasAnyKeys = true;
208
203
  scriptArgsObject.in_footer = false;
209
204
  break;
210
205
  case 'lazy':
211
- hasAnyKeys = true;
212
206
  scriptArgsObject.strategy = 'defer';
213
207
  scriptArgsObject.in_footer = true;
214
208
  break;
215
- case 'inline':
216
- inlinedAsset = { contents: getAssetFileContents(compilation, file) };
217
- removeFileAndAssetPHP(compilation, file);
218
- if ('inline' in pathQueryParameters) {
219
- compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("The inline parameter should not be set when setting strategy to 'inline'", file));
220
- }
221
- break;
222
209
  case true:
223
210
  case false:
224
- hasAnyKeys = true;
225
211
  scriptArgsObject.in_footer = strategy;
226
212
  break;
227
213
  case null:
@@ -231,34 +217,28 @@ function parseScriptArgsObjectFromPathQueryParameters(compilation, file, pathQue
231
217
  default:
232
218
  throw newInvalidLoadingStrategyError(strategy, file);
233
219
  }
234
- const in_footer = pathQueryParameters['in_footer'] ?? pathQueryParameters['in-footer'];
235
220
  if (in_footer !== undefined) {
236
221
  if (!isValidInFooter(in_footer)) {
237
222
  throw newInvalidInFooterError(in_footer, file);
238
223
  }
239
- hasAnyKeys = true;
240
224
  scriptArgsObject.in_footer = in_footer;
241
225
  }
242
- if (inlinedAsset === undefined && pathQueryParameters['inline']) {
226
+ if (inline) {
243
227
  inlinedAsset = { contents: getAssetFileContents(compilation, file) };
244
228
  removeFileAndAssetPHP(compilation, file);
245
- }
246
- if (inlinedAsset !== undefined) {
247
- if (pathQueryParameters['position'] !== undefined) {
248
- if (!isValidPositionForInlineStrategy(pathQueryParameters['position'])) {
249
- throw newInvalidPositionForInlineStrategyError(pathQueryParameters['position'], file);
229
+ if (position !== undefined) {
230
+ if (!isValidPositionForInlineStrategy(position)) {
231
+ throw newInvalidPositionForInlineStrategyError(position, file);
250
232
  }
251
- inlinedAsset.position = pathQueryParameters['position'];
233
+ inlinedAsset.position = position;
252
234
  }
253
235
  if (inlinedAsset.position !== 'before' && scriptArgsObject.strategy !== undefined) {
254
236
  compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Deferred and async scripts that have inlined JS attached in the 'after' position will cause WordPress to convert all of their dependencies to eager scripts at runtime", file));
255
237
  }
256
238
  if (scriptArgsObject.in_footer === undefined) {
257
- hasAnyKeys = true;
258
239
  scriptArgsObject.in_footer = true;
259
240
  }
260
241
  }
261
- const fetchpriority = pathQueryParameters['fetchpriority'];
262
242
  if (fetchpriority !== undefined) {
263
243
  if (!isValidFetchPriority(fetchpriority)) {
264
244
  throw newInvalidFetchPriorityError(fetchpriority, file);
@@ -266,10 +246,9 @@ function parseScriptArgsObjectFromPathQueryParameters(compilation, file, pathQue
266
246
  if (inlinedAsset !== undefined) {
267
247
  compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Fetchpriority has no effect on inlined assets", file));
268
248
  }
269
- hasAnyKeys = true;
270
249
  scriptArgsObject.fetchpriority = fetchpriority;
271
250
  }
272
- return hasAnyKeys ? { scriptArgsObject, inlinedAsset } : { inlinedAsset };
251
+ return Object.keys(scriptArgsObject).length > 0 ? { scriptArgsObject, inlinedAsset } : { inlinedAsset };
273
252
  }
274
253
  function removeFileAndAssetPHP(compilation, file) {
275
254
  const pathParts = (0, node_path_1.parse)(file);
@@ -7,6 +7,8 @@ const promises_1 = require("node:fs/promises");
7
7
  const node_path_1 = require("node:path");
8
8
  const shared_1 = require("./shared");
9
9
  const common_config_helpers_1 = require("./utils/common-config-helpers");
10
+ const location_encoding_filename_parser_1 = require("./utils/location-encoding-filename-parser");
11
+ const path_query_and_related_helpers_1 = require("./utils/path-query-and-related-helpers");
10
12
  const AdditionalDependencyInjectorPlugin_1 = require("./plugins/AdditionalDependencyInjectorPlugin");
11
13
  const BrowserSyncPlugin_1 = require("./plugins/BrowserSyncPlugin");
12
14
  const dependency_extraction_webpack_plugin_config_builder_1 = require("./plugins/dependency-extraction-webpack-plugin-config-builder");
@@ -25,7 +27,6 @@ const UnifiedLoaderGenerator_1 = require("./plugins/UnifiedLoaderGenerator");
25
27
  const copy_webpack_plugin_1 = __importDefault(require("copy-webpack-plugin"));
26
28
  const fork_ts_checker_webpack_plugin_1 = __importDefault(require("fork-ts-checker-webpack-plugin"));
27
29
  const webpack_remove_empty_scripts_1 = __importDefault(require("webpack-remove-empty-scripts"));
28
- const path_query_and_related_helpers_1 = require("./utils/path-query-and-related-helpers");
29
30
  function testForDuplicatedEntryPaths(sources) {
30
31
  const seenPaths = (0, common_config_helpers_1.groupEntrypointsByAssetFile)(Array.isArray(sources)
31
32
  ? sources.map(s => typeof s === 'string' ? s : s[1].destination)
@@ -164,6 +165,7 @@ function injectSupportForInliningSVGsAsStrings(rules) {
164
165
  });
165
166
  }
166
167
  function buildVerifiedConfig(config) {
168
+ config = applyStandards(config);
167
169
  const { standaloneBlocks = false, stats = 'errors-warnings', variables: rawVariables, verbose = process.argv.includes('--verbose') || process.env['VERBOSE'] === 'true', postcss = {}, externals, assumeGlobalizedPlauditLibraries = true, processTranslationConfigs = true, combineAssetMetadata = true, useWebpackResourceFiltering = true, plainEntrypointsVersion = 1, srcDir = "", useUnifiedLoader = false, includePostInitFallback = false, omitDistDev = false, onlyRunPostCSSOnPCSS = false } = config;
168
170
  let outputDir = config.outputDir ?? "";
169
171
  if (outputDir && useUnifiedLoader && !omitDistDev && (process.env['SERVER_MODE'] ?? 'development') === 'development') {
@@ -200,12 +202,57 @@ function buildVerifiedConfig(config) {
200
202
  }
201
203
  return [src, { ...dest, pathQueryParameters }];
202
204
  };
203
- const rawSources = Array.isArray(config.src)
204
- ? config.src.map(s => normalizeSrcAndDestination([s, { destination: s }]))
205
- : Object.entries(config.src)
205
+ let rawSources;
206
+ if (Array.isArray(config.src)) {
207
+ rawSources = config.src.map(s => normalizeSrcAndDestination([s, { destination: s }]));
208
+ }
209
+ else {
210
+ const configSrc = config.src ?? {};
211
+ // We check for files with location-encoding filenames and add the pertinent ones to the sources
212
+ const dynamicallyIncludedEntrypoints = [];
213
+ if (srcDir) { // This will only work if we have a unified src root
214
+ const dynamicallyIncludedEntrypointsRoot = (0, node_path_1.isAbsolute)(srcDir) ? srcDir : (0, node_path_1.join)(process.cwd(), srcDir);
215
+ using dir = (0, node_fs_1.opendirSync)(dynamicallyIncludedEntrypointsRoot);
216
+ for (let dirent; (dirent = dir.readSync()) !== null;) {
217
+ if (dirent.name in configSrc || `./${dirent.name}` in configSrc) {
218
+ continue;
219
+ }
220
+ if (dirent.isDirectory()) {
221
+ // If it's a directory that does not have an existing config entry, but does correspond to one of the non-plain SourceTypes, assume that it matches that source type
222
+ if ((0, shared_1.isSourceType)(dirent.name) && dirent.name !== "plain" /* SourceType.plain */) {
223
+ dynamicallyIncludedEntrypoints.push([dirent.name, { directoryLayout: dirent.name }]);
224
+ continue;
225
+ }
226
+ using nestedDir = (0, node_fs_1.opendirSync)((0, node_path_1.join)(dynamicallyIncludedEntrypointsRoot, dirent.name));
227
+ for (let nestedDirent; (nestedDirent = nestedDir.readSync()) !== null;) {
228
+ if (!nestedDirent.isFile()) {
229
+ continue;
230
+ }
231
+ const cfgName = (0, node_path_1.join)(dirent.name, nestedDirent.name);
232
+ if (cfgName in configSrc || `./${cfgName}` in configSrc) {
233
+ continue;
234
+ }
235
+ const parsedFilename = (0, location_encoding_filename_parser_1.parseLocationEncodingFilename)(cfgName);
236
+ if (parsedFilename === undefined) {
237
+ continue;
238
+ }
239
+ dynamicallyIncludedEntrypoints.push([cfgName, { locations: parsedFilename.locations, enqueuingFlags: parsedFilename.flags }]);
240
+ }
241
+ }
242
+ else if (dirent.isFile()) {
243
+ const parsedFilename = (0, location_encoding_filename_parser_1.parseLocationEncodingFilename)(dirent.name);
244
+ if (parsedFilename === undefined) {
245
+ continue;
246
+ }
247
+ dynamicallyIncludedEntrypoints.push([dirent.name, { locations: parsedFilename.locations, enqueuingFlags: parsedFilename.flags }]);
248
+ }
249
+ }
250
+ }
251
+ rawSources = [...Object.entries(configSrc), ...dynamicallyIncludedEntrypoints]
206
252
  .map(([k, v]) => {
207
253
  return normalizeSrcAndDestination([k, typeof v === 'boolean' ? {} : (typeof v === 'string' ? { destination: v } : v)]);
208
254
  });
255
+ }
209
256
  let variablesFilePath = undefined;
210
257
  const currentVariables = rawVariables ?? {};
211
258
  if (!rawVariables) {
@@ -223,7 +270,9 @@ function buildVerifiedConfig(config) {
223
270
  // Destination -> source map
224
271
  const allocatedDestinations = {};
225
272
  const partiallyVerifiedSources = rawSources.map(rawSource => {
226
- const { destination, additionalDependencies = [], assumeGlobalizedPlauditLibraries = cfg.assumeGlobalizedPlauditLibraries, bundleAnalyzer = false, directoryLayout, externalize, withLegacyBlocksIn = false, lazyLoader, pathQueryParameters } = rawSource[1];
273
+ const { destination, additionalDependencies = [], assumeGlobalizedPlauditLibraries = cfg.assumeGlobalizedPlauditLibraries, bundleAnalyzer = false, directoryLayout, externalize, withLegacyBlocksIn = false, lazyLoader, pathQueryParameters: rawPathQueryParameters, enqueuingFlags: rawEnqueuingFlags } = rawSource[1];
274
+ const { flags: queryEnqueuingFlags, remainder: pathQueryParameters } = (0, path_query_and_related_helpers_1.unpackEnqueuingControlFlagsFromPathQueryParameters)(rawPathQueryParameters, rawSource[0], 'path query parameters');
275
+ const enqueuingFlags = (0, path_query_and_related_helpers_1.mergeTwoScriptEnqueuingControlFlagSets)(rawSource[0], rawEnqueuingFlags, queryEnqueuingFlags);
227
276
  const normalizedParts = { additionalDependencies, assumeGlobalizedPlauditLibraries, bundleAnalyzer, directoryLayout, externalize, withLegacyBlocksIn, lazyLoader };
228
277
  const locations = typeof rawSource[1].locations === 'string' || typeof rawSource[1].locations === 'function'
229
278
  ? { handle: rawSource[1].locations } : rawSource[1].locations ?? {};
@@ -233,12 +282,12 @@ function buildVerifiedConfig(config) {
233
282
  if (destination !== undefined) {
234
283
  const effectiveDestination = toEffectiveWebpackDestination(destination);
235
284
  allocatedDestinations[effectiveDestination] = rawSource[0]; // We need to pre-populate the allocatedDestinations map with statically declared destinations
236
- return [rawSource[0], { ...normalizedParts, locations, destination, effectiveDestination, staticallyDeclaredDestination: true, pathQueryParameters }];
285
+ return [rawSource[0], { ...normalizedParts, locations, destination, effectiveDestination, staticallyDeclaredDestination: true, pathQueryParameters, enqueuingFlags }];
237
286
  }
238
287
  else {
239
288
  const naiveDestination = deriveNaiveDestinationFromUnverifiedSourceEntry(rawSource, srcPrefixes);
240
289
  const effectiveDestination = toEffectiveWebpackDestination(naiveDestination);
241
- return [rawSource[0], { ...normalizedParts, locations, destination: naiveDestination, effectiveDestination, staticallyDeclaredDestination: false, pathQueryParameters }];
290
+ return [rawSource[0], { ...normalizedParts, locations, destination: naiveDestination, effectiveDestination, staticallyDeclaredDestination: false, pathQueryParameters, enqueuingFlags }];
242
291
  }
243
292
  });
244
293
  const dynamicEffectiveDestinationsWithExpectedNaiveDuplicates = partiallyVerifiedSources
@@ -253,6 +302,25 @@ function buildVerifiedConfig(config) {
253
302
  testForDuplicatedEntryPaths(sources);
254
303
  return cfg.outputDir ? { cfg, sources } : withDerivedOutputDir(cfg, sources);
255
304
  }
305
+ function applyStandards(config) {
306
+ switch (config.standard) {
307
+ case '2026-03-13':
308
+ return {
309
+ useWebpackResourceFiltering: true,
310
+ extensionsVersion: 3,
311
+ plainEntrypointsVersion: 2,
312
+ srcDir: "src",
313
+ outputDir: "dist",
314
+ useUnifiedLoader: true,
315
+ onlyRunPostCSSOnPCSS: true,
316
+ ...config
317
+ };
318
+ case undefined:
319
+ return config;
320
+ default:
321
+ throw `Invalid standard: ${config.standard}`;
322
+ }
323
+ }
256
324
  function toEffectiveWebpackDestination(destination) {
257
325
  const pathParts = (0, node_path_1.parse)(destination);
258
326
  return (0, node_path_1.join)(pathParts.dir, pathParts.name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaudit/webpack-extensions",
3
- "version": "2.85.1",
3
+ "version": "2.86.0",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "files": [
6
6
  "/build",
@@ -26,7 +26,7 @@
26
26
  "devDependencies": {
27
27
  "@plaudit/gutenberg-api-extensions": "^2.87.0",
28
28
  "@types/browser-sync-webpack-plugin": "^2.2.5",
29
- "@types/node": "^25.3.5",
29
+ "@types/node": "^25.5.0",
30
30
  "@types/postcss-functions": "^4.0.4",
31
31
  "@types/tapable": "^2.3.0",
32
32
  "@types/webpack-sources": "^3.2.3",
@@ -65,7 +65,7 @@
65
65
  "postcss-url": "^10.1.3",
66
66
  "webpack": "^5.105.4",
67
67
  "webpack-remove-empty-scripts": "^1.1.1",
68
- "xml-formatter": "^3.6.7"
68
+ "xml-formatter": "^3.7.0"
69
69
  },
70
70
  "engines": {
71
71
  "node": ">=20"