@plaudit/webpack-extensions 2.84.1 → 2.85.1

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,15 @@ 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.85.1] - 2026-03-06
9
+ ### Fixed
10
+ - Inlined scripts without any other path query parameters not being placed in the footer by default
11
+
12
+ ## [2.85.0] - 2026-03-05
13
+ ### Added
14
+ - Support for inlining (and otherwise controlling the enqueuing of) assets
15
+ - This only applies to plain entrypoints and assets declared within block.json files at present
16
+
8
17
  ## [2.84.1] - 2026-02-11
9
18
  ### Fixed
10
19
  - Typedef compatibility issues with Webpack 5.105.0
package/USER-GUIDE.md CHANGED
@@ -186,6 +186,10 @@ module.exports = require("@plaudit/webpack-extensions/wordpress-scripts-wrapper"
186
186
  // A script that can be enqueued via its handle, but is not autoloaded
187
187
  "site/manually-loaded.ts": {
188
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"
189
193
  }
190
194
  },
191
195
  useWebpackResourceFiltering: true, // Always use this!
@@ -373,6 +377,11 @@ src: {
373
377
  ## Per-Entrypoint Options
374
378
  This section covers the shared options for entrypoints. See the [Entrypoint Types](#entrypoint-types) section for options specific to individual types
375
379
 
380
+ ### Path Queries
381
+ - 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.
382
+ - At present, this is only being used to configure inlining, but expansion to other systems is being considered
383
+ - The available options are: `strategy`, `inline`, `in_footer`, `fetchpriority`, and `position`
384
+
376
385
  ### Boolean-form
377
386
  - This is only available if `outputDir` has been set
378
387
  - The only supported value is `true` and only works for `blocks` and `plain` entrypoints. It means that every value will have its default value
@@ -1,12 +1,14 @@
1
1
  import { AbstractBiPhasicGroupAndEntryPlugin, EntryProvider } from "./AbstractBiPhasicGroupAndEntryPlugin";
2
- import { ParsedAssetsJson, BlockEntrypointInfo, FileSegmentBlockEntrypointInfo, VerifiedAdvancedOutputConfig, ParsedAssetJsonProvider } from "../shared";
2
+ import { ParsedAssetsJson, BlockEntrypointInfo, FileSegmentBlockEntrypointInfo, VerifiedAdvancedOutputConfig, ParsedAssetJsonProvider, ScriptArgsObject, InlinedAsset } from "../shared";
3
3
  import type { VerifiedPlauditWordpressWebpackConfig } from "../utils/common-config-helpers";
4
4
  import { Compilation } from "webpack";
5
5
  import type WebpackRemoveEmptyScriptsPlugin from "webpack-remove-empty-scripts";
6
- type WorkableBlockEntrypointInfo = Omit<FileSegmentBlockEntrypointInfo, 'originalValue'> & Partial<Pick<FileSegmentBlockEntrypointInfo, 'originalValue'>> & {
6
+ type WorkableBlockEntrypointInfo = Omit<FileSegmentBlockEntrypointInfo, 'originalValue' | 'pathQueryParameters'> & Partial<Pick<FileSegmentBlockEntrypointInfo, 'originalValue'>> & {
7
7
  outputPath: string;
8
8
  assetData: ParsedAssetsJson[string];
9
9
  hash: string;
10
+ scriptArgsObject: ScriptArgsObject | undefined;
11
+ inlinedAsset: InlinedAsset | undefined;
10
12
  };
11
13
  type CollatedWorkableBlockInfo = Record<string, {
12
14
  sourcePath?: string;
@@ -13,6 +13,7 @@ const UnifiedLoaderGenerator_1 = require("./UnifiedLoaderGenerator");
13
13
  const php_writer_1 = require("@plaudit/php-writer");
14
14
  const expressions_1 = require("@plaudit/php-writer/expressions");
15
15
  const shared_1 = require("../shared");
16
+ const path_query_and_related_helpers_1 = require("../utils/path-query-and-related-helpers");
16
17
  const pseduo_semaphore_1 = require("../utils/pseduo-semaphore");
17
18
  const webpack_1 = require("webpack");
18
19
  class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.AbstractBiPhasicGroupAndEntryPlugin {
@@ -34,7 +35,6 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
34
35
  }
35
36
  const asset = [...compilation.chunkGraph.getChunkEntryModulesIterable(entrypoint.getEntrypointChunk())][0]?.originalSource();
36
37
  if (asset) {
37
- //TODO: Can we guarantee that entrypoint.name is always non-null?
38
38
  const epBlockJson = entrypoint.name + ".json";
39
39
  if (!applicableBlockJsonFiles[epBlockJson]) {
40
40
  const blockJsonText = EnhancedBlockJSONPlugin.extractAssetSource(compilation, epBlockJson)
@@ -62,20 +62,24 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
62
62
  continue;
63
63
  }
64
64
  const workableBlockEntrypointsInfo = entrypoint.getFiles()
65
- .map(file => this.stripOffBlocksDestPrefix(file))
66
- .filter(file => !this.webpackRemoveEmptyScriptsPlugin['trash'].includes(node_path_1.default.join(this.dest.destination, file)))
65
+ .filter(file => !this.webpackRemoveEmptyScriptsPlugin['trash'].includes(file))
67
66
  .map(file => [file, (0, shared_1.scriptOrStyleTest)(file, shared_1.scriptExtension)])
68
67
  .filter((item) => item[1] !== '')
69
68
  .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);
70
73
  const wasOriginallyAStyleField = (0, shared_1.isStyleField)(metadata.entrypointField);
74
+ const outputPath = this.stripOffBlocksDestPrefix(file);
71
75
  if (wasOriginallyAStyleField !== (assetType === "style")) { // This means that the file is extracted
72
76
  const entrypointField = (0, shared_1.convertEntrypointFieldForAssetType)(metadata.entrypointField, assetType);
73
77
  return {
74
- ...metadata, outputPath: file, assetData, originalValue: undefined, hash: entrypointChunk.hash ?? "",
75
- entrypointField, handleGroup: (0, shared_1.getHandleGroup)(entrypointField)
78
+ ...metadata, outputPath, assetData, originalValue: undefined, hash: entrypointChunk.hash ?? "",
79
+ entrypointField, handleGroup: (0, shared_1.getHandleGroup)(entrypointField), scriptArgsObject, inlinedAsset
76
80
  };
77
81
  }
78
- return ({ ...metadata, outputPath: file, assetData, hash: entrypointChunk.hash ?? "" });
82
+ return { ...metadata, outputPath, assetData, hash: entrypointChunk.hash ?? "", scriptArgsObject, inlinedAsset };
79
83
  });
80
84
  if (applicableBlockJsonFiles[epBlockJson]) {
81
85
  applicableBlockJsonFiles[epBlockJson].workableBlockEntrypointsInfo.push(...workableBlockEntrypointsInfo);
@@ -114,18 +118,54 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
114
118
  action
115
119
  } : undefined);
116
120
  }
117
- emitBlockLoaderFile(compilation, blockData) {
121
+ emitBlockLoaderFile(compilation, blockDirConfig) {
118
122
  if (this.config.useUnifiedLoader) {
119
- const metadata = blockData['__metadata'];
120
- delete blockData['__metadata'];
121
- (0, shared_1.emitPHPWriterAsAsset)(new php_writer_1.PHPWriter()
123
+ const { '__metadata': metadata, ...blockData } = blockDirConfig;
124
+ const splitOutInlinedAssets = (handles, type) => {
125
+ const registerableHandles = {};
126
+ const inlinedAssets = [];
127
+ for (const [handle, handleData] of Object.entries(handles)) {
128
+ const { inlinedAsset, ...finalizedHandle } = handleData;
129
+ if (inlinedAsset !== undefined) {
130
+ finalizedHandle.src = false;
131
+ inlinedAssets.push([handle, type, inlinedAsset, handleData, handleData.src]);
132
+ }
133
+ registerableHandles[handle] = finalizedHandle;
134
+ }
135
+ return [registerableHandles, inlinedAssets];
136
+ };
137
+ const [finalizedScriptHandles, inlinableScripts] = splitOutInlinedAssets(metadata.scriptHandles, 'script');
138
+ const [finalizedStyleHandles, inlinableStyles] = splitOutInlinedAssets(metadata.styleHandles, 'style');
139
+ const finalizedMetadata = Object.fromEntries(Object.entries(metadata)
140
+ .map(([key, value]) => {
141
+ if (key === 'scriptHandles') {
142
+ return [key, finalizedScriptHandles];
143
+ }
144
+ if (key === 'styleHandles') {
145
+ return [key, finalizedStyleHandles];
146
+ }
147
+ return [key, value];
148
+ }));
149
+ const writer = new php_writer_1.PHPWriter()
122
150
  .action("init", writer => {
123
- writer.call("\\Plaudit\\Common\\ACF\\BlockManager::autoloadSubfoldersV3", [expressions_1.Constants.__DIR__, new expressions_1.EnclosedLiteral((0, shared_1.makeEmittableConfigPHP)(blockData, false, "\t")),
124
- expressions_1.Constants.__FILE__, new expressions_1.EnclosedLiteral((0, shared_1.makeEmittableConfigPHP)(metadata, false, "\t")), null]);
125
- }, { accountForAlreadyDoing: this.config.includePostInitFallback }), compilation, node_path_1.default.join(this.dest.destination, "blockdir-loader.php"));
151
+ writer.call("\\Plaudit\\Common\\ACF\\BlockManager::autoloadSubfoldersV3", [
152
+ expressions_1.Constants.__DIR__, // string $dir
153
+ new expressions_1.EnclosedLiteral((0, shared_1.makeEmittableConfigPHP)(blockData, false, "\t")), // array $blockdirConfig
154
+ expressions_1.Constants.__FILE__, // string $blockdirConfigFile
155
+ new expressions_1.EnclosedLiteral((0, shared_1.makeEmittableConfigPHP)(finalizedMetadata, false, "\t")), // array $metadata
156
+ null // ?string $urlPrefix
157
+ ]);
158
+ for (const inlinableScript of inlinableScripts) {
159
+ (0, shared_1.appendAddInlineAssetCall)(compilation, writer, ...inlinableScript);
160
+ }
161
+ for (const inlinableStyle of inlinableStyles) {
162
+ (0, shared_1.appendAddInlineAssetCall)(compilation, writer, ...inlinableStyle);
163
+ }
164
+ }, { accountForAlreadyDoing: this.config.includePostInitFallback });
165
+ (0, shared_1.emitPHPWriterAsAsset)(writer, compilation, node_path_1.default.join(this.dest.destination, "blockdir-loader.php"));
126
166
  }
127
167
  else {
128
- compilation.emitAsset(node_path_1.default.join(this.dest.destination, "blockdir.config.php"), new webpack_1.sources.RawSource((0, shared_1.makeEmittableConfigPHP)(blockData, true)));
168
+ compilation.emitAsset(node_path_1.default.join(this.dest.destination, "blockdir.config.php"), new webpack_1.sources.RawSource((0, shared_1.makeEmittableConfigPHP)(blockDirConfig, true)));
129
169
  }
130
170
  }
131
171
  transformBlocks(compilation, collatedWorkableBlockInfo, emittingWpmlXml) {
@@ -173,7 +213,7 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
173
213
  .convertToScriptHandles(handleData.filter(hd => !(0, shared_1.isStyleField)(hd.entrypointField) && (0, shared_1.isScriptModuleField)(hd.entrypointField)))),
174
214
  styleHandles: Object.fromEntries(handleData.filter(hd => (0, shared_1.isStyleField)(hd.entrypointField))
175
215
  .map(hd => {
176
- return [hd.handle, { src: hd.outputPath, rest: [hd.originalValue !== undefined ? hd.assetData.dependencies : [], hd.assetData.version] }];
216
+ return [hd.handle, { src: hd.outputPath, rest: [hd.originalValue !== undefined ? hd.assetData.dependencies : [], hd.assetData.version], inlinedAsset: hd.inlinedAsset }];
177
217
  }))
178
218
  },
179
219
  ...Object.fromEntries(Object.entries(blockData).sort((a, b) => a[0].localeCompare(b[0])))
@@ -186,7 +226,20 @@ class EnhancedBlockJSONPlugin extends AbstractBiPhasicGroupAndEntryPlugin_1.Abst
186
226
  static convertToScriptHandles(handleData) {
187
227
  return handleData.map(hd => {
188
228
  const deps = hd.originalValue !== undefined ? hd.assetData.dependencies : [];
189
- return [hd.handle, { src: hd.outputPath, rest: hd.entrypointField.startsWith("editor") ? [deps, hd.assetData.version] : [deps, hd.assetData.version, { strategy: "defer" }] }];
229
+ let scriptArgsObject;
230
+ if (hd.scriptArgsObject !== undefined) {
231
+ scriptArgsObject = hd.scriptArgsObject;
232
+ }
233
+ else if (hd.inlinedAsset !== undefined) {
234
+ scriptArgsObject = hd.inlinedAsset.position === 'before' ? { strategy: "defer" } : undefined;
235
+ }
236
+ else if (!hd.entrypointField.startsWith("editor")) {
237
+ scriptArgsObject = { strategy: "defer" };
238
+ }
239
+ else {
240
+ scriptArgsObject = undefined;
241
+ }
242
+ return [hd.handle, { src: hd.outputPath, rest: scriptArgsObject !== undefined ? [deps, hd.assetData.version, scriptArgsObject] : [deps, hd.assetData.version], inlinedAsset: hd.inlinedAsset }];
190
243
  });
191
244
  }
192
245
  static doFileOrHandleReplacements(compilation, blockJson, workableBlockEntrypointsInfo, getter) {
@@ -1,5 +1,5 @@
1
1
  import { AbstractBiPhasicGroupAndEntryPlugin, EntryProvider } from "./AbstractBiPhasicGroupAndEntryPlugin";
2
- import { ParsedAssetJsonProvider } from "../shared";
2
+ import type { ParsedAssetJsonProvider } from "../shared";
3
3
  import type { VerifiedPlauditWordpressWebpackConfig } from "../utils/common-config-helpers";
4
4
  import { Compilation } from "webpack";
5
5
  export declare class ExtensionsConfigFileGeneratorPluginV1 extends AbstractBiPhasicGroupAndEntryPlugin {
@@ -9,6 +9,7 @@ const php_writer_1 = require("@plaudit/php-writer");
9
9
  const expressions_1 = require("@plaudit/php-writer/expressions");
10
10
  const shared_1 = require("../shared");
11
11
  const pseduo_semaphore_1 = require("../utils/pseduo-semaphore");
12
+ const path_query_and_related_helpers_1 = require("../utils/path-query-and-related-helpers");
12
13
  const AbstractBiPhasicGroupAndEntryPlugin_1 = require("./AbstractBiPhasicGroupAndEntryPlugin");
13
14
  const UnifiedLoaderGenerator_1 = require("./UnifiedLoaderGenerator");
14
15
  const webpack_1 = require("webpack");
@@ -29,6 +30,7 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
29
30
  const emitDir = node_path_1.default.join(this.buildRoot, this.outputDir);
30
31
  const handleLists = {
31
32
  register: [],
33
+ inline: [],
32
34
  ...Object.fromEntries((0, shared_1.constantKeys)(shared_1.standardLocationNamesMeta).map(sln => [sln, []]))
33
35
  };
34
36
  const allNamedHandles = assets
@@ -41,7 +43,7 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
41
43
  .map(handle => [handle.handleName, handle.src]))]));
42
44
  const plainEntrypointsConfig = { scriptHandles: {}, script_moduleHandles: {}, styleHandles: {} };
43
45
  for (const { handles, handlePrefix } of assets) {
44
- for (const { src, rest, locations, type, handleName, lazyLoader } of handles) {
46
+ for (const { src, rest, locations, type, handleName, lazyLoader, ...otherProps } of handles) {
45
47
  const basename = node_path_1.default.basename(src).replace(/_(?:script(?:-\d+)?\.js|style(?:-\d+)?\.css)$|(?<!_(script|style))\.(js|css)$/, "");
46
48
  let finalHandleName;
47
49
  if (typeof handleName === 'string') {
@@ -55,7 +57,7 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
55
57
  }
56
58
  handleNameMap[finalHandleName] = src;
57
59
  }
58
- plainEntrypointsConfig[`${type}Handles`][finalHandleName] = { src, rest, locations, type, lazyLoader };
60
+ plainEntrypointsConfig[`${type}Handles`][finalHandleName] = { src, rest, locations, type, lazyLoader, ...otherProps };
59
61
  }
60
62
  }
61
63
  PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists('script', Object.entries(plainEntrypointsConfig.scriptHandles)
@@ -79,7 +81,26 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
79
81
  writer.action("init", writer => {
80
82
  writer.call("plaudit_webpack_extensions__resolve_base_uri", [expressions_1.Constants.__DIR__], { assignTo: baseUriVar });
81
83
  for (const { handle, type, data } of prioritizedHandleList) {
82
- writer.call(`wp_register_${type}`, [handle, expressions_1.Op.concat(baseUriVar, node_path_1.default.relative(emitDir, data.src)), ...data.rest]);
84
+ const { inlinedAsset, rest, src } = data;
85
+ const emittedSrc = inlinedAsset === undefined
86
+ ? expressions_1.Op.concat(baseUriVar, node_path_1.default.relative(emitDir, src))
87
+ : false;
88
+ writer.call(`wp_register_${type}`, [handle, emittedSrc, ...rest]);
89
+ if (inlinedAsset !== undefined) {
90
+ (0, shared_1.appendAddInlineAssetCall)(compilation, writer, handle, type, inlinedAsset, data, src);
91
+ }
92
+ }
93
+ }, { priority, accountForAlreadyDoing: this.config.includePostInitFallback });
94
+ }
95
+ for (const [priority, prioritizedHandleList] of PlainEntrypointsConfigFileGeneratorPlugin.separateHandleListByPriority(handleLists.inline)) {
96
+ writer.action("init", writer => {
97
+ writer.call("plaudit_webpack_extensions__resolve_base_uri", [expressions_1.Constants.__DIR__], { assignTo: baseUriVar });
98
+ for (const { handle, type, data } of prioritizedHandleList) {
99
+ const { inlinedAsset, rest, src } = data;
100
+ writer.call(`wp_register_${type}`, [handle, false, ...rest]);
101
+ if (inlinedAsset !== undefined) {
102
+ (0, shared_1.appendAddInlineAssetCall)(compilation, writer, handle, type, inlinedAsset, data, src);
103
+ }
83
104
  }
84
105
  }, { priority, accountForAlreadyDoing: this.config.includePostInitFallback });
85
106
  }
@@ -127,6 +148,9 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
127
148
  if (data.locations.register !== false) {
128
149
  handleLists.register.push({ handle, type, data, priority: typeof data.locations.register === 'number' ? data.locations.register : 10 });
129
150
  }
151
+ else if (data.inlinedAsset !== undefined) {
152
+ handleLists.inline.push({ handle, type, data, priority: typeof data.locations.inline === 'number' ? data.locations.inline : 10 });
153
+ }
130
154
  for (const location of (0, shared_1.constantKeys)(shared_1.standardLocationNamesMeta)) {
131
155
  let priority, hook_name;
132
156
  const dataLocation = data.locations[location];
@@ -277,14 +301,18 @@ class PlainEntrypointsConfigFileGeneratorPlugin extends AbstractBiPhasicGroupAnd
277
301
  const type = extension === ".js" ? 'script' : (extension === ".mjs" ? 'script_module' : 'style');
278
302
  const isScript = type !== 'style';
279
303
  const dependencies = isScript === entrypointChunkIsScript ? assetData.dependencies : [];
280
- const { registerScriptArgs } = this.dest.locations;
281
- const rest = isScript && registerScriptArgs !== undefined ? [dependencies, assetData.version, registerScriptArgs] : [dependencies, assetData.version];
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));
306
+ const rest = isScript && scriptArgsObject !== undefined ? [dependencies, assetData.version, scriptArgsObject] : [dependencies, assetData.version];
282
307
  const destPath = node_path_1.default.join(compilation.outputOptions.path, file);
283
308
  handles.push({
284
- src: destPath, rest, type,
285
- locations: this.dest.locations,
286
- handleName: useHandleName ? this.dest.locations.handle : undefined,
287
- lazyLoader: this.dest.lazyLoader
309
+ src: destPath,
310
+ rest,
311
+ type,
312
+ locations,
313
+ handleName: useHandleName ? locations.handle : undefined,
314
+ lazyLoader,
315
+ inlinedAsset
288
316
  });
289
317
  }
290
318
  myAssetHandles.push({ handles, handlePrefix: this.config.targetHandlePrefix });
package/build/shared.d.ts CHANGED
@@ -6,15 +6,20 @@ export type ParsedAssetsJson = Record<string, {
6
6
  version: string;
7
7
  }>;
8
8
  export declare function isParsedAssetsJson(thing: any): thing is ParsedAssetsJson;
9
- type ScriptArgsObject = {
9
+ export type ScriptArgsObject = {
10
10
  strategy?: 'defer' | 'async';
11
11
  in_footer?: boolean;
12
12
  fetchpriority?: 'auto' | 'low' | 'high';
13
13
  };
14
14
  type BaseRestType = [/* dependencies: */ string[], /* version: */ string];
15
+ export type InlinedAsset = {
16
+ contents: string;
17
+ position?: 'before' | 'after';
18
+ };
15
19
  export type HandleData = {
16
20
  src: string;
17
- rest: BaseRestType | [...BaseRestType, ScriptArgsObject | boolean];
21
+ rest: BaseRestType | [...BaseRestType, ScriptArgsObject];
22
+ inlinedAsset?: InlinedAsset;
18
23
  };
19
24
  export declare const standardLocationNamesMeta: {
20
25
  readonly clientView: {
@@ -53,17 +58,19 @@ type LocationWithHookNameSupport = boolean | number | string | string[] | {
53
58
  hook_name?: string | string[];
54
59
  priority?: number;
55
60
  };
61
+ type InputRegisterScriptArgs = ScriptArgsObject | boolean | 'lazy' | 'eager' | 'inline';
56
62
  export type UsageLocations = {
57
63
  [K in StandardLocationNames]?: typeof standardLocationNamesMeta[K] extends {
58
64
  supports_hook_name: true;
59
65
  } ? LocationWithHookNameSupport : boolean | number;
60
66
  } & {
61
67
  register?: boolean | number;
68
+ inline?: number;
62
69
  handle?: string | ((generatedHandle: string) => string);
63
- registerScriptArgs?: ScriptArgsObject | boolean | 'lazy';
70
+ registerScriptArgs?: InputRegisterScriptArgs;
64
71
  };
65
72
  export type NormalizedUsageLocations = Omit<UsageLocations, 'registerScriptArgs'> & {
66
- registerScriptArgs?: ScriptArgsObject | boolean;
73
+ registerScriptArgs?: InputRegisterScriptArgs;
67
74
  };
68
75
  export declare function isNormalizedUsageLocations(usageLocations: UsageLocations): usageLocations is NormalizedUsageLocations;
69
76
  export declare function constantKeys<K extends string, V>(object: {
@@ -99,8 +106,9 @@ export type AdvancedOutputConfig = {
99
106
  bundleAnalyzer?: boolean;
100
107
  locations?: UsageLocations | UsageLocations['handle'];
101
108
  lazyLoader?: string;
109
+ pathQueryParameters?: PathQueryParameters;
102
110
  };
103
- type OptionalCfgFields = 'directoryLayout' | 'externalize' | 'lazyLoader';
111
+ type OptionalCfgFields = 'directoryLayout' | 'externalize' | 'lazyLoader' | 'pathQueryParameters';
104
112
  export type VerifiedAdvancedOutputConfig = Required<Omit<AdvancedOutputConfig, 'locations' | OptionalCfgFields>> & Pick<AdvancedOutputConfig, OptionalCfgFields> & {
105
113
  locations: NormalizedUsageLocations;
106
114
  };
@@ -139,7 +147,9 @@ export type FileSegmentBlockEntrypointInfo = {
139
147
  handle: string;
140
148
  dest: VerifiedAdvancedOutputConfig;
141
149
  absoluteSrc: string;
150
+ pathQueryParameters: PathQueryParameters | undefined;
142
151
  };
152
+ export type PathQueryParameters = Record<string, unknown | [unknown, ...unknown[]]>;
143
153
  export type BlockEntrypointInfo = FileSegmentBlockEntrypointInfo | {
144
154
  dest: VerifiedAdvancedOutputConfig;
145
155
  purpose: string;
@@ -163,6 +173,7 @@ export declare function scriptOrStyleTest(entryPath: string, scriptExtension: Re
163
173
  export declare function isStyleField(field: string): field is 'style' | 'viewStyle' | 'editorStyle';
164
174
  export declare function isScriptModuleField(field: string): field is 'viewScriptModule';
165
175
  export declare function getHandleGroup(field: string): 'styleHandles' | 'scriptHandles' | 'scriptModuleHandles';
176
+ export type StripFirstTwoItems<A extends any[]> = A extends [any, any, ...rest: infer R] ? R : never;
166
177
  export declare function hasAtLeastOneItem<T>(list: T[]): list is [T, ...T[]];
167
178
  export type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
168
179
  type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
@@ -187,4 +198,14 @@ export declare function getParsedAssetsJsonDataForEntrypoint(compilation: Compil
187
198
  export declare function emitPHPWriterAsAsset(writer: PHPWriter, compilation: Compilation, file: string, assetInfo?: AssetInfo): void;
188
199
  export declare function dedent(text: TemplateStringsArray): string;
189
200
  export declare function resolveLegacyBlockScriptsInFolder(folder: string): string[];
201
+ /**
202
+ * @param compilation
203
+ * @param writer
204
+ * @param handle
205
+ * @param type 'script_module' is NOT supported at this time; however, it is *handled* in this function
206
+ * @param inlinedAsset
207
+ * @param handleData
208
+ * @param file something that represents the file that is being inlined (this is purely for error-reporting purposes)
209
+ */
210
+ export declare function appendAddInlineAssetCall(compilation: Compilation, writer: PHPWriter, handle: string, type: 'script' | 'style' | 'script_module', inlinedAsset: InlinedAsset, handleData: HandleData, file: string): void;
190
211
  export {};
package/build/shared.js CHANGED
@@ -29,12 +29,14 @@ exports.getParsedAssetsJsonDataForEntrypoint = getParsedAssetsJsonDataForEntrypo
29
29
  exports.emitPHPWriterAsAsset = emitPHPWriterAsAsset;
30
30
  exports.dedent = dedent;
31
31
  exports.resolveLegacyBlockScriptsInFolder = resolveLegacyBlockScriptsInFolder;
32
+ exports.appendAddInlineAssetCall = appendAddInlineAssetCall;
32
33
  const node_fs_1 = __importDefault(require("node:fs"));
33
34
  const promises_1 = __importDefault(require("node:fs/promises"));
34
35
  const node_path_1 = __importDefault(require("node:path"));
35
36
  const php_writer_1 = require("@plaudit/php-writer");
36
37
  const expressions_1 = require("@plaudit/php-writer/expressions");
37
38
  const webpack_1 = require("webpack");
39
+ const node_crypto_1 = require("node:crypto");
38
40
  function isParsedAssetsJson(thing) {
39
41
  if (!thing || typeof thing !== 'object') {
40
42
  return false;
@@ -103,9 +105,9 @@ function leadingSlashIt(pathOrSomething) {
103
105
  return pathOrSomething.startsWith('/') ? pathOrSomething : ('/' + pathOrSomething);
104
106
  }
105
107
  exports.scriptExtension = /(?<filename>.+)(?<extension>\.m?[jt]sx?)$/i;
106
- exports.scriptWithoutModuleExtension = /(?<filename>.+)(?<extension>\.[jt]sx?)$/i;
107
- exports.scriptWithModuleExtension = /(?<filename>.+)(?<extension>\.m[jt]sx?)$/i;
108
- exports.styleExtension = /(?<filename>.+)(?<extension>\.(p?c|sa)ss)$/i;
108
+ exports.scriptWithoutModuleExtension = /(?<filename>.+)(?<extension>\.[jt]sx?)($|\?)/i;
109
+ exports.scriptWithModuleExtension = /(?<filename>.+)(?<extension>\.m[jt]sx?)($|\?)/i;
110
+ exports.styleExtension = /(?<filename>.+)(?<extension>\.(p?c|sa)ss)($|\?)/i;
109
111
  function scriptOrStyleTest(entryPath, scriptExtension) {
110
112
  return scriptExtension.test(entryPath) ? "script" : (exports.styleExtension.test(entryPath) ? "style" : "");
111
113
  }
@@ -250,3 +252,25 @@ function resolveLegacyBlockScriptsInFolder(folder) {
250
252
  }
251
253
  return blockScriptEntrypoints;
252
254
  }
255
+ /**
256
+ * @param compilation
257
+ * @param writer
258
+ * @param handle
259
+ * @param type 'script_module' is NOT supported at this time; however, it is *handled* in this function
260
+ * @param inlinedAsset
261
+ * @param handleData
262
+ * @param file something that represents the file that is being inlined (this is purely for error-reporting purposes)
263
+ */
264
+ function appendAddInlineAssetCall(compilation, writer, handle, type, inlinedAsset, handleData, file) {
265
+ if (type === 'script_module') {
266
+ throw newWebpackErrorForFile("WordPress does not support inlined script modules", file);
267
+ }
268
+ if (handleData.rest[0]?.length) { // If there are any dependencies
269
+ compilation.errors.push(newWebpackErrorForFile("Inlined scripts MUST NOT have dependencies. It WILL cause runtime inconsistencies and potentially cause sever performance issues", handleData.src));
270
+ }
271
+ const args = [handle, new expressions_1.HereOrNowDoc(`plaudit_inlined_${type}_${(0, node_crypto_1.createHash)('md5').update(handle).digest("hex")}`, inlinedAsset.contents, false)];
272
+ if (type === 'script' && inlinedAsset.position === 'before') {
273
+ args.push('before');
274
+ }
275
+ writer.call(`wp_add_inline_${type}`, args);
276
+ }
@@ -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 path_query_and_related_helpers_1 = require("./path-query-and-related-helpers");
13
14
  const shared_1 = require("../shared");
14
15
  const css_minimizer_webpack_plugin_1 = __importDefault(require("css-minimizer-webpack-plugin"));
15
16
  function joinPossiblyAbsolutePaths(...paths) {
@@ -34,7 +35,8 @@ function mapToRealEntrypoints(entrypoint, dir, supportedExtensions, args) {
34
35
  return (Array.isArray(entrypoint) ? entrypoint : [entrypoint])
35
36
  .map(ep => joinPossiblyAbsolutePaths(dir, mapper(ep)))
36
37
  .filter(ep => supportedExtensions(ep) && node_fs_1.default.statSync(ep, { throwIfNoEntry: false })?.isFile())
37
- .map(ep => {
38
+ .map(path_query_and_related_helpers_1.unpackPotentiallyPrefixedFilePath)
39
+ .map(([ep, pathQueryParameters]) => {
38
40
  const parsedEntrypoint = node_path_1.default.parse(ep);
39
41
  const entrypointField = shared_1.styleExtension.test(ep) ? 'style' : shared_1.scriptWithModuleExtension.test(ep) ? 'viewScriptModule' : 'script';
40
42
  const fakeEntrypointInfo = {
@@ -45,7 +47,8 @@ function mapToRealEntrypoints(entrypoint, dir, supportedExtensions, args) {
45
47
  handle: (0, shared_1.convertUsageLocationsHandleToEmittableHandle)(dest.locations.handle, parsedEntrypoint.name),
46
48
  handleGroup: (0, shared_1.getHandleGroup)(entrypointField),
47
49
  dest,
48
- absoluteSrc: ep
50
+ absoluteSrc: ep,
51
+ pathQueryParameters
49
52
  };
50
53
  return [joinPossiblyAbsolutePaths(dest.destination, node_path_1.default.basename(parsedEntrypoint.dir), parsedEntrypoint.name),
51
54
  { import: [ep], plauditMetadata: fakeEntrypointInfo }];
@@ -134,17 +137,19 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
134
137
  .filter(entrypointField => entrypointField in blockJson)
135
138
  .flatMap(entrypointField => {
136
139
  return (Array.isArray(blockJson[entrypointField]) ? blockJson[entrypointField] : [blockJson[entrypointField]])
137
- .filter(originalValue => originalValue.startsWith("file:"))
140
+ .filter(originalValue => typeof originalValue === 'string')
141
+ .filter(originalValue => originalValue?.startsWith("file:"))
138
142
  .map(originalValue => {
139
- const entrypointPath = originalValue.substring(5);
143
+ const [entrypointPath, localPathQueryParameters] = (0, path_query_and_related_helpers_1.unpackPotentiallyPrefixedFilePath)(originalValue);
140
144
  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);
141
146
  return promises_1.default.stat(absoluteSrc)
142
147
  .then(stats => {
143
148
  if (stats.isFile()) {
144
149
  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)));
145
150
  const extensionlessExpectedSrc = node_path_1.default.normalize(node_path_1.default.join(parsedEntrypoint.dir, parsedEntrypoint.name));
146
151
  const entrypointName = node_path_1.default.normalize(joinPossiblyAbsolutePaths(parsedEntrypoint.dir, parsedEntrypoint.name));
147
- return { entrypointField, originalValue, entrypointName, extensionlessExpectedSrc, absoluteSrc };
152
+ return { entrypointField, originalValue, entrypointName, extensionlessExpectedSrc, absoluteSrc, pathQueryParameters };
148
153
  }
149
154
  else {
150
155
  return undefined;
@@ -196,7 +201,7 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
196
201
  }
197
202
  ];
198
203
  }));
199
- rawEntrypoints.push([blockJsonChunkName, { import: [blockJsonOrigin], plauditMetadata: { purpose: "block-json-inclusion-assurance", dest, absoluteSrc: blockJsonOrigin } }]);
204
+ rawEntrypoints.push([blockJsonChunkName, { import: [blockJsonOrigin], plauditMetadata: { purpose: "block-json-inclusion-assurance", dest, absoluteSrc: blockJsonOrigin, pathQueryParameters: undefined } }]);
200
205
  wpmlFiles.push(node_path_1.default.join(dir, 'block.json'));
201
206
  }
202
207
  catch (e) {
@@ -0,0 +1,53 @@
1
+ import { Compilation } from "webpack";
2
+ import { InlinedAsset, PathQueryParameters, ScriptArgsObject, UsageLocations } from "../shared";
3
+ export declare function getAssetFileContents(compilation: Compilation, name: string): string;
4
+ export declare function unpackPotentiallyPrefixedFilePath(filePath: string): [string, PathQueryParameters | undefined];
5
+ /**
6
+ * The following substitutions are performed:
7
+ * <ul>
8
+ * <li>parameters without an assigned value -> a boolean parameter set to true</li>
9
+ * <li>true and false -> their boolean equivalents</li>
10
+ * <li>numeric values -> their number equivalents</li>
11
+ * </ul>
12
+ *
13
+ * @param pathQuery the part after the "?" in a webpack inclusion path
14
+ * @return the query as an object with common string -> JS type conversions already performed
15
+ */
16
+ export declare function unpackPathQuery(pathQuery: string): PathQueryParameters | undefined;
17
+ export type LoadingStrategy = boolean | 'defer' | 'async' | 'eager' | 'lazy' | 'inline';
18
+ export declare function isValidLoadingStrategy(strategy: any): strategy is LoadingStrategy | undefined;
19
+ export declare function newInvalidLoadingStrategyError(strategy: unknown, file: string): import("webpack").WebpackError;
20
+ export type InFooter = boolean;
21
+ export declare function isValidInFooter(in_footer: unknown): in_footer is InFooter;
22
+ export declare function newInvalidInFooterError(in_footer: unknown, file: string): import("webpack").WebpackError;
23
+ export type FetchPriority = 'auto' | 'low' | 'high';
24
+ export declare function isValidFetchPriority(fetchpriority: unknown): fetchpriority is FetchPriority;
25
+ export declare function newInvalidFetchPriorityError(fetchpriority: unknown, file: string): import("webpack").WebpackError;
26
+ export type PositionForInlineStrategy = 'before' | 'after';
27
+ export declare function isValidPositionForInlineStrategy(position: unknown): position is PositionForInlineStrategy;
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
+ /**
31
+ * This function ensures that the passed pathQueryParameters are normalized for the config-loading step, NOT the emission step
32
+ */
33
+ export type NormalizedScriptEnqueuingControlFlags = {
34
+ strategy?: Exclude<LoadingStrategy, 'inline'>;
35
+ in_footer?: InFooter;
36
+ inline?: boolean;
37
+ fetchpriority?: FetchPriority;
38
+ position?: 'before' | 'after';
39
+ };
40
+ /**
41
+ * This function does a few things:
42
+ * <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>
45
+ * </ol>
46
+ * @param compilation
47
+ * @param file
48
+ * @param pathQueryParameters
49
+ */
50
+ export declare function parseScriptArgsObjectFromPathQueryParameters(compilation: Compilation, file: string, pathQueryParameters: PathQueryParameters | undefined): {
51
+ scriptArgsObject?: ScriptArgsObject;
52
+ inlinedAsset?: InlinedAsset;
53
+ };
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAssetFileContents = getAssetFileContents;
4
+ exports.unpackPotentiallyPrefixedFilePath = unpackPotentiallyPrefixedFilePath;
5
+ exports.unpackPathQuery = unpackPathQuery;
6
+ exports.isValidLoadingStrategy = isValidLoadingStrategy;
7
+ exports.newInvalidLoadingStrategyError = newInvalidLoadingStrategyError;
8
+ exports.isValidInFooter = isValidInFooter;
9
+ exports.newInvalidInFooterError = newInvalidInFooterError;
10
+ exports.isValidFetchPriority = isValidFetchPriority;
11
+ exports.newInvalidFetchPriorityError = newInvalidFetchPriorityError;
12
+ exports.isValidPositionForInlineStrategy = isValidPositionForInlineStrategy;
13
+ exports.newInvalidPositionForInlineStrategyError = newInvalidPositionForInlineStrategyError;
14
+ exports.mergeInPathQueryParameters = mergeInPathQueryParameters;
15
+ exports.parseScriptArgsObjectFromPathQueryParameters = parseScriptArgsObjectFromPathQueryParameters;
16
+ const shared_1 = require("../shared");
17
+ const node_path_1 = require("node:path");
18
+ function getAssetFileContents(compilation, name) {
19
+ const asset = compilation.getAsset(name);
20
+ if (!asset) {
21
+ throw new Error(`${name} is unexpectedly missing`);
22
+ }
23
+ const assetSource = asset.source.source();
24
+ return typeof assetSource === 'string' ? assetSource : assetSource?.toString('utf-8');
25
+ }
26
+ function unpackPotentiallyPrefixedFilePath(filePath) {
27
+ if (filePath.startsWith("file:")) {
28
+ filePath = filePath.substring(5);
29
+ }
30
+ const queryStart = filePath.indexOf("?");
31
+ if (queryStart === -1) {
32
+ return [filePath, undefined];
33
+ }
34
+ else {
35
+ return [filePath.substring(0, queryStart), unpackPathQuery(filePath.substring(queryStart + 1))];
36
+ }
37
+ }
38
+ /**
39
+ * The following substitutions are performed:
40
+ * <ul>
41
+ * <li>parameters without an assigned value -> a boolean parameter set to true</li>
42
+ * <li>true and false -> their boolean equivalents</li>
43
+ * <li>numeric values -> their number equivalents</li>
44
+ * </ul>
45
+ *
46
+ * @param pathQuery the part after the "?" in a webpack inclusion path
47
+ * @return the query as an object with common string -> JS type conversions already performed
48
+ */
49
+ function unpackPathQuery(pathQuery) {
50
+ if (!pathQuery) {
51
+ return undefined;
52
+ }
53
+ return new URLSearchParams(pathQuery.replaceAll(/((?:[&?]|^)\w+)(&|$)/g, "$1=true$2")).entries()
54
+ .map(([key, value]) => {
55
+ if (value === 'true') {
56
+ return [key, true];
57
+ }
58
+ if (value === 'false') {
59
+ return [key, false];
60
+ }
61
+ if (Number.isFinite(+value)) {
62
+ return [key, +value];
63
+ }
64
+ return [key, value];
65
+ })
66
+ .reduce((collector, [key, value]) => {
67
+ if (collector[key] instanceof Array) {
68
+ collector[key].push(value);
69
+ }
70
+ else if (key in collector) {
71
+ collector[key] = [collector[key], value];
72
+ }
73
+ else {
74
+ collector[key] = value;
75
+ }
76
+ return collector;
77
+ }, {});
78
+ }
79
+ const namedStrategies = ['defer', 'async', 'eager', 'lazy', 'inline'];
80
+ function isValidLoadingStrategy(strategy) {
81
+ return strategy === undefined || (typeof strategy === 'string' && namedStrategies.includes(strategy));
82
+ }
83
+ function newInvalidLoadingStrategyError(strategy, file) {
84
+ return (0, shared_1.newWebpackErrorForFile)(`The strategy was invalid. Received: ${strategy}, Expected: undefined|boolean|'${namedStrategies.join("'|'")}'`, file);
85
+ }
86
+ function isValidInFooter(in_footer) {
87
+ return typeof in_footer === 'boolean';
88
+ }
89
+ function newInvalidInFooterError(in_footer, file) {
90
+ return (0, shared_1.newWebpackErrorForFile)(`The in_footer value was invalid. Received: ${in_footer}, Expected: undefined|boolean`, file);
91
+ }
92
+ function isValidFetchPriority(fetchpriority) {
93
+ return typeof fetchpriority === 'string' && ["auto", "low", "high"].includes(fetchpriority);
94
+ }
95
+ function newInvalidFetchPriorityError(fetchpriority, file) {
96
+ return (0, shared_1.newWebpackErrorForFile)(`The fetchpriority value was invalid. Received: ${fetchpriority}, Expected: undefined|'auto'|'low'|'high'`, file);
97
+ }
98
+ function isValidPositionForInlineStrategy(position) {
99
+ return typeof position === 'string' && ["before", "after"].includes(position);
100
+ }
101
+ function newInvalidPositionForInlineStrategyError(position, file) {
102
+ return (0, shared_1.newWebpackErrorForFile)(`The position value for the inlined asset was invalid. Received: ${position}, Expected: undefined|'before'|'after'`, file);
103
+ }
104
+ function mergeInPathQueryParameters(file, registerScriptArgs, pathQueryParameters) {
105
+ const baseArgs = unpackRegisterScriptArgsFromPathQueryParameters(typeof registerScriptArgs === 'object' ? registerScriptArgs : { strategy: registerScriptArgs }, file, 'registerScriptArgs');
106
+ const pathQueryArgs = unpackRegisterScriptArgsFromPathQueryParameters(pathQueryParameters, file, 'path query parameters');
107
+ if (baseArgs === undefined) {
108
+ return pathQueryArgs;
109
+ }
110
+ if (pathQueryArgs === undefined) {
111
+ return baseArgs;
112
+ }
113
+ for (const key of ['strategy', 'in_footer', 'fetchpriority', 'inline', 'position']) {
114
+ if (key in baseArgs) {
115
+ if (key in pathQueryArgs && pathQueryArgs[key] !== baseArgs[key]) {
116
+ throw (0, shared_1.newWebpackErrorForFile)(`The ${key} values in the registerScriptArgs and the pathQueryParameters for the file conflict`, file);
117
+ }
118
+ }
119
+ else if (key in pathQueryArgs) {
120
+ baseArgs[key] = pathQueryArgs[key];
121
+ }
122
+ }
123
+ return baseArgs;
124
+ }
125
+ function unpackRegisterScriptArgsFromPathQueryParameters(pathQueryParameters, file, sourceType) {
126
+ if (pathQueryParameters === undefined) {
127
+ return undefined;
128
+ }
129
+ let hasAnyKeys = false;
130
+ let baseArgs;
131
+ const strategy = pathQueryParameters['strategy'];
132
+ switch (strategy) {
133
+ case 'eager':
134
+ hasAnyKeys = true;
135
+ baseArgs = { strategy: 'eager' };
136
+ break;
137
+ case 'lazy':
138
+ hasAnyKeys = true;
139
+ baseArgs = { strategy: 'defer', in_footer: true };
140
+ break;
141
+ case 'inline':
142
+ hasAnyKeys = true;
143
+ baseArgs = { inline: true };
144
+ break;
145
+ case true:
146
+ case false:
147
+ hasAnyKeys = true;
148
+ baseArgs = { in_footer: strategy };
149
+ break;
150
+ case null:
151
+ case undefined:
152
+ baseArgs = {};
153
+ break;
154
+ default:
155
+ throw newInvalidLoadingStrategyError(strategy, file);
156
+ }
157
+ if (baseArgs.inline === undefined && pathQueryParameters['inline'] !== undefined) {
158
+ baseArgs.inline = !!pathQueryParameters['inline'];
159
+ }
160
+ const in_footer = pathQueryParameters['in_footer'] ?? pathQueryParameters['in-footer'];
161
+ if (in_footer !== undefined) {
162
+ if (!isValidInFooter(in_footer)) {
163
+ throw newInvalidInFooterError(in_footer, file);
164
+ }
165
+ hasAnyKeys = true;
166
+ baseArgs.in_footer = in_footer;
167
+ }
168
+ if (pathQueryParameters['fetchpriority'] !== undefined) {
169
+ if (!isValidFetchPriority(pathQueryParameters['fetchpriority'])) {
170
+ throw newInvalidFetchPriorityError(pathQueryParameters['fetchpriority'], file);
171
+ }
172
+ if (baseArgs.fetchpriority !== undefined && baseArgs.fetchpriority !== pathQueryParameters['fetchpriority']) {
173
+ throw (0, shared_1.newWebpackErrorForFile)(`The strategy and fetchpriority values in the ${sourceType} for the file conflict. Got ${strategy} and ${pathQueryParameters['fetchpriority']}`, file);
174
+ }
175
+ hasAnyKeys = true;
176
+ baseArgs.fetchpriority = pathQueryParameters['fetchpriority'];
177
+ }
178
+ if (pathQueryParameters['inline'] !== undefined) {
179
+ }
180
+ return hasAnyKeys ? baseArgs : undefined;
181
+ }
182
+ /**
183
+ * This function does a few things:
184
+ * <ol>
185
+ * <li>It extracts strategy, in_footer, and fetchpriority from pathQueryParameters</li>
186
+ * <li>If the strategy is, 'inline', it records the contents of the asset for later injection inline and removes the asset from the compilation output</li>
187
+ * </ol>
188
+ * @param compilation
189
+ * @param file
190
+ * @param pathQueryParameters
191
+ */
192
+ function parseScriptArgsObjectFromPathQueryParameters(compilation, file, pathQueryParameters) {
193
+ if (pathQueryParameters === undefined) {
194
+ return {};
195
+ }
196
+ const scriptArgsObject = {};
197
+ let inlinedAsset = undefined;
198
+ let hasAnyKeys = false;
199
+ const strategy = pathQueryParameters['strategy'];
200
+ switch (strategy) {
201
+ case 'async':
202
+ case 'defer':
203
+ hasAnyKeys = true;
204
+ scriptArgsObject.strategy = strategy;
205
+ break;
206
+ case 'eager':
207
+ hasAnyKeys = true;
208
+ scriptArgsObject.in_footer = false;
209
+ break;
210
+ case 'lazy':
211
+ hasAnyKeys = true;
212
+ scriptArgsObject.strategy = 'defer';
213
+ scriptArgsObject.in_footer = true;
214
+ break;
215
+ case 'inline':
216
+ inlinedAsset = { contents: getAssetFileContents(compilation, file) };
217
+ removeFileAndAssetPHP(compilation, file);
218
+ if ('inline' in pathQueryParameters) {
219
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("The inline parameter should not be set when setting strategy to 'inline'", file));
220
+ }
221
+ break;
222
+ case true:
223
+ case false:
224
+ hasAnyKeys = true;
225
+ scriptArgsObject.in_footer = strategy;
226
+ break;
227
+ case null:
228
+ case undefined:
229
+ // This is included because are effectively validating the strategy value with this switch statement, and "not defined" is a valid state
230
+ break;
231
+ default:
232
+ throw newInvalidLoadingStrategyError(strategy, file);
233
+ }
234
+ const in_footer = pathQueryParameters['in_footer'] ?? pathQueryParameters['in-footer'];
235
+ if (in_footer !== undefined) {
236
+ if (!isValidInFooter(in_footer)) {
237
+ throw newInvalidInFooterError(in_footer, file);
238
+ }
239
+ hasAnyKeys = true;
240
+ scriptArgsObject.in_footer = in_footer;
241
+ }
242
+ if (inlinedAsset === undefined && pathQueryParameters['inline']) {
243
+ inlinedAsset = { contents: getAssetFileContents(compilation, file) };
244
+ removeFileAndAssetPHP(compilation, file);
245
+ }
246
+ if (inlinedAsset !== undefined) {
247
+ if (pathQueryParameters['position'] !== undefined) {
248
+ if (!isValidPositionForInlineStrategy(pathQueryParameters['position'])) {
249
+ throw newInvalidPositionForInlineStrategyError(pathQueryParameters['position'], file);
250
+ }
251
+ inlinedAsset.position = pathQueryParameters['position'];
252
+ }
253
+ if (inlinedAsset.position !== 'before' && scriptArgsObject.strategy !== undefined) {
254
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Deferred and async scripts that have inlined JS attached in the 'after' position will cause WordPress to convert all of their dependencies to eager scripts at runtime", file));
255
+ }
256
+ if (scriptArgsObject.in_footer === undefined) {
257
+ hasAnyKeys = true;
258
+ scriptArgsObject.in_footer = true;
259
+ }
260
+ }
261
+ const fetchpriority = pathQueryParameters['fetchpriority'];
262
+ if (fetchpriority !== undefined) {
263
+ if (!isValidFetchPriority(fetchpriority)) {
264
+ throw newInvalidFetchPriorityError(fetchpriority, file);
265
+ }
266
+ if (inlinedAsset !== undefined) {
267
+ compilation.warnings.push((0, shared_1.newWebpackErrorForFile)("Fetchpriority has no effect on inlined assets", file));
268
+ }
269
+ hasAnyKeys = true;
270
+ scriptArgsObject.fetchpriority = fetchpriority;
271
+ }
272
+ return hasAnyKeys ? { scriptArgsObject, inlinedAsset } : { inlinedAsset };
273
+ }
274
+ function removeFileAndAssetPHP(compilation, file) {
275
+ const pathParts = (0, node_path_1.parse)(file);
276
+ compilation.deleteAsset((0, node_path_1.join)(pathParts.dir, pathParts.name) + ".asset.php");
277
+ compilation.deleteAsset(file);
278
+ }
@@ -25,6 +25,7 @@ const UnifiedLoaderGenerator_1 = require("./plugins/UnifiedLoaderGenerator");
25
25
  const copy_webpack_plugin_1 = __importDefault(require("copy-webpack-plugin"));
26
26
  const fork_ts_checker_webpack_plugin_1 = __importDefault(require("fork-ts-checker-webpack-plugin"));
27
27
  const webpack_remove_empty_scripts_1 = __importDefault(require("webpack-remove-empty-scripts"));
28
+ const path_query_and_related_helpers_1 = require("./utils/path-query-and-related-helpers");
28
29
  function testForDuplicatedEntryPaths(sources) {
29
30
  const seenPaths = (0, common_config_helpers_1.groupEntrypointsByAssetFile)(Array.isArray(sources)
30
31
  ? sources.map(s => typeof s === 'string' ? s : s[1].destination)
@@ -188,15 +189,16 @@ function buildVerifiedConfig(config) {
188
189
  throw new Error("Plain Entrypoints V2 and higher require 'outputDir' to be set");
189
190
  }
190
191
  }
191
- const normalizeSrcAndDestination = ([src, dest]) => {
192
+ const normalizeSrcAndDestination = ([rawSrc, dest]) => {
193
+ let [src, pathQueryParameters] = (0, path_query_and_related_helpers_1.unpackPotentiallyPrefixedFilePath)(rawSrc);
192
194
  if (srcDir) {
193
195
  src = (0, node_path_1.join)(srcDir, src);
194
196
  }
195
197
  src = (0, node_path_1.isAbsolute)(src) ? (0, node_path_1.relative)(process.cwd(), src) : src;
196
198
  if (dest.destination !== undefined && (0, node_path_1.isAbsolute)(dest.destination)) {
197
- dest = { ...dest, destination: (0, node_path_1.relative)(process.cwd(), dest.destination) };
199
+ return [src, { ...dest, destination: (0, node_path_1.relative)(process.cwd(), dest.destination), pathQueryParameters }];
198
200
  }
199
- return [src, dest];
201
+ return [src, { ...dest, pathQueryParameters }];
200
202
  };
201
203
  const rawSources = Array.isArray(config.src)
202
204
  ? config.src.map(s => normalizeSrcAndDestination([s, { destination: s }]))
@@ -221,29 +223,22 @@ function buildVerifiedConfig(config) {
221
223
  // Destination -> source map
222
224
  const allocatedDestinations = {};
223
225
  const partiallyVerifiedSources = rawSources.map(rawSource => {
224
- const { destination, additionalDependencies = [], assumeGlobalizedPlauditLibraries = cfg.assumeGlobalizedPlauditLibraries, bundleAnalyzer = false, directoryLayout, externalize, withLegacyBlocksIn = false, lazyLoader } = rawSource[1];
226
+ const { destination, additionalDependencies = [], assumeGlobalizedPlauditLibraries = cfg.assumeGlobalizedPlauditLibraries, bundleAnalyzer = false, directoryLayout, externalize, withLegacyBlocksIn = false, lazyLoader, pathQueryParameters } = rawSource[1];
225
227
  const normalizedParts = { additionalDependencies, assumeGlobalizedPlauditLibraries, bundleAnalyzer, directoryLayout, externalize, withLegacyBlocksIn, lazyLoader };
226
228
  const locations = typeof rawSource[1].locations === 'string' || typeof rawSource[1].locations === 'function'
227
229
  ? { handle: rawSource[1].locations } : rawSource[1].locations ?? {};
228
- let normalizedLocations;
229
- if ((0, shared_1.isNormalizedUsageLocations)(locations)) {
230
- normalizedLocations = locations;
231
- }
232
- else if (locations.registerScriptArgs === 'lazy') {
233
- normalizedLocations = { ...locations, registerScriptArgs: { strategy: 'defer', in_footer: true } };
234
- }
235
- else {
236
- throw new ReferenceError("The only supported string value for registerScript args is 'lazy'");
230
+ if (typeof locations.registerScriptArgs === 'string' && !['lazy', 'eager', 'inline'].includes(locations.registerScriptArgs)) {
231
+ throw new ReferenceError("The only supported string values for registerScriptArgs are 'lazy', 'eager', and 'inline'");
237
232
  }
238
233
  if (destination !== undefined) {
239
234
  const effectiveDestination = toEffectiveWebpackDestination(destination);
240
- allocatedDestinations[effectiveDestination] = rawSource[0]; // We need to pre-populate the allocatedDestinations map with statically-declared destinations
241
- return [rawSource[0], { ...normalizedParts, locations: normalizedLocations, destination, effectiveDestination, staticallyDeclaredDestination: true }];
235
+ 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 }];
242
237
  }
243
238
  else {
244
239
  const naiveDestination = deriveNaiveDestinationFromUnverifiedSourceEntry(rawSource, srcPrefixes);
245
240
  const effectiveDestination = toEffectiveWebpackDestination(naiveDestination);
246
- return [rawSource[0], { ...normalizedParts, locations: normalizedLocations, destination: naiveDestination, effectiveDestination, staticallyDeclaredDestination: false }];
241
+ return [rawSource[0], { ...normalizedParts, locations, destination: naiveDestination, effectiveDestination, staticallyDeclaredDestination: false, pathQueryParameters }];
247
242
  }
248
243
  });
249
244
  const dynamicEffectiveDestinationsWithExpectedNaiveDuplicates = partiallyVerifiedSources
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaudit/webpack-extensions",
3
- "version": "2.84.1",
3
+ "version": "2.85.1",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "files": [
6
6
  "/build",
@@ -24,9 +24,9 @@
24
24
  }
25
25
  },
26
26
  "devDependencies": {
27
- "@plaudit/gutenberg-api-extensions": "^2.85.0",
27
+ "@plaudit/gutenberg-api-extensions": "^2.87.0",
28
28
  "@types/browser-sync-webpack-plugin": "^2.2.5",
29
- "@types/node": "^25.2.3",
29
+ "@types/node": "^25.3.5",
30
30
  "@types/postcss-functions": "^4.0.4",
31
31
  "@types/tapable": "^2.3.0",
32
32
  "@types/webpack-sources": "^3.2.3",
@@ -36,22 +36,22 @@
36
36
  "webpack-bundle-analyzer": "^4.10.2"
37
37
  },
38
38
  "dependencies": {
39
- "@plaudit/php-writer": "^1.3.3",
39
+ "@plaudit/php-writer": "^1.4.1",
40
40
  "@plaudit/postcss-color-function": "^5.0.0",
41
41
  "@plaudit/postcss-legacy-shorthand": "^1.0.0",
42
42
  "@plaudit/postcss-silent-extend": "^3.0.0",
43
43
  "@plaudit/postcss-strip-units": "^3.0.0",
44
44
  "@plaudit/postcss-variables": "^1.1.0",
45
- "@wordpress/dependency-extraction-webpack-plugin": "^6.39.0",
46
- "@wordpress/scripts": "^31.4.0",
47
- "autoprefixer": "^10.4.24",
45
+ "@wordpress/dependency-extraction-webpack-plugin": "^6.41.0",
46
+ "@wordpress/scripts": "^31.6.0",
47
+ "autoprefixer": "^10.4.27",
48
48
  "browser-sync": "^3.0.4",
49
49
  "copy-webpack-plugin": "10.2.4",
50
50
  "css-minimizer-webpack-plugin": "^6.0.0",
51
51
  "fork-ts-checker-webpack-plugin": "^9.1.0",
52
52
  "http-proxy-middleware": "^3.0.5",
53
53
  "json2php": "^0.0.12",
54
- "postcss": "^8.5.6",
54
+ "postcss": "^8.5.8",
55
55
  "postcss-calc": "^9.0.1",
56
56
  "postcss-discard-comments": "^6.0.2",
57
57
  "postcss-functions": "^4.0.2",
@@ -63,7 +63,7 @@
63
63
  "postcss-reporter": "^7.1.0",
64
64
  "postcss-simple-vars": "^7.0.1",
65
65
  "postcss-url": "^10.1.3",
66
- "webpack": "^5.105.1",
66
+ "webpack": "^5.105.4",
67
67
  "webpack-remove-empty-scripts": "^1.1.1",
68
68
  "xml-formatter": "^3.6.7"
69
69
  },