@atomicsmash/cli 9.0.1-beta.3 → 10.0.0-beta.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/package.json +4 -38
- package/dist/commands/blocks.d.ts +0 -77
- package/dist/commands/blocks.d.ts.map +0 -1
- package/dist/commands/blocks.js +0 -550
- package/dist/external/utils.cjs +0 -230
- package/dist/external/utils.d.cts +0 -19
- package/dist/external/utils.d.cts.map +0 -1
- package/dist/external/wordpress.webpack.config.cjs +0 -438
- package/dist/external/wordpress.webpack.config.d.cts +0 -6
- package/dist/external/wordpress.webpack.config.d.cts.map +0 -1
package/dist/commands/blocks.js
DELETED
|
@@ -1,550 +0,0 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
-
import { basename, dirname, extname, join, resolve as resolvePath, sep as pathSeparator, } from "node:path";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { DatePHP } from "@atomicsmash/date-php";
|
|
7
|
-
import autoprefixer from "autoprefixer";
|
|
8
|
-
import CopyWebpackPlugin from "copy-webpack-plugin";
|
|
9
|
-
import { cosmiconfig } from "cosmiconfig";
|
|
10
|
-
import cssnano from "cssnano";
|
|
11
|
-
import glob from "glob-promise";
|
|
12
|
-
import postcss from "postcss";
|
|
13
|
-
import postcssLoadConfig from "postcss-load-config";
|
|
14
|
-
import { compileStringAsync } from "sass";
|
|
15
|
-
import { TsconfigPathsPlugin } from "tsconfig-paths-webpack-plugin";
|
|
16
|
-
import { tsImport } from "tsx/esm/api";
|
|
17
|
-
import webpack from "webpack";
|
|
18
|
-
import defaultConfig from "../external/wordpress.webpack.config.cjs";
|
|
19
|
-
import { getSmashConfig, startRunningMessage, toCamelCase } from "../utils.js";
|
|
20
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
21
|
-
export const command = "blocks";
|
|
22
|
-
export const describe = "A command to generate WordPress blocks from a src folder.";
|
|
23
|
-
export const deprecated = "Please migrate to using the @atomicsmash/compiler package. This command will be removed in the next major version.";
|
|
24
|
-
export const builder = function (yargs) {
|
|
25
|
-
return yargs
|
|
26
|
-
.options({
|
|
27
|
-
in: {
|
|
28
|
-
string: true,
|
|
29
|
-
normalize: true,
|
|
30
|
-
description: "The directory where the WP blocks can be found. Relative to cwd.",
|
|
31
|
-
},
|
|
32
|
-
out: {
|
|
33
|
-
string: true,
|
|
34
|
-
normalize: true,
|
|
35
|
-
description: "The directory where the WP blocks will be output. Relative to cwd.",
|
|
36
|
-
},
|
|
37
|
-
tsConfigPath: {
|
|
38
|
-
string: true,
|
|
39
|
-
normalize: true,
|
|
40
|
-
description: "The directory where the tsconfig file can be found. Relative to cwd. Defaults to the in folder.",
|
|
41
|
-
},
|
|
42
|
-
postcssConfigPath: {
|
|
43
|
-
string: true,
|
|
44
|
-
normalize: true,
|
|
45
|
-
description: "The directory where the postcss config file can be found. Relative to cwd. Defaults to the in folder and then searches up the directory tree.",
|
|
46
|
-
},
|
|
47
|
-
watch: {
|
|
48
|
-
boolean: true,
|
|
49
|
-
default: false,
|
|
50
|
-
description: "Watch the blocks in folder for changes and compile.",
|
|
51
|
-
},
|
|
52
|
-
excludeBlocks: {
|
|
53
|
-
array: true,
|
|
54
|
-
string: true,
|
|
55
|
-
default: ["__TEMPLATE__"],
|
|
56
|
-
description: "A list of the folder names of blocks to exclude from compilation.",
|
|
57
|
-
},
|
|
58
|
-
excludeRootFiles: {
|
|
59
|
-
array: true,
|
|
60
|
-
string: true,
|
|
61
|
-
default: [],
|
|
62
|
-
description: "A list of the root file names to exclude from compilation.",
|
|
63
|
-
},
|
|
64
|
-
alwaysCompileRootFiles: {
|
|
65
|
-
boolean: true,
|
|
66
|
-
default: false,
|
|
67
|
-
description: "By default, we won't compile root files if no blocks are found, this allows you to override that setting.",
|
|
68
|
-
},
|
|
69
|
-
ignoreWarnings: {
|
|
70
|
-
boolean: true,
|
|
71
|
-
default: false,
|
|
72
|
-
description: "If webpack has warnings, don't output them or change error code.",
|
|
73
|
-
},
|
|
74
|
-
})
|
|
75
|
-
.example("$0 blocks --watch --in src --out build --tsConfigPath tsconfig.json", "");
|
|
76
|
-
};
|
|
77
|
-
export async function handler(args) {
|
|
78
|
-
const smashConfig = await getSmashConfig();
|
|
79
|
-
const srcFolder = args.in
|
|
80
|
-
? resolvePath(args.in)
|
|
81
|
-
: smashConfig?.themePath
|
|
82
|
-
? resolvePath(join(smashConfig?.themePath, "src", "blocks"))
|
|
83
|
-
: null;
|
|
84
|
-
const distFolder = args.out
|
|
85
|
-
? resolvePath(args.out)
|
|
86
|
-
: smashConfig?.themePath
|
|
87
|
-
? resolvePath(join(smashConfig?.themePath, smashConfig.assetsOutputFolder, "blocks"))
|
|
88
|
-
: null;
|
|
89
|
-
if (!srcFolder || !distFolder) {
|
|
90
|
-
throw new Error("Failed to get the in or out folders for the blocks. Please add a smash.config.ts file to your project with a themeName and a themePath.");
|
|
91
|
-
}
|
|
92
|
-
const tsConfigLocation = args.tsConfigPath ?? srcFolder;
|
|
93
|
-
const postcssConfigLocation = args.postcssConfigPath ?? srcFolder;
|
|
94
|
-
const excludeBlocks = args.excludeBlocks;
|
|
95
|
-
const excludeRootFiles = args.excludeRootFiles;
|
|
96
|
-
const shouldAlwaysCompileRootFiles = args.alwaysCompileRootFiles;
|
|
97
|
-
const isWatch = args.watch;
|
|
98
|
-
const shouldIgnoreWarnings = args.ignoreWarnings;
|
|
99
|
-
await runCommand({
|
|
100
|
-
srcFolder,
|
|
101
|
-
distFolder,
|
|
102
|
-
tsConfigLocation,
|
|
103
|
-
postcssConfigLocation,
|
|
104
|
-
excludeBlocks,
|
|
105
|
-
excludeRootFiles,
|
|
106
|
-
shouldAlwaysCompileRootFiles,
|
|
107
|
-
isWatch,
|
|
108
|
-
shouldIgnoreWarnings,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
async function runCommand({ srcFolder, distFolder, tsConfigLocation, postcssConfigLocation, excludeBlocks, excludeRootFiles, shouldAlwaysCompileRootFiles, isWatch, shouldIgnoreWarnings, }) {
|
|
112
|
-
const blocks = await getBlockJsonFiles(srcFolder, excludeBlocks);
|
|
113
|
-
const isNoBlocks = Object.keys(blocks).length === 0;
|
|
114
|
-
const compiler = webpack({
|
|
115
|
-
...defaultConfig,
|
|
116
|
-
entry: async () => {
|
|
117
|
-
const entryPoints = {
|
|
118
|
-
...(isNoBlocks && !shouldAlwaysCompileRootFiles
|
|
119
|
-
? {}
|
|
120
|
-
: await getRootFileJSEntryPoints({ srcFolder, excludeRootFiles })),
|
|
121
|
-
...getAllBlocksJSEntryPoints({
|
|
122
|
-
srcFolder,
|
|
123
|
-
blocks,
|
|
124
|
-
}),
|
|
125
|
-
};
|
|
126
|
-
// TODO: improve this console output
|
|
127
|
-
console.log({ entryPoints });
|
|
128
|
-
console.log(`EntryPoints set at ${new DatePHP().format("jS F Y \\a\\t H:i:s")}`);
|
|
129
|
-
return entryPoints;
|
|
130
|
-
},
|
|
131
|
-
context: srcFolder,
|
|
132
|
-
plugins: [
|
|
133
|
-
...(defaultConfig.plugins?.filter((plugin) => {
|
|
134
|
-
return ![
|
|
135
|
-
"CopyPlugin", // CopyWebpackPlugin
|
|
136
|
-
].includes(plugin?.constructor.name ?? "");
|
|
137
|
-
}) ?? []),
|
|
138
|
-
...(isNoBlocks
|
|
139
|
-
? []
|
|
140
|
-
: [
|
|
141
|
-
new CopyWebpackPlugin({
|
|
142
|
-
patterns: [...getCopyPatternsForBlocks(blocks, distFolder)],
|
|
143
|
-
}),
|
|
144
|
-
]),
|
|
145
|
-
new CustomBlocksCSSHandler({
|
|
146
|
-
blocks,
|
|
147
|
-
srcFolder,
|
|
148
|
-
distFolder,
|
|
149
|
-
postcssConfigLocation,
|
|
150
|
-
}),
|
|
151
|
-
new BlockJsonAssetTransformer({ distFolder }),
|
|
152
|
-
],
|
|
153
|
-
output: {
|
|
154
|
-
path: distFolder,
|
|
155
|
-
clean: true,
|
|
156
|
-
},
|
|
157
|
-
resolve: {
|
|
158
|
-
plugins: [
|
|
159
|
-
new TsconfigPathsPlugin({
|
|
160
|
-
configFile: tsConfigLocation,
|
|
161
|
-
}),
|
|
162
|
-
],
|
|
163
|
-
extensions: [".ts", ".js", ".tsx", ".jsx"],
|
|
164
|
-
},
|
|
165
|
-
mode: isProduction ? "production" : "development",
|
|
166
|
-
node: { global: false },
|
|
167
|
-
});
|
|
168
|
-
if (!compiler) {
|
|
169
|
-
throw new Error("Failed to initialise compiler.");
|
|
170
|
-
}
|
|
171
|
-
if (isWatch) {
|
|
172
|
-
console.log("The blocks command is now deprecated. " + deprecated);
|
|
173
|
-
const watching = compiler.watch({
|
|
174
|
-
// Example
|
|
175
|
-
aggregateTimeout: 300,
|
|
176
|
-
poll: undefined,
|
|
177
|
-
}, (error, stats) => {
|
|
178
|
-
if (error) {
|
|
179
|
-
console.error(error.stack ?? error);
|
|
180
|
-
process.exitCode = 1;
|
|
181
|
-
watching?.close((closeError) => {
|
|
182
|
-
console.error(closeError);
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
else if (stats) {
|
|
186
|
-
const info = stats.toJson();
|
|
187
|
-
if (stats.hasErrors()) {
|
|
188
|
-
console.error(info.errors);
|
|
189
|
-
}
|
|
190
|
-
if (stats.hasWarnings() && !shouldIgnoreWarnings) {
|
|
191
|
-
console.warn(info.warnings);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
process.on("SIGINT", function () {
|
|
196
|
-
watching?.close((closeError) => {
|
|
197
|
-
console.log("The blocks command is now deprecated. " + deprecated);
|
|
198
|
-
console.error(closeError);
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
const stopRunningMessage = startRunningMessage("Building blocks");
|
|
204
|
-
compiler.run((error, stats) => {
|
|
205
|
-
void stopRunningMessage();
|
|
206
|
-
if (error) {
|
|
207
|
-
console.error(error);
|
|
208
|
-
process.exitCode = 1;
|
|
209
|
-
}
|
|
210
|
-
else if (stats) {
|
|
211
|
-
const info = stats.toJson();
|
|
212
|
-
if (stats.hasErrors()) {
|
|
213
|
-
console.error(info.errors);
|
|
214
|
-
process.exitCode = 1;
|
|
215
|
-
}
|
|
216
|
-
if (stats.hasWarnings() && !shouldIgnoreWarnings) {
|
|
217
|
-
console.warn(info.warnings);
|
|
218
|
-
if (!stats.hasErrors()) {
|
|
219
|
-
// Only change exit code if there's no errors.
|
|
220
|
-
process.exitCode = 2;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
compiler.close((closeError) => {
|
|
225
|
-
console.log("The blocks command is now deprecated. " + deprecated);
|
|
226
|
-
if (closeError) {
|
|
227
|
-
process.exitCode = 1;
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
export async function getRootFileJSEntryPoints({ srcFolder, excludeRootFiles, }) {
|
|
234
|
-
const entryPoints = {};
|
|
235
|
-
const rootFiles = await readdir(srcFolder, {
|
|
236
|
-
withFileTypes: true,
|
|
237
|
-
}).then((srcDirFiles) => {
|
|
238
|
-
return srcDirFiles
|
|
239
|
-
.filter((dirent) => dirent.isFile())
|
|
240
|
-
.map((dirent) => dirent.name);
|
|
241
|
-
});
|
|
242
|
-
for (const fileNameAndExtension of rootFiles) {
|
|
243
|
-
const [entryName, extension] = fileNameAndExtension.split(".", 2);
|
|
244
|
-
if (excludeRootFiles.includes(entryName) ||
|
|
245
|
-
excludeRootFiles.includes(fileNameAndExtension)) {
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
if (entryName === undefined || extension === undefined) {
|
|
249
|
-
console.log(`Failed to process ${fileNameAndExtension}, continuing anyway.`);
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
if (!["ts", "js", "tsx", "jsx"].includes(extension)) {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
entryPoints[toCamelCase(entryName)] = {
|
|
256
|
-
import: [srcFolder, fileNameAndExtension].join(pathSeparator),
|
|
257
|
-
filename: `${entryName}${isProduction ? `.[contenthash]` : ""}.js`,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
return entryPoints;
|
|
261
|
-
}
|
|
262
|
-
export function getAllBlocksJSEntryPoints({ srcFolder, blocks, }) {
|
|
263
|
-
const entryPoints = {};
|
|
264
|
-
const blocksJsonObjects = Object.values(blocks);
|
|
265
|
-
for (const { blockFolder, blockJson } of blocksJsonObjects) {
|
|
266
|
-
const fields = getBlockJsonScriptFields(blockJson);
|
|
267
|
-
if (!fields) {
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
for (const value of Object.values(fields).flat()) {
|
|
271
|
-
if ("string" !== typeof value || !value.startsWith("file:")) {
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
// Removes the `file:` prefix.
|
|
275
|
-
const filepath = join(blockFolder, value.replace("file:", ""));
|
|
276
|
-
const entryName = filepath
|
|
277
|
-
.replace(srcFolder, "")
|
|
278
|
-
.replace(extname(filepath), "")
|
|
279
|
-
.replace(/\\/g, "/")
|
|
280
|
-
.split("/")
|
|
281
|
-
.filter((string) => string !== "")
|
|
282
|
-
.join("-");
|
|
283
|
-
entryPoints[toCamelCase(entryName)] = {
|
|
284
|
-
import: filepath,
|
|
285
|
-
filename: `${filepath.replace(`${srcFolder}${pathSeparator}`, "").replace(extname(filepath), "")}${isProduction ? `.[contenthash]` : ""}.js`,
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return entryPoints;
|
|
290
|
-
}
|
|
291
|
-
export async function getBlockJsonFiles(srcFolder, excludeBlocks) {
|
|
292
|
-
const blockJsonFiles = await glob
|
|
293
|
-
.promise(`${srcFolder.replaceAll(pathSeparator, "/")}/**/block.json.ts`, {
|
|
294
|
-
ignore: excludeBlocks.map((blockName) => `${srcFolder.replaceAll(pathSeparator, "/")}/**/${blockName}/block.json.ts`),
|
|
295
|
-
})
|
|
296
|
-
.then((paths) => paths.map((path) => path.replaceAll("/", pathSeparator)));
|
|
297
|
-
const blocks = {};
|
|
298
|
-
for (const blockJsonFile of blockJsonFiles) {
|
|
299
|
-
const blockJson = await tsImport(`${blockJsonFile.split(pathSeparator).shift()?.includes(":") ? `file:///` : ""}${blockJsonFile}`, import.meta.url).then((loadedFile) => {
|
|
300
|
-
return loadedFile.blockJson;
|
|
301
|
-
});
|
|
302
|
-
const blockName = blockJsonFile
|
|
303
|
-
.replace(`${pathSeparator}block.json.ts`, "")
|
|
304
|
-
.split(pathSeparator)
|
|
305
|
-
.pop();
|
|
306
|
-
blocks[blockName] = { blockFolder: dirname(blockJsonFile), blockJson };
|
|
307
|
-
}
|
|
308
|
-
return blocks;
|
|
309
|
-
}
|
|
310
|
-
export function getBlockJsonScriptFields(blockJson) {
|
|
311
|
-
const scriptFields = ["viewScript", "script", "editorScript"];
|
|
312
|
-
let result = null;
|
|
313
|
-
for (const field of scriptFields) {
|
|
314
|
-
if (Object.hasOwn(blockJson, field)) {
|
|
315
|
-
result ??= {};
|
|
316
|
-
result[field] = blockJson[field];
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
export function getBlockJsonStyleFields(blockJson) {
|
|
322
|
-
const styleFields = ["viewStyle", "style", "editorStyle"];
|
|
323
|
-
let result = null;
|
|
324
|
-
for (const field of styleFields) {
|
|
325
|
-
if (Object.hasOwn(blockJson, field)) {
|
|
326
|
-
result ??= {};
|
|
327
|
-
result[field] = blockJson[field];
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return result;
|
|
331
|
-
}
|
|
332
|
-
async function getSassOptions(srcFolder) {
|
|
333
|
-
const smashConfig = await getSmashConfig();
|
|
334
|
-
if (smashConfig) {
|
|
335
|
-
return smashConfig.scssAliases;
|
|
336
|
-
}
|
|
337
|
-
// Legacy fallback.
|
|
338
|
-
const explorer = cosmiconfig("scssAliases");
|
|
339
|
-
const config = await explorer
|
|
340
|
-
.load(resolvePath(process.cwd(), "scssAliases.config.ts"))
|
|
341
|
-
.then((result) => {
|
|
342
|
-
if (!result || result.isEmpty) {
|
|
343
|
-
throw new Error("Return default config.");
|
|
344
|
-
}
|
|
345
|
-
const config = result.config;
|
|
346
|
-
if (typeof config === "object" &&
|
|
347
|
-
config !== null &&
|
|
348
|
-
Object.keys(config).length <= 2) {
|
|
349
|
-
if (Object.keys(config).length === 2 &&
|
|
350
|
-
"importers" in config &&
|
|
351
|
-
"loadPaths" in config) {
|
|
352
|
-
return config;
|
|
353
|
-
}
|
|
354
|
-
if (Object.keys(config).length === 1 &&
|
|
355
|
-
("importers" in config || "loadPaths" in config)) {
|
|
356
|
-
return config;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
throw new Error("Return default config.");
|
|
360
|
-
})
|
|
361
|
-
.catch(() => {
|
|
362
|
-
const defaultConfig = {
|
|
363
|
-
importers: [
|
|
364
|
-
{
|
|
365
|
-
findFileUrl(url) {
|
|
366
|
-
if (!url.startsWith("sitecss:"))
|
|
367
|
-
return null;
|
|
368
|
-
const pathname = url.substring(8);
|
|
369
|
-
return pathToFileURL(`${resolvePath(srcFolder, "../css")}${pathname.startsWith("/") ? pathname : `/${pathname}`}`);
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
findFileUrl(url) {
|
|
374
|
-
if (!url.startsWith("launchpad:"))
|
|
375
|
-
return null;
|
|
376
|
-
const pathname = url.substring(10);
|
|
377
|
-
return pathToFileURL(`${resolvePath(process.cwd(), "public/wp-content/themes/launchpad/src/styles")}${pathname.startsWith("/") ? pathname : `/${pathname}`}`);
|
|
378
|
-
},
|
|
379
|
-
},
|
|
380
|
-
],
|
|
381
|
-
};
|
|
382
|
-
return defaultConfig;
|
|
383
|
-
});
|
|
384
|
-
return config;
|
|
385
|
-
}
|
|
386
|
-
function getCopyPatternsForBlocks(blocks, distFolder) {
|
|
387
|
-
const blocksCopyPatterns = [];
|
|
388
|
-
const blocksEntries = Object.entries(blocks);
|
|
389
|
-
for (const [blockName, { blockFolder, blockJson }] of blocksEntries) {
|
|
390
|
-
// Generate block.json file.
|
|
391
|
-
blocksCopyPatterns.push({
|
|
392
|
-
from: `${blockFolder}/block.json.ts`,
|
|
393
|
-
to: `${distFolder}/${blockName}/block.json`,
|
|
394
|
-
noErrorOnMissing: true,
|
|
395
|
-
transform: {
|
|
396
|
-
transformer() {
|
|
397
|
-
return JSON.stringify(blockJson, null, 2);
|
|
398
|
-
},
|
|
399
|
-
cache: true,
|
|
400
|
-
},
|
|
401
|
-
});
|
|
402
|
-
// Copy render.php file
|
|
403
|
-
blocksCopyPatterns.push({
|
|
404
|
-
from: `${blockFolder}/render.php`,
|
|
405
|
-
to: `${distFolder}/${blockName}/render.php`,
|
|
406
|
-
noErrorOnMissing: true,
|
|
407
|
-
});
|
|
408
|
-
// Copy index.php file
|
|
409
|
-
blocksCopyPatterns.push({
|
|
410
|
-
from: `${blockFolder}/index.php`,
|
|
411
|
-
to: `${distFolder}/${blockName}/index.php`,
|
|
412
|
-
noErrorOnMissing: true,
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
return blocksCopyPatterns;
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* This class handles CSS compilation for blocks.
|
|
419
|
-
*/
|
|
420
|
-
class CustomBlocksCSSHandler {
|
|
421
|
-
blocks;
|
|
422
|
-
distFolder;
|
|
423
|
-
srcFolder;
|
|
424
|
-
postcssConfigLocation;
|
|
425
|
-
constructor({ blocks, srcFolder, distFolder, postcssConfigLocation, }) {
|
|
426
|
-
this.blocks = blocks;
|
|
427
|
-
this.srcFolder = srcFolder;
|
|
428
|
-
this.distFolder = distFolder;
|
|
429
|
-
this.postcssConfigLocation = postcssConfigLocation;
|
|
430
|
-
}
|
|
431
|
-
apply(compiler) {
|
|
432
|
-
// RawSource is one of the "sources" classes that should be used
|
|
433
|
-
// to represent asset sources in compilation.
|
|
434
|
-
const { RawSource } = webpack.sources;
|
|
435
|
-
compiler.hooks.thisCompilation.tap({
|
|
436
|
-
name: "CustomBlocksCSSHandler",
|
|
437
|
-
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
|
|
438
|
-
}, (compilation) => {
|
|
439
|
-
compilation.hooks.processAssets.tapPromise("CustomBlocksCSSHandler", async () => {
|
|
440
|
-
const blocksJsonObjects = Object.values(this.blocks);
|
|
441
|
-
for (const { blockFolder, blockJson } of blocksJsonObjects) {
|
|
442
|
-
const fields = getBlockJsonStyleFields(blockJson);
|
|
443
|
-
if (!fields) {
|
|
444
|
-
continue;
|
|
445
|
-
}
|
|
446
|
-
for (const value of Object.values(fields).flat()) {
|
|
447
|
-
if ("string" !== typeof value || !value.startsWith("file:")) {
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
// Removes the `file:` prefix.
|
|
451
|
-
const filepath = join(blockFolder, value.replace("file:", ""));
|
|
452
|
-
const fileNameWithExtension = basename(filepath);
|
|
453
|
-
let css = readFileSync(filepath, "utf8");
|
|
454
|
-
let newFileNameWithExtension = fileNameWithExtension;
|
|
455
|
-
if (extname(fileNameWithExtension) === ".scss") {
|
|
456
|
-
css = await compileStringAsync(css, await getSassOptions(this.srcFolder)).then((result) => result.css);
|
|
457
|
-
newFileNameWithExtension = newFileNameWithExtension.replace(".scss", ".css");
|
|
458
|
-
}
|
|
459
|
-
let newMatch = filepath.replace(`${this.srcFolder}/`, "");
|
|
460
|
-
if (isProduction) {
|
|
461
|
-
const fileBuffer = readFileSync(filepath);
|
|
462
|
-
const contentHash = crypto.createHash("shake256", {
|
|
463
|
-
outputLength: 10,
|
|
464
|
-
});
|
|
465
|
-
contentHash.update(fileBuffer);
|
|
466
|
-
newFileNameWithExtension = `${fileNameWithExtension.split(".")[0]}.${contentHash.digest("hex")}.css`;
|
|
467
|
-
}
|
|
468
|
-
newMatch = newMatch.replace(fileNameWithExtension, newFileNameWithExtension);
|
|
469
|
-
const { plugins, options } = await postcssLoadConfig({}, this.postcssConfigLocation)
|
|
470
|
-
.then(({ plugins, options }) => {
|
|
471
|
-
options.from = filepath;
|
|
472
|
-
options.to = newMatch;
|
|
473
|
-
return { plugins, options };
|
|
474
|
-
})
|
|
475
|
-
.catch((error) => {
|
|
476
|
-
if (error &&
|
|
477
|
-
error instanceof Error &&
|
|
478
|
-
error.message.startsWith("No PostCSS Config found")) {
|
|
479
|
-
const postCSSPlugins = [autoprefixer];
|
|
480
|
-
if (isProduction) {
|
|
481
|
-
postCSSPlugins.push(cssnano({ preset: "default" }));
|
|
482
|
-
}
|
|
483
|
-
return {
|
|
484
|
-
plugins: postCSSPlugins,
|
|
485
|
-
options: {
|
|
486
|
-
from: filepath,
|
|
487
|
-
to: newMatch,
|
|
488
|
-
},
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
throw error;
|
|
492
|
-
});
|
|
493
|
-
const result = await postcss(plugins).process(css, options);
|
|
494
|
-
compilation.emitAsset(newMatch, new RawSource(result.css));
|
|
495
|
-
if (result.map) {
|
|
496
|
-
compilation.emitAsset(`${newMatch}.map`, new RawSource(result.map.toString()));
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
class BlockJsonAssetTransformer {
|
|
505
|
-
distFolder;
|
|
506
|
-
constructor({ distFolder }) {
|
|
507
|
-
this.distFolder = distFolder;
|
|
508
|
-
}
|
|
509
|
-
apply(compiler) {
|
|
510
|
-
compiler.hooks.afterEmit.tapPromise("BlockJsonAssetTransformer", async (compilation) => {
|
|
511
|
-
const blockJsonFiles = Object.keys(compilation.assets).filter((asset) => asset.endsWith("block.json"));
|
|
512
|
-
for (const blockJsonFile of blockJsonFiles) {
|
|
513
|
-
await readFile(`${this.distFolder}/${blockJsonFile}`, "utf-8")
|
|
514
|
-
.then((data) => {
|
|
515
|
-
return JSON.parse(data);
|
|
516
|
-
})
|
|
517
|
-
.then((json) => {
|
|
518
|
-
const blockName = blockJsonFile.replace("/block.json", "");
|
|
519
|
-
[
|
|
520
|
-
getBlockJsonScriptFields(json),
|
|
521
|
-
getBlockJsonStyleFields(json),
|
|
522
|
-
].forEach((fields) => {
|
|
523
|
-
if (fields === null) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
Object.entries(fields).forEach(([fieldName, value]) => {
|
|
527
|
-
const values = [value].flat(1);
|
|
528
|
-
json[fieldName] = values.map((srcFilePath) => {
|
|
529
|
-
const fileName = srcFilePath
|
|
530
|
-
.replace("file:./", "")
|
|
531
|
-
.replace(extname(srcFilePath), "");
|
|
532
|
-
const hashedAsset = Object.keys(compilation.assets).find((asset) => {
|
|
533
|
-
return (asset.startsWith(`${blockName}/${fileName}`) &&
|
|
534
|
-
!asset.endsWith(".php"));
|
|
535
|
-
});
|
|
536
|
-
return hashedAsset
|
|
537
|
-
? `file:./${hashedAsset.replace(`${blockName}/`, "")}`
|
|
538
|
-
: srcFilePath;
|
|
539
|
-
});
|
|
540
|
-
});
|
|
541
|
-
});
|
|
542
|
-
return json;
|
|
543
|
-
})
|
|
544
|
-
.then(async (transformedJson) => {
|
|
545
|
-
await writeFile(`${this.distFolder}/${blockJsonFile}`, JSON.stringify(transformedJson, undefined, 2), "utf-8");
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
}
|