@hypernym/bundler 0.1.1 → 0.2.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.
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import process, { stdout, cwd } from 'node:process';
2
+ import process, { cwd } from 'node:process';
3
3
  import { createArgs } from '@hypernym/args';
4
- import { readFile, mkdir, writeFile, stat } from 'node:fs/promises';
4
+ import { readFile, stat } from 'node:fs/promises';
5
5
  import { resolve, parse } from 'node:path';
6
- import { exists } from '@hypernym/utils/node';
7
- import { dim, magenta, red, cyan, green } from '@hypernym/colors';
6
+ import { exists, writeFile } from '@hypernym/utils/fs';
7
+ import { cyan, dim, red, magenta, green } from '@hypernym/colors';
8
8
  import { build as build$1, transform } from 'esbuild';
9
9
  import { createSpinner } from '@hypernym/spinner';
10
10
  import { isObject } from '@hypernym/utils';
@@ -25,31 +25,28 @@ const externals = [
25
25
  ];
26
26
 
27
27
  const name = "bundler";
28
- const version = `0.1.1`;
28
+ const version = `0.2.0`;
29
29
 
30
30
  const cl = console.log;
31
- const log = (...args) => {
32
- let length = args.length + 2;
33
- const cols = stdout.columns || 80;
34
- const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
35
- for (const arg of args) {
36
- length = length + arg.replace(/\u001b\[.*?m/g, "").length;
37
- }
38
- const repeatLength = cols <= length + time.length ? 0 : cols - (length + time.length);
39
- return cl(...args, " ".repeat(repeatLength), dim(time));
40
- };
41
31
  const logger = {
32
+ info: (...args) => {
33
+ const time = `[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`;
34
+ return cl(cyan(name), dim(time), ...args);
35
+ },
36
+ error: (...args) => {
37
+ const time = `[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`;
38
+ return cl(red(name), dim(time), ...args);
39
+ },
42
40
  exit: (message) => {
43
- cl();
44
- log(dim(name), dim(version));
45
- log(magenta(name), message);
46
- cl();
41
+ const time = `[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`;
42
+ cl(magenta(name), dim(time), version);
43
+ cl(magenta(name), dim(time), message);
47
44
  return process.exit(1);
48
45
  }
49
46
  };
50
47
 
51
48
  function error(err) {
52
- log(red(name), "Something went wrong...");
49
+ logger.error("Something went wrong...");
53
50
  console.error(err);
54
51
  return process.exit(1);
55
52
  }
@@ -106,9 +103,7 @@ async function loadConfig(cwd, filePath, defaults) {
106
103
  packages: "external"
107
104
  });
108
105
  const code = result.outputFiles[0].text;
109
- const tempDir = resolve(cwd, "node_modules", ".hypernym", "bundler");
110
- const tempConfig = resolve(tempDir, "config.mjs");
111
- await mkdir(tempDir, { recursive: true });
106
+ const tempConfig = resolve(cwd, "node_modules/.hypernym/bundler/config.mjs");
112
107
  await writeFile(tempConfig, code, "utf-8");
113
108
  const content = await import(tempConfig);
114
109
  const config = {
@@ -201,8 +196,8 @@ async function build(cwd, options) {
201
196
  buildTime: 0,
202
197
  files: []
203
198
  };
204
- if (hooks?.["build:before"])
205
- await hooks["build:before"](options);
199
+ if (hooks?.["build:start"])
200
+ await hooks["build:start"](options, buildStats);
206
201
  if (options.entries) {
207
202
  start = Date.now();
208
203
  for (const entry of options.entries) {
@@ -260,7 +255,7 @@ async function build(cwd, options) {
260
255
  buildStats.size = buildStats.size + stats.size;
261
256
  }
262
257
  if ("types" in entry) {
263
- const { types, plugins } = entry;
258
+ const { types, externals, plugins, banner, footer } = entry;
264
259
  const buildLogs = [];
265
260
  const _output = getOutputPath(outDir, types, true);
266
261
  let _format = "esm";
@@ -270,6 +265,7 @@ async function build(cwd, options) {
270
265
  const format = entry.format || _format;
271
266
  const builder = await rollup({
272
267
  input: resolve(cwd, types),
268
+ external: externals || options.externals,
273
269
  plugins: [dts(plugins?.dts)],
274
270
  onLog: (level, log) => {
275
271
  if (logFilter(log))
@@ -278,7 +274,9 @@ async function build(cwd, options) {
278
274
  });
279
275
  await builder.write({
280
276
  file: resolve(cwd, output),
281
- format
277
+ format,
278
+ banner,
279
+ footer
282
280
  });
283
281
  const stats = await stat(resolve(cwd, output));
284
282
  buildStats.files.push({
@@ -293,33 +291,24 @@ async function build(cwd, options) {
293
291
  }
294
292
  buildStats.buildTime = Date.now() - start;
295
293
  }
296
- if (hooks?.["build:done"])
297
- await hooks["build:done"](options);
294
+ if (hooks?.["build:end"])
295
+ await hooks["build:end"](options, buildStats);
298
296
  return buildStats;
299
297
  }
300
298
 
301
299
  async function createBuilder(cwd, args, options) {
300
+ const { hooks } = options;
301
+ if (hooks?.["bundle:start"])
302
+ await hooks["bundle:start"](options);
303
+ logger.info(version);
304
+ logger.info(`Bundling started...`);
302
305
  const spinner = createSpinner();
303
- cl();
304
- log(dim(name), dim(version));
305
- log(cyan(name), `Bundling started...`);
306
306
  spinner.start({
307
307
  message: `Transforming modules ${green("for production...")}`
308
308
  });
309
- return await build(cwd, options).then((stats) => {
309
+ await build(cwd, options).then((stats) => {
310
310
  spinner.stop({
311
- message: `Modules transformation is ${green("done!")}`,
312
- template: (mark, message) => {
313
- const temp = `${mark}${message}`;
314
- const cols = stdout.columns || 80;
315
- const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
316
- const length = (
317
- // eslint-disable-next-line no-control-regex
318
- temp.replace(/\u001b\[.*?m/g, "").length + time.length + 1
319
- );
320
- const repeatLength = cols <= length ? 0 : cols - length;
321
- return temp + " ".repeat(repeatLength) + dim(time);
322
- }
311
+ message: `Modules transformation is ${green("done!")}`
323
312
  });
324
313
  const check = green("\u2714");
325
314
  const info = cyan("i");
@@ -333,9 +322,9 @@ async function createBuilder(cwd, args, options) {
333
322
  const longestSize = Math.max(
334
323
  ...stats.files.map((v) => formatBytes(v.size).length)
335
324
  );
336
- log(check, `Bundling completed in ${buildTime}`);
337
- log(check, `${modules} modules transformed. Total size is ${buildSize}`);
338
- log(info, "Individual stats per module");
325
+ cl(check, `Bundling completed in ${buildTime}`);
326
+ cl(check, `${modules} modules transformed. Total size is ${buildSize}`);
327
+ cl(info, "Individual stats per module");
339
328
  for (const file of stats.files) {
340
329
  let format = file.format;
341
330
  const base = parse(file.path).base;
@@ -349,11 +338,11 @@ async function createBuilder(cwd, args, options) {
349
338
  if (base.includes(".d."))
350
339
  format = "dts";
351
340
  if (file.logs) {
352
- for (const log2 of file.logs) {
353
- cl(magenta("!"), magenta(log2.log.message));
341
+ for (const log of file.logs) {
342
+ cl(magenta("!"), magenta(log.log.message));
354
343
  }
355
344
  }
356
- log(
345
+ cl(
357
346
  info,
358
347
  dim("\u251C\u2500"),
359
348
  dim(path) + cyan(base),
@@ -365,7 +354,7 @@ async function createBuilder(cwd, args, options) {
365
354
  dim(formatBytes(file.size).padStart(longestSize))
366
355
  );
367
356
  }
368
- log(
357
+ logger.info(
369
358
  check,
370
359
  `Bundle is now fully generated and ready ${green("for production!")}`
371
360
  );
@@ -374,10 +363,12 @@ async function createBuilder(cwd, args, options) {
374
363
  mark: red("\u2716"),
375
364
  message: `Modules transformation is ${red("interupted!")}`
376
365
  });
377
- log(red(name), "Something went wrong...");
366
+ logger.error("Something went wrong...");
378
367
  console.error(err);
379
368
  return process.exit(1);
380
369
  });
370
+ if (hooks?.["bundle:end"])
371
+ await hooks["bundle:end"](options);
381
372
  }
382
373
 
383
374
  async function main() {
@@ -1,4 +1,4 @@
1
- import { OutputOptions } from 'rollup';
1
+ import { OutputOptions, LogLevel, RollupLog } from 'rollup';
2
2
  import { RollupReplaceOptions } from '@rollup/plugin-replace';
3
3
  import { RollupJsonOptions } from '@rollup/plugin-json';
4
4
  import { RollupNodeResolveOptions } from '@rollup/plugin-node-resolve';
@@ -13,33 +13,109 @@ interface BuildPlugins {
13
13
  replace?: RollupReplaceOptions;
14
14
  }
15
15
 
16
- interface Entries {
16
+ interface Entry {
17
+ /**
18
+ * Specifies the path of the transformed module.
19
+ *
20
+ * If not specified, matches the `input` path with the appropriate extension.
21
+ *
22
+ * @default undefined
23
+ */
17
24
  output?: string;
25
+ /**
26
+ * Specifies the format of the generated module.
27
+ *
28
+ * @default 'esm'
29
+ */
18
30
  format?: OutputOptions['format'];
31
+ /**
32
+ * Specifies the module IDs, or regular expressions to match module IDs,
33
+ * that should remain external to the bundle.
34
+ *
35
+ * If not specified, infers the IDs from the global `options.externals` option.
36
+ *
37
+ * @default undefined
38
+ */
19
39
  externals?: (string | RegExp)[];
40
+ /**
41
+ * Specifies the string to be inserted at the beginning of the module.
42
+ *
43
+ * @default undefined
44
+ */
45
+ banner?: OutputOptions['banner'];
46
+ /**
47
+ * Specifies the string to be inserted at the end of the module.
48
+ *
49
+ * @default undefined
50
+ */
51
+ footer?: OutputOptions['footer'];
52
+ /**
53
+ * Specifies custom filters that will display only certain log messages.
54
+ *
55
+ * @default undefined
56
+ */
20
57
  logFilter?: string[];
21
58
  }
22
- interface EntriesInput extends Entries {
59
+ interface EntryInput extends Entry {
60
+ /**
61
+ * Specifies the path of the module's build source.
62
+ */
23
63
  input: string;
24
- banner?: OutputOptions['banner'];
25
- footer?: OutputOptions['footer'];
64
+ /**
65
+ * Specifies plugin options.
66
+ *
67
+ * @default undefined
68
+ */
26
69
  plugins?: BuildPlugins;
27
70
  }
28
- interface EntriesTypes extends Entries {
71
+ interface EntryTypes extends Entry {
72
+ /**
73
+ * Specifies the path of the module's build source that contains only TS definitions.
74
+ */
29
75
  types: string;
76
+ /**
77
+ * Specifies plugin options.
78
+ *
79
+ * @default undefined
80
+ */
30
81
  plugins?: Pick<BuildPlugins, 'dts'>;
31
82
  }
32
- type EntriesOptions = EntriesInput | EntriesTypes;
83
+ type EntriesOptions = EntryInput | EntryTypes;
84
+
85
+ interface BuildLogs {
86
+ level: LogLevel;
87
+ log: RollupLog;
88
+ }
89
+ interface BuildStats {
90
+ cwd: string;
91
+ size: number;
92
+ buildTime: number;
93
+ files: {
94
+ path: string;
95
+ size: number;
96
+ buildTime: number;
97
+ format: string;
98
+ logs: BuildLogs[];
99
+ }[];
100
+ }
33
101
 
34
- interface BuildHooks {
102
+ interface HooksOptions {
35
103
  /**
36
104
  * Called just before bundling started.
37
105
  */
38
- 'build:before'?: (options?: Options) => void | Promise<void>;
106
+ 'bundle:start'?: (options?: Options) => void | Promise<void>;
107
+ /**
108
+ * Called just before building started.
109
+ */
110
+ 'build:start'?: (options?: Options, buildStats?: BuildStats) => void | Promise<void>;
111
+ /**
112
+ * Called right after building is complete.
113
+ */
114
+ 'build:end'?: (options?: Options, buildStats?: BuildStats) => void | Promise<void>;
39
115
  /**
40
116
  * Called right after bundling is complete.
41
117
  */
42
- 'build:done'?: (options?: Options) => void | Promise<void>;
118
+ 'bundle:end'?: (options?: Options) => void | Promise<void>;
43
119
  }
44
120
 
45
121
  interface Options {
@@ -59,15 +135,19 @@ interface Options {
59
135
  * Specifies the module IDs, or regular expressions to match module IDs,
60
136
  * that should remain external to the bundle.
61
137
  *
138
+ * IDs and regexps from this option are applied globally to all entries.
139
+ *
140
+ * Also, it is possible to define externals individually per entry (`entry.externals`).
141
+ *
62
142
  * @default [/^node:/, /^@types/, /^@rollup/, /^@hypernym/, /^rollup/, ...pkg.dependencies]
63
143
  */
64
144
  externals?: (string | RegExp)[];
65
145
  /**
66
- * Provides a powerful hooking system to further expand build mode.
146
+ * Provides a powerful hooking system to further expand bundling mode.
67
147
  *
68
148
  * @default undefined
69
149
  */
70
- hooks?: BuildHooks;
150
+ hooks?: HooksOptions;
71
151
  }
72
152
 
73
153
  declare const externals: RegExp[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypernym/bundler",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "author": "Hypernym Studio",
5
5
  "description": "ESM & TS module bundler.",
6
6
  "license": "MIT",
@@ -8,6 +8,7 @@
8
8
  "homepage": "https://github.com/hypernym-studio/bundler",
9
9
  "funding": "https://github.com/sponsors/ivodolenc",
10
10
  "type": "module",
11
+ "types": "./dist/types/index.d.ts",
11
12
  "exports": {
12
13
  ".": {
13
14
  "types": "./dist/types/index.d.ts",
@@ -55,22 +56,22 @@
55
56
  }
56
57
  },
57
58
  "dependencies": {
58
- "@hypernym/args": "^0.2.0",
59
- "@hypernym/colors": "^1.0.0",
60
- "@hypernym/spinner": "^0.1.0",
61
- "@hypernym/utils": "^2.0.2",
59
+ "@hypernym/args": "^0.2.1",
60
+ "@hypernym/colors": "^1.0.1",
61
+ "@hypernym/spinner": "^0.2.0",
62
+ "@hypernym/utils": "^2.1.0",
62
63
  "@rollup/plugin-json": "^6.0.1",
63
64
  "@rollup/plugin-node-resolve": "^15.2.3",
64
65
  "@rollup/plugin-replace": "^5.0.3",
65
66
  "esbuild": "^0.19.4",
66
- "rollup": "^4.0.2",
67
+ "rollup": "^4.1.1",
67
68
  "rollup-plugin-dts": "^6.1.0"
68
69
  },
69
70
  "devDependencies": {
70
- "@hypernym/eslint-config": "^2.0.1",
71
- "@hypernym/prettier-config": "^2.0.1",
71
+ "@hypernym/eslint-config": "^2.0.2",
72
+ "@hypernym/prettier-config": "^2.0.2",
72
73
  "@hypernym/tsconfig": "^1.1.0",
73
- "@types/node": "^20.8.4",
74
+ "@types/node": "^20.8.6",
74
75
  "eslint": "^8.51.0",
75
76
  "prettier": "^3.0.3",
76
77
  "tsx": "^3.13.0",