Skip to content

Split node/browser builds with conditional exports - #100

Merged
samchon merged 4 commits into
masterfrom
feature/platform-exports
Sep 22, 2026
Merged

samchon merged 4 commits into
masterfrom
feature/platform-exports

Conversation

@samchon

@samchon samchon commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Problem

WebSocketServer, WorkerConnector, SharedWorkerServer etc. behave differently in NodeJS and browsers, but that difference lived only at runtime (is_node() branches, import2() tricks, NodeModule singletons). The package shipped a single lib/ tree, so every bundler saw ws, http, worker_threads, child_process references even when targeting the browser, and every Node consumer carried the browser polyfill paths.

Solution

The platform is now decided at build time, not runtime.

src/platform/
  IPlatform.ts         # interface of platform-dependent features (@internal)
  node/index.ts        # static import of ws / http / https / worker_threads / child_process
  browser/index.ts     # self.WebSocket / Worker / self only

Shared classes import a single alias, #platform, which tsconfig.node.json and tsconfig.browser.json resolve to different directories via paths (rewritten in the emit by the already-used ts-patch + typescript-transform-paths). One source, one set of .d.ts, two runtime trees.

"main": "lib/node/index.js",
"browser": "lib/browser/index.js",
"sideEffects": false,
"exports": {
  ".":         { "types", "browser": {...}, "react-native": {...}, "node": {...}, "require", "import" },
  "./node":    { ... },   // explicit, e.g. Electron main
  "./browser": { ... }    // explicit, e.g. Electron renderer
}

Verification

  • lib/browser/index.mjs imports only tstl; no ws / http / fs / child_process / worker_threads anywhere in lib/browser/**
  • lib/node/index.mjs statically imports ws and node builtins
  • npm pack + consumer project: require("tgrid") → lib/node, node --conditions=browser and --conditions=react-native → lib/browser, same for ESM and the ./node / ./browser subpaths
  • Consumer type-check passes under moduleResolution node, node16, and bundler
  • Both builds export the identical symbol set; wrong-platform usage throws the same messages as before (WebSocketServer in browser, SharedWorkerServer in node)
  • npm run test:node (17 tests) and npm run test:browser (puppeteer, 3 pages) pass
  • website typedoc: 0 errors (19 pre-existing doc-link warnings, unchanged)
  • npx eslint src: clean

Other changes

  • import2 dependency removed; NodeModule.ts, WebSocketPolyfill.ts deleted
  • NodeWorkerCompiler, FileSystem, Process* / Thread* moved to src/platform/node/, simplified from async factories to plain classes / functions
  • New IWorkerChannel interface abstracts the worker-to-parent channel, so WorkerServer no longer checks self.document itself
  • test/browser/tsconfig.json compiles browser-side test scripts with the browser platform into bin/browser/; dev script watches both projects with tsc --build
  • test_worker_compiler previously ran the browser bundle inside a Node child process (which only worked thanks to runtime detection); it now uses a separate node-platform bundle (bundle/worker-server.node.js)
  • src/typings: Object type parameters renamed to Wrapper; intentional Function / Boolean / Number / String usages get scoped eslint-disable comments. No type semantics changed.
  • Version bumped to 1.3.0 — new subpaths and a changed exports shape

🤖 Generated with Claude Code

https://claude.ai/code/session_01AQaTBspPxvUiYtTCbC8Tcg

samchon and others added 4 commits September 20, 2026 01:18
Runtime `is_node()` branching is replaced by a build-time `#platform`
alias, resolved by `tsconfig.node.json` / `tsconfig.browser.json` to
`src/platform/node` or `src/platform/browser`. The same source now
produces two runtime trees (`lib/node`, `lib/browser`) sharing one set
of declarations, wired through `exports` conditions plus explicit
`tgrid/node` and `tgrid/browser` subpaths.

- browser bundle imports only `tstl`; no `ws`, `http`, `fs`,
  `child_process` or `worker_threads` references remain
- node platform imports its modules statically; `import2` dependency
  and `NodeModule` / `WebSocketPolyfill` indirections are removed
- `IWorkerChannel` abstracts the worker-to-parent channel so
  `WorkerServer` no longer branches on `self.document`
- browser tests compile through `test/browser/tsconfig.json`;
  `test_worker_compiler` runs a node-platform bundle instead of
  the browser one

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQaTBspPxvUiYtTCbC8Tcg
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQaTBspPxvUiYtTCbC8Tcg
- `react-native` export condition maps to the browser tree instead of
  falling through to the node default
- `sideEffects: false` for bundler tree-shaking
- `src/typings`: rename `Object` type parameters to `Wrapper` and mark
  the intentional `Function` / wrapper-object usages with scoped
  eslint-disable comments; `npx eslint src` is now clean. No type
  semantics changed.
- version 1.3.0 (new subpaths and exports shape)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQaTBspPxvUiYtTCbC8Tcg
@samchon
samchon merged commit 26bd908 into master Sep 22, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant