@normed/bundle 4.1.0 → 4.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,11 @@ Given a version number MAJOR.MINOR.PATCH, increment the:
6
6
  2. MINOR version when you add functionality in a backwards compatible manner, and
7
7
  3. PATCH version when you make backwards compatible bug fixes.
8
8
 
9
+ # 4.2.0
10
+
11
+ * MINOR: supports a range of plugins, but these are undocumented.
12
+ * MINOR: supports slightly better error returning, but this is not implemented across the board.
13
+
9
14
  # 4.1.0
10
15
 
11
16
  * MINOR: fixes building of entypoint pug files.
@@ -68580,6 +68580,34 @@ function groupBy(objs, extractSubgroupFuncs) {
68580
68580
  }
68581
68581
  return groups;
68582
68582
  }
68583
+ function keyBy(objs, extractKeyFuncs) {
68584
+ const result = /* @__PURE__ */ new Map();
68585
+ const knownKeys = [];
68586
+ for (const obj of objs) {
68587
+ const key = {};
68588
+ for (const [keyName, extractKeyFunc] of Object.entries(extractKeyFuncs)) {
68589
+ key[keyName] = extractKeyFunc(obj);
68590
+ }
68591
+ let knownKey = knownKeys.find(
68592
+ (knownKey2) => Object.keys(knownKey2).length === Object.keys(key).length && Object.keys(knownKey2).every(
68593
+ (k) => knownKey2[k] === key[k]
68594
+ )
68595
+ );
68596
+ if (!knownKey) {
68597
+ knownKey = key;
68598
+ knownKeys.push(key);
68599
+ result.set(knownKey, [obj]);
68600
+ } else {
68601
+ const group = result.get(knownKey);
68602
+ if (group) {
68603
+ group.push(obj);
68604
+ } else {
68605
+ throw new Error(`Keying failed in keyBy`);
68606
+ }
68607
+ }
68608
+ }
68609
+ return result;
68610
+ }
68583
68611
  function divide(list, divider) {
68584
68612
  const a = [];
68585
68613
  const b = [];
@@ -68876,6 +68904,68 @@ function mergeDeep(into, from) {
68876
68904
  }
68877
68905
 
68878
68906
  // pnp:/builds/normed/bundle/packages/bundle/src/errors.ts
68907
+ function combineErrorLogs(...logs) {
68908
+ const combined = {
68909
+ errors: [],
68910
+ warnings: []
68911
+ };
68912
+ for (const log of logs) {
68913
+ if (log.errors) {
68914
+ combined.errors.push(...log.errors);
68915
+ }
68916
+ if (log.warnings) {
68917
+ combined.warnings.push(...log.warnings);
68918
+ }
68919
+ }
68920
+ return combined;
68921
+ }
68922
+ var WrappedErrors = class _WrappedErrors extends AggregateError {
68923
+ constructor(errors, message, meta) {
68924
+ var __super = (...args) => {
68925
+ super(...args);
68926
+ this.meta = meta;
68927
+ return this;
68928
+ };
68929
+ if (Array.isArray(errors)) {
68930
+ __super(errors, message);
68931
+ } else {
68932
+ __super(errors.errors ?? [], message);
68933
+ this.warnings = errors.warnings ?? [];
68934
+ }
68935
+ this.name = "WrappedError";
68936
+ }
68937
+ warnings;
68938
+ static fromErrorLog(errorLog, message, meta) {
68939
+ return new _WrappedErrors(errorLog, message, meta);
68940
+ }
68941
+ static fromError(error, message, meta) {
68942
+ return new _WrappedErrors([error], message, meta);
68943
+ }
68944
+ static fromErrors(errors, message, meta) {
68945
+ return new _WrappedErrors(errors, message, meta);
68946
+ }
68947
+ static fromValue(value, message, meta) {
68948
+ const error = asError(value);
68949
+ return new _WrappedErrors([error], message, meta);
68950
+ }
68951
+ static fromValues(values, message, meta) {
68952
+ const errors = values.map(asError);
68953
+ return new _WrappedErrors(errors, message, meta);
68954
+ }
68955
+ };
68956
+ function asError(value, meta) {
68957
+ let error;
68958
+ if (!(value instanceof Error)) {
68959
+ error = new Error(String(value));
68960
+ } else {
68961
+ error = value;
68962
+ }
68963
+ if (meta) {
68964
+ let existingMeta = error.meta ?? {};
68965
+ error.meta = { ...existingMeta, ...meta };
68966
+ }
68967
+ return error;
68968
+ }
68879
68969
  var NoMatchingBuilder = class extends Error {
68880
68970
  file;
68881
68971
  ext;
@@ -68900,6 +68990,14 @@ var copyBuilder = {
68900
68990
  copy(infile.absolute, outfile.absolute);
68901
68991
  })
68902
68992
  );
68993
+ return {
68994
+ warnings: [],
68995
+ errors: [],
68996
+ watchFiles: entrypoints.map((e) => e.infile),
68997
+ watchDirs: [],
68998
+ producedFiles: entrypoints.map((e) => e.outfile),
68999
+ completed: true
69000
+ };
68903
69001
  }
68904
69002
  };
68905
69003
 
@@ -69028,6 +69126,30 @@ var load_less_default = plugin;
69028
69126
  var import_pug = __toESM(require_lib14());
69029
69127
  var import_fs4 = __toESM(require("fs"));
69030
69128
  var import_path5 = __toESM(require("path"));
69129
+ function pugTransformer(test, onFound) {
69130
+ return {
69131
+ preCodeGen(ast) {
69132
+ const pending = [...ast?.nodes || []];
69133
+ const found = [];
69134
+ while (pending.length > 0) {
69135
+ const node = pending.shift();
69136
+ if (!node) continue;
69137
+ if (node.block?.nodes) {
69138
+ pending.push(...node.block.nodes);
69139
+ }
69140
+ if (!test(node)) continue;
69141
+ found.push(node);
69142
+ }
69143
+ if (found.length) {
69144
+ onFound(found);
69145
+ }
69146
+ return ast;
69147
+ }
69148
+ };
69149
+ }
69150
+ function isScriptNodeWithSrcAttr(node) {
69151
+ return node.name === "script" && node.attrs.some((attr) => attr.name === "src");
69152
+ }
69031
69153
  async function loadAsText2(_filepath) {
69032
69154
  return {
69033
69155
  loader: "text"
@@ -69052,19 +69174,29 @@ async function loadAsHtml(filepath, options2) {
69052
69174
  const contents = import_pug.default.render(fileData, {
69053
69175
  name: "render",
69054
69176
  filename: filepath,
69055
- basedir: options2.outbase
69177
+ basedir: options2.outbase,
69178
+ plugins: [
69179
+ pugTransformer(isScriptNodeWithSrcAttr, (nodes) => {
69180
+ nodes;
69181
+ })
69182
+ ]
69056
69183
  });
69057
69184
  return {
69058
69185
  contents,
69059
69186
  loader: "js"
69060
69187
  };
69061
69188
  }
69062
- async function loadAsCopy(filepath, options2) {
69189
+ async function loadAsEntrypoint(filepath, options2) {
69063
69190
  const fileData = await import_fs4.default.promises.readFile(filepath, "utf8");
69064
69191
  const contents = import_pug.default.render(fileData, {
69065
69192
  filename: filepath,
69066
69193
  basedir: options2.outbase,
69067
- name: "render"
69194
+ name: "render",
69195
+ plugins: [
69196
+ pugTransformer(isScriptNodeWithSrcAttr, (nodes) => {
69197
+ nodes;
69198
+ })
69199
+ ]
69068
69200
  });
69069
69201
  return {
69070
69202
  contents,
@@ -69116,7 +69248,7 @@ var plugin2 = {
69116
69248
  }) => {
69117
69249
  const isEntryPoint = pluginData?.[name2]?.entrypoint;
69118
69250
  if (isEntryPoint) {
69119
- return loadAsCopy(filepath, build2.initialOptions);
69251
+ return loadAsEntrypoint(filepath, build2.initialOptions);
69120
69252
  }
69121
69253
  const type = withArg?.["type"] ?? "js";
69122
69254
  switch (type) {
@@ -69434,6 +69566,18 @@ var esbuilder = {
69434
69566
  }
69435
69567
  await Promise.all(promises);
69436
69568
  }
69569
+ return {
69570
+ // TODO: rewrite to return warnings
69571
+ warnings: [],
69572
+ // TODO: rewrite to return errors
69573
+ errors: [],
69574
+ watchFiles: entrypoints.map((e) => e.infile),
69575
+ watchDirs: [],
69576
+ // TODO: these are not the correct files
69577
+ producedFiles: entrypoints.map((e) => e.outfile),
69578
+ // TODO: rewrite to only return true if all builds were successful
69579
+ completed: true
69580
+ };
69437
69581
  }
69438
69582
  };
69439
69583
  async function compileToTypeDeclarations(fileNames, options2) {
@@ -70283,7 +70427,7 @@ async function getEntrypoints(buildConfig, manuallySpecified) {
70283
70427
  }
70284
70428
 
70285
70429
  // pnp:/builds/normed/bundle/packages/bundle/src/bundle.ts
70286
- async function bundle(options2 = {}) {
70430
+ async function getBuildConfig(options2) {
70287
70431
  const buildConfig = {
70288
70432
  dir: {
70289
70433
  in: getInDir(options2),
@@ -70397,125 +70541,116 @@ async function bundle(options2 = {}) {
70397
70541
  "Build Config - post reading package.json",
70398
70542
  JSON.stringify(buildConfig.baseConfig, null, 2)
70399
70543
  );
70400
- const [raw_entrypoints, missingBuilders] = divide(
70401
- await getEntrypoints(buildConfig, options2.entrypoints),
70402
- (v) => {
70403
- return !(v instanceof NoMatchingBuilder);
70404
- }
70405
- );
70406
- const [allContextEntrypoints, entrypoints] = divide(
70407
- raw_entrypoints,
70408
- (e) => e.origin === "discovered" && e.infile.modifiers.includes("context")
70544
+ return buildConfig;
70545
+ }
70546
+ async function discoverEntrypoints(buildConfig, entrypoints, plugins = {}) {
70547
+ let initialEntrypoints = await getEntrypoints(buildConfig, entrypoints);
70548
+ initialEntrypoints = await plugins.initialEntrypoints?.(initialEntrypoints) ?? initialEntrypoints;
70549
+ const [missing, found] = divide(
70550
+ initialEntrypoints,
70551
+ (e) => e instanceof NoMatchingBuilder
70409
70552
  );
70410
- let skippedContextFile = false;
70411
- await Promise.all(
70412
- allContextEntrypoints.map(async (contextEntrypoint) => {
70413
- if (4 > 1) {
70414
- if (!skippedContextFile) {
70415
- log_default.warn(`Context files are not yet supported`);
70416
- skippedContextFile = true;
70417
- }
70418
- log_default.warn(` - skipping ${contextEntrypoint.infile.relative}`);
70419
- return;
70420
- }
70421
- skippedContextFile = false;
70422
- const matchingEntrypoints = entrypoints.filter(
70423
- (e) => e.infile.bare === contextEntrypoint.infile.bare
70424
- );
70425
- if (!matchingEntrypoints.length) {
70426
- log_default.warn(
70427
- `Context file ${contextEntrypoint.infile.bare} is not used by any entrypoint`
70428
- );
70429
- return;
70430
- }
70431
- log_default.verbose(
70432
- `Context file ${contextEntrypoint.infile.bare} is used by ${matchingEntrypoints.length} entrypoints`
70433
- );
70434
- async function compile(_v) {
70435
- throw new Error("Function not implemented.");
70436
- }
70437
- const compiled = await compile(contextEntrypoint);
70438
- const { context } = await compiled.run();
70439
- if (!context) {
70440
- log_default.warn(
70441
- `Context file ${contextEntrypoint.infile.bare} did not export a context function`
70442
- );
70443
- return;
70553
+ if (missing.length) {
70554
+ const missingBuilders = missing.filter((v, i, a) => a.indexOf(v) === i);
70555
+ return WrappedErrors.fromErrors(
70556
+ missing,
70557
+ "Missing builders for entrypoints",
70558
+ {
70559
+ missingBuilders: missingBuilders.map((b) => b.name),
70560
+ missingBuilders_count: missingBuilders.length
70444
70561
  }
70445
- const contextResult = await context();
70446
- if (!contextResult) {
70447
- log_default.warn(
70448
- `Context file ${contextEntrypoint.infile.bare} did not return a context object`
70449
- );
70450
- return;
70451
- }
70452
- const builderNames = matchingEntrypoints.map((e) => e.builder.name).filter((v, i, a) => a.indexOf(v) === i);
70453
- const builderNamesWithContexts = builderNames.filter(
70454
- (name3) => contextResult[name3]
70455
- );
70456
- if (!builderNamesWithContexts.length) {
70457
- log_default.warn(
70458
- `Context file ${contextEntrypoint.infile.bare} did not return any context for any of the builders: ${builderNames.join(", ")}`
70459
- );
70460
- return;
70461
- }
70462
- for (const builderName of builderNamesWithContexts) {
70463
- await Promise.all(
70464
- matchingEntrypoints.filter((e) => e.builder.name === builderName).map(async (entrypoint) => {
70465
- const func = contextResult[builderName];
70466
- if (!func) {
70467
- log_default.warn(
70468
- `Context file ${contextEntrypoint.infile.bare} did not return a context for builder ${builderName}`
70469
- );
70470
- return;
70471
- }
70472
- entrypoint.context = await func();
70473
- })
70474
- );
70475
- }
70476
- })
70562
+ );
70563
+ }
70564
+ return plugins.discoveredEntrypoints?.(found) ?? found;
70565
+ }
70566
+ async function bundle(options2 = {}, plugins = {}) {
70567
+ const start = Date.now();
70568
+ const buildConfig = await getBuildConfig(options2);
70569
+ const entrypoints = await discoverEntrypoints(
70570
+ buildConfig,
70571
+ options2.entrypoints,
70572
+ plugins
70477
70573
  );
70478
- log_default.warn(missingBuilders.map((missing) => missing.message).join("\n"));
70479
- log_default.info(entrypoints.length + " entrypoints");
70480
- const grouped = entrypoints.reduce((acc, entryPoint) => {
70481
- const index = buildConfig.builders.indexOf(entryPoint.builder);
70482
- if (!acc[index]) {
70483
- acc[index] = [];
70484
- }
70485
- acc[index]?.push(entryPoint);
70486
- return acc;
70487
- }, []);
70488
- for (const index in grouped) {
70489
- const group = grouped[index];
70490
- const builder = buildConfig.builders[index];
70491
- if (!builder) {
70492
- continue;
70493
- }
70494
- log_default.info(
70495
- builder.color(
70496
- `${builder.name}:
70497
- ${group?.map((i) => `${i.infile.relative} => ${i.outfile.relative}`).join("\n ")}`
70498
- )
70574
+ if (entrypoints instanceof WrappedErrors) {
70575
+ return WrappedErrors.fromError(
70576
+ entrypoints,
70577
+ "Failed to discover entrypoints"
70499
70578
  );
70500
70579
  }
70501
- const results = await Promise.all(
70502
- grouped.map(
70503
- (entrypoints2, index) => (async () => {
70504
- const start = Date.now();
70505
- await buildConfig.builders[index]?.build(entrypoints2);
70506
- const duration = Math.floor((Date.now() - start) / 10) / 100;
70507
- const builder = buildConfig.builders[index];
70508
- if (!builder) {
70509
- log_default.info(`Unknown builder: ${duration} seconds`);
70510
- } else {
70511
- log_default.info(builder.color(`${builder.name}: ${duration} seconds`));
70580
+ let grouped = keyBy(entrypoints, {
70581
+ builder: (entrypoint) => entrypoint.builder
70582
+ });
70583
+ grouped = await plugins.preBuild?.(grouped) ?? grouped;
70584
+ const results = await Promise.allSettled(
70585
+ Array.from(grouped.entries()).map(
70586
+ async ([
70587
+ { builder },
70588
+ entrypoints2
70589
+ ]) => {
70590
+ const start2 = Date.now();
70591
+ const annotations = {
70592
+ entrypoints: entrypoints2,
70593
+ builder,
70594
+ duration_ms: -1
70595
+ };
70596
+ let result;
70597
+ try {
70598
+ result = await builder.build(entrypoints2);
70599
+ } catch (e) {
70600
+ result = {
70601
+ warnings: [],
70602
+ errors: [WrappedErrors.fromValue(e, "Builder failed to build")],
70603
+ watchFiles: entrypoints2.map((e2) => e2.infile),
70604
+ watchDirs: [],
70605
+ producedFiles: [],
70606
+ completed: false
70607
+ };
70608
+ } finally {
70609
+ annotations.duration_ms = Date.now() - start2;
70512
70610
  }
70513
- })().catch((e) => e)
70611
+ result = await plugins.postBuild?.(result, annotations) ?? result;
70612
+ return {
70613
+ ...result,
70614
+ ...annotations
70615
+ };
70616
+ }
70514
70617
  )
70515
70618
  );
70516
- const errors = results.filter(Boolean).concat(missingBuilders);
70517
- if (errors.length) return Promise.reject(errors);
70518
- return;
70619
+ const [rejected, fulfilled] = divide(
70620
+ results,
70621
+ (r) => r.status === "rejected"
70622
+ );
70623
+ const [uncompleted, completed] = divide(fulfilled, (r) => r.value.completed);
70624
+ const errorLogs = combineErrorLogs(
70625
+ ...uncompleted.map((r) => r.value),
70626
+ ...completed.map((r) => r.value)
70627
+ );
70628
+ const errors = [
70629
+ rejected.length ? WrappedErrors.fromValues(
70630
+ rejected.map((r) => r.reason),
70631
+ "Some builders encountered errors when running",
70632
+ {
70633
+ rejected_count: rejected.length,
70634
+ entrypoints: entrypoints.map((e) => e.infile.relative),
70635
+ builders: Array.from(grouped.keys()).map(
70636
+ ({ builder }) => builder.name
70637
+ )
70638
+ }
70639
+ ) : void 0,
70640
+ ...errorLogs.errors ?? []
70641
+ ];
70642
+ const warnings = errorLogs.warnings ?? [];
70643
+ return {
70644
+ completed: rejected.length === 0 && uncompleted.length === 0,
70645
+ errors: errors.filter(isNot.undefined),
70646
+ warnings,
70647
+ watchFiles: fulfilled.flatMap((r) => r.value.watchFiles),
70648
+ watchDirs: fulfilled.flatMap((r) => r.value.watchDirs),
70649
+ producedFiles: fulfilled.flatMap((r) => r.value.producedFiles),
70650
+ entrypoints,
70651
+ duration_ms: Date.now() - start,
70652
+ children: fulfilled.map((r) => r.value)
70653
+ };
70519
70654
  }
70520
70655
 
70521
70656
  // pnp:/builds/normed/bundle/packages/bundle/src/index.ts
@@ -70649,7 +70784,47 @@ if (optClean) {
70649
70784
  }
70650
70785
  if (optBuild) {
70651
70786
  log_default.info(`Building`);
70652
- tasks.push(bundle2(options));
70787
+ tasks.push(
70788
+ bundle2(options, {
70789
+ preBuild(builderMap) {
70790
+ const builders2 = [...builderMap.entries()].sort(
70791
+ ([
70792
+ {
70793
+ builder: { name: a }
70794
+ }
70795
+ ], [
70796
+ {
70797
+ builder: { name: b }
70798
+ }
70799
+ ]) => {
70800
+ return a.localeCompare(b);
70801
+ }
70802
+ );
70803
+ for (const [{ builder }, entrypoints] of builders2) {
70804
+ log_default.info(
70805
+ builder.color(
70806
+ `${builder.name}:
70807
+ ${entrypoints.map((i) => i.infile.relative).join("\n ")}`
70808
+ )
70809
+ );
70810
+ }
70811
+ return builderMap;
70812
+ },
70813
+ postBuild(success, { builder, duration_ms }) {
70814
+ const duration = Math.floor(duration_ms / 10) / 100;
70815
+ if (success) {
70816
+ log_default.info(builder.color(`${builder.name}: ${duration} seconds`));
70817
+ } else {
70818
+ log_default.error(
70819
+ builder.color(`${builder.name}: Failed after ${duration} seconds`)
70820
+ );
70821
+ exitCode = 1;
70822
+ }
70823
+ return success;
70824
+ }
70825
+ }).then(() => {
70826
+ })
70827
+ );
70653
70828
  }
70654
70829
  function errorPrinter(error, index) {
70655
70830
  if (Array.isArray(error)) {