sdc-build-wp 4.1.3 → 4.2.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/.phpcs.xml CHANGED
@@ -51,6 +51,5 @@
51
51
  </rule>
52
52
  <rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
53
53
  <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
54
- <rule ref="Universal.NamingConventions.NoReservedKeywordParameterNames"/>
55
54
 
56
55
  </ruleset>
package/index.js CHANGED
@@ -1,48 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import path from 'path';
3
- import project from './lib/project.js';
4
3
  import parseArgs from 'minimist';
5
4
  const argv = parseArgs(process.argv.slice(2));
6
- import chokidar from 'chokidar';
7
- import { glob, readdir } from 'node:fs/promises';
8
- import { existsSync } from 'node:fs';
5
+ import { glob } from 'node:fs/promises';
6
+ import { promises as fs } from 'fs';
9
7
  import { Tail } from 'tail';
10
-
8
+ import project from './lib/project.js';
9
+ import * as utils from './lib/utils.js';
11
10
  import log from './lib/logging.js';
12
- import { buildSass, buildSassTheme, getImportedFiles as getImportedFilesSass } from './lib/style.js';
13
- import buildJS from './lib/scripts.js';
14
- import { default as buildPHP, shouldPHPLint } from './lib/php.js';
15
- import buildBlock from './lib/blocks.js';
16
- import buildImages from './lib/images.js';
17
- import buildFonts from './lib/fonts.js';
18
- import buildBrowserSync from './lib/browsersync.js';
19
-
20
- let paths = {
21
- theme: {
22
- json: `${project.path}/theme.json`,
23
- scss: `${project.path}/_src/style/partials/_theme.scss`
24
- },
25
- nodeModules: `${project.path}/node_modules`,
26
- composer: {
27
- vendor: `${project.path}/vendor`
28
- },
29
- images: project.package?.sdc?.imagesPath || `${project.path}/_src/images`,
30
- errorLog: process.env.ERROR_LOG_PATH || project.package.sdc?.error_log_path || '../../../../../logs/php/error.log'
31
- };
11
+ import * as Components from './lib/components/index.js';
32
12
 
33
- let chokidarOpts = {
34
- ignoreInitial: true,
35
- ignored: [
36
- paths.nodeModules,
37
- paths.composer.vendor,
38
- paths.theme.scss
39
- ]
40
- };
41
-
42
- let globs = {};
43
- let entries = {};
44
- let filesSass = [];
45
- let filesJS = [];
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();
46
20
 
47
21
  let builds = argv.builds ? argv.builds.split(',') : [
48
22
  'sass',
@@ -53,224 +27,134 @@ let builds = argv.builds ? argv.builds.split(',') : [
53
27
  'php'
54
28
  ];
55
29
 
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
+ }
59
+
56
60
  (async() => {
57
61
 
58
62
  let initialBuildTimerStart = Date.now();
59
63
  log('info', `Starting initial build`);
60
64
 
61
65
  if (builds.includes('sass')) {
62
- globs.sass = await Array.fromAsync(
66
+ project.globs.sass = await Array.fromAsync(
63
67
  glob(project.package?.sdc?.sassGlobPath ||
64
68
  `${project.path}{/_src/style,/blocks}/**/*.scss`)
65
69
  );
70
+ await styleComponent.process();
66
71
  }
72
+
67
73
  if (builds.includes('js')) {
68
- globs.js = await Array.fromAsync(
74
+ project.globs.js = await Array.fromAsync(
69
75
  glob(project.package?.sdc?.jsGlobPath ||
70
76
  `${project.path}/_src/scripts/**/*.js`)
71
77
  );
78
+ await scriptsComponent.process();
72
79
  }
80
+
73
81
  if (builds.includes('blocks')) {
74
- globs.blocks = await Array.fromAsync(
82
+ project.globs.blocks = await Array.fromAsync(
75
83
  glob(`${project.path}/blocks/*`)
76
84
  );
77
- globs.blocksSass = await Array.fromAsync(
85
+ project.globs.blocksSass = await Array.fromAsync(
78
86
  glob(`${project.path}/blocks/*/src/*.scss`)
79
87
  );
80
- // for (var filename of globs.blocksSass) {
81
- // entries[`blocks/${path.basename(path.dirname(filename))}/style`] = [ filename ];
88
+ // for (var filename of project.globs.blocksSass) {
89
+ // project.entries[`blocks/${path.basename(path.dirname(filename))}/style`] = [ filename ];
82
90
  // }
91
+ await blocksComponent.process();
83
92
  }
93
+
84
94
  if (builds.includes('images')) {
85
- globs.images = await Array.fromAsync(
95
+ project.globs.images = await Array.fromAsync(
86
96
  glob(project.package?.sdc?.imagesPath ||
87
- `${paths.images}/**/*`)
97
+ `${project.paths.images}/**/*`)
88
98
  );
89
- globs.imageDirectories = [
90
- paths.images,
91
- ...await getAllSubdirectories(paths.images)
99
+ project.globs.imageDirectories = [
100
+ project.paths.images,
101
+ ...await utils.getAllSubdirectories(project.paths.images)
92
102
  ];
103
+ await imagesComponent.process();
104
+ }
105
+
106
+ if (builds.includes('fonts')) {
107
+ await fontsComponent.process();
93
108
  }
109
+
94
110
  if (builds.includes('php')) {
95
- globs.php = await Array.fromAsync(
111
+ project.globs.php = await Array.fromAsync(
96
112
  glob(project.package?.sdc?.jsGlobPath ||
97
113
  `${project.path}/**/*.php`)
98
114
  );
99
- globs.blocksPHP = await Array.fromAsync(
115
+ project.globs.blocksPHP = await Array.fromAsync(
100
116
  glob(`${project.path}/blocks/*/build/*.php`)
101
117
  );
102
- chokidarOpts.ignored = [
103
- ...chokidarOpts.ignored,
104
- ...globs.blocksPHP
105
- ];
106
- }
107
-
108
- for (const [name, files] of Object.entries(project.package.sdc.entries)) {
109
- entries[name] = [];
110
- files.forEach(function(file) {
111
- entries[name].push(project.path + file);
112
- });
118
+ project.chokidarOpts.ignored.concat(project.globs.blocksPHP);
119
+ // await phpComponent.process(null, { lintType: 'warn' }); // this errors "Fatal error: Allowed memory size"
113
120
  }
114
121
 
115
- for (const [name, files] of Object.entries(entries)) {
116
- files.forEach(function(file) {
117
- switch (path.parse(file).ext) {
118
- case '.scss':
119
- if (builds.includes('sass')) {
120
- filesSass.push({
121
- 'name': name,
122
- 'file': file
123
- });
124
- }
125
- break;
126
- case '.js':
127
- if (builds.includes('js')) {
128
- filesJS.push({
129
- 'name': name,
130
- 'file': file
131
- });
132
- }
133
- break;
134
- }
135
- });
136
- }
137
-
138
- if (builds.includes('sass')) {
139
- await runSass(null, true);
140
- }
141
- if (builds.includes('js')) {
142
- await runJS();
143
- }
144
- if (builds.includes('blocks')) {
145
- await runBlocks();
146
- }
147
- if (builds.includes('images')) {
148
- await frontrunImages();
149
- }
150
- if (builds.includes('fonts')) {
151
- await buildFonts(project.path + '/_src/fonts');
152
- }
153
- // if (builds.includes('php') && shouldPHPLint) {
154
- // await runPHP(null, 'warn'); // this errors "Fatal error: Allowed memory size"
155
- // }
156
-
157
122
  log('info', `Finished initial build in ${Math.round((Date.now() - initialBuildTimerStart) / 1000)} seconds`);
158
123
 
159
124
  if (argv.watch) {
160
125
 
161
- buildBrowserSync();
126
+ serverComponent.start();
162
127
 
163
128
  if (builds.includes('sass')) {
164
- chokidar.watch([
165
- ...[paths.theme.json],
166
- globs.sass
167
- ], {
168
- ...chokidarOpts
169
- }).on('all', (event, path) => {
170
- let hasRanSingle = false;
171
- for (var block of filesSass) {
172
- if (path == block.file || getImportedFilesSass(block.file).includes(path)) {
173
- runSass(block.file, path == paths.theme.json);
174
- hasRanSingle = true;
175
- }
176
- }
177
- if (!hasRanSingle) {
178
- runSass(null, path == paths.theme.json);
179
- }
180
- });
129
+ styleComponent.watch();
181
130
  }
182
131
 
183
132
  if (builds.includes('js')) {
184
- chokidar.watch(globs.js, {
185
- ...chokidarOpts
186
- }).on('all', (event, path) => {
187
- runJS();
188
- });
133
+ scriptsComponent.watch();
189
134
  }
190
135
 
191
136
  if (builds.includes('blocks')) {
192
- for (let block of globs.blocks) {
193
- chokidar.watch(`${block}/src`, {
194
- ...chokidarOpts
195
- }).on('all', (event, path) => {
196
- runBlocks(block);
197
- });
198
- }
137
+ blocksComponent.watch();
199
138
  }
200
139
 
201
140
  if (builds.includes('images')) {
202
- chokidar.watch(paths.images, chokidarOpts).on('all', (event, path) => {
203
- frontrunImages();
204
- });
141
+ imagesComponent.watch();
205
142
  }
206
143
 
207
- if (builds.includes('php') && shouldPHPLint) {
208
- chokidar.watch(globs.php, {
209
- ...chokidarOpts
210
- }).on('all', (event, path) => {
211
- runPHP(path);
212
- });
144
+ if (builds.includes('php') && project.shouldPHPLint) {
145
+ phpComponent.watch();
213
146
  }
214
147
 
215
- if (existsSync(paths.errorLog)) {
216
- let errorLogTail = new Tail(paths.errorLog);
148
+ try {
149
+ await fs.access(project.paths.errorLog);
150
+ let errorLogTail = new Tail(project.paths.errorLog);
217
151
  errorLogTail.on('line', function(data) {
218
152
  log('php', data);
219
153
  });
220
- } else {
221
- log('info', `Cannot find error log @ ${paths.errorLog}. Skipping watching php error logs`);
154
+ } catch (error) {
155
+ log('info', `Cannot find error log @ ${project.paths.errorLog}. Skipping watching php error logs`);
222
156
  }
223
- }
224
-
225
- })();
226
-
227
- async function frontrunImages() {
228
- const promisesImages = globs.imageDirectories.map(directory => buildImages(directory));
229
- await Promise.all(promisesImages);
230
- }
231
157
 
232
- async function runBlocks(singleBlock) {
233
- if (singleBlock) {
234
- await buildBlock(singleBlock);
235
- } else {
236
- const promisesBlocks = globs.blocks.map(block => buildBlock(block));
237
- await Promise.all(promisesBlocks);
238
158
  }
239
- }
240
-
241
- async function runSass(singleEntry, buildTheme = true) {
242
- if (buildTheme) {
243
- await buildSassTheme();
244
- }
245
- for (var block of filesSass) {
246
- if (!singleEntry || singleEntry == block.file) {
247
- await buildSass(block.file, block.name, globs.sass);
248
- if (singleEntry == block.file) {
249
- break;
250
- }
251
- }
252
- }
253
- }
254
159
 
255
- async function runJS() {
256
- const promisesJS = filesJS.map(block => buildJS(block.file, block.name, globs.js));
257
- await Promise.all(promisesJS);
258
- }
259
-
260
- async function runPHP(file, method) {
261
- await buildPHP(file, method);
262
- }
263
-
264
- async function getAllSubdirectories(dir) {
265
- let subdirectories = [];
266
- const subdirectoriesEntries = await readdir(dir, { withFileTypes: true });
267
- for (const subdirectoriesEntry of subdirectoriesEntries) {
268
- if (subdirectoriesEntry.isDirectory()) {
269
- const subdirPath = path.join(dir, subdirectoriesEntry.name);
270
- subdirectories.push(subdirPath);
271
- const nestedSubdirs = await getAllSubdirectories(subdirPath);
272
- subdirectories = subdirectories.concat(nestedSubdirs);
273
- }
274
- }
275
- return subdirectories;
276
- }
160
+ })();
@@ -0,0 +1,36 @@
1
+ import path from 'path';
2
+ import project from '../project.js';
3
+ import log from '../logging.js';
4
+ import chokidar from 'chokidar';
5
+
6
+ class BaseComponent {
7
+
8
+ constructor() {
9
+ this.timer = null;
10
+ this.path = path;
11
+ this.project = project;
12
+ this.log = log;
13
+ this.chokidar = chokidar;
14
+ }
15
+
16
+ start() {
17
+ this.timer = Date.now();
18
+ }
19
+
20
+ end(options) {
21
+ options = Object.assign({}, {
22
+ verb: 'Built',
23
+ itemLabel: null,
24
+ timerStart: this.timer,
25
+ timerEnd: Date.now()
26
+ }, options);
27
+ this.log('success', `${options.verb}${options.itemLabel ? ` ${options.itemLabel}` : ''} in ${options.timerEnd - options.timerStart}ms`);
28
+ }
29
+
30
+ entryBasename(entry) {
31
+ return this.path.parse(entry).base;
32
+ }
33
+
34
+ }
35
+
36
+ export { BaseComponent as default }
@@ -0,0 +1,98 @@
1
+ import BaseComponent from './base.js';
2
+ import { stat } from 'fs/promises';
3
+ import { spawn } from 'child_process';
4
+ import process from 'process';
5
+
6
+ class BlocksComponent extends BaseComponent {
7
+
8
+ constructor() {
9
+ super();
10
+ }
11
+
12
+ async build(entry, options) {
13
+ options = Object.assign({}, {}, options);
14
+ let entryLabel = entry.replace(this.project.path, '');
15
+
16
+ let timerStart = Date.now();
17
+
18
+ this.start();
19
+
20
+ let workingBlockJson = null;
21
+ let potentialBlockJsonLocations = [
22
+ `${entry}/src/block.json`,
23
+ // `${entry}/block.json`
24
+ ];
25
+ for (var location of potentialBlockJsonLocations) {
26
+ try {
27
+ await stat(location);
28
+ workingBlockJson = location
29
+ break;
30
+ } catch (error) {
31
+ //
32
+ }
33
+ }
34
+ if (workingBlockJson === null) {
35
+ this.log('error', `Failed building ${entry} blocks - no block.json found.`);
36
+ return false;
37
+ }
38
+ let cmds = [
39
+ `${this.project.path}/node_modules/@wordpress/scripts/bin/wp-scripts.js`,
40
+ `build`,
41
+ `--source-path=.${entry.replace(this.project.path, '')}/src`,
42
+ `--output-path=.${entry.replace(this.project.path, '')}/build`,
43
+ `--webpack-copy-php`
44
+ ];
45
+ await cmd(cmds, { entryLabel: entryLabel });
46
+
47
+ this.end({
48
+ itemLabel: entryLabel,
49
+ timerStart: timerStart,
50
+ timerEnd: Date.now()
51
+ });
52
+ }
53
+
54
+ async process(entry) {
55
+ if (entry) {
56
+ await this.build(entry);
57
+ } else {
58
+ const promisesBlocks = this.project.globs.blocks.map(block => this.build(block));
59
+ await Promise.all(promisesBlocks);
60
+ }
61
+ }
62
+
63
+ watch() {
64
+ for (let block of this.project.globs.blocks) {
65
+ this.chokidar.watch(`${block}/src`, {
66
+ ...this.project.chokidarOpts
67
+ }).on('all', (event, path) => {
68
+ this.process(block);
69
+ });
70
+ }
71
+ }
72
+
73
+ }
74
+
75
+ function cmd(commands) {
76
+ let p = spawn(commands[0], commands.slice(1), {
77
+ shell: true
78
+ });
79
+ return new Promise((resolveFunc) => {
80
+ p.stdout.on('data', (x) => {
81
+ if (x.toString().includes('Error:')) {
82
+ process.stdout.write(x.toString());
83
+ log('error', `Failed building ${entryLabel} block - See above error.`);
84
+ }
85
+ });
86
+ p.stderr.on('data', (x) => {
87
+ if (x.toString().includes('Error:')) {
88
+ process.stderr.write(x.toString());
89
+ log('error', `Failed building ${entryLabel} block - See above error.`);
90
+ }
91
+ });
92
+ p.on('exit', (code) => {
93
+ resolveFunc(code);
94
+ });
95
+ });
96
+ }
97
+
98
+ export { BlocksComponent as default }
@@ -0,0 +1,36 @@
1
+ import BaseComponent from './base.js';
2
+ import { readdir } from 'fs/promises';
3
+ import fs from 'fs-extra';
4
+
5
+ class FontsComponent extends BaseComponent {
6
+
7
+ constructor() {
8
+ super();
9
+ }
10
+
11
+ async build(entry) {
12
+ let entryLabel = `/dist/fonts`;
13
+
14
+ this.start();
15
+
16
+ try {
17
+ const fontsDir = await readdir(entry);
18
+ if (fontsDir.length == 0) { throw new Error('No files present'); }
19
+ await fs.copy(entry, `${this.project.path}${entryLabel}`);
20
+ } catch(error) {
21
+ this.log('info', `${error} at ${entry.replace(this.project.path, '')}/. Skipping font copy`);
22
+ return false;
23
+ }
24
+
25
+ this.end({
26
+ itemLabel: entryLabel
27
+ });
28
+ }
29
+
30
+ async process() {
31
+ await this.build(`${this.project.path}/_src/fonts`);
32
+ }
33
+
34
+ }
35
+
36
+ export { FontsComponent as default }
@@ -0,0 +1,47 @@
1
+ import BaseComponent from './base.js';
2
+ import imagemin from 'imagemin';
3
+ import imageminJpegtran from 'imagemin-jpegtran';
4
+ import imageminPngquant from 'imagemin-pngquant';
5
+ import imageminSvgo from 'imagemin-svgo';
6
+
7
+ class ImagesComponent extends BaseComponent {
8
+
9
+ constructor() {
10
+ super();
11
+ }
12
+
13
+ async build(entry, options) {
14
+ let timerStart = Date.now();
15
+ let dest = entry.replace('_src/images', 'dist/images');
16
+ const files = await imagemin([entry + '/*'], {
17
+ destination: dest,
18
+ plugins: [
19
+ imageminJpegtran(),
20
+ imageminPngquant(),
21
+ imageminSvgo()
22
+ ]
23
+ });
24
+
25
+ this.end({
26
+ itemLabel: `${dest.replace(this.project.path, '')} (${files.length} image${files.length == 1 ? '' : 's'})`,
27
+ timerStart: timerStart,
28
+ timerEnd: Date.now()
29
+ });
30
+ }
31
+
32
+ async process() {
33
+ const promisesImages = this.project.globs.imageDirectories.map(directory => this.build(directory));
34
+ await Promise.all(promisesImages);
35
+ }
36
+
37
+ watch() {
38
+ this.chokidar.watch(this.project.paths.images, {
39
+ ...this.project.chokidarOpts
40
+ }).on('all', (event, path) => {
41
+ this.process();
42
+ });
43
+ }
44
+
45
+ }
46
+
47
+ export { ImagesComponent as default }
@@ -0,0 +1,7 @@
1
+ export { default as style } from './style.js';
2
+ export { default as scripts } from './scripts.js';
3
+ export { default as blocks } from './blocks.js';
4
+ export { default as images } from './images.js';
5
+ export { default as fonts } from './fonts.js';
6
+ export { default as php } from './php.js';
7
+ export { default as server } from './server.js';
@@ -0,0 +1,91 @@
1
+ import BaseComponent from './base.js';
2
+ import { fileURLToPath } from 'url';
3
+ import { exec } from 'child_process';
4
+ import { promisify } from 'util';
5
+
6
+ class PHPComponent extends BaseComponent {
7
+
8
+ constructor() {
9
+ super();
10
+ this.execPromise = promisify(exec);
11
+ }
12
+
13
+ async build(entry, options) {
14
+ options = Object.assign({}, {
15
+ lintType: 'fix'
16
+ }, options);
17
+ let entryLabel = `all PHP files`;
18
+
19
+ this.start();
20
+ let workingLintBin = 'phpcbf';
21
+ if (options.lintType == 'warn') {
22
+ workingLintBin = 'phpcs';
23
+ }
24
+ let phpFiles = '.';
25
+ let additionalFlags = '';
26
+ if (entry) {
27
+ phpFiles = entry;
28
+ entryLabel = entry.replace(this.project.path, '');
29
+ } else {
30
+ additionalFlags += ' -d memory_limit=2G'; // FIXME: this doesn't solve error issue "Fatal error: Allowed memory size"
31
+ }
32
+ try {
33
+ const cmds = [
34
+ `vendor/bin/${workingLintBin}`,
35
+ `--parallel=5`,
36
+ `--error-severity=1`,
37
+ `--warning-severity=1`,
38
+ `--colors`,
39
+ `--basepath=${this.project.path}`,
40
+ phpFiles,
41
+ additionalFlags
42
+ ];
43
+
44
+ const { stdout, stderr } = await this.execPromise(cmds.join(' '), {
45
+ cwd: this.path.resolve(this.path.dirname(fileURLToPath(import.meta.url)), '../../')
46
+ }); // returns an error if any violations are found, so we can't rely on the try/catch as usual
47
+ } catch (error) {
48
+ if (
49
+ error.stderr?.length ||
50
+ (
51
+ error.stdout?.length &&
52
+ (
53
+ error.stdout.startsWith('ERROR:') ||
54
+ error.stdout.includes('FAILED TO FIX')
55
+ )
56
+ )
57
+ ) {
58
+ console.error(error.stderr?.length ? error.stderr : error.stdout);
59
+ this.log('error', `Failed linting ${entryLabel.replace(this.project.path, '')} - See above error.`);
60
+ return false;
61
+ } else {
62
+ if (error.stdout?.length) {
63
+ console.log(error.stdout);
64
+ }
65
+ }
66
+ }
67
+ if (this.project.server) {
68
+ this.project.server.reload();
69
+ }
70
+
71
+ this.end({
72
+ itemLabel: entryLabel,
73
+ verb: `Linted (${options.lintType})`
74
+ });
75
+ }
76
+
77
+ async process(entry, options) {
78
+ await this.build(entry, options);
79
+ }
80
+
81
+ watch() {
82
+ this.chokidar.watch(this.project.globs.php, {
83
+ ...this.project.chokidarOpts
84
+ }).on('all', (event, path) => {
85
+ this.process(path);
86
+ });
87
+ }
88
+
89
+ }
90
+
91
+ export { PHPComponent as default }