@plaudit/webpack-extensions 2.53.1 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/build/{wordpress-scripts-wrapper → plugins}/AdditionalDependencyInjectorPlugin.js +3 -3
  2. package/build/{wordpress-scripts-wrapper → plugins}/BlockJSONManagingPlugin.d.ts +5 -5
  3. package/build/{wordpress-scripts-wrapper → plugins}/BlockJSONManagingPlugin.js +17 -33
  4. package/build/plugins/ExtensionsConfigFileGeneratorPlugin.d.ts +13 -0
  5. package/build/plugins/ExtensionsConfigFileGeneratorPlugin.js +173 -0
  6. package/build/plugins/PlainEntrypointsConfigFileGeneratorPlugin.d.ts +24 -0
  7. package/build/plugins/PlainEntrypointsConfigFileGeneratorPlugin.js +244 -0
  8. package/build/plugins/SpecialAssetHandlingPlugin.d.ts +12 -0
  9. package/build/plugins/SpecialAssetHandlingPlugin.js +135 -0
  10. package/build/{wordpress-scripts-wrapper → plugins}/VariablesJSMonitorPlugin.js +1 -1
  11. package/build/{wordpress-scripts-wrapper → plugins}/WPMLConfigBuilder.d.ts +3 -2
  12. package/build/{wordpress-scripts-wrapper → plugins}/WPMLConfigBuilder.js +7 -3
  13. package/build/{wordpress-scripts-wrapper → plugins}/dependency-extraction-webpack-plugin-config-builder.d.ts +1 -7
  14. package/build/shared.d.ts +86 -9
  15. package/build/shared.js +70 -0
  16. package/build/utils/common-config-helpers.d.ts +26 -0
  17. package/build/utils/common-config-helpers.js +336 -0
  18. package/build/{wordpress-scripts-wrapper → utils}/php-serializer.d.ts +1 -1
  19. package/build/{wordpress-scripts-wrapper → utils}/php-serializer.js +1 -1
  20. package/build/utils/php-writer.d.ts +54 -0
  21. package/build/utils/php-writer.js +191 -0
  22. package/build/utils/pseduo-semaphore.d.ts +13 -0
  23. package/build/utils/pseduo-semaphore.js +63 -0
  24. package/build/wordpress-scripts-wrapper.d.ts +1 -27
  25. package/build/wordpress-scripts-wrapper.js +233 -441
  26. package/package.json +4 -4
  27. package/build/wordpress-scripts-wrapper/ExtensionsConfigFileGeneratorPlugin.d.ts +0 -6
  28. package/build/wordpress-scripts-wrapper/ExtensionsConfigFileGeneratorPlugin.js +0 -52
  29. package/build/wordpress-scripts-wrapper/SpecialAssetHandlingPlugin.d.ts +0 -7
  30. package/build/wordpress-scripts-wrapper/SpecialAssetHandlingPlugin.js +0 -107
  31. /package/build/{wordpress-scripts-wrapper → plugins}/AdditionalDependencyInjectorPlugin.d.ts +0 -0
  32. /package/build/{wordpress-scripts-wrapper → plugins}/BrowserSyncPlugin.d.ts +0 -0
  33. /package/build/{wordpress-scripts-wrapper → plugins}/BrowserSyncPlugin.js +0 -0
  34. /package/build/{wordpress-scripts-wrapper → plugins}/MiniCSSExtractPluginErrorCleaner.d.ts +0 -0
  35. /package/build/{wordpress-scripts-wrapper → plugins}/MiniCSSExtractPluginErrorCleaner.js +0 -0
  36. /package/build/{wordpress-scripts-wrapper → plugins}/PackageConfigSanityChecker.d.ts +0 -0
  37. /package/build/{wordpress-scripts-wrapper → plugins}/PackageConfigSanityChecker.js +0 -0
  38. /package/build/{wordpress-scripts-wrapper → plugins}/VariablesJSMonitorPlugin.d.ts +0 -0
  39. /package/build/{wordpress-scripts-wrapper → plugins}/dependency-extraction-webpack-plugin-config-builder.js +0 -0
  40. /package/build/{wordpress-scripts-wrapper → plugins}/static-configs.d.ts +0 -0
  41. /package/build/{wordpress-scripts-wrapper → plugins}/static-configs.js +0 -0
  42. /package/build/{wordpress-scripts-wrapper → utils}/json-to-php-but-with-__-injection.d.ts +0 -0
  43. /package/build/{wordpress-scripts-wrapper → utils}/json-to-php-but-with-__-injection.js +0 -0
@@ -16,9 +16,9 @@ class AdditionalDependencyInjectorPlugin {
16
16
  this.addExternalizedDep = addExternalizedDep;
17
17
  }
18
18
  apply(compiler) {
19
- compiler.hooks.thisCompilation.tap("AdditionalDependencyInjectorPlugin", compilation => {
19
+ compiler.hooks.thisCompilation.tap(this.constructor.name, compilation => {
20
20
  compilation.hooks.processAssets.tap({
21
- name: "AdditionalDependencyInjectorPlugin_ProcessAssets_AddFakeModules",
21
+ name: `${this.constructor.name}_ProcessAssets_AddFakeModules`,
22
22
  stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
23
23
  }, () => {
24
24
  const usableEntrypointTest = this.processingModules ? /\.m[jt]sx?$/i : /\.[jt]sx?$/i;
@@ -40,7 +40,7 @@ class AdditionalDependencyInjectorPlugin {
40
40
  }
41
41
  });
42
42
  compilation.hooks.processAssets.tap({
43
- name: "AdditionalDependencyInjectorPlugin_ProcessAssets_RemoveFakeModules",
43
+ name: `${this.constructor.name}_ProcessAssets_RemoveFakeModules`,
44
44
  stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_REPORT
45
45
  }, () => {
46
46
  for (const entrypoint of compilation.entrypoints.values()) {
@@ -2,6 +2,7 @@ import { Compilation, type Compiler, type WebpackPluginInstance } from "webpack"
2
2
  export declare class BlockJSONManagingPlugin implements WebpackPluginInstance {
3
3
  private readonly standaloneBlocks;
4
4
  private readonly processingModules;
5
+ private readonly blocksDest;
5
6
  static readonly mappableModuleKeys: readonly ["viewScriptModule", "scriptModule"];
6
7
  static readonly mappableNonModuleKeys: readonly ["editorScript", "script", "viewScript", "editorStyle", "style", "viewStyle"];
7
8
  private static readonly styleExtensionPattern;
@@ -11,9 +12,9 @@ export declare class BlockJSONManagingPlugin implements WebpackPluginInstance {
11
12
  private static readonly blockJsonRawDependenciesMap;
12
13
  private static readonly blockJSONAssetSourceDirs;
13
14
  private static readonly blockJsonAssetKeyMapping;
14
- private static readonly blockJsonAssetKeyReadinessMapping;
15
+ private static readonly syncsManager;
15
16
  readonly additionalMetadata: Map<string, any>;
16
- constructor(standaloneBlocks: boolean, processingModules: boolean);
17
+ constructor(standaloneBlocks: boolean, processingModules: boolean | undefined, blocksDest: string);
17
18
  static recordRawDependency(entrypoint: string, dependency: string): void;
18
19
  static recordBlockJSONAssetSourceDir(entrypoint: string, source: string): void;
19
20
  apply(compiler: Compiler): void;
@@ -32,10 +33,9 @@ export declare class BlockJSONManagingPlugin implements WebpackPluginInstance {
32
33
  private static populateEntrypointsMap;
33
34
  private registerBlockJsonProcessor;
34
35
  private static normalizeRenderTemplate;
35
- private static getSyncs;
36
- private static makeSync;
37
- private static getAssetDetails;
36
+ private getAssetDetails;
38
37
  private static getAssetDataAccountingForCSS;
39
38
  private static findFirstChunkFileWithExtension;
40
39
  private static getChunkFilesByRuntimeName;
40
+ private stripBlocksDest;
41
41
  }
@@ -7,11 +7,12 @@ exports.BlockJSONManagingPlugin = void 0;
7
7
  const node_crypto_1 = __importDefault(require("node:crypto"));
8
8
  const node_fs_1 = __importDefault(require("node:fs"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
- const json_to_php_but_with____injection_1 = __importDefault(require("./json-to-php-but-with-__-injection"));
10
+ const shared_1 = require("../shared");
11
11
  const webpack_1 = require("webpack");
12
12
  class BlockJSONManagingPlugin {
13
13
  standaloneBlocks;
14
14
  processingModules;
15
+ blocksDest;
15
16
  static mappableModuleKeys = ["viewScriptModule", "scriptModule"];
16
17
  static mappableNonModuleKeys = ["editorScript", "script", "viewScript", "editorStyle", "style", "viewStyle"];
17
18
  static styleExtensionPattern = /\.(p?c|sa)ss$/i;
@@ -21,11 +22,12 @@ class BlockJSONManagingPlugin {
21
22
  static blockJsonRawDependenciesMap = new Map();
22
23
  static blockJSONAssetSourceDirs = new Map();
23
24
  static blockJsonAssetKeyMapping = new Map();
24
- static blockJsonAssetKeyReadinessMapping = new Map();
25
+ static syncsManager = new shared_1.SyncsManager();
25
26
  additionalMetadata = new Map();
26
- constructor(standaloneBlocks, processingModules) {
27
+ constructor(standaloneBlocks, processingModules, blocksDest) {
27
28
  this.standaloneBlocks = standaloneBlocks;
28
29
  this.processingModules = processingModules;
30
+ this.blocksDest = blocksDest;
29
31
  }
30
32
  static recordRawDependency(entrypoint, dependency) {
31
33
  let deps = BlockJSONManagingPlugin.blockJsonRawDependenciesMap.get(entrypoint);
@@ -254,7 +256,7 @@ class BlockJSONManagingPlugin {
254
256
  }
255
257
  }
256
258
  }
257
- const syncs = BlockJSONManagingPlugin.getSyncs(name);
259
+ const syncs = BlockJSONManagingPlugin.syncsManager.get(name);
258
260
  let assetKeyMappings = BlockJSONManagingPlugin.blockJsonAssetKeyMapping.get(name);
259
261
  if (!assetKeyMappings) {
260
262
  BlockJSONManagingPlugin.blockJsonAssetKeyMapping.set(name, assetKeyMappings = {});
@@ -404,7 +406,7 @@ class BlockJSONManagingPlugin {
404
406
  if (cfg) {
405
407
  if (Array.isArray(cfg)) {
406
408
  for (let i = 0; i < cfg.length; i++) {
407
- const assetDetails = BlockJSONManagingPlugin
409
+ const assetDetails = this
408
410
  .getAssetDetails(blockFolder, config["name"], cfg[i], rawAssetData, derivedFilesMap, mappableKey, usedHandles);
409
411
  if (assetDetails) {
410
412
  (assetDetails.isCss ? styleHandles : scriptHandles)[assetDetails.handle] = assetDetails.handleData;
@@ -413,7 +415,7 @@ class BlockJSONManagingPlugin {
413
415
  }
414
416
  }
415
417
  else {
416
- const assetDetails = BlockJSONManagingPlugin
418
+ const assetDetails = this
417
419
  .getAssetDetails(blockFolder, config["name"], cfg, rawAssetData, derivedFilesMap, mappableKey, usedHandles);
418
420
  if (assetDetails) {
419
421
  (assetDetails.isCss ? styleHandles : scriptHandles)[assetDetails.handle] = assetDetails.handleData;
@@ -425,22 +427,17 @@ class BlockJSONManagingPlugin {
425
427
  }
426
428
  sortedBlockDirConfigData = Object.fromEntries([
427
429
  ["__metadata", { version: 3, ...Object.fromEntries(this.additionalMetadata.entries()), scriptHandles, styleHandles }],
428
- ...blockDirConfigs.map(entry => [entry[0], Object.fromEntries(Object.entries(entry[1]).filter(e => e[0] !== '$schema'))])
430
+ ...blockDirConfigs.map(entry => [this.stripBlocksDest(entry[0]), Object.fromEntries(Object.entries(entry[1]).filter(e => e[0] !== '$schema'))])
429
431
  ]);
430
432
  compilation.deleteAsset("assets.json");
431
433
  }
432
434
  else {
433
435
  sortedBlockDirConfigData = Object.fromEntries([
434
436
  ["__metadata", { version: 1, ...Object.fromEntries(this.additionalMetadata.entries()) }],
435
- ...blockDirConfigs.map(entry => [entry[0], Object.fromEntries(Object.entries(entry[1]).filter(e => e[0] !== '$schema'))])
437
+ ...blockDirConfigs.map(entry => [this.stripBlocksDest(entry[0]), Object.fromEntries(Object.entries(entry[1]).filter(e => e[0] !== '$schema'))])
436
438
  ]);
437
439
  }
438
- compilation.emitAsset("blockdir.config.php", new webpack_1.sources.RawSource("<?php return "
439
- + json_to_php_but_with____injection_1.default.make({ indent: "\t", linebreak: "\n", shortArraySyntax: true })(sortedBlockDirConfigData, "")
440
- .replaceAll(/(\n\t*)\[\s+],/gs, "$1[],")
441
- .replaceAll(/\[\n\t+([^\n]+)\n\t+]/gs, (_, inner) => `[${inner.trim()}]`)
442
- .replaceAll(/'rest' => \[\n\t+(\[(?:'[^']+')?]),\n\t+('[^']+')(?:,\n\t+(\[[^\n]+]))?\n\t+]/gs, (_, deps, hash, args) => `'rest' => [${[deps, hash, args].filter(value => !!value).join(", ")}]`)
443
- + ";"));
440
+ compilation.emitAsset(node_path_1.default.join(this.blocksDest, "blockdir.config.php"), new webpack_1.sources.RawSource((0, shared_1.makeEmittableConfigPHP)(sortedBlockDirConfigData)));
444
441
  });
445
442
  }
446
443
  static normalizeRenderTemplate(json, pathsNeedRemapping, sourceDir, outputDir, compilation) {
@@ -494,23 +491,7 @@ class BlockJSONManagingPlugin {
494
491
  }
495
492
  }
496
493
  }
497
- static getSyncs(name) {
498
- const res = BlockJSONManagingPlugin.blockJsonAssetKeyReadinessMapping.get(name);
499
- if (res) {
500
- return res;
501
- }
502
- BlockJSONManagingPlugin.blockJsonAssetKeyReadinessMapping.set(name, { nonModule: BlockJSONManagingPlugin.makeSync(), module: BlockJSONManagingPlugin.makeSync() });
503
- return BlockJSONManagingPlugin.blockJsonAssetKeyReadinessMapping.get(name);
504
- }
505
- static makeSync() {
506
- const res = { done: false };
507
- res.sync = new Promise((resolve, reject) => {
508
- res.resolve = (v) => resolve(v);
509
- res.reject = () => reject();
510
- });
511
- return res;
512
- }
513
- static getAssetDetails(blockFolder, blockName, asset, rawAssetData, derivedFilesMap, mappableKey, usedHandles) {
494
+ getAssetDetails(blockFolder, blockName, asset, rawAssetData, derivedFilesMap, mappableKey, usedHandles) {
514
495
  if (!asset.startsWith("file:./")) {
515
496
  return undefined;
516
497
  }
@@ -541,7 +522,7 @@ class BlockJSONManagingPlugin {
541
522
  if (!assetData) {
542
523
  return undefined;
543
524
  }
544
- let handle = src.substring(0, src.length - (isCss ? 4 : 3));
525
+ let handle = this.stripBlocksDest(src.substring(0, src.length - (isCss ? 4 : 3)));
545
526
  if (blockName && handle.startsWith(blockName.substring(blockName.indexOf("/") + 1) + "/")) {
546
527
  handle = blockName + handle.substring(handle.indexOf("/"));
547
528
  }
@@ -554,7 +535,7 @@ class BlockJSONManagingPlugin {
554
535
  const rest = isCss || mappableKey.startsWith("editor")
555
536
  ? [assetData.dependencies, assetData.version]
556
537
  : [assetData.dependencies, assetData.version, { strategy: 'defer' }];
557
- return { isCss, handle, handleData: { src, rest } };
538
+ return { isCss, handle, handleData: { src: this.stripBlocksDest(src), rest } };
558
539
  }
559
540
  static getAssetDataAccountingForCSS(src, rawAssetData) {
560
541
  if (rawAssetData[src]) {
@@ -587,5 +568,8 @@ class BlockJSONManagingPlugin {
587
568
  }
588
569
  return [...files];
589
570
  }
571
+ stripBlocksDest(item) {
572
+ return this.blocksDest && item.startsWith(this.blocksDest + "/") ? item.substring(this.blocksDest.length + 1 /* we also need to drop the "/" */) : item;
573
+ }
590
574
  }
591
575
  exports.BlockJSONManagingPlugin = BlockJSONManagingPlugin;
@@ -0,0 +1,13 @@
1
+ import { type Compiler, type WebpackPluginInstance } from "webpack";
2
+ export declare class ExtensionsConfigFileGeneratorPlugin implements WebpackPluginInstance {
3
+ private readonly extensionsPath;
4
+ private readonly version;
5
+ private readonly extensionsDest;
6
+ private static readonly semaphore;
7
+ private static cache;
8
+ private readonly id;
9
+ constructor(extensionsPath: string, version: 1 | 2, extensionsDest: string);
10
+ apply(compiler: Compiler): void;
11
+ private makeVersionOneAfterProcessAssets;
12
+ private stripExtensionsDest;
13
+ }
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ExtensionsConfigFileGeneratorPlugin = void 0;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const shared_1 = require("../shared");
10
+ const php_serializer_1 = require("../utils/php-serializer");
11
+ const pseduo_semaphore_1 = require("../utils/pseduo-semaphore");
12
+ const webpack_1 = require("webpack");
13
+ class ExtensionsConfigFileGeneratorPlugin {
14
+ extensionsPath;
15
+ version;
16
+ extensionsDest;
17
+ static semaphore = new pseduo_semaphore_1.PseudoSemaphore({ assets: [], setupFiles: [] });
18
+ static cache = undefined;
19
+ id;
20
+ constructor(extensionsPath, version, extensionsDest) {
21
+ this.extensionsPath = extensionsPath;
22
+ this.version = version;
23
+ this.extensionsDest = extensionsDest;
24
+ this.id = Math.random().toString();
25
+ ExtensionsConfigFileGeneratorPlugin.semaphore.register(this.id);
26
+ }
27
+ apply(compiler) {
28
+ compiler.hooks.make.tapPromise(this.constructor.name, async (compilation) => {
29
+ if (!compilation.contextDependencies.has(this.extensionsPath)) {
30
+ compilation.contextDependencies.add(this.extensionsPath);
31
+ }
32
+ const emissionPromises = [];
33
+ for (const setupFilePath of await promises_1.default.readdir(this.extensionsPath)) {
34
+ if (setupFilePath.endsWith("-setup.php")) {
35
+ const setupFileSourcePath = node_path_1.default.join(this.extensionsPath, setupFilePath);
36
+ compilation.fileDependencies.add(setupFileSourcePath);
37
+ emissionPromises.push(promises_1.default.readFile(setupFileSourcePath).then(contents => {
38
+ return compilation.emitAsset(node_path_1.default.join(this.extensionsDest, setupFilePath), new webpack_1.sources.RawSource(contents), { size: Buffer.byteLength(contents) });
39
+ }));
40
+ }
41
+ }
42
+ await Promise.all(emissionPromises);
43
+ });
44
+ if (this.version === 2) {
45
+ const tapName = { name: `${this.constructor.name}_ProcessBlockJSONFiles`, stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_REPORT };
46
+ compiler.hooks.compilation.tap(this.constructor.name, compilation => {
47
+ ExtensionsConfigFileGeneratorPlugin.cache = undefined;
48
+ ExtensionsConfigFileGeneratorPlugin.semaphore.reset(this.id);
49
+ compilation.hooks.processAssets.tapPromise(tapName, async (assets) => {
50
+ try {
51
+ //TODO: Add handling of extracted CSS from JS
52
+ //TODO: It should be possible to use EntryPoints to determine the "original" file
53
+ //TODO: Use the version hash of the original file, but not its dependencies for the additional file's "rest" parameter (see BlockJSONManagingPlugin.ts for how to do so)
54
+ //TODO: There is no reason to not use basically the exact same logic to implement support for this in plain file contexts
55
+ //TODO: BlockJSONManagingPlugin should be rewritten from scratch to use this method instead of its 15 layers of tracking + syncing
56
+ if (ExtensionsConfigFileGeneratorPlugin.cache === undefined) {
57
+ ExtensionsConfigFileGeneratorPlugin.cache = { assets: [], setupFiles: [] };
58
+ compilation.hooks.afterProcessAssets.tap(`${this.constructor.name}_GenerateConfigFile`, () => {
59
+ const regex = /^(.+?)-((?:editor-|view-|)(?:style|script|script-module))\.(?:css|m?js)$/i;
60
+ const blockExtensionsConfig = { metadata: { version: this.version }, scriptHandles: {}, styleHandles: {}, blocks: {}, setupFiles: {} };
61
+ for (const assetDataSource of ExtensionsConfigFileGeneratorPlugin.cache?.assets ?? []) {
62
+ const normalizedAssetData = Object.entries(assetDataSource)
63
+ .map(entry => {
64
+ const assetPath = this.extensionsDest && entry[0].startsWith(this.extensionsDest + "/")
65
+ ? entry[0].substring(this.extensionsDest.length + 1) : entry[0];
66
+ return [assetPath, entry[1]];
67
+ });
68
+ for (const [assetPath, assetData] of normalizedAssetData) {
69
+ const match = regex.exec(assetPath);
70
+ if (!match) {
71
+ continue;
72
+ }
73
+ const blockSlug = match[1], assetType = match[2];
74
+ if (blockSlug && assetType) {
75
+ const key = assetType.replace(/-[sm]/gi, chars => chars.substring(1).toUpperCase());
76
+ const handle = `plaudit_block-extension_${blockSlug}-${assetType}`;
77
+ const isCss = assetType.endsWith("tyle");
78
+ blockExtensionsConfig[isCss ? 'styleHandles' : 'scriptHandles'][handle] = {
79
+ src: isCss ? assetPath.replace(/\.js$/, ".css") : assetPath,
80
+ rest: isCss || key.startsWith("editor")
81
+ ? [assetData.dependencies, assetData.version]
82
+ : [assetData.dependencies, assetData.version, { strategy: 'defer' }]
83
+ };
84
+ (blockExtensionsConfig.blocks[blockSlug] ?? (blockExtensionsConfig.blocks[blockSlug] = {}))[key] = handle;
85
+ }
86
+ }
87
+ for (const [blockSlug, asset] of ExtensionsConfigFileGeneratorPlugin.cache?.setupFiles ?? []) {
88
+ blockExtensionsConfig.setupFiles[blockSlug] = asset;
89
+ }
90
+ }
91
+ blockExtensionsConfig.scriptHandles = Object.fromEntries(Object.entries(blockExtensionsConfig.scriptHandles)
92
+ .toSorted(([a], [b]) => a.localeCompare(b)));
93
+ blockExtensionsConfig.styleHandles = Object.fromEntries(Object.entries(blockExtensionsConfig.styleHandles)
94
+ .toSorted(([a], [b]) => a.localeCompare(b)));
95
+ blockExtensionsConfig.setupFiles = Object.fromEntries(Object.entries(blockExtensionsConfig.setupFiles)
96
+ .toSorted(([a], [b]) => a.localeCompare(b)));
97
+ blockExtensionsConfig.blocks = Object.fromEntries(Object.entries(blockExtensionsConfig.blocks)
98
+ .map(block => {
99
+ return [block[0], Object.fromEntries(Object.entries(block[1]).toSorted(([a], [b]) => a.localeCompare(b)))];
100
+ })
101
+ .toSorted(([a], [b]) => a.localeCompare(b)));
102
+ compilation.emitAsset(node_path_1.default.join(this.extensionsDest, "mapping.config.php"), new webpack_1.sources.RawSource((0, shared_1.makeEmittableConfigPHP)(blockExtensionsConfig)));
103
+ });
104
+ }
105
+ const myCacheData = { assets: [], setupFiles: [] };
106
+ for (const asset of Object.keys(compilation.assets).map(asset => this.stripExtensionsDest(asset))) {
107
+ const blockSlug = /^(.+?)-setup.php$/i.exec(asset)?.[1];
108
+ if (blockSlug) {
109
+ myCacheData.setupFiles.push([blockSlug, asset]);
110
+ }
111
+ }
112
+ const rawAssetDataSource = assets["assets.json"]?.source();
113
+ if (typeof rawAssetDataSource !== 'string') {
114
+ ExtensionsConfigFileGeneratorPlugin.semaphore.reject(this.id);
115
+ compilation.errors.push(new Error("assets.json is unexpectedly missing or not a string"));
116
+ return;
117
+ }
118
+ const assetDataSource = JSON.parse(rawAssetDataSource);
119
+ if (!(0, shared_1.isRawAssetData)(assetDataSource)) {
120
+ ExtensionsConfigFileGeneratorPlugin.semaphore.reject(this.id);
121
+ compilation.errors.push(new Error("assets.json is does not match the RawAssetData format"));
122
+ return;
123
+ }
124
+ myCacheData.assets.push(assetDataSource);
125
+ compilation.deleteAsset("assets.json");
126
+ ExtensionsConfigFileGeneratorPlugin.semaphore.resolve(this.id, myCacheData);
127
+ ExtensionsConfigFileGeneratorPlugin.cache = (await ExtensionsConfigFileGeneratorPlugin.semaphore.wait())
128
+ .reduce((main, { assets, setupFiles }) => {
129
+ main.assets.push(...assets);
130
+ main.setupFiles.push(...setupFiles);
131
+ return main;
132
+ }, { assets: [], setupFiles: [] });
133
+ }
134
+ catch (e) {
135
+ ExtensionsConfigFileGeneratorPlugin.semaphore.reject(this.id);
136
+ if (e instanceof Error) {
137
+ compilation.errors.push(e);
138
+ return;
139
+ }
140
+ throw e;
141
+ }
142
+ });
143
+ });
144
+ }
145
+ else {
146
+ compiler.hooks.thisCompilation.tap(this.constructor.name, compilation => {
147
+ compilation.hooks.afterProcessAssets.tap(`${this.constructor.name}_AfterProcessAssets`, this.makeVersionOneAfterProcessAssets(compilation));
148
+ });
149
+ }
150
+ }
151
+ makeVersionOneAfterProcessAssets(compilation) {
152
+ return compilationAssets => {
153
+ const regex = /^(.+?)-((?:editor-|view-|)(?:style|script|script-module))\.(?:css|m?js)$/i;
154
+ const mapping = {};
155
+ for (const asset of Object.keys(compilationAssets)) {
156
+ let match;
157
+ if ((match = /^(.+?)-setup.php$/i.exec(asset)) && match[1]) {
158
+ (mapping[match[1]] ?? (mapping[match[1]] = [{}]))[1] = `${asset}`;
159
+ }
160
+ else if ((match = regex.exec(asset)) && match[1] && match[2]) {
161
+ const resourceInfo = (mapping[match[1]] ?? (mapping[match[1]] = [{}]))[0];
162
+ const key = match[2].replace(/-[sm]/gi, chars => chars.substring(1).toUpperCase());
163
+ (resourceInfo[key] ?? (resourceInfo[key] = [])).push([`plaudit_block-extension_${match[1]}-${match[2]}`, asset]);
164
+ }
165
+ }
166
+ compilation.emitAsset(node_path_1.default.join(this.extensionsDest, "mapping.config"), new webpack_1.sources.RawSource((0, php_serializer_1.phpSerialize)(mapping)));
167
+ };
168
+ }
169
+ stripExtensionsDest(item) {
170
+ return this.extensionsDest && item.startsWith(this.extensionsDest + "/") ? item.substring(this.extensionsDest.length + 1 /* we also need to drop the "/" */) : item;
171
+ }
172
+ }
173
+ exports.ExtensionsConfigFileGeneratorPlugin = ExtensionsConfigFileGeneratorPlugin;
@@ -0,0 +1,24 @@
1
+ import { UsageLocations } from "../shared";
2
+ import { PHPWriter } from "../utils/php-writer";
3
+ import { type Compiler, type WebpackPluginInstance } from "webpack";
4
+ export declare class PlainEntrypointsConfigFileGeneratorPlugin implements WebpackPluginInstance {
5
+ private readonly buildRoot;
6
+ private readonly outputDir;
7
+ private readonly usageLocations;
8
+ private readonly handlePrefix;
9
+ private static readonly semaphore;
10
+ private static cache?;
11
+ private readonly id;
12
+ constructor(buildRoot: string, outputDir: string, usageLocations: UsageLocations, handlePrefix: string);
13
+ apply(compiler: Compiler): void;
14
+ private static afterProcessAssets;
15
+ private static getEmitPath;
16
+ private static addHandlesToHandleLists;
17
+ private static appendEnqueuingHandleLists;
18
+ private static separateHandleListByPriority;
19
+ /**
20
+ * The primary benefit of emitting a function instead of baking its contents into each function that uses it is that it allows us to avoid recomputing the base uri multiple times
21
+ */
22
+ static emitResolveBaseUriFunction(writer: PHPWriter): void;
23
+ }
24
+ export declare function kebabCase(value: string): string;
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PlainEntrypointsConfigFileGeneratorPlugin = void 0;
7
+ exports.kebabCase = kebabCase;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const shared_1 = require("../shared");
10
+ const php_writer_1 = require("../utils/php-writer");
11
+ const pseduo_semaphore_1 = require("../utils/pseduo-semaphore");
12
+ const webpack_1 = require("webpack");
13
+ class PlainEntrypointsConfigFileGeneratorPlugin {
14
+ buildRoot;
15
+ outputDir;
16
+ usageLocations;
17
+ handlePrefix;
18
+ static semaphore = new pseduo_semaphore_1.PseudoSemaphore([]);
19
+ static cache = undefined;
20
+ id;
21
+ constructor(buildRoot, outputDir, usageLocations, handlePrefix) {
22
+ this.buildRoot = buildRoot;
23
+ this.outputDir = outputDir;
24
+ this.usageLocations = usageLocations;
25
+ this.handlePrefix = handlePrefix;
26
+ this.id = Math.random().toString();
27
+ PlainEntrypointsConfigFileGeneratorPlugin.semaphore.register(this.id);
28
+ }
29
+ apply(compiler) {
30
+ const tapName = { name: `${this.constructor.name}_ProcessPlainEntrypointFiles`, stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_REPORT };
31
+ compiler.hooks.compilation.tap(this.constructor.name, compilation => {
32
+ PlainEntrypointsConfigFileGeneratorPlugin.cache = undefined;
33
+ PlainEntrypointsConfigFileGeneratorPlugin.semaphore.reset(this.id);
34
+ compilation.hooks.processAssets.tapPromise(tapName, async (assets) => {
35
+ try {
36
+ const emitDir = node_path_1.default.join(this.buildRoot, this.outputDir);
37
+ if (PlainEntrypointsConfigFileGeneratorPlugin.cache === undefined) {
38
+ PlainEntrypointsConfigFileGeneratorPlugin.cache = { assets: [] };
39
+ compilation.hooks.afterProcessAssets.tap(`${this.constructor.name}_CompileLoader`, () => {
40
+ PlainEntrypointsConfigFileGeneratorPlugin.afterProcessAssets(compilation, emitDir);
41
+ });
42
+ }
43
+ const rawAssetDataSource = assets["assets.json"]?.source();
44
+ if (typeof rawAssetDataSource !== 'string') {
45
+ PlainEntrypointsConfigFileGeneratorPlugin.semaphore.reject(this.id);
46
+ compilation.errors.push(new Error("assets.json is unexpectedly missing or not a string"));
47
+ return;
48
+ }
49
+ const assetDataSource = JSON.parse(rawAssetDataSource);
50
+ if (!(0, shared_1.isRawAssetData)(assetDataSource)) {
51
+ PlainEntrypointsConfigFileGeneratorPlugin.semaphore.reject(this.id);
52
+ compilation.errors.push(new Error("assets.json is does not match the RawAssetData format"));
53
+ return;
54
+ }
55
+ const isScriptRegex = /\.m?[jt]sx?(\?|$)/i;
56
+ const myAssetHandles = [];
57
+ for (const entrypoint of compilation.entrypoints.values()) {
58
+ const srcPath = entrypoint.origins.map(origin => origin.request)[0];
59
+ if (!srcPath) {
60
+ continue;
61
+ }
62
+ const entrypointChunk = entrypoint.getEntrypointChunk();
63
+ const assetData = entrypointChunk.files.values()
64
+ .map(file => assetDataSource[file]).find(v => v !== undefined);
65
+ if (!assetData) {
66
+ continue;
67
+ }
68
+ const handles = [];
69
+ const entrypointChunkIsScript = isScriptRegex.test(srcPath);
70
+ const chunkFiles = entrypoint.chunks.flatMap(chunk => {
71
+ return [...chunk.files].filter(file => entrypointChunkIsScript || !isScriptRegex.test(file))
72
+ .map(file => [file, chunk === entrypointChunk]);
73
+ });
74
+ if (chunkFiles.length === 1) {
75
+ chunkFiles[0][1] = true;
76
+ }
77
+ for (const [file, useHandleName] of chunkFiles) {
78
+ const isScript = file.toLowerCase().endsWith(".js");
79
+ const dependencies = isScript === entrypointChunkIsScript ? assetData.dependencies : [];
80
+ const rest = isScript && this.usageLocations.registerScriptArgs !== undefined
81
+ ? [dependencies, assetData.version, this.usageLocations.registerScriptArgs] : [dependencies, assetData.version];
82
+ const destPath = node_path_1.default.join(compilation.outputOptions.path, file);
83
+ handles.push({
84
+ src: destPath,
85
+ rest,
86
+ locations: this.usageLocations,
87
+ isScript,
88
+ handleName: useHandleName ? this.usageLocations.handle : undefined
89
+ });
90
+ }
91
+ myAssetHandles.push({
92
+ handles,
93
+ usageLocations: this.usageLocations,
94
+ handlePrefix: this.handlePrefix
95
+ });
96
+ }
97
+ compilation.deleteAsset("assets.json");
98
+ PlainEntrypointsConfigFileGeneratorPlugin.semaphore.resolve(this.id, myAssetHandles);
99
+ PlainEntrypointsConfigFileGeneratorPlugin.cache.assets = (await PlainEntrypointsConfigFileGeneratorPlugin.semaphore.wait()).flat();
100
+ }
101
+ catch (e) {
102
+ PlainEntrypointsConfigFileGeneratorPlugin.semaphore.reject(this.id);
103
+ throw e;
104
+ }
105
+ });
106
+ });
107
+ }
108
+ static afterProcessAssets(compilation, emitDir) {
109
+ //TODO: Add support for editorStyles via the 'block_editor_settings_all' filter
110
+ const handleLists = {
111
+ register: [],
112
+ clientView: [],
113
+ clientEditor: [],
114
+ admin: [],
115
+ login: [],
116
+ customizer: [],
117
+ analytics: []
118
+ };
119
+ const plainEntrypointsConfig = { scriptHandles: {}, styleHandles: {} };
120
+ for (const { handles, handlePrefix } of PlainEntrypointsConfigFileGeneratorPlugin.cache?.assets ?? []) {
121
+ for (const { src, rest, locations, isScript, handleName } of handles) {
122
+ const finalHandleName = handleName || `${handlePrefix}.${kebabCase(node_path_1.default.basename(src))}`;
123
+ plainEntrypointsConfig[isScript ? 'scriptHandles' : 'styleHandles'][finalHandleName] = {
124
+ src,
125
+ rest,
126
+ locations,
127
+ isScript
128
+ };
129
+ }
130
+ }
131
+ PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists('script', Object.entries(plainEntrypointsConfig.scriptHandles)
132
+ .toSorted(([a], [b]) => a.localeCompare(b)), handleLists);
133
+ const sortedStyleHandles = Object.entries(plainEntrypointsConfig.styleHandles)
134
+ .toSorted(([a], [b]) => a.localeCompare(b));
135
+ PlainEntrypointsConfigFileGeneratorPlugin.addHandlesToHandleLists('style', sortedStyleHandles, handleLists);
136
+ //TODO: Do we want to sort by type first? Pro: all calls of the same type are together; Con: not all calls for the same block will be together
137
+ const callItemSorter = (a, b) => /*a.type.localeCompare(b.type) || */ a.handle.localeCompare(b.handle);
138
+ for (const list of Object.values(handleLists)) {
139
+ list.sort(callItemSorter);
140
+ }
141
+ const writer = new php_writer_1.PHPWriter();
142
+ if (handleLists.register.length > 0) {
143
+ PlainEntrypointsConfigFileGeneratorPlugin.emitResolveBaseUriFunction(writer);
144
+ for (const [priority, prioritizedHandleList] of PlainEntrypointsConfigFileGeneratorPlugin.separateHandleListByPriority(handleLists.register)) {
145
+ writer.action("init", writer => {
146
+ writer.call("plaudit_webpack_extensions__resolve_base_uri", [], { assignTo: "$base_uri" });
147
+ for (const { handle, type, data } of prioritizedHandleList) {
148
+ writer.call(`wp_register_${type}`, [handle, new php_writer_1.Expr(`$base_uri.${php_writer_1.Expr.jsonToPHPConverter(node_path_1.default.relative(emitDir, data.src))}`), ...data.rest]);
149
+ }
150
+ }, { priority });
151
+ }
152
+ const sortedEditorStyleHandles = sortedStyleHandles
153
+ .filter(([_, { locations: { clientEditor }, isScript }]) => !isScript && (clientEditor || typeof clientEditor === 'number'))
154
+ .map(info => info[1]);
155
+ if (sortedEditorStyleHandles.length > 0) {
156
+ writer.linebreak();
157
+ const themePath = process.cwd();
158
+ for (const handleData of sortedEditorStyleHandles) {
159
+ const handlePath = node_path_1.default.relative(themePath, handleData.src);
160
+ writer.call("add_editor_style", [(0, shared_1.leadingSlashIt)(handlePath)]);
161
+ }
162
+ }
163
+ }
164
+ PlainEntrypointsConfigFileGeneratorPlugin.appendEnqueuingHandleLists(writer, handleLists);
165
+ const assetPath = PlainEntrypointsConfigFileGeneratorPlugin.getEmitPath(compilation, emitDir);
166
+ writer.emitAsset(compilation, assetPath);
167
+ }
168
+ static getEmitPath(compilation, emitDir) {
169
+ return node_path_1.default.relative(compilation.outputOptions.path ?? process.cwd(), node_path_1.default.join(emitDir, "plain-entrypoints-loader.php"));
170
+ }
171
+ static addHandlesToHandleLists(type, handles, handleLists) {
172
+ for (const [handle, data] of handles) {
173
+ if (data.locations.register !== false) {
174
+ handleLists.register.push({ handle, type, data, priority: typeof data.locations.register === 'number' ? data.locations.register : 10 });
175
+ }
176
+ for (const location of shared_1.standardLocationNames) {
177
+ if (location === 'clientEditor' && type === 'style') {
178
+ // We don't include editor styles in the handle list because editor styles are enqueued via a completely separate mechanism at runtime and, therefore,
179
+ // cannot be handled by the same code as every other enqueueable item
180
+ continue;
181
+ }
182
+ if (typeof data.locations[location] === 'number') {
183
+ handleLists[location].push({ handle, type, priority: data.locations[location] });
184
+ }
185
+ else if (data.locations[location]) {
186
+ handleLists[location].push({ handle, type, priority: 10 });
187
+ }
188
+ }
189
+ }
190
+ }
191
+ static appendEnqueuingHandleLists(writer, handleLists) {
192
+ const enqueuingHandleActions = [
193
+ ["wp_enqueue_scripts", handleLists.clientView], ["enqueue_block_editor_assets", handleLists.clientEditor], ["admin_enqueue_scripts", handleLists.admin],
194
+ ["login_enqueue_scripts", handleLists.login], ["customize_controls_enqueue_scripts", handleLists.customizer], ["plaudit_enqueue_analytics", handleLists.analytics]
195
+ ];
196
+ for (const [action, handleList] of enqueuingHandleActions) {
197
+ if (handleList.length > 0) {
198
+ for (const [priority, prioritizedHandleList] of PlainEntrypointsConfigFileGeneratorPlugin.separateHandleListByPriority(handleList)) {
199
+ writer.action(action, () => {
200
+ for (const handle of prioritizedHandleList) {
201
+ writer.call(`wp_enqueue_${handle.type}`, [handle.handle]);
202
+ }
203
+ }, { priority });
204
+ }
205
+ }
206
+ }
207
+ }
208
+ static separateHandleListByPriority(handleItems) {
209
+ const lists = new Map();
210
+ for (const handleItem of handleItems) {
211
+ const list = lists.get(handleItem.priority);
212
+ if (list) {
213
+ list.push(handleItem);
214
+ }
215
+ else {
216
+ lists.set(handleItem.priority, [handleItem]);
217
+ }
218
+ }
219
+ return lists.entries().toArray().sort((a, b) => a[0] - b[0]);
220
+ }
221
+ /**
222
+ * The primary benefit of emitting a function instead of baking its contents into each function that uses it is that it allows us to avoid recomputing the base uri multiple times
223
+ */
224
+ static emitResolveBaseUriFunction(writer) {
225
+ writer.function("plaudit_webpack_extensions__resolve_base_uri", [], () => {
226
+ writer
227
+ .static("$base_uri", { withTest: 'chainable' })
228
+ .elseIf("str_starts_with(__DIR__, ABSPATH)")
229
+ .append("$path = ltrim(substr(__DIR__, strlen(ABSPATH)), '/');")
230
+ .elseIf("str_starts_with(__DIR__, '/workspace/website')")
231
+ .append("$path = ltrim(substr(__DIR__, 18), '/');")
232
+ .else()
233
+ .call("error_log", ["UNABLE TO FIGURE OUT WHAT THE RELATIVE PATH TO THE BUILT FILES DIRECTORY SHOULD BE"])
234
+ .append("$path = '';")
235
+ .endIf()
236
+ .append("return $base_uri = trailingslashit(home_url($path));");
237
+ }, { returnType: "string", includeExistenceCheck: true });
238
+ }
239
+ }
240
+ exports.PlainEntrypointsConfigFileGeneratorPlugin = PlainEntrypointsConfigFileGeneratorPlugin;
241
+ function kebabCase(value) {
242
+ const kebabCaseRegexes = [[/([a-z])([A-Z])/g, "$1-$2"], [/[\s_.\-]+/g, "-"]];
243
+ return kebabCaseRegexes.reduce((str, [pattern, replacement]) => str.replace(pattern, replacement), value).toLowerCase();
244
+ }