fix(config): update .smartconfig.json handling and harden bundler runtime compatibility

This commit is contained in:
2026-05-09 12:34:00 +00:00
parent f19c7c69af
commit e5b2f2ba30
18 changed files with 2712 additions and 3190 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"json.schemas": [ "json.schemas": [
{ {
"fileMatch": ["/smartconfig.json"], "fileMatch": ["/.smartconfig.json"],
"schema": { "schema": {
"type": "object", "type": "object",
"properties": { "properties": {
+1 -1
View File
@@ -3,7 +3,7 @@
"target": "ES2022", "target": "ES2022",
"module": "ES2022", "module": "ES2022",
"moduleResolution": "node12", "moduleResolution": "node12",
"preserveValueImports": true, "verbatimModuleSyntax": true,
"esModuleInterop": true "esModuleInterop": true
} }
} }
+8
View File
@@ -1,5 +1,13 @@
# Changelog # Changelog
## 2026-05-09 - 2.10.2 - fix(config)
update .smartconfig.json handling and harden bundler runtime compatibility
- switch init, CLI, docs, and custom config loading references from smartconfig.json to .smartconfig.json
- add guards for missing transportOptions and missing rspack stats in spawned bundler processes
- align TypeScript and test fixtures with newer decorator and module syntax requirements
- adjust rspack module output configuration and refactor asset/html option handling for safer processing
## 2026-04-30 - 2.10.1 - fix(mod_custom) ## 2026-04-30 - 2.10.1 - fix(mod_custom)
make base64ts bundle output deterministic and clean up generated temp sourcemaps make base64ts bundle output deterministic and clean up generated temp sourcemaps
+17 -20
View File
@@ -6,7 +6,7 @@
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
"type": "module", "type": "module",
"author": "Lossless GmbH", "author": "Task Venture Capital GmbH",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"test": "pnpm run build && (tstest test/ --verbose)", "test": "pnpm run build && (tstest test/ --verbose)",
@@ -17,29 +17,29 @@
"tsbundle": "cli.js" "tsbundle": "cli.js"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^4.3.0", "@git.zone/tsbuild": "^4.4.0",
"@git.zone/tsrun": "^2.0.1", "@git.zone/tsrun": "^2.0.3",
"@git.zone/tstest": "^3.5.1", "@git.zone/tstest": "^3.6.5",
"@types/node": "^25.5.0" "@types/node": "^25.6.2"
}, },
"dependencies": { "dependencies": {
"@push.rocks/early": "^4.0.4", "@push.rocks/early": "^4.0.4",
"@push.rocks/smartconfig": "^6.0.1", "@push.rocks/smartcli": "^4.0.21",
"@push.rocks/smartcli": "^4.0.20", "@push.rocks/smartconfig": "^6.1.1",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdelay": "^3.1.0",
"@push.rocks/smartfs": "^1.5.0", "@push.rocks/smartfs": "^1.5.1",
"@push.rocks/smartinteract": "^2.0.16", "@push.rocks/smartinteract": "^2.0.16",
"@push.rocks/smartlog": "^3.2.1", "@push.rocks/smartlog": "^3.2.2",
"@push.rocks/smartlog-destination-local": "^9.0.2", "@push.rocks/smartlog-destination-local": "^9.0.2",
"@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3", "@push.rocks/smartpromise": "^4.2.4",
"@push.rocks/smartspawn": "^3.0.3", "@push.rocks/smartspawn": "^3.0.4",
"@rspack/core": "^1.7.10", "@rspack/core": "^2.0.2",
"@types/html-minifier": "^4.0.6", "@types/html-minifier": "^4.0.6",
"esbuild": "^0.27.4", "esbuild": "^0.28.0",
"html-minifier": "^4.0.0", "html-minifier": "^4.0.0",
"rolldown": "1.0.0-rc.11", "rolldown": "1.0.0",
"typescript": "6.0.2" "typescript": "6.0.3"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",
@@ -64,8 +64,5 @@
"bugs": { "bugs": {
"url": "https://gitlab.com/gitzone/tsbundle/issues" "url": "https://gitlab.com/gitzone/tsbundle/issues"
}, },
"homepage": "https://gitlab.com/gitzone/tsbundle#readme", "homepage": "https://gitlab.com/gitzone/tsbundle#readme"
"pnpm": {
"overrides": {}
}
} }
+2613 -3101
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -41,19 +41,19 @@ Once configured, simply run:
tsbundle tsbundle
``` ```
Your bundles will be built according to your `smartconfig.json` configuration. Your bundles will be built according to your `.smartconfig.json` configuration.
## CLI Commands ## CLI Commands
| Command | Description | | Command | Description |
|---------|-------------| |---------|-------------|
| `tsbundle` | Build all bundles from `smartconfig.json` configuration | | `tsbundle` | Build all bundles from `.smartconfig.json` configuration |
| `tsbundle custom` | Same as above (explicit) | | `tsbundle custom` | Same as above (explicit) |
| `tsbundle init` | Interactive wizard to create/update bundle configuration | | `tsbundle init` | Interactive wizard to create/update bundle configuration |
## Configuration ## Configuration
tsbundle uses `smartconfig.json` for configuration. Here's an example: tsbundle uses `.smartconfig.json` for configuration. Here's an example:
```json ```json
{ {
@@ -188,7 +188,7 @@ await output.writeToFile('./ts/embedded-bundle.ts', 200); // optional maxLineLen
### CustomBundleHandler Class ### CustomBundleHandler Class
Process multiple bundle configurations from `smartconfig.json`: Process multiple bundle configurations from `.smartconfig.json`:
```typescript ```typescript
import { CustomBundleHandler } from '@git.zone/tsbundle'; import { CustomBundleHandler } from '@git.zone/tsbundle';
@@ -253,12 +253,12 @@ your-project/
├── assets/ # Static assets (images, fonts, etc.) ├── assets/ # Static assets (images, fonts, etc.)
├── dist_bundle/ # Output for element/npm bundles ├── dist_bundle/ # Output for element/npm bundles
├── dist_serve/ # Output for website bundles ├── dist_serve/ # Output for website bundles
└── smartconfig.json # tsbundle configuration └── .smartconfig.json # tsbundle configuration
``` ```
## License and Legal Information ## License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file. This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./license) file.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file. **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
+4 -5
View File
@@ -1,16 +1,15 @@
// Test file to verify decorator functionality // Test file to verify decorator functionality
const decoratedClasses: Function[] = []; const decoratedClasses: Function[] = [];
function trackClass(constructor: Function) { function trackClass(value: Function, _context: ClassDecoratorContext) {
decoratedClasses.push(constructor); decoratedClasses.push(value);
return constructor;
} }
function logMethod(_target: any, context: ClassMethodDecoratorContext) { function logMethod(method: Function, context: ClassMethodDecoratorContext) {
const methodName = String(context.name); const methodName = String(context.name);
return function (this: any, ...args: any[]) { return function (this: any, ...args: any[]) {
console.log(`Calling method: ${methodName}`); console.log(`Calling method: ${methodName}`);
return (_target as Function).apply(this, args); return method.apply(this, args);
}; };
} }
+2 -2
View File
@@ -11,10 +11,10 @@ export class MyElement extends LitElement {
`; `;
@property({ type: String }) @property({ type: String })
name = 'World'; accessor name = 'World';
@property({ type: Number }) @property({ type: Number })
count = 0; accessor count = 0;
render() { render() {
return html` return html`
+3 -3
View File
@@ -4,9 +4,9 @@ const myConst: string = 'hello';
await smartdelay.delayFor(1000); await smartdelay.delayFor(1000);
function sealed(constructor: Function) { function sealed(value: Function, _context: ClassDecoratorContext) {
Object.seal(constructor); Object.seal(value);
Object.seal(constructor.prototype); Object.seal(value.prototype);
} }
@sealed @sealed
+1 -1
View File
@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/tsbundle', name: '@git.zone/tsbundle',
version: '2.10.1', version: '2.10.2',
description: 'a multi-bundler tool supporting esbuild, rolldown, and rspack for painless bundling of web projects' description: 'a multi-bundler tool supporting esbuild, rolldown, and rspack for painless bundling of web projects'
} }
+8 -10
View File
@@ -40,29 +40,27 @@ export class AssetsHandler {
// copies the html // copies the html
public async processAssets(optionsArg?: { from?: string; to?: string }) { public async processAssets(optionsArg?: { from?: string; to?: string }) {
// lets assemble the options // lets assemble the options
optionsArg = { const options = {
...{
from: this.defaultFromDirPath, from: this.defaultFromDirPath,
to: this.defaultToDirPath, to: this.defaultToDirPath,
},
...(optionsArg || {}), ...(optionsArg || {}),
}; };
await this.ensureAssetsDir(); await this.ensureAssetsDir();
optionsArg.from = plugins.smartpath.transform.toAbsolute( options.from = plugins.smartpath.transform.toAbsolute(
optionsArg.from, options.from,
paths.cwd, paths.cwd,
) as string; ) as string;
optionsArg.to = plugins.smartpath.transform.toAbsolute( options.to = plugins.smartpath.transform.toAbsolute(
optionsArg.to, options.to,
paths.cwd, paths.cwd,
) as string; ) as string;
// lets clean the target directory // lets clean the target directory
const toExists = await plugins.fs.directory(optionsArg.to).exists(); const toExists = await plugins.fs.directory(options.to).exists();
if (toExists) { if (toExists) {
await plugins.fs.directory(optionsArg.to).delete(); await plugins.fs.directory(options.to).delete();
} }
await this.copyDirectoryRecursive(optionsArg.from, optionsArg.to); await this.copyDirectoryRecursive(options.from, options.to);
} }
} }
+1 -1
View File
@@ -16,7 +16,7 @@ export class CustomBundleHandler {
} }
/** /**
* Load configuration from smartconfig.json * Load configuration from .smartconfig.json
*/ */
public async loadConfig(): Promise<boolean> { public async loadConfig(): Promise<boolean> {
const smartconfigInstance = new plugins.smartconfig.Smartconfig(this.cwd); const smartconfigInstance = new plugins.smartconfig.Smartconfig(this.cwd);
+5 -3
View File
@@ -83,9 +83,11 @@ export class TsBundleProcess {
const run = async () => { const run = async () => {
try { try {
console.log('running spawned compilation process'); console.log('running spawned compilation process');
const transportOptions: interfaces.IEnvTransportOptions = JSON.parse( const transportOptionsJson = process.env.transportOptions;
process.env.transportOptions, if (!transportOptionsJson) {
); throw new Error('Missing transportOptions environment variable');
}
const transportOptions: interfaces.IEnvTransportOptions = JSON.parse(transportOptionsJson);
console.log('=======> ESBUILD'); console.log('=======> ESBUILD');
console.log(transportOptions); console.log(transportOptions);
process.chdir(transportOptions.cwd); process.chdir(transportOptions.cwd);
+10 -12
View File
@@ -21,30 +21,28 @@ export class HtmlHandler {
to?: string; to?: string;
minify?: boolean; minify?: boolean;
}) { }) {
optionsArg = { const options = {
...{
from: this.defaultFromPath, from: this.defaultFromPath,
to: this.defaultToPath, to: this.defaultToPath,
minify: false, minify: false,
},
...optionsArg, ...optionsArg,
}; };
if (await this.checkIfExists()) { if (await this.checkIfExists()) {
console.log(`${optionsArg.from} replaces file at ${optionsArg.to}`); console.log(`${options.from} replaces file at ${options.to}`);
} }
optionsArg.from = plugins.smartpath.transform.toAbsolute( options.from = plugins.smartpath.transform.toAbsolute(
optionsArg.from, options.from,
paths.cwd, paths.cwd,
) as string; ) as string;
optionsArg.to = plugins.smartpath.transform.toAbsolute( options.to = plugins.smartpath.transform.toAbsolute(
optionsArg.to, options.to,
paths.cwd, paths.cwd,
) as string; ) as string;
let fileString = (await plugins.fs let fileString = (await plugins.fs
.file(optionsArg.from) .file(options.from)
.encoding('utf8') .encoding('utf8')
.read()) as string; .read()) as string;
if (optionsArg.minify) { if (options.minify) {
fileString = plugins.htmlMinifier.minify(fileString, { fileString = plugins.htmlMinifier.minify(fileString, {
minifyCSS: true, minifyCSS: true,
minifyJS: true, minifyJS: true,
@@ -56,9 +54,9 @@ export class HtmlHandler {
removeComments: true, removeComments: true,
}); });
} }
const toDir = plugins.path.dirname(optionsArg.to); const toDir = plugins.path.dirname(options.to);
await plugins.fs.directory(toDir).create(); await plugins.fs.directory(toDir).create();
await plugins.fs.file(optionsArg.to).encoding('utf8').write(fileString); await plugins.fs.file(options.to).encoding('utf8').write(fileString);
console.log(`html processing succeeded!`); console.log(`html processing succeeded!`);
} }
} }
+5 -5
View File
@@ -40,11 +40,11 @@ export class InitHandler {
constructor(cwd: string = paths.cwd) { constructor(cwd: string = paths.cwd) {
this.cwd = cwd; this.cwd = cwd;
this.smartconfigPath = plugins.path.join(this.cwd, 'smartconfig.json'); this.smartconfigPath = plugins.path.join(this.cwd, '.smartconfig.json');
} }
/** /**
* Load existing smartconfig.json or create empty config * Load existing .smartconfig.json or create empty config
*/ */
private async loadExistingConfig(): Promise<any> { private async loadExistingConfig(): Promise<any> {
const fileExists = await plugins.fs.file(this.smartconfigPath).exists(); const fileExists = await plugins.fs.file(this.smartconfigPath).exists();
@@ -60,12 +60,12 @@ export class InitHandler {
} }
/** /**
* Save config to smartconfig.json * Save config to .smartconfig.json
*/ */
private async saveConfig(config: any): Promise<void> { private async saveConfig(config: any): Promise<void> {
const content = JSON.stringify(config, null, 2); const content = JSON.stringify(config, null, 2);
await plugins.fs.file(this.smartconfigPath).encoding('utf8').write(content); await plugins.fs.file(this.smartconfigPath).encoding('utf8').write(content);
console.log(`\n✅ Configuration saved to smartconfig.json`); console.log(`\n✅ Configuration saved to .smartconfig.json`);
} }
/** /**
@@ -73,7 +73,7 @@ export class InitHandler {
*/ */
public async runWizard(): Promise<void> { public async runWizard(): Promise<void> {
console.log('\n🚀 tsbundle configuration wizard\n'); console.log('\n🚀 tsbundle configuration wizard\n');
console.log('This wizard will help you configure bundle settings in smartconfig.json.\n'); console.log('This wizard will help you configure bundle settings in .smartconfig.json.\n');
const smartconfigJson = await this.loadExistingConfig(); const smartconfigJson = await this.loadExistingConfig();
+5 -3
View File
@@ -90,9 +90,11 @@ export class TsBundleProcess {
const run = async () => { const run = async () => {
console.log('running spawned compilation process'); console.log('running spawned compilation process');
const transportOptions: interfaces.IEnvTransportOptions = JSON.parse( const transportOptionsJson = process.env.transportOptions;
process.env.transportOptions, if (!transportOptionsJson) {
); throw new Error('Missing transportOptions environment variable');
}
const transportOptions: interfaces.IEnvTransportOptions = JSON.parse(transportOptionsJson);
console.log('=======> ROLLDOWN'); console.log('=======> ROLLDOWN');
console.log(transportOptions); console.log(transportOptions);
process.chdir(transportOptions.cwd); process.chdir(transportOptions.cwd);
+15 -9
View File
@@ -52,6 +52,7 @@ export class TsBundleProcess {
output: { output: {
path: outputDir, path: outputDir,
filename: outputFilename, filename: outputFilename,
module: true,
library: { library: {
type: 'module' as const, type: 'module' as const,
}, },
@@ -86,9 +87,6 @@ export class TsBundleProcess {
}, },
], ],
}, },
experiments: {
outputModule: true,
},
}; };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -98,6 +96,10 @@ export class TsBundleProcess {
reject(err); reject(err);
return; return;
} }
if (!stats) {
reject(new Error('Rspack did not return stats'));
return;
}
if (stats.hasErrors()) { if (stats.hasErrors()) {
console.error(stats.toString()); console.error(stats.toString());
@@ -140,6 +142,7 @@ export class TsBundleProcess {
output: { output: {
path: outputDir, path: outputDir,
filename: outputFilename, filename: outputFilename,
module: true,
library: { library: {
type: 'module' as const, type: 'module' as const,
}, },
@@ -184,9 +187,6 @@ export class TsBundleProcess {
usedExports: true, usedExports: true,
sideEffects: true, sideEffects: true,
}, },
experiments: {
outputModule: true,
},
}; };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -196,6 +196,10 @@ export class TsBundleProcess {
reject(err); reject(err);
return; return;
} }
if (!stats) {
reject(new Error('Rspack did not return stats'));
return;
}
if (stats.hasErrors()) { if (stats.hasErrors()) {
console.error(stats.toString()); console.error(stats.toString());
@@ -221,9 +225,11 @@ export class TsBundleProcess {
const run = async () => { const run = async () => {
console.log('running spawned compilation process'); console.log('running spawned compilation process');
const transportOptions: interfaces.IEnvTransportOptions = JSON.parse( const transportOptionsJson = process.env.transportOptions;
process.env.transportOptions, if (!transportOptionsJson) {
); throw new Error('Missing transportOptions environment variable');
}
const transportOptions: interfaces.IEnvTransportOptions = JSON.parse(transportOptionsJson);
console.log('=======> RSPACK'); console.log('=======> RSPACK');
console.log(transportOptions); console.log(transportOptions);
process.chdir(transportOptions.cwd); process.chdir(transportOptions.cwd);
+1 -1
View File
@@ -5,7 +5,7 @@ import { runInit } from './mod_init/index.js';
export const runCli = async () => { export const runCli = async () => {
const tsBundleCli = new plugins.smartcli.Smartcli(); const tsBundleCli = new plugins.smartcli.Smartcli();
// Default command: run custom bundles from smartconfig.json // Default command: run custom bundles from .smartconfig.json
tsBundleCli.standardCommand().subscribe(async (argvArg) => { tsBundleCli.standardCommand().subscribe(async (argvArg) => {
await runCustomBundles(); await runCustomBundles();
}); });