@hypernym/bundler 0.3.1 → 0.4.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.4.0`;
29
29
 
30
30
  const cl = console.log;
31
31
  const logger = {
@@ -204,106 +204,114 @@ 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
+ };
222
+ if (_entry.pluginsOptions?.json) {
223
+ const jsonOptions = isObject(_entry.pluginsOptions.json) ? _entry.pluginsOptions.json : void 0;
224
+ _entry.plugins.push(jsonPlugin(jsonOptions));
219
225
  }
220
- if (plugins?.replace) {
221
- _plugins.unshift(
226
+ if (_entry.pluginsOptions?.replace) {
227
+ _entry.plugins.unshift(
222
228
  replacePlugin({
223
229
  true: true,
224
- ...plugins.replace
230
+ ..._entry.pluginsOptions.replace
225
231
  })
226
232
  );
227
233
  }
228
- if (plugins?.resolve) {
229
- const resolveOptions = isObject(plugins.resolve) ? plugins.resolve : void 0;
230
- _plugins.unshift(resolvePlugin(resolveOptions));
234
+ if (_entry.pluginsOptions?.resolve) {
235
+ const resolveOptions = isObject(_entry.pluginsOptions.resolve) ? _entry.pluginsOptions.resolve : void 0;
236
+ _entry.plugins.unshift(resolvePlugin(resolveOptions));
231
237
  }
232
- if (hooks?.["rollup:plugins"]) {
233
- hooks["rollup:plugins"](_plugins, {
234
- ...entry,
235
- input,
236
- output,
237
- format
238
- });
238
+ if (hooks?.["build:entry:start"]) {
239
+ await hooks["build:entry:start"](_entry, buildStats);
239
240
  }
240
- const builder = await rollup({
241
- input: resolve(cwd, input),
242
- external: externals || options.externals,
243
- plugins: _plugins,
241
+ const _build = await rollup({
242
+ input: resolve(cwd, _entry.input),
243
+ external: _entry.externals,
244
+ plugins: _entry.plugins,
244
245
  onLog: (level, log) => {
245
246
  if (logFilter(log))
246
247
  buildLogs.push({ level, log });
247
248
  }
248
249
  });
249
- await builder.write({
250
- file: resolve(cwd, output),
251
- format,
252
- banner,
253
- footer
250
+ await _build.write({
251
+ file: resolve(cwd, _entry.output),
252
+ format: _entry.format,
253
+ banner: _entry.banner,
254
+ footer: _entry.footer
254
255
  });
255
- const stats = await stat(resolve(cwd, output));
256
+ const stats = await stat(resolve(cwd, _entry.output));
256
257
  buildStats.files.push({
257
- path: output,
258
+ path: _entry.output,
258
259
  size: stats.size,
259
260
  buildTime: Date.now() - entryStart,
260
- format,
261
+ format: _entry.format,
261
262
  logs: buildLogs
262
263
  });
263
264
  buildStats.size = buildStats.size + stats.size;
265
+ if (hooks?.["build:entry:end"]) {
266
+ await hooks["build:entry:end"](_entry, buildStats);
267
+ }
264
268
  }
265
269
  if ("types" in entry) {
266
- const { types, externals, plugins, banner, footer } = entry;
267
- const buildLogs = [];
268
- const _output = getOutputPath(outDir, types, true);
270
+ const _output = getOutputPath(outDir, entry.types, true);
269
271
  let _format = "esm";
270
272
  if (_output.endsWith(".d.cts"))
271
273
  _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
- });
274
+ const buildLogs = [];
275
+ const _entry = {
276
+ types: entry.types,
277
+ output: entry.output || _output,
278
+ externals: entry.externals || options.externals,
279
+ format: entry.format || _format,
280
+ plugins: [dts(entry.plugins?.dts)],
281
+ pluginsOptions: entry.plugins,
282
+ banner: entry.banner,
283
+ footer: entry.footer
284
+ };
285
+ if (hooks?.["build:entry:start"]) {
286
+ await hooks["build:entry:start"](_entry, buildStats);
282
287
  }
283
- const builder = await rollup({
284
- input: resolve(cwd, types),
285
- external: externals || options.externals,
286
- plugins: _plugins,
288
+ const _build = await rollup({
289
+ input: resolve(cwd, _entry.types),
290
+ external: _entry.externals,
291
+ plugins: _entry.plugins,
287
292
  onLog: (level, log) => {
288
293
  if (logFilter(log))
289
294
  buildLogs.push({ level, log });
290
295
  }
291
296
  });
292
- await builder.write({
293
- file: resolve(cwd, output),
294
- format,
295
- banner,
296
- footer
297
+ await _build.write({
298
+ file: resolve(cwd, _entry.output),
299
+ format: _entry.format,
300
+ banner: _entry.banner,
301
+ footer: _entry.footer
297
302
  });
298
- const stats = await stat(resolve(cwd, output));
303
+ const stats = await stat(resolve(cwd, _entry.output));
299
304
  buildStats.files.push({
300
- path: output,
305
+ path: _entry.output,
301
306
  size: stats.size,
302
307
  buildTime: Date.now() - entryStart,
303
- format,
308
+ format: _entry.format,
304
309
  logs: buildLogs
305
310
  });
306
311
  buildStats.size = buildStats.size + stats.size;
312
+ if (hooks?.["build:entry:end"]) {
313
+ await hooks["build:entry:end"](_entry, buildStats);
314
+ }
307
315
  }
308
316
  }
309
317
  buildStats.buildTime = Date.now() - start;
@@ -356,7 +364,12 @@ async function createBuilder(cwd, args, options) {
356
364
  format = "dts";
357
365
  if (file.logs) {
358
366
  for (const log of file.logs) {
359
- cl(magenta("!"), magenta(log.log.message));
367
+ cl(
368
+ magenta("!"),
369
+ dim("\u251C\u2500"),
370
+ magenta(`${log.level}:`),
371
+ magenta(log.log.message)
372
+ );
360
373
  }
361
374
  }
362
375
  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
  /**
@@ -66,7 +68,7 @@ interface EntryInput extends EntryBase {
66
68
  *
67
69
  * @default undefined
68
70
  */
69
- plugins?: PluginsOptions;
71
+ plugins?: PluginsInput;
70
72
  }
71
73
  interface EntryTypes extends EntryBase {
72
74
  /**
@@ -78,7 +80,7 @@ interface EntryTypes extends EntryBase {
78
80
  *
79
81
  * @default undefined
80
82
  */
81
- plugins?: Pick<PluginsOptions, 'dts'>;
83
+ plugins?: PluginsTypes;
82
84
  }
83
85
  type EntryOptions = EntryInput | EntryTypes;
84
86
 
@@ -181,10 +183,22 @@ interface BuildStats {
181
183
  logs: BuildLogs[];
182
184
  }[];
183
185
  }
186
+ interface BuildEntryOptions extends EntryBase, Partial<Omit<EntryInput, 'plugins'>>, Partial<Omit<EntryTypes, 'plugins'>> {
187
+ /**
188
+ * Specifies list of plugins.
189
+ */
190
+ plugins: Plugin[];
191
+ /**
192
+ * Specifies options for default plugins.
193
+ *
194
+ * @default undefined
195
+ */
196
+ pluginsOptions?: PluginsInput & PluginsTypes;
197
+ }
184
198
 
185
199
  interface HooksOptions {
186
200
  /**
187
- * Called just before bundling started.
201
+ * Called at the beginning of bundling.
188
202
  *
189
203
  * @example
190
204
  *
@@ -200,16 +214,16 @@ interface HooksOptions {
200
214
  *
201
215
  * @default undefined
202
216
  */
203
- 'bundle:start'?: (options?: Options) => void | Promise<void>;
217
+ 'bundle:start'?: (options: Options) => void | Promise<void>;
204
218
  /**
205
- * Called just before building started.
219
+ * Called at the beginning of building.
206
220
  *
207
221
  * @example
208
222
  *
209
223
  * ```ts
210
224
  * export default defineConfig({
211
225
  * hooks: {
212
- * 'build:start': async (options, buildStats) => {
226
+ * 'build:start': async (options, stats) => {
213
227
  * // ...
214
228
  * }
215
229
  * }
@@ -218,11 +232,11 @@ interface HooksOptions {
218
232
  *
219
233
  * @default undefined
220
234
  */
221
- 'build:start'?: (options?: Options, buildStats?: BuildStats) => void | Promise<void>;
235
+ 'build:start'?: (options: Options, stats: BuildStats) => void | Promise<void>;
222
236
  /**
223
- * Called just before the initialization of the Rollup plugin.
237
+ * Called on each entry just before the build process.
224
238
  *
225
- * Provides the ability to add and manipulate custom plugins.
239
+ * Provides the ability to customize entry options before they are passed to the next phase.
226
240
  *
227
241
  * @example
228
242
  *
@@ -231,17 +245,40 @@ interface HooksOptions {
231
245
  *
232
246
  * export default defineConfig({
233
247
  * 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())
248
+ * 'build:entry:start': async (options, stats) => {
249
+ * // adds custom plugins for a specific entry only
250
+ * if (options.input?.includes('./src/index.ts')) {
251
+ * options.plugins = [
252
+ * plugin1(), // adds a custom plugin before the default bundler plugins
253
+ * ...options.plugins, // list of default bundler plugins
254
+ * plugin2(), // adds a custom plugin after the default bundler plugins
255
+ * ]
242
256
  * }
243
- * // returns the final list of plugins
244
- * return plugins
257
+ * // adds custom plugins for a specific types only
258
+ * if (options.types?.includes('./src/types.ts')) {
259
+ * options.plugins = [
260
+ * ...options.plugins, // list of default bundler plugins
261
+ * plugin3(), // adds a custom plugin designed to work only with TS declarations
262
+ * ]
263
+ * }
264
+ * }
265
+ * }
266
+ * })
267
+ * ```
268
+ *
269
+ * @default undefined
270
+ */
271
+ 'build:entry:start'?: (options: BuildEntryOptions, stats: BuildStats) => void | Promise<void>;
272
+ /**
273
+ * Called on each entry right after the build process is completed.
274
+ *
275
+ * @example
276
+ *
277
+ * ```ts
278
+ * export default defineConfig({
279
+ * hooks: {
280
+ * 'build:entry:end': async (options, stats) => {
281
+ * // ...
245
282
  * }
246
283
  * }
247
284
  * })
@@ -249,7 +286,7 @@ interface HooksOptions {
249
286
  *
250
287
  * @default undefined
251
288
  */
252
- 'rollup:plugins'?: (plugins: Plugin[], entry?: Partial<EntryInput> & Partial<Omit<EntryTypes, 'plugins'>>) => Plugin[];
289
+ 'build:entry:end'?: (options: BuildEntryOptions, stats: BuildStats) => void | Promise<void>;
253
290
  /**
254
291
  * Called right after building is complete.
255
292
  *
@@ -258,7 +295,7 @@ interface HooksOptions {
258
295
  * ```ts
259
296
  * export default defineConfig({
260
297
  * hooks: {
261
- * 'build:end': async (options, buildStats) => {
298
+ * 'build:end': async (options, stats) => {
262
299
  * // ...
263
300
  * }
264
301
  * }
@@ -267,7 +304,7 @@ interface HooksOptions {
267
304
  *
268
305
  * @default undefined
269
306
  */
270
- 'build:end'?: (options?: Options, buildStats?: BuildStats) => void | Promise<void>;
307
+ 'build:end'?: (options: Options, stats: BuildStats) => void | Promise<void>;
271
308
  /**
272
309
  * Called right after bundling is complete.
273
310
  *
@@ -285,7 +322,7 @@ interface HooksOptions {
285
322
  *
286
323
  * @default undefined
287
324
  */
288
- 'bundle:end'?: (options?: Options) => void | Promise<void>;
325
+ 'bundle:end'?: (options: Options) => void | Promise<void>;
289
326
  }
290
327
 
291
328
  /**
@@ -309,4 +346,4 @@ interface HooksOptions {
309
346
  declare const externals: RegExp[];
310
347
  declare function defineConfig(options: Options): Options;
311
348
 
312
- export { type BuildLogs, type BuildStats, type EntryBase, type EntryInput, type EntryOptions, type EntryTypes, type HooksOptions, type Options, type PluginsOptions, defineConfig, externals };
349
+ 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.4.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": {