sdc-build-wp 5.0.0 → 5.0.2
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 +1 -0
- package/lib/build.js +6 -8
- package/lib/components/scripts.js +6 -2
- package/lib/components/server.js +9 -8
- package/lib/config-validator.js +104 -0
- package/lib/help.js +35 -21
- package/lib/project.js +45 -27
- package/lib/utils.js +3 -3
- package/package.json +3 -3
package/README.md
CHANGED
package/lib/build.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as utils from './utils.js';
|
|
|
3
3
|
import log from './logging.js';
|
|
4
4
|
|
|
5
5
|
export async function build(watch = false) {
|
|
6
|
-
if (project.
|
|
6
|
+
if (project.builds.includes('cache')) {
|
|
7
7
|
try {
|
|
8
8
|
await project.components.cache.init();
|
|
9
9
|
} catch (error) {
|
|
@@ -48,7 +48,7 @@ export async function build(watch = false) {
|
|
|
48
48
|
project.builds.splice(project.builds.indexOf('server'), 1);
|
|
49
49
|
project.builds.push('server');
|
|
50
50
|
log('info', `Started watching [${project.builds.join(', ')}]`);
|
|
51
|
-
log('info', `[r] to restart, [p] to pause/resume, [q] to quit`);
|
|
51
|
+
log('info', `[r] to restart, [c] to clear cache, [p] to pause/resume, [q] to quit`);
|
|
52
52
|
|
|
53
53
|
for (let build of project.builds) {
|
|
54
54
|
try {
|
|
@@ -63,10 +63,8 @@ export async function build(watch = false) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export function restartBuild() {
|
|
67
|
-
utils.stopActiveComponents();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
build(true);
|
|
71
|
-
}, 100);
|
|
66
|
+
export async function restartBuild() {
|
|
67
|
+
await utils.stopActiveComponents();
|
|
68
|
+
utils.clearScreen();
|
|
69
|
+
build(true);
|
|
72
70
|
}
|
|
@@ -54,7 +54,10 @@ export default class ScriptsComponent extends BaseComponent {
|
|
|
54
54
|
|
|
55
55
|
try {
|
|
56
56
|
const result = await esbuild.build({
|
|
57
|
-
platform: '
|
|
57
|
+
platform: 'browser',
|
|
58
|
+
format: 'iife',
|
|
59
|
+
globalName: 'sdcBuild',
|
|
60
|
+
treeShaking: true,
|
|
58
61
|
entryPoints: [entry],
|
|
59
62
|
bundle: true,
|
|
60
63
|
minify: true,
|
|
@@ -97,7 +100,8 @@ export default class ScriptsComponent extends BaseComponent {
|
|
|
97
100
|
try {
|
|
98
101
|
await this.process();
|
|
99
102
|
} catch (error) {
|
|
100
|
-
|
|
103
|
+
console.error(error);
|
|
104
|
+
this.log('error', `Failed to process scripts`);
|
|
101
105
|
}
|
|
102
106
|
});
|
|
103
107
|
}
|
package/lib/components/server.js
CHANGED
|
@@ -11,14 +11,7 @@ export default class ServerComponent extends BaseComponent {
|
|
|
11
11
|
this.description = `Run a dev proxy server for live reloading`;
|
|
12
12
|
this.sessions = {};
|
|
13
13
|
this.server = create('SDC WP Build Server');
|
|
14
|
-
this.watchedFiles = [
|
|
15
|
-
`${this.project.paths.dist}/**/*`,
|
|
16
|
-
`**/*.html`,
|
|
17
|
-
`**/*.json`
|
|
18
|
-
];
|
|
19
|
-
if (!this.project.shouldPHPLint) {
|
|
20
|
-
this.watchedFiles.push(`**/*.php`);
|
|
21
|
-
}
|
|
14
|
+
this.watchedFiles = [];
|
|
22
15
|
this.ignoredFiles = [
|
|
23
16
|
`.sdc-build-wp/cache/**`,
|
|
24
17
|
`node_modules/**`,
|
|
@@ -28,6 +21,14 @@ export default class ServerComponent extends BaseComponent {
|
|
|
28
21
|
}
|
|
29
22
|
|
|
30
23
|
async init() {
|
|
24
|
+
this.watchedFiles = [
|
|
25
|
+
`${this.project.paths.dist}/**/*`,
|
|
26
|
+
`**/*.html`,
|
|
27
|
+
`**/*.json`
|
|
28
|
+
];
|
|
29
|
+
if (!this.project.config.php.enabled) {
|
|
30
|
+
this.watchedFiles.push(`**/*.php`);
|
|
31
|
+
}
|
|
31
32
|
this.project.pageScript = await readFile(path.join(path.dirname(fileURLToPath(import.meta.url)), '../page-script.js'), 'utf8');
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import log from './logging.js';
|
|
2
|
+
|
|
3
|
+
const configSchema = {
|
|
4
|
+
imagesPath: { type: 'string', optional: true },
|
|
5
|
+
errorLogPath: { type: 'string', optional: true },
|
|
6
|
+
entries: { type: 'object', optional: true },
|
|
7
|
+
php: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
optional: true,
|
|
10
|
+
properties: {
|
|
11
|
+
enabled: { type: 'boolean', optional: true }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function validateType(value, expectedType, path = '') {
|
|
17
|
+
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
18
|
+
if (actualType !== expectedType) {
|
|
19
|
+
throw new Error(`Configuration error at '${path}': expected ${expectedType}, got ${actualType}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function validateObject(obj, schema, path = '') {
|
|
24
|
+
if (!obj || typeof obj !== 'object') {
|
|
25
|
+
if (!schema.optional) {
|
|
26
|
+
throw new Error(`Configuration error at '${path}': expected object, got ${typeof obj}`);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const allowedKeys = Object.keys(schema.properties || {});
|
|
32
|
+
const objKeys = Object.keys(obj);
|
|
33
|
+
const unknownKeys = objKeys.filter(key => !allowedKeys.includes(key));
|
|
34
|
+
|
|
35
|
+
if (unknownKeys.length > 0) {
|
|
36
|
+
log('warn', `Unknown configuration properties: ${unknownKeys.map(key => `${path}.${key}`).join(', ')}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const [key, subSchema] of Object.entries(schema.properties || {})) {
|
|
40
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
41
|
+
const value = obj[key];
|
|
42
|
+
|
|
43
|
+
if (value === undefined || value === null) {
|
|
44
|
+
if (!subSchema.optional) {
|
|
45
|
+
throw new Error(`Configuration error: required property '${currentPath}' is missing`);
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (subSchema.type === 'object' && subSchema.properties) {
|
|
51
|
+
validateObject(value, subSchema, currentPath);
|
|
52
|
+
} else {
|
|
53
|
+
validateType(value, subSchema.type, currentPath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function validateConfig(config) {
|
|
59
|
+
try {
|
|
60
|
+
for (const [key, schema] of Object.entries(configSchema)) {
|
|
61
|
+
const value = config[key];
|
|
62
|
+
const currentPath = key;
|
|
63
|
+
|
|
64
|
+
if (value === undefined || value === null) {
|
|
65
|
+
if (!schema.optional) {
|
|
66
|
+
throw new Error(`Configuration error: required property '${currentPath}' is missing`);
|
|
67
|
+
}
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (schema.type === 'object' && schema.properties) {
|
|
72
|
+
validateObject(value, schema, currentPath);
|
|
73
|
+
} else {
|
|
74
|
+
validateType(value, schema.type, currentPath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
} catch (error) {``
|
|
79
|
+
console.error(error);
|
|
80
|
+
log('error', `Configuration validation failed`);
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getDefaultConfig() {
|
|
86
|
+
return {
|
|
87
|
+
errorLogPath: '../../../../../logs/php/error.log', // default Local by Flywheel error log path
|
|
88
|
+
php: {
|
|
89
|
+
enabled: true
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function mergeWithDefaults(userConfig) {
|
|
95
|
+
const defaults = getDefaultConfig();
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
...defaults,
|
|
99
|
+
...userConfig,
|
|
100
|
+
php: { ...defaults.php, ...userConfig.php }
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { validateConfig, getDefaultConfig, mergeWithDefaults };
|
package/lib/help.js
CHANGED
|
@@ -1,34 +1,48 @@
|
|
|
1
1
|
import project from './project.js';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
|
|
3
4
|
export default function() {
|
|
4
5
|
console.log(`
|
|
5
|
-
|
|
6
|
+
${chalk.bold.blue('SDC Build WP')} - Custom WordPress build process
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
-h, --help Show help message and exit
|
|
9
|
-
-v, --version Version
|
|
10
|
-
-w, --watch Build and watch
|
|
11
|
-
-b, --builds BUILDS Build with specific components
|
|
12
|
-
--no-cache Disable build caching
|
|
13
|
-
--clear-cache Clear build cache and exit
|
|
8
|
+
${chalk.yellow('Usage:')} sdc-build-wp [options] [arguments]
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
${chalk.yellow('Options:')}
|
|
11
|
+
${chalk.green('-h, --help')} Show this help message and exit
|
|
12
|
+
${chalk.green('-v, --version')} Show version number and exit
|
|
13
|
+
${chalk.green('-w, --watch')} Build and watch for changes
|
|
14
|
+
${chalk.green('-b, --builds BUILDS')} Build with specific components (comma-separated)
|
|
15
|
+
${chalk.green('--no-cache')} Disable build caching for this run
|
|
16
|
+
${chalk.green('--clear-cache')} Clear all cached data and exit
|
|
16
17
|
|
|
18
|
+
${chalk.yellow('Available Components:')}
|
|
17
19
|
${Object.entries(project.components).map(([key, component]) => {
|
|
18
|
-
|
|
19
|
-
}).join('')}
|
|
20
|
-
Examples:
|
|
20
|
+
return ` ${chalk.cyan(key.padEnd(12))} ${component.description}`;
|
|
21
|
+
}).join('\n')}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
sdc-build-wp
|
|
25
|
-
sdc-build-wp --no-cache
|
|
26
|
-
sdc-build-wp --clear-cache
|
|
23
|
+
${chalk.yellow('Examples:')}
|
|
24
|
+
${chalk.dim('# Basic build')}
|
|
25
|
+
sdc-build-wp
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
${chalk.dim('# Build and watch for changes')}
|
|
28
|
+
sdc-build-wp --watch
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
${chalk.dim('# Build only specific components')}
|
|
31
|
+
sdc-build-wp --watch --builds=style,scripts
|
|
32
|
+
|
|
33
|
+
${chalk.dim('# Build without cache')}
|
|
34
|
+
sdc-build-wp --no-cache
|
|
35
|
+
|
|
36
|
+
${chalk.dim('# Clear cache and exit')}
|
|
37
|
+
sdc-build-wp --clear-cache
|
|
38
|
+
|
|
39
|
+
${chalk.yellow('Watch Mode Controls:')}
|
|
40
|
+
${chalk.green('[r]')} Restart build process
|
|
41
|
+
${chalk.green('[p]')} Pause/Resume watching
|
|
42
|
+
${chalk.green('[q]')} Quit and exit
|
|
43
|
+
|
|
44
|
+
${chalk.yellow('Configuration:')}
|
|
45
|
+
Place your configuration in ${chalk.cyan('.sdc-build-wp/config.json')}
|
|
46
|
+
See documentation for available options.
|
|
33
47
|
`);
|
|
34
48
|
}
|
package/lib/project.js
CHANGED
|
@@ -9,6 +9,7 @@ import * as utils from './utils.js';
|
|
|
9
9
|
import log from './logging.js';
|
|
10
10
|
import * as LibComponents from './components/index.js';
|
|
11
11
|
import help from './help.js';
|
|
12
|
+
import { validateConfig, mergeWithDefaults } from './config-validator.js';
|
|
12
13
|
|
|
13
14
|
let project = {
|
|
14
15
|
config: {},
|
|
@@ -69,7 +70,16 @@ project.chokidarOpts = {
|
|
|
69
70
|
|
|
70
71
|
export async function init() {
|
|
71
72
|
|
|
72
|
-
project.
|
|
73
|
+
project.components = Object.fromEntries(Object.entries(LibComponents).map(([name, Class]) => [name, new Class()]));
|
|
74
|
+
|
|
75
|
+
project.argv = yargs(hideBin(process.argv))
|
|
76
|
+
.help(false)
|
|
77
|
+
.argv;
|
|
78
|
+
|
|
79
|
+
if (project.argv.help || project.argv.h) {
|
|
80
|
+
help();
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
73
83
|
|
|
74
84
|
if (project.package.sdc) {
|
|
75
85
|
await convertPackageToConfig();
|
|
@@ -77,24 +87,18 @@ export async function init() {
|
|
|
77
87
|
|
|
78
88
|
await loadConfig();
|
|
79
89
|
|
|
80
|
-
project.components = Object.fromEntries(Object.entries(LibComponents).map(([name, Class]) => [name, new Class()]));
|
|
81
90
|
const styleDir = `${project.path}/${project.paths.src.src}/${project.paths.src.style}`;
|
|
82
91
|
|
|
83
|
-
if (project.argv.
|
|
84
|
-
help();
|
|
85
|
-
process.exit(0);
|
|
86
|
-
} else if (project.argv.version || project.argv.v) {
|
|
92
|
+
if (project.argv.version || project.argv.v) {
|
|
87
93
|
console.log(await utils.getThisPackageVersion());
|
|
88
94
|
process.exit(0);
|
|
89
95
|
} else if (project.argv['clear-cache']) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
log('error', `Failed to clear cache`);
|
|
97
|
-
}
|
|
96
|
+
try {
|
|
97
|
+
await project.components.cache.init();
|
|
98
|
+
await project.components.cache.clearCache();
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error(error);
|
|
101
|
+
log('error', `Failed to clear cache`);
|
|
98
102
|
}
|
|
99
103
|
process.exit(0);
|
|
100
104
|
}
|
|
@@ -139,17 +143,17 @@ export async function init() {
|
|
|
139
143
|
log('warn', 'Continuing build process despite error');
|
|
140
144
|
});
|
|
141
145
|
|
|
142
|
-
process.on('uncaughtException', (error) => {
|
|
146
|
+
process.on('uncaughtException', async (error) => {
|
|
143
147
|
log('error', `Uncaught Exception: ${error.message}`);
|
|
144
148
|
log('warn', 'Attempting graceful shutdown');
|
|
145
|
-
utils.stopActiveComponents();
|
|
149
|
+
await utils.stopActiveComponents();
|
|
146
150
|
process.exit(1);
|
|
147
151
|
});
|
|
148
152
|
|
|
149
153
|
process.on('SIGINT', async function() {
|
|
150
154
|
console.log(`\r`);
|
|
151
155
|
if (project.isRunning) {
|
|
152
|
-
utils.stopActiveComponents();
|
|
156
|
+
await utils.stopActiveComponents();
|
|
153
157
|
project.isRunning = false;
|
|
154
158
|
utils.clearScreen();
|
|
155
159
|
}
|
|
@@ -174,7 +178,7 @@ export function keypressListen() {
|
|
|
174
178
|
process.stdin.resume();
|
|
175
179
|
process.stdin.setEncoding('utf8');
|
|
176
180
|
|
|
177
|
-
process.stdin.on('data', (key) => {
|
|
181
|
+
process.stdin.on('data', async (key) => {
|
|
178
182
|
switch (key) {
|
|
179
183
|
case '\r': // [Enter]/[Return]
|
|
180
184
|
console.log('\r');
|
|
@@ -192,9 +196,12 @@ export function keypressListen() {
|
|
|
192
196
|
log('warn', 'Paused build process');
|
|
193
197
|
}
|
|
194
198
|
break;
|
|
199
|
+
case 'c':
|
|
200
|
+
await project.components.cache.clearCache();
|
|
201
|
+
break;
|
|
195
202
|
case 'r':
|
|
196
203
|
log('info', 'Restarted build process');
|
|
197
|
-
restartBuild();
|
|
204
|
+
await restartBuild();
|
|
198
205
|
break;
|
|
199
206
|
}
|
|
200
207
|
});
|
|
@@ -217,16 +224,27 @@ export async function convertPackageToConfig() {
|
|
|
217
224
|
export async function loadConfig() {
|
|
218
225
|
try {
|
|
219
226
|
const configFile = await fs.readFile(configPath, 'utf-8');
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
227
|
+
const rawConfig = JSON.parse(configFile);
|
|
228
|
+
if (!validateConfig(rawConfig)) {
|
|
229
|
+
log('error', 'Configuration validation failed');
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
project.config = mergeWithDefaults(rawConfig);
|
|
233
|
+
project.paths.images = project.config.imagesPath || `${project.path}/${project.paths.src.src}/${project.paths.src.images}`;
|
|
234
|
+
project.paths.errorLog = project.config.errorLogPath;
|
|
223
235
|
project.entries = project.config.entries || {};
|
|
224
|
-
project.shouldPHPLint = typeof project.config.php === 'undefined' || typeof project.config.php.enabled === 'undefined' || project.config.php.enabled == true;
|
|
225
236
|
setupConfigWatcher();
|
|
226
237
|
} catch (error) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
if (error.code === 'ENOENT') {
|
|
239
|
+
log('warn', 'No config file found, using defaults');
|
|
240
|
+
project.config = mergeWithDefaults({});
|
|
241
|
+
project.paths.images = `${project.path}/${project.paths.src.src}/${project.paths.src.images}`;
|
|
242
|
+
project.entries = {};
|
|
243
|
+
} else {
|
|
244
|
+
console.error(error);
|
|
245
|
+
log('error', `Failed to load config: ${error.message}`);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
230
248
|
}
|
|
231
249
|
}
|
|
232
250
|
|
|
@@ -239,7 +257,7 @@ export function setupConfigWatcher() {
|
|
|
239
257
|
configWatcher.on('change', async () => {
|
|
240
258
|
if (!project.isRunning) { return; }
|
|
241
259
|
await loadConfig();
|
|
242
|
-
restartBuild();
|
|
260
|
+
await restartBuild();
|
|
243
261
|
});
|
|
244
262
|
configWatcher.on('error', (error) => {
|
|
245
263
|
console.error(error);
|
package/lib/utils.js
CHANGED
|
@@ -13,10 +13,10 @@ export function clearScreen() {
|
|
|
13
13
|
process.stdout.write('\x1B[2J\x1B[0f');
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function stopActiveComponents() {
|
|
16
|
+
export async function stopActiveComponents() {
|
|
17
17
|
if (project.configWatcher) {
|
|
18
18
|
try {
|
|
19
|
-
project.configWatcher.close();
|
|
19
|
+
await project.configWatcher.close();
|
|
20
20
|
} catch (error) {
|
|
21
21
|
console.error(error);
|
|
22
22
|
log('error', 'Failed to stop config file watcher');
|
|
@@ -36,7 +36,7 @@ export function stopActiveComponents() {
|
|
|
36
36
|
}
|
|
37
37
|
if (component.watcher) {
|
|
38
38
|
try {
|
|
39
|
-
component.watcher.close();
|
|
39
|
+
await component.watcher.close();
|
|
40
40
|
} catch (error) {
|
|
41
41
|
console.warn(`Failed to stop watcher for ${component.constructor.name}:`, error.message);
|
|
42
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdc-build-wp",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Custom WordPress build process.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"chalk": "^5.4.1",
|
|
33
33
|
"chokidar": "^4.0.3",
|
|
34
34
|
"esbuild": "^0.25.8",
|
|
35
|
-
"eslint": "^9.
|
|
35
|
+
"eslint": "^9.32.0",
|
|
36
36
|
"fs-extra": "^11.3.0",
|
|
37
37
|
"postcss": "^8.5.6",
|
|
38
38
|
"postcss-scss": "^4.0.9",
|
|
39
39
|
"postcss-sort-media-queries": "^5.2.0",
|
|
40
40
|
"sass": "^1.89.2",
|
|
41
41
|
"sharp": "^0.34.3",
|
|
42
|
-
"stylelint": "^16.
|
|
42
|
+
"stylelint": "^16.23.0",
|
|
43
43
|
"svgo": "^4.0.0",
|
|
44
44
|
"tail": "^2.2.6",
|
|
45
45
|
"yargs": "^18.0.0"
|