@hypernym/bundler 0.3.1 → 0.5.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
@@ -62,6 +62,162 @@ export default defineConfig({
62
62
  npx hyperbundler
63
63
  ```
64
64
 
65
+ ## Hooks
66
+
67
+ List of lifecycle hooks that are called at various phases:
68
+
69
+ | Name | Description |
70
+ | --------------------------------------- | ---------------------------------------------------------------- |
71
+ | [`bundle:start`](#bundlestart) | Called at the beginning of bundling. |
72
+ | [`build:start`](#buildstart) | Called at the beginning of building. |
73
+ | [`build:entry:start`](#buildentrystart) | Called on each entry just before the build process. |
74
+ | [`build:entry:end`](#buildentryend) | Called on each entry right after the build process is completed. |
75
+ | [`build:end`](#buildend) | Called right after building is complete. |
76
+ | [`bundle:end`](#bundleend) | Called right after bundling is complete. |
77
+
78
+ ### bundle:start
79
+
80
+ - Type: `(options: Options) => void | Promise<void>`
81
+ - Default: `undefined`
82
+
83
+ Called at the beginning of bundling.
84
+
85
+ ```ts
86
+ // bundler.config.ts
87
+
88
+ import { defineConfig } from '@hypernym/bundler'
89
+
90
+ export default defineConfig({
91
+ hooks: {
92
+ 'bundle:start': async (options) => {
93
+ // ...
94
+ },
95
+ },
96
+ })
97
+ ```
98
+
99
+ ### build:start
100
+
101
+ - Type: `(options: Options, stats: BuildStats) => void | Promise<void>`
102
+ - Default: `undefined`
103
+
104
+ Called at the beginning of building.
105
+
106
+ ```ts
107
+ // bundler.config.ts
108
+
109
+ import { defineConfig } from '@hypernym/bundler'
110
+
111
+ export default defineConfig({
112
+ hooks: {
113
+ 'build:start': async (options, stats) => {
114
+ // ...
115
+ },
116
+ },
117
+ })
118
+ ```
119
+
120
+ ### build:entry:start
121
+
122
+ - Type: `(options: BuildEntryOptions, stats: BuildStats) => void | Promise<void>`
123
+ - Default: `undefined`
124
+
125
+ Called on each entry just before the build process.
126
+
127
+ Provides the ability to customize entry options before they are passed to the next phase.
128
+
129
+ ```ts
130
+ // bundler.config.ts
131
+
132
+ import { defineConfig } from '@hypernym/bundler'
133
+ import { plugin1, plugin2, plugin3 } from './src/utils/plugins.js'
134
+
135
+ export default defineConfig({
136
+ hooks: {
137
+ 'build:entry:start': async (options, stats) => {
138
+ // adds custom plugins for a specific entry only
139
+ if (options.input?.includes('./src/index.ts')) {
140
+ options.plugins = [
141
+ plugin1(), // adds a custom plugin before the default bundler plugins
142
+ ...options.plugins, // list of default bundler plugins
143
+ plugin2(), // adds a custom plugin after the default bundler plugins
144
+ ]
145
+ }
146
+ // adds custom plugins for a specific types only
147
+ if (options.types?.includes('./src/types.ts')) {
148
+ options.plugins = [
149
+ ...options.plugins, // list of default bundler plugins
150
+ plugin3(), // adds a custom plugin designed to work only with TS declarations
151
+ ]
152
+ }
153
+ },
154
+ },
155
+ })
156
+ ```
157
+
158
+ ### build:entry:end
159
+
160
+ - Type: `(options: BuildEntryOptions, stats: BuildStats) => void | Promise<void>`
161
+ - Default: `undefined`
162
+
163
+ Called on each entry right after the build process is completed.
164
+
165
+ ```ts
166
+ // bundler.config.ts
167
+
168
+ import { defineConfig } from '@hypernym/bundler'
169
+
170
+ export default defineConfig({
171
+ hooks: {
172
+ 'build:entry:end': async (options, stats) => {
173
+ // ...
174
+ },
175
+ },
176
+ })
177
+ ```
178
+
179
+ ### build:end
180
+
181
+ - Type: `(options: Options, stats: BuildStats) => void | Promise<void>`
182
+ - Default: `undefined`
183
+
184
+ Called right after building is complete.
185
+
186
+ ```ts
187
+ // bundler.config.ts
188
+
189
+ import { defineConfig } from '@hypernym/bundler'
190
+
191
+ export default defineConfig({
192
+ hooks: {
193
+ 'build:end': async (options, stats) => {
194
+ // ...
195
+ },
196
+ },
197
+ })
198
+ ```
199
+
200
+ ### bundle:end
201
+
202
+ - Type: `(options: Options) => void | Promise<void>`
203
+ - Default: `undefined`
204
+
205
+ Called right after bundling is complete.
206
+
207
+ ```ts
208
+ // bundler.config.ts
209
+
210
+ import { defineConfig } from '@hypernym/bundler'
211
+
212
+ export default defineConfig({
213
+ hooks: {
214
+ 'bundle:end': async (options) => {
215
+ // ...
216
+ },
217
+ },
218
+ })
219
+ ```
220
+
65
221
  ## Options
66
222
 
67
223
  ### outDir
@@ -74,11 +230,36 @@ Specifies the output directory for production bundle.
74
230
  ```ts
75
231
  // bundler.config.ts
76
232
 
233
+ import { defineConfig } from '@hypernym/bundler'
234
+
77
235
  export default defineConfig({
78
236
  outDir: 'output',
79
237
  })
80
238
  ```
81
239
 
240
+ ### externals
241
+
242
+ - Type: `(string | RegExp)[]`
243
+ - Default: `[/^node:/, /^@types/, /^@rollup/, /^@hypernym/, /^rollup/, ...pkg.dependencies]`
244
+
245
+ Specifies the module IDs, or regular expressions to match module IDs, that should remain external to the bundle.
246
+
247
+ IDs and regexps from this option are applied globally to all entries.
248
+
249
+ Also, it is possible to define externals individually per entry (`entry.externals`).
250
+
251
+ ```ts
252
+ // bundler.config.ts
253
+
254
+ import { defineConfig } from '@hypernym/bundler'
255
+
256
+ export default defineConfig({
257
+ externals: ['id-1', 'id-2', /regexp/],
258
+ })
259
+ ```
260
+
261
+ ## CLI
262
+
82
263
  ### Custom Config
83
264
 
84
265
  Set a custom config path via the CLI command:
@@ -25,7 +25,7 @@ const externals = [
25
25
  ];
26
26
 
27
27
  const name = "bundler";
28
- const version = `0.3.1`;
28
+ const version = `0.5.0`;
29
29
 
30
30
  const cl = console.log;
31
31
  const logger = {
@@ -204,106 +204,132 @@ async function build(cwd, options) {
204
204
  const entryStart = Date.now();
205
205
  const logFilter = getLogFilter(entry.logFilter || []);
206
206
  if ("input" in entry) {
207
- const { input, externals, plugins, banner, footer } = entry;
208
- const buildLogs = [];
209
- const _output = getOutputPath(outDir, input);
207
+ const _output = getOutputPath(outDir, entry.input);
210
208
  let _format = "esm";
211
209
  if (_output.endsWith(".cjs"))
212
210
  _format = "cjs";
213
- const output = entry.output || _output;
214
- const format = entry.format || _format;
215
- const _plugins = [esbuild(plugins?.esbuild)];
216
- if (plugins?.json) {
217
- const jsonOptions = isObject(plugins.json) ? plugins.json : void 0;
218
- _plugins.push(jsonPlugin(jsonOptions));
211
+ const buildLogs = [];
212
+ const _entry = {
213
+ input: entry.input,
214
+ output: entry.output || _output,
215
+ externals: entry.externals || options.externals,
216
+ format: entry.format || _format,
217
+ plugins: [esbuild(entry.plugins?.esbuild)],
218
+ pluginsOptions: entry.plugins,
219
+ banner: entry.banner,
220
+ footer: entry.footer,
221
+ intro: entry.intro,
222
+ outro: entry.outro,
223
+ paths: entry.paths,
224
+ name: entry.name,
225
+ globals: entry.globals,
226
+ extend: entry.extend
227
+ };
228
+ if (_entry.pluginsOptions?.json) {
229
+ const jsonOptions = isObject(_entry.pluginsOptions.json) ? _entry.pluginsOptions.json : void 0;
230
+ _entry.plugins.push(jsonPlugin(jsonOptions));
219
231
  }
220
- if (plugins?.replace) {
221
- _plugins.unshift(
232
+ if (_entry.pluginsOptions?.replace) {
233
+ _entry.plugins.unshift(
222
234
  replacePlugin({
223
235
  true: true,
224
- ...plugins.replace
236
+ ..._entry.pluginsOptions.replace
225
237
  })
226
238
  );
227
239
  }
228
- if (plugins?.resolve) {
229
- const resolveOptions = isObject(plugins.resolve) ? plugins.resolve : void 0;
230
- _plugins.unshift(resolvePlugin(resolveOptions));
240
+ if (_entry.pluginsOptions?.resolve) {
241
+ const resolveOptions = isObject(_entry.pluginsOptions.resolve) ? _entry.pluginsOptions.resolve : void 0;
242
+ _entry.plugins.unshift(resolvePlugin(resolveOptions));
231
243
  }
232
- if (hooks?.["rollup:plugins"]) {
233
- hooks["rollup:plugins"](_plugins, {
234
- ...entry,
235
- input,
236
- output,
237
- format
238
- });
244
+ if (hooks?.["build:entry:start"]) {
245
+ await hooks["build:entry:start"](_entry, buildStats);
239
246
  }
240
- const builder = await rollup({
241
- input: resolve(cwd, input),
242
- external: externals || options.externals,
243
- plugins: _plugins,
247
+ const _build = await rollup({
248
+ input: resolve(cwd, _entry.input),
249
+ external: _entry.externals,
250
+ plugins: _entry.plugins,
244
251
  onLog: (level, log) => {
245
252
  if (logFilter(log))
246
253
  buildLogs.push({ level, log });
247
254
  }
248
255
  });
249
- await builder.write({
250
- file: resolve(cwd, output),
251
- format,
252
- banner,
253
- footer
256
+ await _build.write({
257
+ file: resolve(cwd, _entry.output),
258
+ format: _entry.format,
259
+ banner: _entry.banner,
260
+ footer: _entry.footer,
261
+ intro: _entry.intro,
262
+ outro: _entry.outro,
263
+ paths: _entry.paths,
264
+ name: _entry.name,
265
+ globals: _entry.globals,
266
+ extend: _entry.extend
254
267
  });
255
- const stats = await stat(resolve(cwd, output));
268
+ const stats = await stat(resolve(cwd, _entry.output));
256
269
  buildStats.files.push({
257
- path: output,
270
+ path: _entry.output,
258
271
  size: stats.size,
259
272
  buildTime: Date.now() - entryStart,
260
- format,
273
+ format: _entry.format,
261
274
  logs: buildLogs
262
275
  });
263
276
  buildStats.size = buildStats.size + stats.size;
277
+ if (hooks?.["build:entry:end"]) {
278
+ await hooks["build:entry:end"](_entry, buildStats);
279
+ }
264
280
  }
265
281
  if ("types" in entry) {
266
- const { types, externals, plugins, banner, footer } = entry;
267
- const buildLogs = [];
268
- const _output = getOutputPath(outDir, types, true);
282
+ const _output = getOutputPath(outDir, entry.types, true);
269
283
  let _format = "esm";
270
284
  if (_output.endsWith(".d.cts"))
271
285
  _format = "cjs";
272
- const output = entry.output || _output;
273
- const format = entry.format || _format;
274
- const _plugins = [dts(plugins?.dts)];
275
- if (hooks?.["rollup:plugins"]) {
276
- hooks["rollup:plugins"](_plugins, {
277
- ...entry,
278
- types,
279
- output,
280
- format
281
- });
286
+ const buildLogs = [];
287
+ const _entry = {
288
+ types: entry.types,
289
+ output: entry.output || _output,
290
+ externals: entry.externals || options.externals,
291
+ format: entry.format || _format,
292
+ plugins: [dts(entry.plugins?.dts)],
293
+ pluginsOptions: entry.plugins,
294
+ banner: entry.banner,
295
+ footer: entry.footer,
296
+ intro: entry.intro,
297
+ outro: entry.outro,
298
+ paths: entry.paths
299
+ };
300
+ if (hooks?.["build:entry:start"]) {
301
+ await hooks["build:entry:start"](_entry, buildStats);
282
302
  }
283
- const builder = await rollup({
284
- input: resolve(cwd, types),
285
- external: externals || options.externals,
286
- plugins: _plugins,
303
+ const _build = await rollup({
304
+ input: resolve(cwd, _entry.types),
305
+ external: _entry.externals,
306
+ plugins: _entry.plugins,
287
307
  onLog: (level, log) => {
288
308
  if (logFilter(log))
289
309
  buildLogs.push({ level, log });
290
310
  }
291
311
  });
292
- await builder.write({
293
- file: resolve(cwd, output),
294
- format,
295
- banner,
296
- footer
312
+ await _build.write({
313
+ file: resolve(cwd, _entry.output),
314
+ format: _entry.format,
315
+ banner: _entry.banner,
316
+ footer: _entry.footer,
317
+ intro: _entry.intro,
318
+ outro: _entry.outro,
319
+ paths: _entry.paths
297
320
  });
298
- const stats = await stat(resolve(cwd, output));
321
+ const stats = await stat(resolve(cwd, _entry.output));
299
322
  buildStats.files.push({
300
- path: output,
323
+ path: _entry.output,
301
324
  size: stats.size,
302
325
  buildTime: Date.now() - entryStart,
303
- format,
326
+ format: _entry.format,
304
327
  logs: buildLogs
305
328
  });
306
329
  buildStats.size = buildStats.size + stats.size;
330
+ if (hooks?.["build:entry:end"]) {
331
+ await hooks["build:entry:end"](_entry, buildStats);
332
+ }
307
333
  }
308
334
  }
309
335
  buildStats.buildTime = Date.now() - start;
@@ -356,7 +382,12 @@ async function createBuilder(cwd, args, options) {
356
382
  format = "dts";
357
383
  if (file.logs) {
358
384
  for (const log of file.logs) {
359
- cl(magenta("!"), magenta(log.log.message));
385
+ cl(
386
+ magenta("!"),
387
+ dim("\u251C\u2500"),
388
+ magenta(`${log.level}:`),
389
+ magenta(log.log.message)
390
+ );
360
391
  }
361
392
  }
362
393
  cl(
@@ -5,13 +5,15 @@ import { RollupNodeResolveOptions } from '@rollup/plugin-node-resolve';
5
5
  import { TransformOptions } from 'esbuild';
6
6
  import { Options as Options$1 } from 'rollup-plugin-dts';
7
7
 
8
- interface PluginsOptions {
8
+ interface PluginsInput {
9
9
  esbuild?: TransformOptions;
10
- dts?: Options$1;
11
10
  resolve?: RollupNodeResolveOptions | true;
12
11
  json?: RollupJsonOptions | true;
13
12
  replace?: RollupReplaceOptions;
14
13
  }
14
+ interface PluginsTypes {
15
+ dts?: Options$1;
16
+ }
15
17
 
16
18
  interface EntryBase {
17
19
  /**
@@ -49,6 +51,24 @@ interface EntryBase {
49
51
  * @default undefined
50
52
  */
51
53
  footer?: OutputOptions['footer'];
54
+ /**
55
+ * Specifies the code at the beginning that goes inside any _format-specific_ wrapper.
56
+ *
57
+ * @default undefined
58
+ */
59
+ intro?: OutputOptions['intro'];
60
+ /**
61
+ * Specifies the code at the end that goes inside any _format-specific_ wrapper.
62
+ *
63
+ * @default undefined
64
+ */
65
+ outro?: OutputOptions['outro'];
66
+ /**
67
+ * Maps external module IDs to paths.
68
+ *
69
+ * @default undefined
70
+ */
71
+ paths?: OutputOptions['paths'];
52
72
  /**
53
73
  * Specifies custom filters that will display only certain log messages.
54
74
  *
@@ -66,7 +86,29 @@ interface EntryInput extends EntryBase {
66
86
  *
67
87
  * @default undefined
68
88
  */
69
- plugins?: PluginsOptions;
89
+ plugins?: PluginsInput;
90
+ /**
91
+ * Specifies the global variable name that representing exported bundle.
92
+ *
93
+ * Intended for `umd/iife` formats.
94
+ *
95
+ * @default undefined
96
+ */
97
+ name?: OutputOptions['name'];
98
+ /**
99
+ * Specifies global _module ID_ and _variable name_ pairs necessary for external imports.
100
+ *
101
+ * Intended for `umd/iife` formats.
102
+ *
103
+ * @default undefined
104
+ */
105
+ globals?: OutputOptions['globals'];
106
+ /**
107
+ * Specifies whether to extend the global variable defined by the `name` option.
108
+ *
109
+ * Intended for `umd/iife` formats.
110
+ */
111
+ extend?: OutputOptions['extend'];
70
112
  }
71
113
  interface EntryTypes extends EntryBase {
72
114
  /**
@@ -78,7 +120,7 @@ interface EntryTypes extends EntryBase {
78
120
  *
79
121
  * @default undefined
80
122
  */
81
- plugins?: Pick<PluginsOptions, 'dts'>;
123
+ plugins?: PluginsTypes;
82
124
  }
83
125
  type EntryOptions = EntryInput | EntryTypes;
84
126
 
@@ -181,10 +223,22 @@ interface BuildStats {
181
223
  logs: BuildLogs[];
182
224
  }[];
183
225
  }
226
+ interface BuildEntryOptions extends EntryBase, Partial<Omit<EntryInput, 'plugins'>>, Partial<Omit<EntryTypes, 'plugins'>> {
227
+ /**
228
+ * Specifies list of plugins.
229
+ */
230
+ plugins: Plugin[];
231
+ /**
232
+ * Specifies options for default plugins.
233
+ *
234
+ * @default undefined
235
+ */
236
+ pluginsOptions?: PluginsInput & PluginsTypes;
237
+ }
184
238
 
185
239
  interface HooksOptions {
186
240
  /**
187
- * Called just before bundling started.
241
+ * Called at the beginning of bundling.
188
242
  *
189
243
  * @example
190
244
  *
@@ -200,16 +254,16 @@ interface HooksOptions {
200
254
  *
201
255
  * @default undefined
202
256
  */
203
- 'bundle:start'?: (options?: Options) => void | Promise<void>;
257
+ 'bundle:start'?: (options: Options) => void | Promise<void>;
204
258
  /**
205
- * Called just before building started.
259
+ * Called at the beginning of building.
206
260
  *
207
261
  * @example
208
262
  *
209
263
  * ```ts
210
264
  * export default defineConfig({
211
265
  * hooks: {
212
- * 'build:start': async (options, buildStats) => {
266
+ * 'build:start': async (options, stats) => {
213
267
  * // ...
214
268
  * }
215
269
  * }
@@ -218,11 +272,11 @@ interface HooksOptions {
218
272
  *
219
273
  * @default undefined
220
274
  */
221
- 'build:start'?: (options?: Options, buildStats?: BuildStats) => void | Promise<void>;
275
+ 'build:start'?: (options: Options, stats: BuildStats) => void | Promise<void>;
222
276
  /**
223
- * Called just before the initialization of the Rollup plugin.
277
+ * Called on each entry just before the build process.
224
278
  *
225
- * Provides the ability to add and manipulate custom plugins.
279
+ * Provides the ability to customize entry options before they are passed to the next phase.
226
280
  *
227
281
  * @example
228
282
  *
@@ -231,17 +285,22 @@ interface HooksOptions {
231
285
  *
232
286
  * export default defineConfig({
233
287
  * hooks: {
234
- * 'rollup:plugins': (plugins, entry) => {
235
- * // adds a custom plugin before the default bundler plugins
236
- * plugins.unshift(plugin1())
237
- * // adds a custom plugin after the default bundler plugins
238
- * plugins.push(plugin2())
239
- * // adds a custom plugin for a specific entry only
240
- * if (entry?.input?.includes('./src/index.ts')) {
241
- * plugins.push(plugin3())
288
+ * 'build:entry:start': async (options, stats) => {
289
+ * // adds custom plugins for a specific entry only
290
+ * if (options.input?.includes('./src/index.ts')) {
291
+ * options.plugins = [
292
+ * plugin1(), // adds a custom plugin before the default bundler plugins
293
+ * ...options.plugins, // list of default bundler plugins
294
+ * plugin2(), // adds a custom plugin after the default bundler plugins
295
+ * ]
296
+ * }
297
+ * // adds custom plugins for a specific types only
298
+ * if (options.types?.includes('./src/types.ts')) {
299
+ * options.plugins = [
300
+ * ...options.plugins, // list of default bundler plugins
301
+ * plugin3(), // adds a custom plugin designed to work only with TS declarations
302
+ * ]
242
303
  * }
243
- * // returns the final list of plugins
244
- * return plugins
245
304
  * }
246
305
  * }
247
306
  * })
@@ -249,7 +308,25 @@ interface HooksOptions {
249
308
  *
250
309
  * @default undefined
251
310
  */
252
- 'rollup:plugins'?: (plugins: Plugin[], entry?: Partial<EntryInput> & Partial<Omit<EntryTypes, 'plugins'>>) => Plugin[];
311
+ 'build:entry:start'?: (options: BuildEntryOptions, stats: BuildStats) => void | Promise<void>;
312
+ /**
313
+ * Called on each entry right after the build process is completed.
314
+ *
315
+ * @example
316
+ *
317
+ * ```ts
318
+ * export default defineConfig({
319
+ * hooks: {
320
+ * 'build:entry:end': async (options, stats) => {
321
+ * // ...
322
+ * }
323
+ * }
324
+ * })
325
+ * ```
326
+ *
327
+ * @default undefined
328
+ */
329
+ 'build:entry:end'?: (options: BuildEntryOptions, stats: BuildStats) => void | Promise<void>;
253
330
  /**
254
331
  * Called right after building is complete.
255
332
  *
@@ -258,7 +335,7 @@ interface HooksOptions {
258
335
  * ```ts
259
336
  * export default defineConfig({
260
337
  * hooks: {
261
- * 'build:end': async (options, buildStats) => {
338
+ * 'build:end': async (options, stats) => {
262
339
  * // ...
263
340
  * }
264
341
  * }
@@ -267,7 +344,7 @@ interface HooksOptions {
267
344
  *
268
345
  * @default undefined
269
346
  */
270
- 'build:end'?: (options?: Options, buildStats?: BuildStats) => void | Promise<void>;
347
+ 'build:end'?: (options: Options, stats: BuildStats) => void | Promise<void>;
271
348
  /**
272
349
  * Called right after bundling is complete.
273
350
  *
@@ -285,7 +362,7 @@ interface HooksOptions {
285
362
  *
286
363
  * @default undefined
287
364
  */
288
- 'bundle:end'?: (options?: Options) => void | Promise<void>;
365
+ 'bundle:end'?: (options: Options) => void | Promise<void>;
289
366
  }
290
367
 
291
368
  /**
@@ -309,4 +386,4 @@ interface HooksOptions {
309
386
  declare const externals: RegExp[];
310
387
  declare function defineConfig(options: Options): Options;
311
388
 
312
- export { type BuildLogs, type BuildStats, type EntryBase, type EntryInput, type EntryOptions, type EntryTypes, type HooksOptions, type Options, type PluginsOptions, defineConfig, externals };
389
+ export { type BuildLogs, type BuildStats, type EntryBase, type EntryInput, type EntryOptions, type EntryTypes, type HooksOptions, type Options, type PluginsInput, type PluginsTypes, defineConfig, externals };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypernym/bundler",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "author": "Hypernym Studio",
5
5
  "description": "ESM & TS module bundler.",
6
6
  "license": "MIT",
@@ -71,10 +71,10 @@
71
71
  "@hypernym/eslint-config": "^2.0.2",
72
72
  "@hypernym/prettier-config": "^2.0.2",
73
73
  "@hypernym/tsconfig": "^1.1.0",
74
- "@types/node": "^20.8.6",
74
+ "@types/node": "^20.8.7",
75
75
  "eslint": "^8.51.0",
76
76
  "prettier": "^3.0.3",
77
- "tsx": "^3.13.0",
77
+ "tsx": "^3.14.0",
78
78
  "typescript": "^5.2.2"
79
79
  },
80
80
  "engines": {