@withstudiocms/buildkit 0.1.0-beta.1 → 0.1.0-beta.3
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/LICENSE +1 -1
- package/index.js +218 -38
- package/package.json +5 -5
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025
|
|
3
|
+
Copyright (c) 2025-present StudioCMS - withstudiocms (https://github.com/withstudiocms)
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/index.js
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { run } from 'node:test';
|
|
6
|
+
import { spec } from 'node:test/reporters';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { parseArgs } from 'node:util';
|
|
4
9
|
import esbuild from 'esbuild';
|
|
5
10
|
import glob from 'fast-glob';
|
|
6
11
|
import { dim, gray, green, red, yellow } from 'kleur/colors';
|
|
7
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @type {boolean} Indicates if the script is running in a CI environment.
|
|
15
|
+
*/
|
|
16
|
+
const isCI = !!process.env.CI;
|
|
17
|
+
|
|
18
|
+
/** * Default timeout for tests in milliseconds.
|
|
19
|
+
* In CI, we set a longer timeout to accommodate potential delays.
|
|
20
|
+
* In local development, we use a shorter timeout for faster feedback.
|
|
21
|
+
* @type {number}
|
|
22
|
+
*/
|
|
23
|
+
const defaultTimeout = isCI ? 1400000 : 600000;
|
|
24
|
+
|
|
8
25
|
/** @type {import('esbuild').BuildOptions} */
|
|
9
26
|
const defaultConfig = {
|
|
10
27
|
minify: false,
|
|
@@ -25,14 +42,28 @@ const defaultConfig = {
|
|
|
25
42
|
'.webp': 'copy',
|
|
26
43
|
'.avif': 'copy',
|
|
27
44
|
'.svg': 'copy',
|
|
45
|
+
'.woff2': 'copy',
|
|
46
|
+
'.woff': 'copy',
|
|
47
|
+
'.ttf': 'copy',
|
|
48
|
+
'.eot': 'copy',
|
|
49
|
+
'.otf': 'copy',
|
|
28
50
|
},
|
|
29
51
|
};
|
|
30
52
|
|
|
53
|
+
// DateTime format for logging
|
|
54
|
+
/**
|
|
55
|
+
* @type {Intl.DateTimeFormat}
|
|
56
|
+
*/
|
|
31
57
|
const dt = new Intl.DateTimeFormat('en-us', {
|
|
32
58
|
hour: '2-digit',
|
|
33
59
|
minute: '2-digit',
|
|
34
60
|
});
|
|
35
61
|
|
|
62
|
+
/** * Plugin to generate TypeScript declarations using the TypeScript compiler.
|
|
63
|
+
* @param {string} buildTsConfig - The path to the TypeScript configuration file.
|
|
64
|
+
* @param {string} outdir - The output directory for the generated declarations.
|
|
65
|
+
* @returns {import('esbuild').Plugin} The esbuild plugin for generating TypeScript declarations.
|
|
66
|
+
*/
|
|
36
67
|
const dtsGen = (buildTsConfig, outdir) => ({
|
|
37
68
|
name: 'TypeScriptDeclarationsPlugin',
|
|
38
69
|
setup(build) {
|
|
@@ -51,17 +82,61 @@ const dtsGen = (buildTsConfig, outdir) => ({
|
|
|
51
82
|
},
|
|
52
83
|
});
|
|
53
84
|
|
|
54
|
-
|
|
55
|
-
|
|
85
|
+
/** * Clean the output directory by removing all files except those specified in the skip array.
|
|
86
|
+
* @param {string} outdir - The output directory to clean.
|
|
87
|
+
* @param {string} date - The date string for logging.
|
|
88
|
+
* @param {string[]} skip - An array of glob patterns to skip when cleaning.
|
|
89
|
+
* @throws {Error} If the glob operation fails or if there are issues removing files.
|
|
90
|
+
*/
|
|
91
|
+
async function clean(outdir, date, skip = []) {
|
|
92
|
+
const files = await glob([`${outdir}/**`, ...skip], { filesOnly: true });
|
|
93
|
+
console.log(dim(`[${date}] `) + dim(`Cleaning ${files.length} files from ${outdir}`));
|
|
94
|
+
await Promise.all(files.map((file) => fs.rm(file, { force: true })));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** * Read and parse the package.json file.
|
|
98
|
+
* @param {string} path - The path to the package.json file.
|
|
99
|
+
* @returns {Promise<Object>} The parsed JSON object.
|
|
100
|
+
* @throws {Error} If the file cannot be read or is not valid JSON.
|
|
101
|
+
*/
|
|
102
|
+
async function readPackageJSON(path) {
|
|
103
|
+
try {
|
|
104
|
+
const content = await fs.readFile(path, { encoding: 'utf8' });
|
|
105
|
+
try {
|
|
106
|
+
return JSON.parse(content);
|
|
107
|
+
} catch (parseError) {
|
|
108
|
+
throw new Error(`Invalid JSON in ${path}: ${parseError.message}`);
|
|
109
|
+
}
|
|
110
|
+
} catch (readError) {
|
|
111
|
+
throw new Error(`Failed to read ${path}: ${readError.message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Run the dev or build command with the provided arguments.
|
|
117
|
+
* @param {string} cmd - The command to run ('dev' or 'build').
|
|
118
|
+
* @param {string[]} args - The arguments to pass to the command.
|
|
119
|
+
*/
|
|
120
|
+
async function devAndBuild(cmd, args) {
|
|
56
121
|
const config = Object.assign({}, defaultConfig);
|
|
57
122
|
const patterns = args
|
|
58
123
|
.filter((f) => !!f) // remove empty args
|
|
59
124
|
.map((f) => f.replace(/^'/, '').replace(/'$/, '')); // Needed for Windows: glob strings contain surrounding string chars??? remove these
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Collect all entry points based on the provided patterns.
|
|
128
|
+
* @type {string[]}
|
|
129
|
+
*/
|
|
60
130
|
const entryPoints = [].concat(
|
|
61
131
|
...(await Promise.all(
|
|
62
132
|
patterns.map((pattern) => glob(pattern, { filesOnly: true, absolute: true }))
|
|
63
133
|
))
|
|
64
134
|
);
|
|
135
|
+
|
|
136
|
+
if (entryPoints.length === 0) {
|
|
137
|
+
throw new Error(`No entry points found for pattern(s): ${patterns.join(', ')}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
65
140
|
const date = dt.format(new Date());
|
|
66
141
|
|
|
67
142
|
const noClean = args.includes('--no-clean-dist');
|
|
@@ -84,6 +159,12 @@ export default async function run() {
|
|
|
84
159
|
await clean(outdir, date, [`!${outdir}/**/*.d.ts`]);
|
|
85
160
|
}
|
|
86
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Plugin to handle rebuilds during development.
|
|
164
|
+
* It logs the result of the build process and any warnings or errors.
|
|
165
|
+
* @type {import('esbuild').Plugin}
|
|
166
|
+
* @description This plugin is used to provide feedback during development builds.
|
|
167
|
+
*/
|
|
87
168
|
const rebuildPlugin = {
|
|
88
169
|
name: 'dev:rebuild',
|
|
89
170
|
setup(build) {
|
|
@@ -147,17 +228,100 @@ export default async function run() {
|
|
|
147
228
|
console.log(dim(`[${date}] `) + green('√ Build Complete'));
|
|
148
229
|
break;
|
|
149
230
|
}
|
|
150
|
-
case 'help': {
|
|
151
|
-
showHelp();
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
default: {
|
|
155
|
-
showHelp();
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
231
|
}
|
|
159
232
|
}
|
|
160
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Run tests using the Node.js test runner.
|
|
236
|
+
* @param {string[]} args - The command line arguments for the test command.
|
|
237
|
+
*/
|
|
238
|
+
async function test(args) {
|
|
239
|
+
const parsedArgs = parseArgs({
|
|
240
|
+
args,
|
|
241
|
+
allowPositionals: true,
|
|
242
|
+
options: {
|
|
243
|
+
// aka --test-name-pattern: https://nodejs.org/api/test.html#filtering-tests-by-name
|
|
244
|
+
match: { type: 'string', alias: 'm' },
|
|
245
|
+
// aka --test-only: https://nodejs.org/api/test.html#only-tests
|
|
246
|
+
only: { type: 'boolean', alias: 'o' },
|
|
247
|
+
// aka --test-concurrency: https://nodejs.org/api/test.html#test-runner-execution-model
|
|
248
|
+
parallel: { type: 'boolean', alias: 'p' },
|
|
249
|
+
// experimental: https://nodejs.org/api/test.html#watch-mode
|
|
250
|
+
watch: { type: 'boolean', alias: 'w' },
|
|
251
|
+
// Test timeout in milliseconds (default: 30000ms)
|
|
252
|
+
timeout: { type: 'string', alias: 't' },
|
|
253
|
+
// Test setup file
|
|
254
|
+
setup: { type: 'string', alias: 's' },
|
|
255
|
+
// Test teardown file
|
|
256
|
+
teardown: { type: 'string' },
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const pattern = parsedArgs.positionals[0];
|
|
261
|
+
if (!pattern) throw new Error('Missing test glob pattern');
|
|
262
|
+
|
|
263
|
+
const files = await glob(pattern, {
|
|
264
|
+
filesOnly: true,
|
|
265
|
+
absolute: true,
|
|
266
|
+
ignore: ['**/node_modules/**'],
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
if (files.length === 0) {
|
|
270
|
+
throw new Error(`No test files found matching pattern: ${pattern}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// For some reason, the `only` option does not work and we need to explicitly set the CLI flag instead.
|
|
274
|
+
// Node.js requires opt-in to run .only tests :(
|
|
275
|
+
// https://nodejs.org/api/test.html#only-tests
|
|
276
|
+
if (parsedArgs.values.only) {
|
|
277
|
+
process.env.NODE_OPTIONS ??= '';
|
|
278
|
+
process.env.NODE_OPTIONS += ' --test-only';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!parsedArgs.values.parallel) {
|
|
282
|
+
// If not parallel, we create a temporary file that imports all the test files
|
|
283
|
+
// so that it all runs in a single process.
|
|
284
|
+
const tempTestFile = path.resolve('./node_modules/.withstudiocms/test.mjs');
|
|
285
|
+
await fs.mkdir(path.dirname(tempTestFile), { recursive: true });
|
|
286
|
+
await fs.writeFile(
|
|
287
|
+
tempTestFile,
|
|
288
|
+
files.map((f) => `import ${JSON.stringify(pathToFileURL(f).toString())};`).join('\n')
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
files.length = 0;
|
|
292
|
+
files.push(tempTestFile);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const teardownModule = parsedArgs.values.teardown
|
|
296
|
+
? await import(pathToFileURL(path.resolve(parsedArgs.values.teardown)).toString())
|
|
297
|
+
: undefined;
|
|
298
|
+
|
|
299
|
+
// https://nodejs.org/api/test.html#runoptions
|
|
300
|
+
run({
|
|
301
|
+
files,
|
|
302
|
+
testNamePatterns: parsedArgs.values.match,
|
|
303
|
+
concurrency: parsedArgs.values.parallel,
|
|
304
|
+
only: parsedArgs.values.only,
|
|
305
|
+
setup: parsedArgs.values.setup,
|
|
306
|
+
watch: parsedArgs.values.watch,
|
|
307
|
+
timeout: parsedArgs.values.timeout ? Number(parsedArgs.values.timeout) : defaultTimeout, // Node.js defaults to Infinity, so set better fallback
|
|
308
|
+
})
|
|
309
|
+
.on('test:fail', () => {
|
|
310
|
+
// For some reason, a test fail using the JS API does not set an exit code of 1,
|
|
311
|
+
// so we set it here manually
|
|
312
|
+
process.exitCode = 1;
|
|
313
|
+
})
|
|
314
|
+
.on('end', () => {
|
|
315
|
+
const testPassed = process.exitCode === 0 || process.exitCode === undefined;
|
|
316
|
+
teardownModule?.default(testPassed);
|
|
317
|
+
})
|
|
318
|
+
.pipe(new spec())
|
|
319
|
+
.pipe(process.stdout);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Show the help message for the buildkit CLI.
|
|
324
|
+
*/
|
|
161
325
|
function showHelp() {
|
|
162
326
|
console.log(`
|
|
163
327
|
${green('StudioCMS Buildkit')} - Build tool for StudioCMS packages
|
|
@@ -166,42 +330,58 @@ ${yellow('Usage:')}
|
|
|
166
330
|
buildkit <command> [...files] [...options]
|
|
167
331
|
|
|
168
332
|
${yellow('Commands:')}
|
|
169
|
-
dev
|
|
170
|
-
build
|
|
171
|
-
|
|
333
|
+
dev Watch files and rebuild on changes
|
|
334
|
+
build Perform a one-time build
|
|
335
|
+
test Run tests with Node.js test runner
|
|
336
|
+
help Show this help message
|
|
337
|
+
|
|
338
|
+
${yellow('Dev and Build Options:')}
|
|
339
|
+
--no-clean-dist Skip cleaning the dist directory
|
|
340
|
+
--bundle Enable bundling mode
|
|
341
|
+
--force-cjs Force CommonJS output format
|
|
342
|
+
--tsconfig=<path> Specify TypeScript config file (default: tsconfig.json)
|
|
343
|
+
--outdir=<path> Specify output directory (default: dist)
|
|
172
344
|
|
|
173
|
-
${yellow('Options:')}
|
|
174
|
-
|
|
175
|
-
--
|
|
176
|
-
|
|
177
|
-
--
|
|
178
|
-
--
|
|
345
|
+
${yellow('Test Options:')}
|
|
346
|
+
-m, --match <pattern> Filter tests by name pattern
|
|
347
|
+
-o, --only Run only tests marked with .only
|
|
348
|
+
-p, --parallel Run tests in parallel (default: true)
|
|
349
|
+
-w, --watch Watch for file changes and rerun tests
|
|
350
|
+
-t, --timeout <ms> Set test timeout in milliseconds (default: ${defaultTimeout})
|
|
351
|
+
-s, --setup <file> Specify setup file to run before tests
|
|
352
|
+
--teardown <file> Specify teardown file to run after tests
|
|
179
353
|
|
|
180
354
|
${yellow('Examples:')}
|
|
181
|
-
buildkit
|
|
182
|
-
buildkit
|
|
183
|
-
buildkit build "src/**/*.ts" --bundle --force-cjs
|
|
355
|
+
- buildkit dev "src/**/*.ts" --no-clean-dist
|
|
356
|
+
- buildkit build "src/**/*.ts"
|
|
357
|
+
- buildkit build "src/**/*.ts" --bundle --force-cjs
|
|
358
|
+
- buildkit test "test/**/*.test.js" --timeout 50000
|
|
359
|
+
- buildkit test "test/**/*.test.js" --match "studiocms" --only
|
|
184
360
|
`);
|
|
185
361
|
}
|
|
186
362
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
363
|
+
/**
|
|
364
|
+
* Main function to handle command line arguments and execute the appropriate command.
|
|
365
|
+
*/
|
|
366
|
+
export default async function main() {
|
|
367
|
+
const [cmd, ...args] = process.argv.slice(2);
|
|
368
|
+
switch (cmd) {
|
|
369
|
+
case 'dev':
|
|
370
|
+
case 'build':
|
|
371
|
+
await devAndBuild(cmd, args);
|
|
372
|
+
break;
|
|
373
|
+
case 'test':
|
|
374
|
+
await test(args);
|
|
375
|
+
break;
|
|
376
|
+
default: {
|
|
377
|
+
showHelp();
|
|
378
|
+
break;
|
|
200
379
|
}
|
|
201
|
-
} catch (readError) {
|
|
202
|
-
throw new Error(`Failed to read ${path}: ${readError.message}`);
|
|
203
380
|
}
|
|
204
381
|
}
|
|
205
382
|
|
|
206
383
|
// THIS IS THE ENTRY POINT FOR THE CLI - DO NOT REMOVE
|
|
207
|
-
|
|
384
|
+
main().catch((error) => {
|
|
385
|
+
console.error(error);
|
|
386
|
+
process.exit(1);
|
|
387
|
+
});
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withstudiocms/buildkit",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
4
|
"description": "Build kit based on esbuild for the withstudiocms github org",
|
|
5
5
|
"author": {
|
|
6
|
-
"name": "
|
|
6
|
+
"name": "withstudiocms",
|
|
7
7
|
"url": "https://studiocms.dev"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/withstudiocms/studiocms.git",
|
|
12
|
-
"directory": "packages/
|
|
12
|
+
"directory": "packages/@withstudiocms/buildkit"
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"files": [
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"buildkit": "./index.js"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"esbuild": "^0.25.
|
|
29
|
+
"esbuild": "^0.25.8",
|
|
30
30
|
"fast-glob": "^3.3.3",
|
|
31
31
|
"kleur": "^4.1.5"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"vitest": "^3.
|
|
34
|
+
"vitest": "^3.2.4",
|
|
35
35
|
"strip-ansi": "^7.1.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|