sdc-build-wp 4.2.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  npm install sdc-build-wp
5
5
  sdc-build-wp # build
6
6
  sdc-build-wp --watch # build and watch
7
+ sdc-build-wp --watch --builds=style,scripts # comma-seperated list of components to include
8
+ sdc-build-wp --help
7
9
  ```
8
10
 
9
11
  ## Develop
package/index.js CHANGED
@@ -1,150 +1,53 @@
1
1
  #!/usr/bin/env node
2
- import path from 'path';
3
2
  import parseArgs from 'minimist';
4
3
  const argv = parseArgs(process.argv.slice(2));
5
- import { glob } from 'node:fs/promises';
6
4
  import { promises as fs } from 'fs';
7
5
  import { Tail } from 'tail';
8
6
  import project from './lib/project.js';
9
- import * as utils from './lib/utils.js';
10
7
  import log from './lib/logging.js';
11
- import * as Components from './lib/components/index.js';
8
+ import * as LibComponents from './lib/components/index.js';
12
9
 
13
- const styleComponent = new Components.style();
14
- const scriptsComponent = new Components.scripts();
15
- const blocksComponent = new Components.blocks();
16
- const imagesComponent = new Components.images();
17
- const fontsComponent = new Components.fonts();
18
- const phpComponent = new Components.php();
19
- const serverComponent = new Components.server();
10
+ project.components = Object.fromEntries(Object.entries(LibComponents).map(([name, Class]) => [name, new Class()]));
20
11
 
21
- let builds = argv.builds ? argv.builds.split(',') : [
22
- 'sass',
23
- 'js',
24
- 'blocks',
25
- 'images',
26
- 'fonts',
27
- 'php'
28
- ];
12
+ if (argv.help || argv.h) {
13
+ console.log(`
14
+ Usage: sdc-build-wp [options] [arguments]
29
15
 
30
- for (const [name, files] of Object.entries(project.package.sdc.entries)) {
31
- project.entries[name] = [];
32
- files.forEach(function(file) {
33
- project.entries[name].push(project.path + file);
34
- });
35
- }
36
-
37
- for (const [name, files] of Object.entries(project.entries)) {
38
- files.forEach(function(file) {
39
- switch (path.parse(file).ext) {
40
- case '.scss':
41
- if (builds.includes('sass')) {
42
- project.files.sass.push({
43
- 'name': name,
44
- 'file': file
45
- });
46
- }
47
- break;
48
- case '.js':
49
- if (builds.includes('js')) {
50
- project.files.js.push({
51
- 'name': name,
52
- 'file': file
53
- });
54
- }
55
- break;
56
- }
57
- });
58
- }
16
+ Options:
17
+ -h, --help Show help message and exit
18
+ -w, --watch Build and watch
19
+ -b, --build BUILDS Build with specific components
59
20
 
60
- (async() => {
21
+ Components:
61
22
 
62
- let initialBuildTimerStart = Date.now();
63
- log('info', `Starting initial build`);
23
+ ${Object.entries(project.components).map(([key, component]) => {
24
+ return `${key}\t\t${component.description}\r\n`;
25
+ }).join('')}
26
+ Examples:
64
27
 
65
- if (builds.includes('sass')) {
66
- project.globs.sass = await Array.fromAsync(
67
- glob(project.package?.sdc?.sassGlobPath ||
68
- `${project.path}{/_src/style,/blocks}/**/*.scss`)
69
- );
70
- await styleComponent.process();
71
- }
28
+ sdc-build-wp
29
+ sdc-build-wp --watch
30
+ sdc-build-wp --watch --builds=style,scripts
31
+ `);
72
32
 
73
- if (builds.includes('js')) {
74
- project.globs.js = await Array.fromAsync(
75
- glob(project.package?.sdc?.jsGlobPath ||
76
- `${project.path}/_src/scripts/**/*.js`)
77
- );
78
- await scriptsComponent.process();
79
- }
33
+ process.exit(0);
34
+ }
80
35
 
81
- if (builds.includes('blocks')) {
82
- project.globs.blocks = await Array.fromAsync(
83
- glob(`${project.path}/blocks/*`)
84
- );
85
- project.globs.blocksSass = await Array.fromAsync(
86
- glob(`${project.path}/blocks/*/src/*.scss`)
87
- );
88
- // for (var filename of project.globs.blocksSass) {
89
- // project.entries[`blocks/${path.basename(path.dirname(filename))}/style`] = [ filename ];
90
- // }
91
- await blocksComponent.process();
92
- }
36
+ project.builds = argv.builds ? argv.builds.split(',') : Object.keys(project.components);
93
37
 
94
- if (builds.includes('images')) {
95
- project.globs.images = await Array.fromAsync(
96
- glob(project.package?.sdc?.imagesPath ||
97
- `${project.paths.images}/**/*`)
98
- );
99
- project.globs.imageDirectories = [
100
- project.paths.images,
101
- ...await utils.getAllSubdirectories(project.paths.images)
102
- ];
103
- await imagesComponent.process();
104
- }
105
-
106
- if (builds.includes('fonts')) {
107
- await fontsComponent.process();
108
- }
38
+ (async() => {
109
39
 
110
- if (builds.includes('php')) {
111
- project.globs.php = await Array.fromAsync(
112
- glob(project.package?.sdc?.jsGlobPath ||
113
- `${project.path}/**/*.php`)
114
- );
115
- project.globs.blocksPHP = await Array.fromAsync(
116
- glob(`${project.path}/blocks/*/build/*.php`)
117
- );
118
- project.chokidarOpts.ignored.concat(project.globs.blocksPHP);
119
- // await phpComponent.process(null, { lintType: 'warn' }); // this errors "Fatal error: Allowed memory size"
40
+ let initialBuildTimerStart = Date.now();
41
+ log('info', `Starting initial build`);
42
+ for (let build of project.builds) {
43
+ await project.components[build].init();
120
44
  }
121
-
122
45
  log('info', `Finished initial build in ${Math.round((Date.now() - initialBuildTimerStart) / 1000)} seconds`);
123
46
 
124
- if (argv.watch) {
125
-
126
- serverComponent.start();
127
-
128
- if (builds.includes('sass')) {
129
- styleComponent.watch();
130
- }
131
-
132
- if (builds.includes('js')) {
133
- scriptsComponent.watch();
47
+ if (argv.watch || argv.w) {
48
+ for (let build of project.builds) {
49
+ await project.components[build].watch();
134
50
  }
135
-
136
- if (builds.includes('blocks')) {
137
- blocksComponent.watch();
138
- }
139
-
140
- if (builds.includes('images')) {
141
- imagesComponent.watch();
142
- }
143
-
144
- if (builds.includes('php') && project.shouldPHPLint) {
145
- phpComponent.watch();
146
- }
147
-
148
51
  try {
149
52
  await fs.access(project.paths.errorLog);
150
53
  let errorLogTail = new Tail(project.paths.errorLog);
@@ -154,7 +57,6 @@ for (const [name, files] of Object.entries(project.entries)) {
154
57
  } catch (error) {
155
58
  log('info', `Cannot find error log @ ${project.paths.errorLog}. Skipping watching php error logs`);
156
59
  }
157
-
158
60
  }
159
61
 
160
62
  })();
@@ -1,16 +1,27 @@
1
1
  import path from 'path';
2
+ import * as utils from '../utils.js';
2
3
  import project from '../project.js';
3
4
  import log from '../logging.js';
4
5
  import chokidar from 'chokidar';
6
+ import { glob } from 'node:fs/promises';
5
7
 
6
8
  class BaseComponent {
7
9
 
8
10
  constructor() {
11
+ this.description = '';
9
12
  this.timer = null;
10
13
  this.path = path;
14
+ this.utils = utils;
11
15
  this.project = project;
12
16
  this.log = log;
13
17
  this.chokidar = chokidar;
18
+ this.glob = glob;
19
+ this.files = [];
20
+ this.globs = [];
21
+ }
22
+
23
+ async init() {
24
+ //
14
25
  }
15
26
 
16
27
  start() {
@@ -27,8 +38,8 @@ class BaseComponent {
27
38
  this.log('success', `${options.verb}${options.itemLabel ? ` ${options.itemLabel}` : ''} in ${options.timerEnd - options.timerStart}ms`);
28
39
  }
29
40
 
30
- entryBasename(entry) {
31
- return this.path.parse(entry).base;
41
+ async watch() {
42
+ //
32
43
  }
33
44
 
34
45
  }
@@ -3,10 +3,24 @@ import { stat } from 'fs/promises';
3
3
  import { spawn } from 'child_process';
4
4
  import process from 'process';
5
5
 
6
- class BlocksComponent extends BaseComponent {
6
+ export default class BlocksComponent extends BaseComponent {
7
7
 
8
8
  constructor() {
9
9
  super();
10
+ this.description = `Process the theme's WordPress blocks`;
11
+ }
12
+
13
+ async init() {
14
+ this.globs = await Array.fromAsync(
15
+ this.glob(`${this.project.path}/blocks/*`)
16
+ );
17
+ this.globsSass = await Array.fromAsync(
18
+ this.glob(`${this.project.path}/blocks/*/src/*.scss`)
19
+ );
20
+ // for (var filename of this.globsSass) {
21
+ // this.project.entries[`blocks/${this.path.basename(this.path.dirname(filename))}/style`] = [ filename ];
22
+ // }
23
+ await this.process();
10
24
  }
11
25
 
12
26
  async build(entry, options) {
@@ -55,13 +69,13 @@ class BlocksComponent extends BaseComponent {
55
69
  if (entry) {
56
70
  await this.build(entry);
57
71
  } else {
58
- const promisesBlocks = this.project.globs.blocks.map(block => this.build(block));
72
+ const promisesBlocks = this.globs.map(block => this.build(block));
59
73
  await Promise.all(promisesBlocks);
60
74
  }
61
75
  }
62
76
 
63
77
  watch() {
64
- for (let block of this.project.globs.blocks) {
78
+ for (let block of this.globs) {
65
79
  this.chokidar.watch(`${block}/src`, {
66
80
  ...this.project.chokidarOpts
67
81
  }).on('all', (event, path) => {
@@ -94,5 +108,3 @@ function cmd(commands) {
94
108
  });
95
109
  });
96
110
  }
97
-
98
- export { BlocksComponent as default }
@@ -2,10 +2,15 @@ import BaseComponent from './base.js';
2
2
  import { readdir } from 'fs/promises';
3
3
  import fs from 'fs-extra';
4
4
 
5
- class FontsComponent extends BaseComponent {
5
+ export default class FontsComponent extends BaseComponent {
6
6
 
7
7
  constructor() {
8
8
  super();
9
+ this.description = `Copy font files`;
10
+ }
11
+
12
+ async init() {
13
+ await this.process();
9
14
  }
10
15
 
11
16
  async build(entry) {
@@ -32,5 +37,3 @@ class FontsComponent extends BaseComponent {
32
37
  }
33
38
 
34
39
  }
35
-
36
- export { FontsComponent as default }
@@ -4,10 +4,23 @@ import imageminJpegtran from 'imagemin-jpegtran';
4
4
  import imageminPngquant from 'imagemin-pngquant';
5
5
  import imageminSvgo from 'imagemin-svgo';
6
6
 
7
- class ImagesComponent extends BaseComponent {
7
+ export default class ImagesComponent extends BaseComponent {
8
8
 
9
9
  constructor() {
10
10
  super();
11
+ this.description = `Compress image files`;
12
+ }
13
+
14
+ async init() {
15
+ this.globs = await Array.fromAsync(
16
+ this.glob(this.project.package?.sdc?.imagesPath ||
17
+ `${this.project.paths.images}/**/*`)
18
+ );
19
+ this.globsDirectories = [
20
+ this.project.paths.images,
21
+ ...await this.utils.getAllSubdirectories(this.project.paths.images)
22
+ ];
23
+ await this.process();
11
24
  }
12
25
 
13
26
  async build(entry, options) {
@@ -30,7 +43,7 @@ class ImagesComponent extends BaseComponent {
30
43
  }
31
44
 
32
45
  async process() {
33
- const promisesImages = this.project.globs.imageDirectories.map(directory => this.build(directory));
46
+ const promisesImages = this.globsDirectories.map(directory => this.build(directory));
34
47
  await Promise.all(promisesImages);
35
48
  }
36
49
 
@@ -43,5 +56,3 @@ class ImagesComponent extends BaseComponent {
43
56
  }
44
57
 
45
58
  }
46
-
47
- export { ImagesComponent as default }
@@ -3,13 +3,29 @@ import { fileURLToPath } from 'url';
3
3
  import { exec } from 'child_process';
4
4
  import { promisify } from 'util';
5
5
 
6
- class PHPComponent extends BaseComponent {
6
+ export default class PHPComponent extends BaseComponent {
7
7
 
8
8
  constructor() {
9
9
  super();
10
+ this.description = `Lint (and fix) php files`;
10
11
  this.execPromise = promisify(exec);
11
12
  }
12
13
 
14
+ async init() {
15
+ this.globs = await Array.fromAsync(
16
+ this.glob(this.project.package?.sdc?.phpGlobPath ||
17
+ `${this.project.path}/**/*.php`)
18
+ );
19
+ this.globsBlocks = await Array.fromAsync(
20
+ this.glob(`${this.project.path}/blocks/*/build/*.php`)
21
+ );
22
+ this.project.chokidarOpts.ignored = [
23
+ ...this.project.chokidarOpts.ignored,
24
+ ...this.globsBlocks
25
+ ];
26
+ // await this.process(null, { lintType: 'warn' }); // this errors "Fatal error: Allowed memory size"
27
+ }
28
+
13
29
  async build(entry, options) {
14
30
  options = Object.assign({}, {
15
31
  lintType: 'fix'
@@ -64,8 +80,8 @@ class PHPComponent extends BaseComponent {
64
80
  }
65
81
  }
66
82
  }
67
- if (this.project.server) {
68
- this.project.server.reload();
83
+ if (this.project.components.server?.server) {
84
+ this.project.components.server?.server.reload();
69
85
  }
70
86
 
71
87
  this.end({
@@ -79,7 +95,7 @@ class PHPComponent extends BaseComponent {
79
95
  }
80
96
 
81
97
  watch() {
82
- this.chokidar.watch(this.project.globs.php, {
98
+ this.chokidar.watch(this.globs, {
83
99
  ...this.project.chokidarOpts
84
100
  }).on('all', (event, path) => {
85
101
  this.process(path);
@@ -87,5 +103,3 @@ class PHPComponent extends BaseComponent {
87
103
  }
88
104
 
89
105
  }
90
-
91
- export { PHPComponent as default }
@@ -3,17 +3,27 @@ import * as esbuild from 'esbuild';
3
3
  import { ESLint } from 'eslint';
4
4
  import * as eslintConfig from '../../eslint.config.js';
5
5
 
6
- class ScriptsComponent extends BaseComponent {
6
+ export default class ScriptsComponent extends BaseComponent {
7
7
 
8
8
  constructor() {
9
9
  super();
10
+ this.description = `Process javascript files`;
11
+ }
12
+
13
+ async init() {
14
+ this.files = this.utils.addEntriesByFiletypes(['.js']);
15
+ this.globs = await Array.fromAsync(
16
+ this.glob(this.project.package?.sdc?.jsGlobPath ||
17
+ `${this.project.path}/_src/scripts/**/*.js`)
18
+ );
19
+ await this.process();
10
20
  }
11
21
 
12
22
  async build(entry, options) {
13
23
  options = Object.assign({}, {
14
24
  entriesToLint: null
15
25
  }, options);
16
- let entryLabel = `/dist/scripts/${this.entryBasename(entry).replace('.js', '.min.js')}`;
26
+ let entryLabel = `/dist/scripts/${this.utils.entryBasename(entry).replace('.js', '.min.js')}`;
17
27
 
18
28
  this.start();
19
29
 
@@ -60,12 +70,12 @@ class ScriptsComponent extends BaseComponent {
60
70
  }
61
71
 
62
72
  async process() {
63
- const promisesJS = this.project.files.js.map(block => this.build(block.file, { entriesToLint: this.project.globs.js }));
73
+ const promisesJS = this.files.map(block => this.build(block.file, { entriesToLint: this.globs }));
64
74
  await Promise.all(promisesJS);
65
75
  }
66
76
 
67
77
  watch() {
68
- this.chokidar.watch(this.project.globs.js, {
78
+ this.chokidar.watch(this.globs, {
69
79
  ...this.project.chokidarOpts
70
80
  }).on('all', (event, path) => {
71
81
  this.process();
@@ -73,5 +83,3 @@ class ScriptsComponent extends BaseComponent {
73
83
  }
74
84
 
75
85
  }
76
-
77
- export { ScriptsComponent as default }
@@ -1,12 +1,16 @@
1
1
  import BaseComponent from './base.js';
2
2
  import { create as bsCreate } from 'browser-sync';
3
3
 
4
- class ServerComponent extends BaseComponent {
4
+ export default class ServerComponent extends BaseComponent {
5
5
 
6
6
  constructor() {
7
7
  super();
8
+ this.description = `Run a dev proxy server for live reloading`;
8
9
  this.server = bsCreate();
9
- this.project.server = this.server;
10
+ }
11
+
12
+ async init() {
13
+ //
10
14
  }
11
15
 
12
16
  start() {
@@ -57,6 +61,8 @@ class ServerComponent extends BaseComponent {
57
61
  this.server.init(bsOptions);
58
62
  }
59
63
 
60
- }
64
+ async watch() {
65
+ this.start();
66
+ }
61
67
 
62
- export { ServerComponent as default }
68
+ }
@@ -1,5 +1,4 @@
1
1
  import BaseComponent from './base.js';
2
- import * as utils from '../utils.js';
3
2
  import fs from 'fs';
4
3
  import { promises } from 'fs';
5
4
  import { fileURLToPath } from 'url';
@@ -9,10 +8,20 @@ import autoprefixer from 'autoprefixer';
9
8
  import sortMQ from 'postcss-sort-media-queries';
10
9
  import stylelint from 'stylelint';
11
10
 
12
- class StyleComponent extends BaseComponent {
11
+ export default class StyleComponent extends BaseComponent {
13
12
 
14
13
  constructor() {
15
14
  super();
15
+ this.description = `Process sass files`;
16
+ }
17
+
18
+ async init() {
19
+ this.files = this.utils.addEntriesByFiletypes(['.scss']);
20
+ this.globs = await Array.fromAsync(
21
+ this.glob(this.project.package?.sdc?.sassGlobPath ||
22
+ `${this.project.path}{/_src/style,/blocks}/**/*.scss`)
23
+ );
24
+ await this.process();
16
25
  }
17
26
 
18
27
  async buildTheme() {
@@ -22,11 +31,11 @@ class StyleComponent extends BaseComponent {
22
31
  if (theme.settings?.custom) {
23
32
  for (var customAttribute in theme.settings.custom) {
24
33
  if (customAttribute == 'dirDist') {
25
- themeFileData += `$themeDirDist: "${theme.settings.custom[customAttribute]}"; // --wp--custom--${utils.camelToDash(customAttribute)}\n`;
34
+ themeFileData += `$themeDirDist: "${theme.settings.custom[customAttribute]}"; // --wp--custom--${this.utils.camelToDash(customAttribute)}\n`;
26
35
  } else if (['breakpoints-rem', 'breakpoint-mobile'].includes(customAttribute)) {
27
36
  // handled later in file - see 'fontSizeBase'
28
37
  } else {
29
- themeFileData += `$${customAttribute}: "${theme.settings.custom[customAttribute]}"; // --wp--custom--${utils.camelToDash(customAttribute)}\n`;
38
+ themeFileData += `$${customAttribute}: "${theme.settings.custom[customAttribute]}"; // --wp--custom--${this.utils.camelToDash(customAttribute)}\n`;
30
39
  }
31
40
  }
32
41
  }
@@ -151,12 +160,12 @@ class StyleComponent extends BaseComponent {
151
160
  if (options.buildTheme) {
152
161
  await this.buildTheme();
153
162
  }
154
- for (var block of this.project.files.sass) {
163
+ for (var block of this.files) {
155
164
  if (!entry || entry == block.file) {
156
165
  await this.build(block.file, {
157
166
  name: block.name,
158
- entriesToLint: this.project.globs.sass
159
- });
167
+ entriesToLint: this.globs
168
+ });
160
169
  if (entry == block.file) {
161
170
  break;
162
171
  }
@@ -167,13 +176,13 @@ class StyleComponent extends BaseComponent {
167
176
  watch() {
168
177
  this.chokidar.watch([
169
178
  ...[this.project.paths.theme.json],
170
- this.project.globs.sass
179
+ this.globs
171
180
  ], {
172
181
  ...this.project.chokidarOpts
173
182
  }).on('all', (event, path) => {
174
183
  let hasRanSingle = false;
175
- for (var block of this.project.files.sass) {
176
- if (path == block.file || utils.getImportedSASSFiles(block.file).includes(path)) {
184
+ for (var block of this.files) {
185
+ if (path == block.file || this.utils.getImportedSASSFiles(block.file).includes(path)) {
177
186
  this.process(block.file, { buildTheme: path == this.project.paths.theme.json });
178
187
  hasRanSingle = true;
179
188
  }
@@ -185,5 +194,3 @@ class StyleComponent extends BaseComponent {
185
194
  }
186
195
 
187
196
  }
188
-
189
- export { StyleComponent as default }
package/lib/project.js CHANGED
@@ -3,6 +3,8 @@ import { readFile } from 'fs/promises';
3
3
  let project = {
4
4
  path: process.cwd(),
5
5
  package: JSON.parse(await readFile(new URL(process.cwd() + '/package.json', import.meta.url))),
6
+ components: {},
7
+ builds: [],
6
8
  globs: {},
7
9
  entries: {},
8
10
  files: {
package/lib/utils.js CHANGED
@@ -1,11 +1,16 @@
1
1
  import fs from 'fs';
2
2
  import { readdir } from 'node:fs/promises';
3
3
  import path from 'path';
4
+ import project from './project.js';
4
5
 
5
6
  export function camelToDash(camel) {
6
7
  return camel.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
7
8
  }
8
9
 
10
+ export function entryBasename(entry) {
11
+ return path.parse(entry).base;
12
+ }
13
+
9
14
  export async function getAllSubdirectories(dir) {
10
15
  let subdirectories = [];
11
16
  const subdirectoriesEntries = await readdir(dir, { withFileTypes: true });
@@ -36,3 +41,20 @@ export function getImportedSASSFiles(filePath) {
36
41
  }
37
42
  return imports;
38
43
  }
44
+
45
+ export function addEntriesByFiletypes(filetypes = []) {
46
+ let finalFiles = [];
47
+ for (const [name, files] of Object.entries(project.package.sdc.entries)) {
48
+ for (let file of files) {
49
+ let fullPath = project.path + file;
50
+ let extension = path.parse(fullPath).ext;
51
+ if (filetypes.includes(extension)) {
52
+ finalFiles.push({
53
+ 'name': name,
54
+ 'file': fullPath
55
+ });
56
+ }
57
+ }
58
+ }
59
+ return finalFiles;
60
+ }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "sdc-build-wp",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "description": "Custom WordPress build process.",
5
5
  "engines": {
6
- "node": ">=20"
6
+ "node": ">=22"
7
7
  },
8
8
  "author": {
9
9
  "name": "Robert Sefer",