quickbundle 2.6.0 → 2.7.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/README.md CHANGED
@@ -10,22 +10,27 @@
10
10
 
11
11
  Quickbundle allows you to bundle a library in a **quick**, **fast** and **easy** way:
12
12
 
13
- - Fast build and watch mode powered by Rollup[^1] and SWC[^2].
14
- - Zero configuration: define the build artifacts in your `package.json`, and you're all set!
15
- - Support of `cjs` & `esm` module formats output.
16
- - Support of several loaders including JavaScript, TypeScript, JSX, JSON, and Images.
17
- - TypeScript's declaration file (`.d.ts`) bundling.
18
- - Automatic dependency inclusion (`peerDependencies` and `dependencies` are not bundled in the final output, `devDependencies` are unless they're not imported).
13
+ - Fast build and watch mode powered by Rollup[^1] and SWC[^2].
14
+ - Compile mode to create standalone binaries for systems that do not have Node.js installed[^3].
15
+ - Zero configuration: define the build artifacts in your `package.json`, and you're all set!
16
+ - Support of `cjs` & `esm` module formats output.
17
+ - Support of several loaders including JavaScript, TypeScript, JSX, JSON, and Images.
18
+ - TypeScript's declaration file (`.d.ts`) bundling.
19
+ - Automatic dependency inclusion:
20
+ - For the build and watch mode, `peerDependencies` and `dependencies` are not bundled in the final output, `devDependencies` are unless they're not imported.
21
+ - For the compile mode, all dependencies are included to make the code standalone.
19
22
 
20
23
  [^1]: A [module bundler](https://rollupjs.org/) optimized for better tree-shaking processing and seamless interoperability of CommonJS and ESM formats with minimal code footprint.
21
24
 
22
25
  [^2]: A [TypeScript / JavaScript transpiler](https://swc.rs/) for quicker code processing including TypeScript transpilation, JavaScript transformation, and, minification.
23
26
 
27
+ [^3]: It relies on the [Node.js single executable applications feature](https://nodejs.org/api/single-executable-applications.html).
28
+
24
29
  <br>
25
30
 
26
31
  ## 🚀 Quick Start
27
32
 
28
- 1️⃣ Install by running:
33
+ ### 1️⃣ Install
29
34
 
30
35
  ```bash
31
36
  # Npm
@@ -36,9 +41,11 @@ pnpm add quickbundle
36
41
  yarn add quickbundle
37
42
  ```
38
43
 
39
- 2️⃣ Set up your package configuration (`package.json`):
44
+ ### 2️⃣ Update your package configuration (`package.json`)
45
+
46
+ #### For building libraries
40
47
 
41
- - When exporting exclusively ESM format:
48
+ - When exporting exclusively ESM format:
42
49
 
43
50
  ```jsonc
44
51
  {
@@ -53,8 +60,8 @@ yarn add quickbundle
53
60
  },
54
61
  "./otherModulePath": {
55
62
  // ...
56
- }
57
- }
63
+ },
64
+ },
58
65
  "scripts": {
59
66
  "build": "quickbundle build", // Production mode (optimizes bundle)
60
67
  "watch": "quickbundle watch", // Development mode (watches each file change)
@@ -63,7 +70,10 @@ yarn add quickbundle
63
70
  }
64
71
  ```
65
72
 
66
- - When exporting both CommonJS (CJS) and ECMAScript Modules (ESM) format (please be aware of [dual package hazard risk](https://nodejs.org/api/packages.html#dual-package-hazard)):
73
+ - When exporting both CommonJS (CJS) and ECMAScript Modules (ESM) format:
74
+
75
+ > [!warning]
76
+ > Please be aware of [dual-package-hazard-related risks](https://github.com/nodejs/package-examples?tab=readme-ov-file#dual-package-hazard) first.
67
77
 
68
78
  ```jsonc
69
79
  {
@@ -80,8 +90,8 @@ yarn add quickbundle
80
90
  },
81
91
  "./otherModulePath": {
82
92
  // ...
83
- }
84
- }
93
+ },
94
+ },
85
95
  "scripts": {
86
96
  "build": "quickbundle build", // Production mode (optimizes bundle)
87
97
  "watch": "quickbundle watch", // Development mode (watches each file change)
@@ -90,7 +100,30 @@ yarn add quickbundle
90
100
  }
91
101
  ```
92
102
 
93
- 3️⃣ Try it by running:
103
+ #### For compiling executables
104
+
105
+ > [!warning]
106
+ > The `compile` command relies on the [Node.js SEA (Single Executable Applications feature)](https://nodejs.org/api/single-executable-applications.html). This feature comes with some limitations which Quickbundle attempts to address:
107
+ >
108
+ > - The source code must not rely on [external dependencies](https://github.com/nodejs/single-executable/discussions/70) 🛑. To partially address this, Quickbundle bundles all dependencies (whatever their types, as long as they're used) and inline dynamic imports by default ✅.
109
+ > - The bundled code must use the CommonJS module system 🛑. To address this, Quickbundle always outputs CJS modules in compile mode ✅.
110
+
111
+ ```jsonc
112
+ {
113
+ "name": "lib", // Package name
114
+ "source": "./src/index.ts", // Source code entry point. Make sure that it starts with `#!/usr/bin/env node` pragme to make the binary portable for consumers who would like to use it by installing the package instead of using the generated standalone executable.
115
+ "bin": {
116
+ "your-binary-name": "./dist/index.cjs", // Binary information to get the executable name from the key and, from the value, the bundled file to generate from the source code and inject into the executable. The generated executable will be located in the same folder as the bundled file and, dependending on the current operating system running the `compile` command, the executable will be named either `your-binary-name.exe` on Windows or `your-binary-name` on Linux and macOS.
117
+ },
118
+ // "bin": "./dist/index.cjs", // Or, if the binary name follows the package name, you can define a string-based `bin` value.
119
+ "scripts": {
120
+ "build": "quickbundle compile",
121
+ },
122
+ // ...
123
+ }
124
+ ```
125
+
126
+ ### 3️⃣ Try it
94
127
 
95
128
  ```bash
96
129
  # Npm
@@ -109,14 +142,14 @@ yarn build
109
142
 
110
143
  By default, Quickbundle does the following built-in optimizations during the bundling process:
111
144
 
112
- - Include, in the build output, only the code that is effectively imported and used in the source code. Setting the `sideEffects` package.json field to `false` marks the package as a side-effect-free one and helps Quickbundle to safely prune unused exports.
113
- - [Identify and annotate](https://rollupjs.org/configuration-options/#treeshake-annotations) side-effect-free code (functions, ...) to enable a fine-grained dead-code elimination process later consumer side. For example, if a consumer uses only one library API, build output annotations added by Quickbundle allow the consumer's bundler remove all other unused APIs.
145
+ - Include, in the build output, only the code that is effectively imported and used in the source code. Setting the `sideEffects` package.json field to `false` marks the package as a side-effect-free one and helps Quickbundle to safely prune unused exports.
146
+ - [Identify and annotate](https://rollupjs.org/configuration-options/#treeshake-annotations) side-effect-free code (functions, ...) to enable a fine-grained dead-code elimination process later consumer side. For example, if a consumer uses only one library API, build output annotations added by Quickbundle allow the consumer's bundler remove all other unused APIs.
114
147
 
115
148
  However, Quickbundle doesn't minify the build output. Indeed, in general, **if the build targets a library (the most Quickbundle use case)**, minification is not necessary since enabling it can introduce some challenges:
116
149
 
117
- - Reduce the build output discoverability inside the `node_modules` folder (minified code is an obfuscated code that can be hard to read for code audit/debugging purposes).
118
- - Generate suboptimal source maps for the bundled library, as the consumer bundler will generate source maps based on the already minified library build (transformed code, mangled variable names, etc.).
119
- - Risk of side effects with double optimizations (producer side and then consumer side).
150
+ - Reduce the build output discoverability inside the `node_modules` folder (minified code is an obfuscated code that can be hard to read for code audit/debugging purposes).
151
+ - Generate suboptimal source maps for the bundled library, as the consumer bundler will generate source maps based on the already minified library build (transformed code, mangled variable names, etc.).
152
+ - Risk of side effects with double optimizations (producer side and then consumer side).
120
153
 
121
154
  Popular open source libraries ([Vue](https://unpkg.com/browse/vue@3.4.24/dist/), [SolidJS](https://unpkg.com/browse/solid-js@1.8.17/dist/), [Material UI](https://unpkg.com/browse/@material-ui/core@4.12.4/), ...) do not provide minified builds as the build optimization is sensitive to the consumer context (e.g. environment targets (browser support), ...) and needs to be fully owned upstream (i.e. consumer/application-side).
122
155
 
@@ -142,7 +175,7 @@ Enabling source map generation is needed only if a build is [obfuscated (minifie
142
175
 
143
176
  ## 🤩 Used by
144
177
 
145
- - [@adbayb/stack](https://github.com/adbayb/stack) My opinionated toolbox for JavaScript/TypeScript projects.
178
+ - [@adbayb/stack](https://github.com/adbayb/stack) My opinionated toolbox for JavaScript/TypeScript projects.
146
179
 
147
180
  <br>
148
181
 
@@ -154,8 +187,8 @@ We're open to new contributions, you can find more details [here](./CONTRIBUTING
154
187
 
155
188
  ## 💙 Acknowledgements
156
189
 
157
- - The backend is powered by [Rollup](https://github.com/rollup/rollup) and its plugin ecosystem (including [SWC](https://github.com/swc-project/swc)) to make blazing-fast builds. A special shoutout to all contributors involved.
158
- - The zero-configuration approach was inspired by [microbundle](https://github.com/developit/microbundle). A special shoutout to its author [Jason Miller](https://github.com/developit) and [all contributors](https://github.com/developit/microbundle/graphs/contributors).
190
+ - The backend is powered by [Rollup](https://github.com/rollup/rollup) and its plugin ecosystem (including [SWC](https://github.com/swc-project/swc)) to make blazing-fast builds. A special shoutout to all contributors involved.
191
+ - The zero-configuration approach was inspired by [microbundle](https://github.com/developit/microbundle). A special shoutout to its author [Jason Miller](https://github.com/developit) and [all contributors](https://github.com/developit/microbundle/graphs/contributors).
159
192
 
160
193
  <br>
161
194
 
package/bin/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { createRequire } from "node:module";
4
3
  import { join } from "node:path";
4
+ import { createRequire } from "node:module";
5
5
 
6
6
  const pkg = createRequire(import.meta.url)("../package.json");
7
7
 
package/dist/index.mjs CHANGED
@@ -1,17 +1,38 @@
1
- #!/usr/bin/env node
2
1
  import { helpers, termost } from 'termost';
3
- import { gzipSize } from 'gzip-size';
4
- import { rollup, watch as watch$1 } from 'rollup';
5
- import commonjs from '@rollup/plugin-commonjs';
6
- import json from '@rollup/plugin-json';
7
- import { nodeResolve } from '@rollup/plugin-node-resolve';
8
- import url from '@rollup/plugin-url';
2
+ import { readFile as readFile$1, writeFile, rm } from 'node:fs/promises';
3
+ import process from 'node:process';
4
+ import { watch as watch$1, rollup } from 'rollup';
5
+ import { join, basename, dirname, resolve } from 'node:path';
9
6
  import { createRequire } from 'node:module';
10
- import { join } from 'node:path';
11
- import dts from 'rollup-plugin-dts';
12
- import externals from 'rollup-plugin-node-externals';
13
7
  import { swc } from 'rollup-plugin-swc3';
14
- import { readFile as readFile$1 } from 'node:fs/promises';
8
+ import externals from 'rollup-plugin-node-externals';
9
+ import dts from 'rollup-plugin-dts';
10
+ import url from '@rollup/plugin-url';
11
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
12
+ import json from '@rollup/plugin-json';
13
+ import commonjs from '@rollup/plugin-commonjs';
14
+ import os from 'node:os';
15
+ import { gzipSize } from 'gzip-size';
16
+
17
+ var name = "quickbundle";
18
+ var version = "2.7.0";
19
+
20
+ const CWD = process.cwd();
21
+
22
+ const readFile = readFile$1;
23
+ const createCommand = (program, input)=>{
24
+ return program.command(input).option({
25
+ key: "minification",
26
+ name: "minification",
27
+ description: "Enable minification",
28
+ defaultValue: false
29
+ }).option({
30
+ key: "sourceMaps",
31
+ name: "source-maps",
32
+ description: "Enable source maps generation",
33
+ defaultValue: false
34
+ });
35
+ };
15
36
 
16
37
  const onLog = (_, log)=>{
17
38
  if (log.message.includes("Generated an empty chunk")) return;
@@ -20,68 +41,109 @@ const isRecord = (value)=>{
20
41
  return typeof value === "object" && value !== null && !Array.isArray(value);
21
42
  };
22
43
 
23
- const build = async (configurations)=>{
24
- process.env.NODE_ENV ??= "production";
25
- const output = [];
26
- for (const config of configurations){
27
- const initialTime = Date.now();
28
- const bundle = await rollup({
29
- ...config,
44
+ const watch = (input)=>{
45
+ process.env.NODE_ENV ??= "development";
46
+ const watcher = watch$1(input.data.map((configItem)=>({
47
+ ...configItem,
30
48
  onLog
31
- });
32
- if (config.output) {
33
- const outputEntries = Array.isArray(config.output) ? config.output : [
34
- config.output
35
- ];
36
- const promises = [];
37
- for (const outputEntry of outputEntries){
38
- const outputFilename = outputEntry.file ?? outputEntry.dir;
39
- if (!outputFilename) {
40
- throw new Error("Misconfigured file entry point. Make sure to define an `import`, `require`, or `default` field.");
49
+ })));
50
+ let startDuration;
51
+ console.clear();
52
+ watcher.on("event", async (event)=>{
53
+ switch(event.code){
54
+ case "START":
55
+ {
56
+ startDuration = Date.now();
57
+ clearLog(`Build in progress...`, {
58
+ type: "information"
59
+ });
60
+ return;
61
+ }
62
+ case "BUNDLE_END":
63
+ await event.result.close();
64
+ break;
65
+ case "END":
66
+ {
67
+ const duration = Date.now() - startDuration;
68
+ clearLog(`Build done in ${duration}ms (at ${new Date().toLocaleTimeString()})`, {
69
+ type: "success"
70
+ });
71
+ return;
72
+ }
73
+ case "ERROR":
74
+ {
75
+ const { error } = event;
76
+ clearLog(error.message, {
77
+ type: "error"
78
+ });
79
+ console.error("\n", error);
80
+ return;
41
81
  }
42
- promises.push(new Promise((resolve, reject)=>{
43
- bundle.write(outputEntry).then(()=>{
44
- resolve({
45
- elapsedTime: Date.now() - initialTime,
46
- filename: outputFilename
47
- });
48
- }).catch(reject);
49
- }));
50
- }
51
- output.push(...await Promise.all(promises));
52
82
  }
53
- }
54
- return output;
83
+ });
84
+ };
85
+ const clearLog = (...input)=>{
86
+ console.clear();
87
+ helpers.message(...input);
55
88
  };
56
-
57
- const CWD = process.cwd();
58
89
 
59
90
  const require = createRequire(import.meta.url);
60
91
  const PKG = require(join(CWD, "./package.json"));
61
- const createConfigurations = (options = {
92
+ const createConfiguration = (options = {
62
93
  minification: false,
63
- sourceMaps: false
94
+ sourceMaps: false,
95
+ standalone: false
64
96
  })=>{
65
- return getBuildableExports().flatMap((buildableExport)=>{
66
- return [
67
- buildableExport.source && createMainConfig({
68
- ...buildableExport,
69
- source: buildableExport.source
70
- }, options),
71
- buildableExport.source && buildableExport.types && createTypesConfig({
72
- source: buildableExport.source,
73
- types: buildableExport.types
74
- })
75
- ].filter(Boolean);
76
- });
97
+ const buildableExports = getBuildableExports(options);
98
+ return {
99
+ data: buildableExports.flatMap((buildableExport)=>{
100
+ return [
101
+ buildableExport.source && createMainConfig({
102
+ ...buildableExport,
103
+ source: buildableExport.source
104
+ }, options),
105
+ buildableExport.source && buildableExport.types && createTypesConfig({
106
+ source: buildableExport.source,
107
+ types: buildableExport.types
108
+ }, options)
109
+ ].filter(Boolean);
110
+ }),
111
+ metadata: buildableExports
112
+ };
77
113
  };
78
- const getBuildableExports = ()=>{
114
+ // eslint-disable-next-line sonarjs/cyclomatic-complexity
115
+ const getBuildableExports = ({ standalone })=>{
116
+ if (standalone) {
117
+ /**
118
+ * Entry-point resolution invariants for standalone target (mostly binaries).
119
+ */ if (!PKG.source || !PKG.bin || !PKG.name) {
120
+ throw new Error("Invalid package entry points contract. Standalone compilation is enabled but required fields are missing. Make sure to set `name`, `source`, and `bin` fields.");
121
+ }
122
+ const bin = PKG.bin;
123
+ const name = PKG.name;
124
+ const source = PKG.source;
125
+ if (isRecord(bin)) {
126
+ return Object.entries(bin).map((data)=>({
127
+ bin: data[0],
128
+ require: data[1],
129
+ source
130
+ }));
131
+ }
132
+ return [
133
+ {
134
+ // For scoped packages and if the `bin` is defined with a string value, the [scope name is discarded](the scope name is discarded when creating a binary) when creating a binary.
135
+ bin: name.replace(/^(@.*?\/)/, ""),
136
+ require: bin,
137
+ source
138
+ }
139
+ ];
140
+ }
79
141
  /**
80
- * Entry-point resolution:
142
+ * Entry-point resolution invariants for non-standalone target (mostly libraries):
81
143
  * Following the [package entry-point specification](https://nodejs.org/api/packages.html#package-entry-points),
82
144
  * whenever an export object is defined, it take precedence over other classical entry-point fields
83
145
  * (such as main, module, and types defined at the root package.json level).
84
- */ if (PKG.main ?? PKG.module ?? PKG.types ?? !PKG.exports) {
146
+ */ if (PKG.main || PKG.module || PKG.types || !PKG.exports) {
85
147
  throw new Error("Invalid package entry points contract. Use the recommended [`exports` field](https://nodejs.org/api/packages.html#package-entry-points) instead and, for TypeScript-based projects, update the `tsconfig.json` file to resolve it properly (`moduleResolution` must be set to `Bundler` (or `NodeNext`)).");
86
148
  }
87
149
  const buildableExportFields = [
@@ -130,15 +192,15 @@ const getBuildableExports = ()=>{
130
192
  }
131
193
  return output;
132
194
  };
133
- const getPlugins = (...customPlugins)=>{
195
+ const getPlugins = (customPlugins, options)=>{
134
196
  return [
135
- externals({
197
+ !options.standalone && externals({
136
198
  builtins: true,
137
199
  deps: true,
138
200
  /**
139
- * As they're not installed consumer side, `devDependencies` are declared as internal dependencies (via the `false` value)
140
- * and bundled into the dist if and only if imported and not listed as `peerDependencies` (otherwise, they're considered external).
141
- */ devDeps: false,
201
+ * As they're not installed consumer side, `devDependencies` are declared as internal dependencies (via the `false` value)
202
+ * and bundled into the dist if and only if imported and not listed as `peerDependencies` (otherwise, they're considered external).
203
+ */ devDeps: false,
142
204
  optDeps: true,
143
205
  peerDeps: true
144
206
  }),
@@ -146,7 +208,7 @@ const getPlugins = (...customPlugins)=>{
146
208
  url(),
147
209
  json(),
148
210
  ...customPlugins
149
- ];
211
+ ].filter(Boolean);
150
212
  };
151
213
  const createMainConfig = (entryPoints, options)=>{
152
214
  const { minification, sourceMaps } = options;
@@ -158,6 +220,7 @@ const createMainConfig = (entryPoints, options)=>{
158
220
  entryPoints.require && {
159
221
  file: entryPoints.require,
160
222
  format: "cjs",
223
+ inlineDynamicImports: Boolean(options.standalone),
161
224
  sourcemap: sourceMaps
162
225
  },
163
226
  esmInput && {
@@ -169,13 +232,16 @@ const createMainConfig = (entryPoints, options)=>{
169
232
  return {
170
233
  input: entryPoints.source,
171
234
  output,
172
- plugins: getPlugins(nodeResolve(), swc({
173
- minify: minification,
174
- sourceMaps
175
- }))
235
+ plugins: getPlugins([
236
+ nodeResolve(),
237
+ swc({
238
+ minify: minification,
239
+ sourceMaps
240
+ })
241
+ ], options)
176
242
  };
177
243
  };
178
- const createTypesConfig = (entryPoints)=>{
244
+ const createTypesConfig = (entryPoints, options)=>{
179
245
  return {
180
246
  input: entryPoints.source,
181
247
  output: [
@@ -183,52 +249,169 @@ const createTypesConfig = (entryPoints)=>{
183
249
  file: entryPoints.types
184
250
  }
185
251
  ],
186
- plugins: getPlugins(nodeResolve({
187
- /**
188
- * The `exports` conditional fields definition order is important in the `package.json file`.
189
- * To be resolved first, `types` field must always come first in the package.json exports definition.
190
- * @see https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#package-json-exports-imports-and-self-referencing.
191
- */ exportConditions: [
192
- "types"
193
- ]
194
- }), dts({
195
- compilerOptions: {
196
- incremental: false
197
- },
198
- respectExternal: true
199
- }))
252
+ plugins: getPlugins([
253
+ nodeResolve({
254
+ /**
255
+ * The `exports` conditional fields definition order is important in the `package.json file`.
256
+ * To be resolved first, `types` field must always come first in the package.json exports definition.
257
+ * @see https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#package-json-exports-imports-and-self-referencing.
258
+ */ exportConditions: [
259
+ "types"
260
+ ]
261
+ }),
262
+ dts({
263
+ compilerOptions: {
264
+ incremental: false
265
+ },
266
+ respectExternal: true
267
+ })
268
+ ], options)
200
269
  };
201
270
  };
202
- // eslint-disable-next-line import/no-default-export
203
- createConfigurations();
204
271
 
205
- const readFile = readFile$1;
272
+ const createWatchCommand = (program)=>{
273
+ return createCommand(program, {
274
+ name: "watch",
275
+ description: "Watch and rebuild on any code change (development mode)"
276
+ }).task({
277
+ handler (context) {
278
+ watch(createConfiguration({
279
+ minification: context.minification,
280
+ sourceMaps: context.sourceMaps,
281
+ standalone: false
282
+ }));
283
+ }
284
+ });
285
+ };
206
286
 
207
- const createCommand = (program, input)=>{
208
- return program.command(input).option({
209
- key: "minification",
210
- name: "minification",
211
- description: "Enable minification",
212
- defaultValue: false
213
- }).option({
214
- key: "sourceMaps",
215
- name: "source-maps",
216
- description: "Enable source maps generation",
217
- defaultValue: false
287
+ const build = async (input)=>{
288
+ process.env.NODE_ENV ??= "production";
289
+ const { data: configurations } = input;
290
+ const output = [];
291
+ for (const config of configurations){
292
+ const initialTime = Date.now();
293
+ const bundle = await rollup({
294
+ ...config,
295
+ onLog
296
+ });
297
+ if (config.output) {
298
+ const outputEntries = Array.isArray(config.output) ? config.output : [
299
+ config.output
300
+ ];
301
+ const promises = [];
302
+ for (const outputEntry of outputEntries){
303
+ const outputFilePath = outputEntry.file ?? outputEntry.dir;
304
+ if (!outputFilePath) {
305
+ throw new Error("Misconfigured file entry point. Make sure to define an `import`, `require`, or `default` field.");
306
+ }
307
+ promises.push(new Promise((resolve, reject)=>{
308
+ bundle.write(outputEntry).then(()=>{
309
+ resolve({
310
+ elapsedTime: Date.now() - initialTime,
311
+ filePath: outputFilePath
312
+ });
313
+ }).catch((reason)=>{
314
+ reject(reason);
315
+ });
316
+ }));
317
+ }
318
+ output.push(...await Promise.all(promises));
319
+ }
320
+ }
321
+ return output;
322
+ };
323
+
324
+ const createCompileCommand = (program)=>{
325
+ return program.command({
326
+ name: "compile",
327
+ description: "Compiles the source code into a self-contained executable"
328
+ }).task({
329
+ key: "osType",
330
+ label: "Get context",
331
+ handler () {
332
+ const type = os.type();
333
+ return type === "Windows_NT" ? "windows" : type === "Darwin" ? "macos" : "linux";
334
+ }
335
+ }).task({
336
+ key: "config",
337
+ label: "Create configuration",
338
+ handler () {
339
+ return createConfiguration({
340
+ minification: true,
341
+ sourceMaps: false,
342
+ standalone: true
343
+ });
344
+ }
345
+ }).task({
346
+ label: "Build",
347
+ async handler ({ config }) {
348
+ await build(config);
349
+ }
350
+ }).task({
351
+ label ({ config }) {
352
+ const binaries = config.metadata.map(({ bin })=>{
353
+ if (!bin) return undefined;
354
+ return `\`${bin}\``;
355
+ }).filter(Boolean).join(", ");
356
+ return `Compile ${binaries}`;
357
+ },
358
+ async handler ({ config, osType }) {
359
+ await Promise.all(config.metadata.map(async ({ bin, require })=>{
360
+ if (!require || !bin) return;
361
+ return compile({
362
+ bin,
363
+ input: require,
364
+ osType
365
+ });
366
+ }));
367
+ }
218
368
  });
219
369
  };
370
+ const compile = async ({ bin, input, osType })=>{
371
+ const inputFileName = basename(input);
372
+ const inputDirectory = dirname(input);
373
+ const resolveFromInputDirectory = (...paths)=>{
374
+ return resolve(inputDirectory, ...paths);
375
+ };
376
+ const blobFileName = resolveFromInputDirectory(`${inputFileName}.blob`);
377
+ const executableFileName = resolveFromInputDirectory(`${bin}${osType === "windows" ? ".exe" : ""}`);
378
+ const seaConfigFileName = resolveFromInputDirectory(`${inputFileName}.sea-config.json`);
379
+ await writeFile(seaConfigFileName, JSON.stringify({
380
+ disableExperimentalSEAWarning: true,
381
+ main: input,
382
+ output: blobFileName,
383
+ useCodeCache: false,
384
+ useSnapshot: false
385
+ }), "utf-8");
386
+ await Promise.all([
387
+ `node --experimental-sea-config ${seaConfigFileName}`,
388
+ `node -e "require('fs').copyFileSync(process.execPath, '${executableFileName}')"`
389
+ ].map(async (command)=>helpers.exec(command)));
390
+ if (osType === "macos") {
391
+ await helpers.exec(`codesign --remove-signature ${executableFileName}`);
392
+ }
393
+ await helpers.exec(`npx postject ${executableFileName} NODE_SEA_BLOB ${blobFileName} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 ${osType === "macos" ? "--macho-segment-name NODE_SEA" : ""}`);
394
+ if (osType === "macos") {
395
+ await helpers.exec(`codesign --sign - ${executableFileName}`);
396
+ }
397
+ await Promise.all([
398
+ blobFileName,
399
+ seaConfigFileName
400
+ ].map(async (file)=>rm(file)));
401
+ };
220
402
 
221
403
  const createBuildCommand = (program)=>{
222
- createCommand(program, {
404
+ return createCommand(program, {
223
405
  name: "build",
224
406
  description: "Build the source code (production mode)"
225
407
  }).task({
226
408
  key: "buildOutput",
227
409
  label: "Bundle assets 📦",
228
410
  async handler (context) {
229
- return build(createConfigurations({
411
+ return build(createConfiguration({
230
412
  minification: context.minification,
231
- sourceMaps: context.sourceMaps
413
+ sourceMaps: context.sourceMaps,
414
+ standalone: false
232
415
  }));
233
416
  }
234
417
  }).task({
@@ -249,7 +432,7 @@ const createBuildCommand = (program)=>{
249
432
  ].map((message, index)=>{
250
433
  return index === 0 ? message : ` ${message}`;
251
434
  }).join("\n"), {
252
- label: `${item.filename} (took ${item.elapsedTime}ms)`,
435
+ label: `${item.filePath} (took ${item.elapsedTime}ms)`,
253
436
  type: "information"
254
437
  });
255
438
  });
@@ -261,7 +444,7 @@ const createBuildCommand = (program)=>{
261
444
  };
262
445
  const computeBundleSize = async (buildOutput)=>{
263
446
  const computeFileSize = async (buildItemOutput)=>{
264
- const content = await readFile(buildItemOutput.filename);
447
+ const content = await readFile(buildItemOutput.filePath);
265
448
  const gzSize = await gzipSize(content);
266
449
  return {
267
450
  ...buildItemOutput,
@@ -276,70 +459,14 @@ const formatSize = (bytes)=>{
276
459
  return kiloBytes < 1 ? `${bytes} B` : `${kiloBytes.toFixed(2)} kB`;
277
460
  };
278
461
 
279
- const watch = (configurations)=>{
280
- process.env.NODE_ENV ??= "development";
281
- const watcher = watch$1(configurations.map((config)=>({
282
- ...config,
283
- onLog
284
- })));
285
- let startDuration;
286
- console.clear();
287
- watcher.on("event", async (event)=>{
288
- switch(event.code){
289
- case "START":
290
- {
291
- startDuration = Date.now();
292
- clearLog(`Build in progress...`, {
293
- type: "information"
294
- });
295
- return;
296
- }
297
- case "BUNDLE_END":
298
- await event.result.close();
299
- break;
300
- case "END":
301
- {
302
- const duration = Date.now() - startDuration;
303
- clearLog(`Build done in ${duration}ms (at ${new Date().toLocaleTimeString()})`, {
304
- type: "success"
305
- });
306
- return;
307
- }
308
- case "ERROR":
309
- {
310
- const { error } = event;
311
- clearLog(`${String(error)}`, {
312
- type: "error"
313
- });
314
- console.error("\n", error);
315
- return;
316
- }
317
- }
318
- });
319
- };
320
- const clearLog = (...input)=>{
321
- console.clear();
322
- helpers.message(...input);
323
- };
324
-
325
- const createWatchCommand = (program)=>{
326
- createCommand(program, {
327
- name: "watch",
328
- description: "Watch and rebuild on any code change (development mode)"
329
- }).task({
330
- handler (context) {
331
- watch(createConfigurations({
332
- minification: context.minification,
333
- sourceMaps: context.sourceMaps
334
- }));
335
- }
336
- });
337
- };
338
-
339
462
  const createProgram = (...commandBuilders)=>{
340
- const program = termost("The zero-configuration transpiler and bundler for the web");
463
+ const program = termost({
464
+ name,
465
+ description: "The zero-configuration transpiler and bundler for the web",
466
+ version
467
+ });
341
468
  for (const commandBuilder of commandBuilders){
342
469
  commandBuilder(program);
343
470
  }
344
471
  };
345
- createProgram(createBuildCommand, createWatchCommand);
472
+ createProgram(createBuildCommand, createWatchCommand, createCompileCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quickbundle",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "The zero-configuration transpiler and bundler for the web",
5
5
  "author": {
6
6
  "name": "Ayoub Adib",
@@ -40,7 +40,7 @@
40
40
  }
41
41
  },
42
42
  "peerDependencies": {
43
- "typescript": ">=4.7.0"
43
+ "typescript": "^4.7.0 || ^5.0.0"
44
44
  },
45
45
  "peerDependenciesMeta": {
46
46
  "typescript": {
@@ -52,19 +52,19 @@
52
52
  "@rollup/plugin-json": "^6.1.0",
53
53
  "@rollup/plugin-node-resolve": "^15.3.0",
54
54
  "@rollup/plugin-url": "^8.0.2",
55
- "@swc/core": "^1.7.42",
55
+ "@swc/core": "^1.9.1",
56
56
  "gzip-size": "^7.0.0",
57
- "rollup": "^4.24.3",
57
+ "rollup": "^4.24.4",
58
58
  "rollup-plugin-dts": "^6.1.1",
59
59
  "rollup-plugin-node-externals": "^7.1.3",
60
60
  "rollup-plugin-swc3": "^0.12.1",
61
- "termost": "^0.18.0"
61
+ "termost": "^1.2.0"
62
62
  },
63
63
  "devDependencies": {
64
- "@types/node": "22.8.5"
64
+ "@types/node": "22.9.0"
65
65
  },
66
66
  "scripts": {
67
- "build": "rollup --config ./src/bundler/config.ts --configPlugin rollup-plugin-swc3",
68
- "watch": "rollup --watch --config ./src/bundler/config.ts --configPlugin rollup-plugin-swc3"
67
+ "build": "rollup --config ./bundler.config.ts --configPlugin rollup-plugin-swc3",
68
+ "watch": "rollup --watch --config ./bundler.config.ts --configPlugin rollup-plugin-swc3"
69
69
  }
70
70
  }