Client Import Rewriting In Library Builds
Design Log #116 — Client Import Rewriting in Library Builds
Written for AI agents. See Log Methodology Note below for details.
Background
Jay plugins have dual entry points: main (index.ts) for server code and /client (index.client.ts) for browser code. The jayStackCompiler plugin handles code splitting — removing server code from client builds and vice versa — but import specifiers pointing to other plugins need rewriting from bare (@jay-framework/wix-cart) to client subpath (@jay-framework/wix-cart/client).
Related
compiler-jay-stack/lib/plugin-client-import-resolver.ts— the rewriting plugincompiler-jay-stack/lib/index.ts—jayStackCompiler()orchestration
Problem
When building a Jay plugin library (e.g., wix-stores) with vite build (client build, ssr: false), bare @jay-framework/* imports are not rewritten to /client subpaths in the output.
Example output (wix-stores/dist/index.client.js):
// Line 1 — correct (manually written as /client in index.client.ts)
import { WIX_CART_CONTEXT } from '@jay-framework/wix-cart/client';
// Line 7 — WRONG (bare import from wix-stores-context.ts, not rewritten)
import { WIX_CART_CONTEXT as WIX_CART_CONTEXT2 } from '@jay-framework/wix-cart';
Source (wix-stores-context.ts):
import {
WIX_CART_CONTEXT,
type CartState,
type CartOperationResult as CartResult,
} from '@jay-framework/wix-cart';
This file is shared between server and client builds. The bare import is correct for server; needs /client for client.
How it breaks
In the monorepo dev-server, Vite's middleware intercepts imports at serve time, masking the bug. In a standalone project, Vite's dependency pre-bundling (esbuild) follows the bare import into dist/index.js (server entry), which contains module.createRequire — a Node-only API:
Uncaught TypeError: (0, import_module.createRequire) is not a function
Analysis
The existing createPluginClientImportResolver used a transform hook with regex-based import rewriting. The regex used .+? which does not match across newlines, silently skipping multi-line imports like:
import { WIX_CART_CONTEXT, type CartState } from '@jay-framework/wix-cart';
Single-line imports (manually written as /client in index.client.ts) worked fine. Multi-line imports (common in shared context files) were silently skipped.
Design
Fix the regex in transform hook
Change .+? to [\s\S]+? in both IMPORT_REGEX and EXPORT_FROM_REGEX to match across newlines.
Rejected approach: resolveId hook
We initially tried replacing transform with a resolveId hook (this.resolve() to re-resolve with /client suffix). This approach was theoretically cleaner (no regex needed — Rollup provides each import specifier individually) but caused the Vite dev-server to crash during hydration tests. The resolveId hook runs for every import in every module, and even with synchronous null returns for non-plugin imports, the added overhead in the Vite dev-server resolution pipeline caused process crashes during test suite execution. The root cause was not fully diagnosed but appeared related to how Vite's dev-server handles plugin hooks during concurrent module resolution.
The transform hook approach works reliably because:
- It runs once per file (not per import)
- The regex fix handles multi-line imports correctly
- In dev serve mode, Vite serves the transformed source directly
- In build mode, Rollup processes the transformed source and preserves the rewritten specifiers for externals
Implementation Plan
Phase 1: Fix regex
Change .+? to [\s\S]+? in IMPORT_REGEX and EXPORT_FROM_REGEX.
Phase 2: Add multi-line test
Add test case for multi-line import rewriting.
Phase 3: Verify
- Build wix-stores client bundle — check output has
/clientimports - Run in standalone project — no
createRequireerror - Run dev-server tests — no regressions
Verification Criteria
wix-stores/dist/index.client.jshas@jay-framework/wix-cart/client(not bare)- Standalone project
yarn devloads withoutcreateRequireerror - Dev-server tests pass
Implementation Results
Fixed IMPORT_REGEX and EXPORT_FROM_REGEX to use [\s\S]+? for multi-line matching. Added multi-line import test case.
Files changed
compiler-jay-stack/lib/plugin-client-import-resolver.ts— fixed regex patterns for multi-line importscompiler-jay-stack/test/plugin-client-import-resolver.test.ts— added multi-line import test
Test results
- 61/61 compiler-jay-stack tests pass (including new multi-line test)
- 68/68 packages build successfully
- Dev-server tests pass
Future: replace regex with AST-based transformation
The regex approach is fragile — it can break on edge cases like import specifiers inside comments, string literals, or unusual formatting. A proper AST-based transformation (e.g., using TypeScript's compiler API or a lightweight parser like es-module-lexer) would be more robust. The resolveId approach was the ideal solution (no parsing needed at all) but was blocked by Vite dev-server stability issues. If those are resolved upstream, resolveId should be revisited.
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.