solar-spa/.wiki/Bundler-Compatibility.md
Aric Camarata fb0c14e761 v2.0.0: TypeScript rewrite with WASM recompilation
Complete rewrite of the package from plain JavaScript to TypeScript,
compiled to dual CJS/ESM via tsup. The NREL SPA C source is recompiled
to WASM with Emscripten using SINGLE_FILE base64 inlining, eliminating
bundler path-resolution issues.

Changes:
- Rewrite JS wrapper in TypeScript with full type definitions
- Recompile WASM with -O3 -flto, 1MB fixed memory, no filesystem
- Add input validation with descriptive error messages
- Add spaFormatted() for HH:MM:SS time strings
- Add formatTime() utility and init() for eager WASM loading
- Add SPA_ZA, SPA_ZA_INC, SPA_ZA_RTS, SPA_ALL function code exports
- Dual CJS/ESM output via tsup with proper exports map
- Test suite: 68 ESM + 13 CJS assertions
- 100-scenario validation suite across 7 categories
- GitHub Wiki with 8 documentation pages
- CI workflow: Node 20/22/24 matrix, typecheck, pack-check
- NREL attribution in LICENSE and README per their license terms
- Minimum Node.js 20
2026-02-25 10:35:24 -05:00

4.2 KiB

Bundler Compatibility

solar-spa works out of the box in every tested environment. No special configuration is required.

How it works

The WASM binary is inlined as base64 inside wasm/spa-module.js (compiled with Emscripten's SINGLE_FILE flag). There is no external .wasm file to resolve, fetch, or copy. The package is just JavaScript files, and every bundler knows how to handle JavaScript. See WebAssembly in npm Packages for a deeper discussion of why this approach works.

Tested environments

Node.js (18+)

Both ES module and CommonJS imports work:

// ESM
import { spa } from 'solar-spa';

// CommonJS
const { spa } = require('solar-spa');

The package.json exports map routes import to dist/index.mjs and require() to dist/index.cjs. Full TypeScript declarations are provided for both formats.

Webpack 5

No configuration needed. Webpack resolves the package through the exports map and bundles the JavaScript files normally. The base64-encoded WASM is just a string literal inside the bundle.

// webpack project
import { spa } from 'solar-spa';
const result = await spa(new Date(), 40.7128, -74.006);

If your Webpack config includes rules for .wasm files, they will not interfere because solar-spa does not import or reference any .wasm files.

Vite

Works without configuration. Vite handles the CJS-to-ESM interop through its pre-bundling step (esbuild), and the inlined WASM is transparent to the build pipeline.

// vite project
import { spa } from 'solar-spa';

Next.js (Pages Router)

Works in both API routes (server-side) and getServerSideProps. The WASM initializes on the server without filesystem access issues because the binary is inlined.

// pages/api/sun.js
import { spa } from 'solar-spa';

export default async function handler(req, res) {
  const result = await spa(new Date(), 40.7128, -74.006, { timezone: -4 });
  res.json(result);
}

Next.js (App Router)

Works in server components, route handlers, and middleware. The same inlining approach avoids the edge runtime file resolution problems that affect packages with separate .wasm files.

// app/api/sun/route.js
import { spa } from 'solar-spa';

export async function GET() {
  const result = await spa(new Date(), 40.7128, -74.006, { timezone: -4 });
  return Response.json(result);
}

Browser (direct)

If you are not using a bundler, use a CDN that provides ESM builds:

<script type="module">
  import { spa } from 'https://esm.sh/solar-spa';
  const result = await spa(new Date(), 40.7128, -74.006);
  console.log(result.zenith);
</script>

Web Workers

The Emscripten output includes worker in its environment list. WASM initialization works inside a Web Worker without modification:

// worker.js
import { spa } from 'solar-spa';

self.addEventListener('message', async (event) => {
  const { date, lat, lon } = event.data;
  const result = await spa(new Date(date), lat, lon);
  self.postMessage(result);
});

Common questions

Do I need to configure wasm asset handling in my bundler?

No. There is no .wasm file in the package. The binary is a base64 string inside a .js file.

Will this work with tree-shaking?

The package exports individual named functions. Bundlers that support tree-shaking will include only the functions you import. However, the WASM module is initialized as a whole, so the binary size is fixed regardless of which functions you use.

Is there a browser-only build?

The same build works in both Node.js and browsers. The Emscripten output detects the environment at runtime (ENVIRONMENT='node,web,worker').

What about Content Security Policy (CSP)?

The WASM binary is decoded from base64 and compiled via WebAssembly.instantiate(). If your CSP restricts wasm-eval or wasm-unsafe-eval, you will need to add the appropriate directive. This is a general WASM requirement, not specific to this package.

What is the minimum browser version?

Any browser that supports WebAssembly: Chrome 57+, Firefox 52+, Safari 11+, Edge 16+. This covers all browsers released since 2017.


Home · WebAssembly in npm Packages · Architecture