@plaudit/webpack-extensions 3.15.1 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/shared.js CHANGED
@@ -28,7 +28,6 @@ exports.determineCurrentSourceType = determineCurrentSourceType;
28
28
  exports.applyStandards = applyStandards;
29
29
  exports.resolveStandard = resolveStandard;
30
30
  exports.convertUsageLocationsHandleToEmittableHandle = convertUsageLocationsHandleToEmittableHandle;
31
- exports.convertUsageLocationsHandleToEmittableHandleSuffix = convertUsageLocationsHandleToEmittableHandleSuffix;
32
31
  exports.makeEmittableConfigPHP = makeEmittableConfigPHP;
33
32
  exports.convertEntrypointFieldForAssetType = convertEntrypointFieldForAssetType;
34
33
  exports.leadingSlashIt = leadingSlashIt;
@@ -55,6 +54,7 @@ exports.appendAddInlineAssetCall = appendAddInlineAssetCall;
55
54
  exports.wrapInlineAssetContents = wrapInlineAssetContents;
56
55
  exports.synchronousDirIterator = synchronousDirIterator;
57
56
  exports.instanceOfNodeError = instanceOfNodeError;
57
+ exports.shouldBeSkipped = shouldBeSkipped;
58
58
  const node_crypto_1 = require("node:crypto");
59
59
  const node_fs_1 = require("node:fs");
60
60
  const promises_1 = require("node:fs/promises");
@@ -86,7 +86,7 @@ exports.standardLocationNamesMeta = {
86
86
  analytics: { action: "plaudit_enqueue_analytics" },
87
87
  };
88
88
  function isStandardLocationName(name) {
89
- return name in exports.standardLocationNamesMeta;
89
+ return Object.hasOwn(exports.standardLocationNamesMeta, name);
90
90
  }
91
91
  function isRegisterScriptArgsShorthandName(name) {
92
92
  return ['lazy', 'eager', 'inline'].includes(name);
@@ -145,37 +145,23 @@ function resolveStandard(standard) {
145
145
  return undefined;
146
146
  }
147
147
  }
148
- function convertUsageLocationsHandleToEmittableHandle(handle, handlePrefix, generatedNiceName) {
148
+ function convertUsageLocationsHandleToEmittableHandle(handle, handlePrefix, handleSuffix) {
149
+ const generatedHandle = `${handlePrefix}/${handleSuffix}`;
149
150
  let emittableHandle;
150
151
  if (!handle) {
151
- return `${handlePrefix}/${generatedNiceName}`;
152
+ return generatedHandle;
152
153
  }
153
154
  if (typeof handle === 'string') {
154
155
  emittableHandle = handle;
155
156
  }
156
157
  else if (typeof handle === 'function') {
157
- emittableHandle = handle(`${handlePrefix}/${generatedNiceName}`);
158
+ emittableHandle = handle(generatedHandle);
158
159
  }
159
160
  else {
160
- emittableHandle = `${handlePrefix}/${handle.niceName}`;
161
+ emittableHandle = handle.niceName && `${handlePrefix}/${handle.niceName}`;
161
162
  }
162
- return emittableHandle.replaceAll("{basename}", generatedNiceName);
163
- }
164
- function convertUsageLocationsHandleToEmittableHandleSuffix(handle, generatedNiceName) {
165
- let emittableHandle;
166
- if (!handle) {
167
- return generatedNiceName;
168
- }
169
- if (typeof handle === 'string') {
170
- emittableHandle = handle;
171
- }
172
- else if (typeof handle === 'function') {
173
- emittableHandle = handle(generatedNiceName);
174
- }
175
- else {
176
- emittableHandle = handle.niceName;
177
- }
178
- return emittableHandle.replaceAll("{basename}", generatedNiceName);
163
+ // A callback is free to return nothing, which means "use the handle you would have generated"
164
+ return emittableHandle ? emittableHandle.replaceAll("{basename}", handleSuffix) : generatedHandle;
179
165
  }
180
166
  function makeEmittableConfigPHP(data, asFullFile, parentIndent = "") {
181
167
  const prettyPrintedMetadata = php_writer_1.json2php.make({ indent: "\t", linebreak: "\n", shortArraySyntax: true })(data, parentIndent)
@@ -312,18 +298,10 @@ function emitResolveBaseUriFunction(writer) {
312
298
  }
313
299
  function getAssetsJson(compilation) {
314
300
  const asset = compilation.getAsset("assets.json");
315
- if (!asset) {
316
- throw new Error("assets.json is unexpectedly missing");
317
- }
318
- if (isParsedAssetsJson(asset.info["parsed-assets-json"])) {
301
+ if (asset && isParsedAssetsJson(asset.info["parsed-assets-json"])) {
319
302
  return asset.info["parsed-assets-json"];
320
303
  }
321
- const parsedAssetJson = JSON.parse(asset.source.source().toString('utf-8'));
322
- if (!isParsedAssetsJson(parsedAssetJson)) {
323
- throw new Error("assets.json is does not match the RawAssetData format");
324
- }
325
- compilation.updateAsset("assets.json", s => s, a => ({ ...a, "parsed-assets-json": parsedAssetJson }));
326
- return parsedAssetJson;
304
+ throw new Error("assets.json is unexpectedly missing");
327
305
  }
328
306
  function getParsedAssetsJsonDataForEntrypoint(compilation, entrypoint, metadata) {
329
307
  const dest = metadata.dest;
@@ -363,13 +341,24 @@ function emitPHPWriterAsAsset(writer, compilation, file, assetInfo) {
363
341
  const contents = writer.toString() + "\n";
364
342
  compilation[file in compilation.assets ? 'updateAsset' : 'emitAsset'](file, new webpack_1.sources.RawSource(contents), { size: Buffer.byteLength(contents), ...assetInfo });
365
343
  }
366
- function dedent(text) {
367
- const actualText = text.join("\n");
368
- const firstLineIndent = actualText.split("\r?\n").find(line => line.length > 0)?.match(/^\s+/)?.[0];
369
- if (!firstLineIndent) {
370
- return actualText;
371
- }
372
- return actualText.replaceAll(new RegExp("^" + firstLineIndent, 'mg'), "").replace(/\n{2}$/, "\n");
344
+ function dedent(text, ...values) {
345
+ const lines = text.reduce((result, part, i) => result + String(values[i - 1]) + part).split(/\r?\n/);
346
+ // A template that opens and closes on its own line frames the content with two throwaway lines
347
+ if (lines[0]?.trim() === "") {
348
+ lines.shift();
349
+ }
350
+ if (lines.length > 1 && lines.at(-1)?.trim() === "") {
351
+ lines.pop();
352
+ }
353
+ // Only the lines with content get a say in the indent, and a line break must never count as one
354
+ const indent = lines.filter(line => line.trim() !== "")
355
+ .reduce((shortest, line) => {
356
+ const lineIndent = /^[ \t]*/.exec(line)?.[0] ?? "";
357
+ return shortest === undefined || lineIndent.length < shortest.length ? lineIndent : shortest;
358
+ }, undefined);
359
+ return indent
360
+ ? lines.map(line => line.startsWith(indent) ? line.substring(indent.length) : line).join("\n")
361
+ : lines.join("\n");
373
362
  }
374
363
  function resolveLegacyBlockScriptsInFolder(folder) {
375
364
  const blockScriptEntrypoints = [];
@@ -432,3 +421,9 @@ function* synchronousDirIterator(dirPath) {
432
421
  function instanceOfNodeError(value, errorType) {
433
422
  return !!value && value instanceof errorType;
434
423
  }
424
+ /**
425
+ * This tests if the name starts with a "~", is equal to "example" or "examples", or includes "example" as a name segment
426
+ */
427
+ function shouldBeSkipped(name) {
428
+ return name.startsWith("~") || name === "example" || name === "examples" || name.startsWith("example.") || name.includes(".example.");
429
+ }
@@ -126,7 +126,7 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
126
126
  const seenExplicitHandles = { scriptHandles: {}, styleHandles: {}, scriptModuleHandles: {} };
127
127
  const loadingEntrypoints = [];
128
128
  for await (const dirent of await (0, promises_1.opendir)(srcRoot)) {
129
- if (!dirent.isDirectory() || dirent.name.startsWith("~")) {
129
+ if (!dirent.isDirectory() || (0, shared_1.shouldBeSkipped)(dirent.name)) {
130
130
  continue;
131
131
  }
132
132
  const dir = joinPossiblyAbsolutePaths(srcRoot, dirent.name);
@@ -243,8 +243,10 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
243
243
  const resolvedBlockEntrypoints = presentEntrypoints
244
244
  .map(presentEntrypoint => {
245
245
  const overallSource = (0, node_path_1.dirname)(blockJsonOrigin);
246
- const overallSourceRelativeName = (0, node_path_1.relative)(overallSource, (0, node_path_1.normalize)((0, node_path_1.join)(blockJsonOrigin, (0, node_path_1.relative)(overallSource, presentEntrypoint.extensionlessExpectedSrc))));
247
- const handleSuffix = (0, shared_1.convertUsageLocationsHandleToEmittableHandleSuffix)(dest.locations.handle, overallSourceRelativeName);
246
+ // The handle suffix is the asset's extensionless name relative to its own block, so it
247
+ // has to come off the real on-disk path rather than the destination-relative expected src.
248
+ const parsedAbsoluteSrc = (0, node_path_1.parse)(presentEntrypoint.absoluteSrc);
249
+ const overallSourceRelativeName = (0, node_path_1.normalize)((0, node_path_1.join)((0, node_path_1.relative)(overallSource, parsedAbsoluteSrc.dir), parsedAbsoluteSrc.name));
248
250
  const handleGroup = (0, shared_1.getHandleGroup)(presentEntrypoint.entrypointField);
249
251
  let explicitlySetHandle;
250
252
  if (presentEntrypoint.handle) {
@@ -265,8 +267,9 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
265
267
  else {
266
268
  explicitlySetHandle = undefined;
267
269
  }
270
+ const emittableHandle = (0, shared_1.convertUsageLocationsHandleToEmittableHandle)(dest.locations.handle, handlePrefix, explicitlySetHandle ? explicitlySetHandle.substring(handlePrefix.length + 1) : overallSourceRelativeName);
268
271
  if (!entrypointNamesWithEffectiveDuplicates[presentEntrypoint.entrypointName]) {
269
- return { blockJsonOrigin, ...presentEntrypoint, handle: explicitlySetHandle ?? `${handlePrefix}/${handleSuffix}`, dest, handleGroup };
272
+ return { blockJsonOrigin, ...presentEntrypoint, handle: emittableHandle, dest, handleGroup };
270
273
  }
271
274
  const baseSuffix = `_${(0, shared_1.isStyleField)(presentEntrypoint.entrypointField) ? "style" : "script"}`;
272
275
  let count = 0, suffix = baseSuffix, deduplicatedEntrypointName;
@@ -281,7 +284,7 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
281
284
  ...presentEntrypoint,
282
285
  entrypointName: deduplicatedEntrypointName,
283
286
  extensionlessExpectedSrc: deduplicatedExtensionlessExpectedSrc,
284
- handle: explicitlySetHandle ?? `${handlePrefix}/${count ? handleSuffix + "_" + count : handleSuffix}`,
287
+ handle: emittableHandle,
285
288
  handleGroup,
286
289
  dest
287
290
  };
@@ -304,15 +307,15 @@ function resolveEntryFromDirectory(commonConfig, srcRoot, dest) {
304
307
  const packageJsonOrigin = (0, node_path_1.join)(dir, 'package.json');
305
308
  const packageJson = JSON.parse(await (0, promises_1.readFile)(packageJsonOrigin, 'utf8'));
306
309
  if (packageJson['main']) {
307
- rawEntrypoints.push(...mapToRealEntrypoints(packageJson['main'], dir, commonConfig.scriptExtension.test, { dest, entrypointJsonOrigin: packageJsonOrigin }));
310
+ rawEntrypoints.push(...mapToRealEntrypoints(packageJson['main'], dir, s => commonConfig.scriptExtension.test(s), { dest, entrypointJsonOrigin: packageJsonOrigin }));
308
311
  }
309
312
  if (!processingModules && packageJson['style']) {
310
- rawEntrypoints.push(...mapToRealEntrypoints(packageJson['style'], dir, shared_1.styleExtension.test, { dest, entrypointJsonOrigin: packageJsonOrigin }));
313
+ rawEntrypoints.push(...mapToRealEntrypoints(packageJson['style'], dir, s => shared_1.styleExtension.test(s), { dest, entrypointJsonOrigin: packageJsonOrigin }));
311
314
  }
312
315
  }
313
316
  catch (e) {
314
317
  try {
315
- rawEntrypoints.push(...parseEntrypointsJSON(dir, dest, ep => commonConfig.scriptExtension.test(ep) || shared_1.styleExtension.test(ep)));
318
+ rawEntrypoints.push(...parseEntrypointsJSON(dir, dest, ep => commonConfig.scriptExtension.test(ep) || (!processingModules && shared_1.styleExtension.test(ep))));
316
319
  }
317
320
  catch (e) {
318
321
  // This just means that the directory doesn't contain any declared entrypoints.
@@ -9,7 +9,7 @@ function convertToCSS(writableCSSObject, pretty = true) {
9
9
  return convertToCSSPretty(writableCSSObject, "");
10
10
  }
11
11
  else {
12
- return convertToCSS(writableCSSObject);
12
+ return convertToCSSUgly(writableCSSObject);
13
13
  }
14
14
  }
15
15
  function convertToCSSPretty(writableCSSObject, indentation) {
@@ -23,7 +23,7 @@ function convertToCSSPretty(writableCSSObject, indentation) {
23
23
  }
24
24
  else {
25
25
  res += " {\n";
26
- res += convertToCSSPretty(writableCSSObject, indentation + "\t");
26
+ res += convertToCSSPretty(value, indentation + "\t");
27
27
  res += indentation + "}\n";
28
28
  }
29
29
  }
@@ -39,7 +39,7 @@ function convertToCSSUgly(writableCSSObject) {
39
39
  res += `:${value};`;
40
40
  }
41
41
  else {
42
- res += "{" + convertToCSSUgly(writableCSSObject) + "}";
42
+ res += "{" + convertToCSSUgly(value) + "}";
43
43
  }
44
44
  }
45
45
  return res;
@@ -1,10 +1,13 @@
1
- import { type AdvancedOutputConfig, type PlauditWordpressWebpackConfig } from "../shared";
1
+ import { type AdvancedOutputConfig, type PathQueryParameters, type PlauditWordpressWebpackConfig } from "../shared";
2
2
  export declare const enum SourceType {
3
3
  blocks = "blocks",
4
4
  extensions = "extensions",
5
5
  plain = "plain"
6
6
  }
7
7
  export declare function isSourceType(str: string): str is SourceType;
8
- type Entrypoints = [string, AdvancedOutputConfig][];
8
+ type ResolvedSourceConfig = AdvancedOutputConfig & {
9
+ pathQueryParameters?: PathQueryParameters;
10
+ };
11
+ type Entrypoints = [string, ResolvedSourceConfig][];
9
12
  export declare function resolveAllConfigLevelEntrypoints(config: PlauditWordpressWebpackConfig, projectRoot?: string): Record<'staticEntrypoints' | 'dynamicEntrypoints', Entrypoints>;
10
13
  export {};
@@ -10,7 +10,6 @@ export type ParsedLocationEncodingFilename = {
10
10
  };
11
11
  };
12
12
  flags: NormalizedEnqueuingControlFlags;
13
- niceName?: string;
14
13
  };
15
14
  export declare function parseLocationEncodingFilename(filename: string): ParsedLocationEncodingFilename | undefined;
16
15
  export declare function parseLocationEncodingFilenameForBlock(filename: string, errorBuilder?: (message: string, filename: string) => unknown): (Omit<ParsedLocationEncodingFilename, 'locations'> & {
@@ -7,7 +7,7 @@ const node_path_1 = require("node:path");
7
7
  const shared_1 = require("../shared");
8
8
  function parseLocationEncodingFilename(filename) {
9
9
  const { name, ext } = (0, node_path_1.parse)(filename);
10
- if (name.startsWith("~")) {
10
+ if ((0, shared_1.shouldBeSkipped)(name)) {
11
11
  return undefined;
12
12
  }
13
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;
@@ -79,16 +79,17 @@ function parseLocationEncodingFilename(filename) {
79
79
  return undefined;
80
80
  }
81
81
  if (niceName !== undefined) {
82
- locations.handle = { niceName };
82
+ // We need to kebab-case the nice name value for web-safety
83
+ locations.handle = { niceName: (0, shared_1.kebabCase)(niceName) };
83
84
  }
84
- return { type, locations, flags, niceName };
85
+ return { type, locations, flags };
85
86
  }
86
87
  function parseLocationEncodingFilenameForBlock(filename, errorBuilder = shared_1.newWebpackErrorForFile) {
87
88
  const parsedFilename = parseLocationEncodingFilename(filename);
88
89
  if (parsedFilename !== undefined) {
89
90
  const { handle, ...locations } = parsedFilename.locations;
90
91
  if ((0, shared_1.constantKeys)(locations).some(value => value !== 'clientView' && value !== 'clientEditor')) {
91
- throw errorBuilder(`Block assets cannot be enqueued anywhere other than view, editor, or both. Saw: ${(0, shared_1.constantKeys)(parsedFilename).join(", ")}`, filename);
92
+ throw errorBuilder(`Block assets cannot be enqueued anywhere other than view, editor, or both. Saw: ${(0, shared_1.constantKeys)(parsedFilename.locations).join(", ")}`, filename);
92
93
  }
93
94
  if (Object.values(locations).some(value => typeof value === 'number')) {
94
95
  throw errorBuilder(`Block assets cannot be enqueued with a priority`, filename);
@@ -15,7 +15,6 @@ export declare function unpackPotentiallyPrefixedFilePath(filePath: string): [st
15
15
  */
16
16
  export declare function unpackPathQuery(pathQuery: string): PathQueryParameters | undefined;
17
17
  export type LoadingStrategy = boolean | 'defer' | 'async' | 'eager' | 'lazy' | 'inline';
18
- export declare function isValidLoadingStrategy(strategy: any): strategy is LoadingStrategy | undefined;
19
18
  export declare function newInvalidLoadingStrategyError(strategy: unknown, file: string): import("webpack").WebpackError;
20
19
  export type InFooter = boolean;
21
20
  export declare function isValidInFooter(in_footer: unknown): in_footer is InFooter;
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getAssetFileContents = getAssetFileContents;
4
4
  exports.unpackPotentiallyPrefixedFilePath = unpackPotentiallyPrefixedFilePath;
5
5
  exports.unpackPathQuery = unpackPathQuery;
6
- exports.isValidLoadingStrategy = isValidLoadingStrategy;
7
6
  exports.newInvalidLoadingStrategyError = newInvalidLoadingStrategyError;
8
7
  exports.isValidInFooter = isValidInFooter;
9
8
  exports.newInvalidInFooterError = newInvalidInFooterError;
@@ -78,9 +77,6 @@ function unpackPathQuery(pathQuery) {
78
77
  }, {});
79
78
  }
80
79
  const namedStrategies = ['defer', 'async', 'eager', 'lazy', 'inline'];
81
- function isValidLoadingStrategy(strategy) {
82
- return strategy === undefined || (typeof strategy === 'string' && namedStrategies.includes(strategy));
83
- }
84
80
  function newInvalidLoadingStrategyError(strategy, file) {
85
81
  return (0, shared_1.newWebpackErrorForFile)(`The strategy was invalid. Received: ${strategy}, Expected: undefined|boolean|'${namedStrategies.join("'|'")}'`, file);
86
82
  }
@@ -0,0 +1,72 @@
1
+ import LegacyDependencyExtractionWebpackPlugin from "@wordpress/dependency-extraction-webpack-plugin";
2
+ import { Compilation, Compiler, Module, type TemplatePath } from "webpack";
3
+ export interface EnhancedDependencyExtractionWebpackPluginOptions {
4
+ injectPolyfill?: boolean;
5
+ useDefaults?: boolean;
6
+ outputFilename?: TemplatePath;
7
+ requestToExternal?: (request: string) => string | string[] | undefined;
8
+ requestToExternalModule?: (request: string) => string | boolean | undefined;
9
+ requestToHandle?: (request: string) => string | undefined;
10
+ requestToScriptModule?: (request: string) => string | boolean | Error | undefined;
11
+ externalizedReport?: string | boolean;
12
+ }
13
+ export declare class EnhancedDependencyExtractionWebpackPlugin {
14
+ /**
15
+ * Track requests that are externalized.
16
+ *
17
+ * Because we don't have a closed set of dependencies, we need to track what has
18
+ * been externalized so we can recognize them in a later phase when the dependency
19
+ * lists are generated.
20
+ */
21
+ private readonly externalizedDeps;
22
+ private readonly options;
23
+ /**
24
+ * Track requests that a script build externalizes as script modules.
25
+ *
26
+ * These can only be reached through a runtime `import()`, which requires
27
+ * support for dynamic import in the output environment. The set is used
28
+ * to report an actionable error when that support is missing.
29
+ */
30
+ private readonly scriptModuleDepsInScript;
31
+ /**
32
+ * Whether we should use modules. This will be set later to match webpack's `output.module` option.
33
+ */
34
+ private useModules;
35
+ constructor(options: EnhancedDependencyExtractionWebpackPluginOptions | LegacyDependencyExtractionWebpackPlugin | EnhancedDependencyExtractionWebpackPlugin);
36
+ /**
37
+ * Convert a script module request to an external suitable for a script
38
+ * build.
39
+ *
40
+ * A classic script cannot contain a static `import`, so the `module`
41
+ * external type is not usable here: the script module has to be reached
42
+ * through a runtime `import()`, which is the `import` external type. The
43
+ * bare specifier is resolved by the import map that WordPress prints for
44
+ * enqueued script modules.
45
+ *
46
+ * @param {string} request Original module request.
47
+ * @param {string|boolean|Error=} external Result of a script module external resolution.
48
+ * @return {string|undefined} The resulting external definition.
49
+ */
50
+ static toScriptModuleExternalInScript(request: string, external?: string | boolean | Error): string | undefined;
51
+ private readonly externalizeWpDeps;
52
+ /**
53
+ * @param {string} request
54
+ * @return {string} Mapped dependency name
55
+ */
56
+ protected mapRequestToDependency(request: string): string;
57
+ apply(compiler: Compiler): void;
58
+ /**
59
+ * Check for magic comments before minification, so minification doesn't have to preserve them.
60
+ */
61
+ checkForMagicComments(compilation: Compilation): void;
62
+ addAssets(compilation: Compilation): void;
63
+ private static readonly staticDepsCurrent;
64
+ private static readonly staticDepsCache;
65
+ /**
66
+ * Can we trace a line of static dependencies from an entry to a module
67
+ *
68
+ * @return {boolean} True if there is a static import path to the root
69
+ */
70
+ static hasStaticDependencyPathToRoot(compilation: Compilation, block: Module): boolean;
71
+ readonly addExternalizedDep: (value: string) => Set<string>;
72
+ }