@opendatacapture/instrument-bundler 2.1.4 → 2.2.1

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/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@opendatacapture/instrument-bundler",
3
3
  "type": "module",
4
- "version": "2.1.4",
4
+ "version": "2.2.1",
5
+ "sideEffects": [
6
+ "**/cli.ts"
7
+ ],
5
8
  "license": "Apache-2.0",
6
9
  "publishConfig": {
7
10
  "access": "public"
@@ -31,16 +34,15 @@
31
34
  "esbuild-wasm": "0.23.x",
32
35
  "glob": "^11.1.0",
33
36
  "slashes": "^3.0.12",
34
- "type-fest": "npm:type-fest__4.x@4.23.0",
35
- "zod": "npm:zod__3.x@3.25.36",
37
+ "@opendatacapture/runtime-internal": "0.0.0",
36
38
  "ts-pattern": "npm:ts-pattern__5.x@5.3.1",
37
- "@opendatacapture/runtime-internal": "0.0.0"
39
+ "type-fest": "npm:type-fest__4.x@4.23.0",
40
+ "zod": "npm:zod__3.x@3.25.36"
38
41
  },
39
42
  "devDependencies": {
40
- "@opendatacapture/instrument-stubs": "0.0.0",
41
- "@opendatacapture/runtime-v1": "2.1.4",
42
- "react-dom": "npm:react-dom__19.x@19.1.0",
43
- "react": "npm:react__19.x@19.1.0"
43
+ "react": "npm:react__19.x@19.1.0",
44
+ "@opendatacapture/runtime-v1": "2.2.1",
45
+ "react-dom": "npm:react-dom__19.x@19.1.0"
44
46
  },
45
47
  "scripts": {
46
48
  "format": "prettier --write src",
package/src/build.ts CHANGED
@@ -9,7 +9,37 @@ import * as esbuild from './vendor/esbuild.js';
9
9
 
10
10
  import type { BundlerInput } from './schemas.js';
11
11
  import type { BuildOutput } from './types.js';
12
- import type { BuildResult } from './vendor/esbuild.js';
12
+ import type { BuildFailure, BuildResult } from './vendor/esbuild.js';
13
+
14
+ const DEFAULT_REACT_PACKAGE = 'react@19.x';
15
+
16
+ const REACT_PACKAGE_PATTERN = /['"]\/runtime\/v1\/(react@[^'"/]+)/g;
17
+
18
+ /**
19
+ * JSX compiles to the JSX runtime of one react, and elements of one major are not renderable by
20
+ * another, so the runtime has to be the react the source itself imports rather than a fixed version.
21
+ */
22
+ function resolveJsxImportSource(inputs: BundlerInput[]): string {
23
+ const packages = new Set<string>();
24
+ for (const { content } of inputs) {
25
+ if (typeof content !== 'string') {
26
+ continue;
27
+ }
28
+ for (const [, name] of content.matchAll(REACT_PACKAGE_PATTERN)) {
29
+ packages.add(name!);
30
+ }
31
+ }
32
+ if (packages.size > 1) {
33
+ throw new InstrumentBundlerError(
34
+ `Cannot resolve the JSX runtime: expected at most one version of react, found ${[...packages].join(', ')}`
35
+ );
36
+ }
37
+ return `/runtime/v1/${packages.values().next().value ?? DEFAULT_REACT_PACKAGE}`;
38
+ }
39
+
40
+ function describeError(err: unknown): string {
41
+ return err instanceof Error ? `${err.name}: ${err.message}` : `${typeof err}: ${String(err)}`;
42
+ }
13
43
 
14
44
  function parseBuildResult(result: BuildResult): BuildOutput {
15
45
  const cssOutput = result.outputFiles?.find((output) => output.path.endsWith('bundle.css'));
@@ -41,6 +71,8 @@ export async function build({
41
71
  logLevel?: LogLevel;
42
72
  }): Promise<BuildOutput> {
43
73
  const index = resolveIndexInput(inputs);
74
+ // outside the try, whose catch rewrites anything it sees into an esbuild failure
75
+ const jsxImportSource = resolveJsxImportSource(inputs);
44
76
  let result: BuildResult;
45
77
  try {
46
78
  result = await esbuild.build({
@@ -48,7 +80,7 @@ export async function build({
48
80
  charset: 'ascii',
49
81
  format: 'esm',
50
82
  jsx: 'automatic',
51
- jsxImportSource: '/runtime/v1/react@19.x',
83
+ jsxImportSource,
52
84
  keepNames: true,
53
85
  logLevel,
54
86
  metafile: true,
@@ -67,9 +99,12 @@ export async function build({
67
99
  } catch (err) {
68
100
  const parseResult = await $BuildFailure.safeParseAsync(err);
69
101
  if (parseResult.success) {
70
- throw new InstrumentBundlerError('Unknown Error', { cause: err });
102
+ // the original error, rather than the parsed copy, so that `cause instanceof Error` holds downstream
103
+ throw new InstrumentBundlerError('Failed to Compile', { cause: err as BuildFailure, kind: 'ESBUILD_FAILURE' });
71
104
  }
72
- throw new InstrumentBundlerError('Failed to Compile', { cause: parseResult.error, kind: 'ESBUILD_FAILURE' });
105
+ // anything esbuild did not report as a compilation failure is a fault in the bundler itself, not in the
106
+ // instrument, so name it here rather than leaving the reader with 'Unknown Error' and a stack
107
+ throw new InstrumentBundlerError(`Unexpected error while invoking esbuild: ${describeError(err)}`, { cause: err });
73
108
  }
74
109
  return parseBuildResult(result);
75
110
  }
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-var */
2
-
3
1
  declare module 'esbuild' {
4
2
  // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/consistent-type-definitions
5
3
  export interface BuildResult<ProvidedOptions extends BuildOptions = BuildOptions> {
@@ -14,11 +12,10 @@ declare module 'esbuild-wasm' {
14
12
  }
15
13
  }
16
14
 
17
- if (typeof window === 'undefined') {
18
- var { build, transform } = await import('esbuild');
19
- } else {
20
- var { build, transform } = await import('esbuild-wasm');
21
- }
15
+ // The bindings must be initialized by their own declaration, not by a detached `if`/`else`.
16
+ // This package declares `sideEffects: ['**/cli.ts']`, so a bundler is free to drop a
17
+ // standalone statement here, which silently leaves `build` undeclared in the output.
18
+ const { build, transform } = typeof window === 'undefined' ? await import('esbuild') : await import('esbuild-wasm');
22
19
 
23
20
  export { build, transform };
24
21
  export type { BuildFailure, BuildResult, Loader, Location, Message, Plugin } from 'esbuild';