@atomicsmash/cli 3.0.0 → 3.1.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.
@@ -0,0 +1,3 @@
1
+ export declare const blocksHelpMessage = "\n Atomic Smash CLI - Blocks command.\n\n A command to generate WordPress blocks from a src folder.\n\n Options:\n --in The directory where the WP blocks can be found.\n --out The directory where the WP blocks will be output.\n\t\t--watch Watch the blocks in folder for changes and compile.\n\n Example usage:\n $ smash-cli blocks --watch --in src --out build\n";
2
+ export default function blocks(args: string[]): void;
3
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/commands/blocks.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,iBAAiB,+YAY7B,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QA+J5C"}
@@ -0,0 +1,223 @@
1
+ import crypto from "node:crypto";
2
+ import { readFileSync, writeFileSync, renameSync } from "node:fs";
3
+ import { readdir } from "node:fs/promises";
4
+ import { resolve as resolvePath, dirname as pathDirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import DependencyExtractionWebpackPlugin from "@wordpress/dependency-extraction-webpack-plugin";
7
+ import defaultConfig from "@wordpress/scripts/config/webpack.config.js";
8
+ import autoprefixer from "autoprefixer";
9
+ import cssnano from "cssnano";
10
+ import glob from "glob-promise";
11
+ import postcss from "postcss";
12
+ import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
13
+ import webpack from "webpack";
14
+ import { hasHelpFlag, toCamelCase } from "../utils.js";
15
+ // eslint-disable-next-line @typescript-eslint/naming-convention
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ // eslint-disable-next-line @typescript-eslint/naming-convention
18
+ const __dirname = pathDirname(__filename);
19
+ const isProduction = process.env.NODE_ENV === "production";
20
+ export const blocksHelpMessage = `
21
+ Atomic Smash CLI - Blocks command.
22
+
23
+ A command to generate WordPress blocks from a src folder.
24
+
25
+ Options:
26
+ --in The directory where the WP blocks can be found.
27
+ --out The directory where the WP blocks will be output.
28
+ --watch Watch the blocks in folder for changes and compile.
29
+
30
+ Example usage:
31
+ $ smash-cli blocks --watch --in src --out build
32
+ `;
33
+ export default function blocks(args) {
34
+ if (hasHelpFlag(args)) {
35
+ console.log(blocksHelpMessage);
36
+ return;
37
+ }
38
+ const inFlag = args[args.findIndex((arg) => arg === "--in") + 1];
39
+ if (!inFlag || inFlag.startsWith("--")) {
40
+ throw new Error("You need to provide a value for the --in flag.");
41
+ }
42
+ const outFlag = args[args.findIndex((arg) => arg === "--out") + 1];
43
+ if (!outFlag || outFlag.startsWith("--")) {
44
+ throw new Error("You need to provide a value for the --out flag.");
45
+ }
46
+ const srcFolder = resolvePath(inFlag);
47
+ const distFolder = resolvePath(outFlag);
48
+ const compiler = webpack({
49
+ ...defaultConfig,
50
+ entry: () => getAllBlocksJSEntryPoints(srcFolder),
51
+ plugins: [
52
+ ...(defaultConfig.plugins?.filter((plugin) => plugin.constructor.name !== "DependencyExtractionWebpackPlugin") ?? []),
53
+ new DependencyExtractionWebpackPlugin({
54
+ combineAssets: true,
55
+ }),
56
+ {
57
+ apply: (compiler) => {
58
+ compiler.hooks.afterEmit.tapPromise("Build CSS and copy to test area", async () => {
59
+ const plugins = [autoprefixer];
60
+ if (isProduction) {
61
+ plugins.push(cssnano({ preset: "default" }));
62
+ }
63
+ await glob
64
+ .promise(`${srcFolder}/blocks/**/*.css`)
65
+ .then(async (matches) => {
66
+ for (const match of matches) {
67
+ const fileName = match.split("/").pop();
68
+ if (!fileName) {
69
+ console.error(`Failed to get filename of ${match}`);
70
+ continue;
71
+ }
72
+ const css = readFileSync(match);
73
+ await postcss(plugins)
74
+ .process(css, {
75
+ from: match,
76
+ to: match.replace(srcFolder, distFolder),
77
+ })
78
+ .then((result) => {
79
+ writeFileSync(match.replace(srcFolder, distFolder), result.css);
80
+ if (result.map) {
81
+ writeFileSync(`${match.replace(srcFolder, distFolder)}.map`, result.map.toString());
82
+ }
83
+ });
84
+ }
85
+ })
86
+ .then(async () => {
87
+ if (!isProduction) {
88
+ return;
89
+ }
90
+ return glob
91
+ .promise(`${distFolder}/**/*.css`)
92
+ .then((matches) => {
93
+ for (const match of matches) {
94
+ const fileBuffer = readFileSync(match);
95
+ const contentHash = crypto.createHash("shake256", {
96
+ outputLength: 10,
97
+ });
98
+ contentHash.update(fileBuffer);
99
+ const fileMatchArray = match.split("/");
100
+ const newFileName = fileMatchArray
101
+ .pop()
102
+ ?.replace(".css", `.${contentHash.digest("hex")}.css`);
103
+ if (!newFileName || newFileName === "") {
104
+ throw new Error("Error getting CSS file name.");
105
+ }
106
+ renameSync(match, `${fileMatchArray.join("/")}/${newFileName}`);
107
+ }
108
+ return;
109
+ });
110
+ })
111
+ .catch((error) => {
112
+ console.error(error);
113
+ throw error;
114
+ });
115
+ });
116
+ },
117
+ },
118
+ ],
119
+ output: {
120
+ path: distFolder,
121
+ clean: true,
122
+ },
123
+ resolve: {
124
+ plugins: [new TsconfigPathsPlugin()],
125
+ extensions: [".ts", ".js", ".tsx", ".jsx"],
126
+ },
127
+ });
128
+ if (args.find((value) => value === "--watch")) {
129
+ const watching = compiler.watch({
130
+ // Example
131
+ aggregateTimeout: 300,
132
+ poll: undefined,
133
+ }, (error, stats) => {
134
+ if (error) {
135
+ console.error(error);
136
+ process.exitCode = 1;
137
+ watching.close((closeError) => {
138
+ console.error(closeError);
139
+ });
140
+ }
141
+ if (stats?.hasErrors()) {
142
+ for (const statError of stats.compilation.errors) {
143
+ console.error(statError.message);
144
+ }
145
+ }
146
+ });
147
+ process.on("SIGINT", function () {
148
+ watching.close((closeError) => {
149
+ console.error(closeError);
150
+ });
151
+ });
152
+ }
153
+ else {
154
+ compiler.run((error, stats) => {
155
+ if (error) {
156
+ console.error(error);
157
+ process.exitCode = 1;
158
+ }
159
+ if (stats?.hasErrors()) {
160
+ process.exitCode = 1;
161
+ }
162
+ compiler.close((closeError) => {
163
+ if (closeError) {
164
+ process.exitCode = 1;
165
+ }
166
+ });
167
+ });
168
+ }
169
+ }
170
+ async function getAllBlocksJSEntryPoints(srcFolder) {
171
+ const entryPoints = {
172
+ helpers: {
173
+ import: `${__dirname}/helpers.tsx`,
174
+ filename: `helpers${isProduction ? `.[contenthash]` : ""}.js`,
175
+ },
176
+ svgs: {
177
+ import: `${srcFolder}/svgs.tsx`,
178
+ filename: `svgs${isProduction ? `.[contenthash]` : ""}.js`,
179
+ },
180
+ };
181
+ const blockFolders = await readdir(`${srcFolder}/blocks`, {
182
+ withFileTypes: true,
183
+ }).then((srcDirFiles) => {
184
+ return srcDirFiles
185
+ .filter((dirent) => dirent.isDirectory())
186
+ .map((dirent) => dirent.name);
187
+ });
188
+ for (const block of blockFolders) {
189
+ const blockFiles = await readdir(`${srcFolder}/blocks/${block}`, {
190
+ withFileTypes: true,
191
+ }).then((blockDirFiles) => {
192
+ return blockDirFiles
193
+ .filter((file) => !file.isDirectory())
194
+ .map((file) => file.name)
195
+ .filter((file) => file.endsWith(".js") || file.endsWith(".ts"));
196
+ });
197
+ for (const blockJSFile of blockFiles) {
198
+ let blockBonusScriptNumber = 1;
199
+ const [filename] = blockJSFile.split(".");
200
+ let entryName;
201
+ if (filename === "index") {
202
+ entryName = block;
203
+ }
204
+ else if (filename === "editor") {
205
+ entryName = `${block}-editor`;
206
+ }
207
+ else if (filename === "view") {
208
+ entryName = `${block}-view`;
209
+ }
210
+ else {
211
+ entryName = `${block}-bonus${blockBonusScriptNumber}`;
212
+ blockBonusScriptNumber++;
213
+ }
214
+ entryPoints[toCamelCase(entryName)] = {
215
+ import: `${srcFolder}/blocks/${block}/${blockJSFile}`,
216
+ filename: `blocks/${block}/${filename}${isProduction ? `.[contenthash]` : ""}.js`,
217
+ };
218
+ }
219
+ }
220
+ // eslint-disable-next-line no-console
221
+ console.log({ entryPoints });
222
+ return entryPoints;
223
+ }
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ switch (commandArg) {
19
19
  console.log(packageJson.version);
20
20
  break;
21
21
  case "svg":
22
+ case "blocks":
22
23
  try {
23
24
  await import(`./commands/${commandArg}.js`).then((module) => module.default(process.argv.slice(3)));
24
25
  }
@@ -23,7 +23,7 @@ const __filename = fileURLToPath(import.meta.url);
23
23
  const __dirname = pathDirname(__filename);
24
24
  export async function setup() {
25
25
  console.log("Packing a test version...");
26
- await execute(`cd ${resolvePath(__dirname, "../")} && npm pack --pack-destination ${__dirname}/tests/artifacts`);
26
+ await execute(`cd ${resolvePath(__dirname, "../")} && ls && npm pack --pack-destination ${__dirname}/tests/artifacts`);
27
27
  console.log(`Install test package...`);
28
28
  writeFile(`${__dirname}/tests/package.json`, "{}", (err) => {
29
29
  if (err) {
package/dist/utils.d.ts CHANGED
@@ -10,4 +10,5 @@ export declare function execute(command: string): Promise<{
10
10
  stdout: string;
11
11
  stderr: string;
12
12
  }>;
13
+ export declare function toCamelCase(text: string): string;
13
14
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAY7C,eAAO,MAAM,WAAW,aAA4C,CAAC;AAIrE,eAAO,MAAM,WAAW,QAAkC,CAAC;AAC3D,eAAO,MAAM,WAAW,QAAwD,CAAC;AAIjF,eAAO,MAAM,cAAc,QAAsB,CAAC;AAElD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAEzC;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM;WAEpC,aAAa,GAAG,IAAI;YACnB,MAAM;YACN,MAAM;GASf"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAY7C,eAAO,MAAM,WAAW,aAA4C,CAAC;AAIrE,eAAO,MAAM,WAAW,QAAkC,CAAC;AAC3D,eAAO,MAAM,WAAW,QAAwD,CAAC;AAIjF,eAAO,MAAM,cAAc,QAAsB,CAAC;AAElD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAEzC;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM;WAEpC,aAAa,GAAG,IAAI;YACnB,MAAM;YACN,MAAM;GAUf;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,UAQvC"}
package/dist/utils.js CHANGED
@@ -24,9 +24,17 @@ export async function execute(command) {
24
24
  return new Promise((resolve, reject) => {
25
25
  exec(command, (error, stdout, stderr) => {
26
26
  if (error) {
27
+ console.error({ error, stdout, stderr });
27
28
  reject({ error, stdout, stderr });
28
29
  }
29
30
  resolve({ error, stdout, stderr });
30
31
  });
31
32
  });
32
33
  }
34
+ export function toCamelCase(text) {
35
+ return text.replace(/^([A-Z])|[\s-_](\w)/g, function (match, p1, p2) {
36
+ if (p2)
37
+ return p2.toUpperCase();
38
+ return p1.toLowerCase();
39
+ });
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomicsmash/cli",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "A collection of CLI tools by Atomic Smash.",
5
5
  "keywords": [],
6
6
  "homepage": "https://www.atomicsmash.co.uk/",
@@ -40,16 +40,26 @@
40
40
  "lint:types": "tsc -b"
41
41
  },
42
42
  "dependencies": {
43
+ "@wordpress/dependency-extraction-webpack-plugin": "^4.12.0",
44
+ "@wordpress/scripts": "^26.0.0",
45
+ "autoprefixer": "^10.4.14",
46
+ "cssnano": "^5.1.15",
43
47
  "glob": "^8.0.3",
44
48
  "glob-promise": "^6.0.2",
45
49
  "path": "^0.12.7",
50
+ "postcss": "^8.4.21",
46
51
  "svg-sprite": "^2.0.2",
47
- "vinyl": "^3.0.0"
52
+ "tsconfig-paths": "^4.1.2",
53
+ "tsconfig-paths-webpack-plugin": "^4.0.1",
54
+ "vinyl": "^3.0.0",
55
+ "webpack": "^5.76.3"
48
56
  },
49
57
  "devDependencies": {
50
58
  "@types/glob": "^8.0.0",
59
+ "@types/node": "^18.15.5",
51
60
  "@types/svg-sprite": "^0.0.34",
52
61
  "@types/vinyl": "^2.0.6",
62
+ "@types/webpack": "^5.28.0",
53
63
  "type-fest": "^3.0.0"
54
64
  }
55
65
  }