Skip to content

Commit ce098ef

Browse files
committed
fix(runtime): enable node source execution for tsx/jsx with remote deps
1 parent 1fb88d9 commit ce098ef

File tree

10 files changed

+312
-65
lines changed

10 files changed

+312
-65
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,13 @@ const tsxPlan = {
273273
source: {
274274
language: "tsx",
275275
runtime: "renderify",
276-
code: [
277-
"import { format } from \"https://ga.jspm.io/npm:date-fns@4.1.0/format.js\";",
278-
"",
279-
"export default function App() {",
280-
" return <section>Today: {format(new Date(), \"yyyy-MM-dd\")}</section>;",
281-
"}",
282-
].join("\n"),
276+
code: `
277+
import { format } from "https://ga.jspm.io/npm:date-fns@4.1.0/format.js";
278+
279+
export default function App() {
280+
return <section>Today: {format(new Date(), "yyyy-MM-dd")}</section>;
281+
}
282+
`,
283283
},
284284
};
285285

packages/renderify/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ const tsxPlan = {
8484
source: {
8585
language: "tsx",
8686
runtime: "renderify",
87-
code: [
88-
"import { format } from \"https://ga.jspm.io/npm:date-fns@4.1.0/format.js\";",
89-
"",
90-
"export default function App() {",
91-
" return <section>Today: {format(new Date(), \"yyyy-MM-dd\")}</section>;",
92-
"}",
93-
].join("\n"),
87+
code: `
88+
import { format } from "https://ga.jspm.io/npm:date-fns@4.1.0/format.js";
89+
90+
export default function App() {
91+
return <section>Today: {format(new Date(), "yyyy-MM-dd")}</section>;
92+
}
93+
`,
9494
},
9595
};
9696

packages/runtime/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"dependencies": {
5656
"@renderify/ir": "workspace:^",
5757
"@renderify/security": "workspace:^",
58+
"esbuild": "^0.27.3",
5859
"preact": "^10.28.3",
5960
"preact-render-to-string": "^6.6.5"
6061
}

packages/runtime/src/manager.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ import {
9292
resolveRuntimeSpecifier,
9393
resolveSourceImportLoaderCandidate,
9494
} from "./runtime-specifier";
95-
import { BabelRuntimeSourceTranspiler } from "./transpiler";
95+
import { DefaultRuntimeSourceTranspiler } from "./transpiler";
9696

9797
interface ExecutionFrame {
9898
startedAt: number;
@@ -133,7 +133,7 @@ export class DefaultRuntimeManager implements RuntimeManager {
133133
constructor(options: RuntimeManagerOptions = {}) {
134134
this.moduleLoader = options.moduleLoader;
135135
this.sourceTranspiler =
136-
options.sourceTranspiler ?? new BabelRuntimeSourceTranspiler();
136+
options.sourceTranspiler ?? new DefaultRuntimeSourceTranspiler();
137137
this.defaultMaxImports = options.defaultMaxImports ?? FALLBACK_MAX_IMPORTS;
138138
this.defaultMaxComponentInvocations =
139139
options.defaultMaxComponentInvocations ??
@@ -473,7 +473,7 @@ export class DefaultRuntimeManager implements RuntimeManager {
473473
this.createSourceModuleLoader(
474474
manifest,
475475
runtimeDiagnostics,
476-
).materializeBrowserRemoteModule(url),
476+
).materializeRemoteModule(url),
477477
fetchRemoteModuleCodeWithFallback: (url, runtimeDiagnostics) =>
478478
this.createSourceModuleLoader(
479479
undefined,
@@ -601,17 +601,17 @@ export class DefaultRuntimeManager implements RuntimeManager {
601601
return new RuntimeSourceModuleLoader({
602602
moduleManifest,
603603
diagnostics,
604-
browserModuleUrlCache: this.browserModuleUrlCache,
605-
browserModuleInflight: this.browserModuleInflight,
604+
materializedModuleUrlCache: this.browserModuleUrlCache,
605+
materializedModuleInflight: this.browserModuleInflight,
606606
remoteFallbackCdnBases: this.remoteFallbackCdnBases,
607607
remoteFetchTimeoutMs: this.remoteFetchTimeoutMs,
608608
remoteFetchRetries: this.remoteFetchRetries,
609609
remoteFetchBackoffMs: this.remoteFetchBackoffMs,
610-
canMaterializeBrowserModules: () => canMaterializeBrowserModules(),
610+
canMaterializeRuntimeModules: () =>
611+
canMaterializeBrowserModules() || typeof Buffer !== "undefined",
611612
rewriteImportsAsync: (code, resolver) =>
612613
this.rewriteImportsAsync(code, resolver),
613-
createBrowserBlobModuleUrl: (code) =>
614-
this.createBrowserBlobModuleUrl(code),
614+
createInlineModuleUrl: (code) => this.createInlineModuleUrl(code),
615615
resolveRuntimeSourceSpecifier: (
616616
specifier,
617617
manifest,
@@ -659,6 +659,19 @@ export class DefaultRuntimeManager implements RuntimeManager {
659659
return rewriteImportsAsync(code, resolver);
660660
}
661661

662+
private createInlineModuleUrl(code: string): string {
663+
if (isBrowserRuntime() && canMaterializeBrowserModules()) {
664+
return this.createBrowserBlobModuleUrl(code);
665+
}
666+
667+
if (typeof Buffer !== "undefined") {
668+
const encoded = Buffer.from(code, "utf8").toString("base64");
669+
return `data:text/javascript;base64,${encoded}`;
670+
}
671+
672+
throw new Error("No runtime module URL strategy is available");
673+
}
674+
662675
private createBrowserBlobModuleUrl(code: string): string {
663676
return createBrowserBlobModuleUrl(code, this.browserBlobUrls);
664677
}

packages/runtime/src/runtime-source-module-loader.ts

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ import { isHttpUrl } from "./runtime-specifier";
1818
export interface RuntimeSourceModuleLoaderOptions {
1919
moduleManifest: RuntimeModuleManifest | undefined;
2020
diagnostics: RuntimeDiagnostic[];
21-
browserModuleUrlCache: Map<string, string>;
22-
browserModuleInflight: Map<string, Promise<string>>;
21+
materializedModuleUrlCache: Map<string, string>;
22+
materializedModuleInflight: Map<string, Promise<string>>;
2323
remoteFallbackCdnBases: string[];
2424
remoteFetchTimeoutMs: number;
2525
remoteFetchRetries: number;
2626
remoteFetchBackoffMs: number;
27-
canMaterializeBrowserModules: () => boolean;
27+
canMaterializeRuntimeModules: () => boolean;
2828
rewriteImportsAsync: (
2929
code: string,
3030
resolver: (specifier: string) => Promise<string>,
3131
) => Promise<string>;
32-
createBrowserBlobModuleUrl: (code: string) => string;
32+
createInlineModuleUrl: (code: string) => string;
3333
resolveRuntimeSourceSpecifier: (
3434
specifier: string,
3535
moduleManifest: RuntimeModuleManifest | undefined,
@@ -41,18 +41,18 @@ export interface RuntimeSourceModuleLoaderOptions {
4141
export class RuntimeSourceModuleLoader {
4242
private readonly moduleManifest: RuntimeModuleManifest | undefined;
4343
private readonly diagnostics: RuntimeDiagnostic[];
44-
private readonly browserModuleUrlCache: Map<string, string>;
45-
private readonly browserModuleInflight: Map<string, Promise<string>>;
44+
private readonly materializedModuleUrlCache: Map<string, string>;
45+
private readonly materializedModuleInflight: Map<string, Promise<string>>;
4646
private readonly remoteFallbackCdnBases: string[];
4747
private readonly remoteFetchTimeoutMs: number;
4848
private readonly remoteFetchRetries: number;
4949
private readonly remoteFetchBackoffMs: number;
50-
private readonly canMaterializeBrowserModulesFn: () => boolean;
50+
private readonly canMaterializeRuntimeModulesFn: () => boolean;
5151
private readonly rewriteImportsAsyncFn: (
5252
code: string,
5353
resolver: (specifier: string) => Promise<string>,
5454
) => Promise<string>;
55-
private readonly createBrowserBlobModuleUrlFn: (code: string) => string;
55+
private readonly createInlineModuleUrlFn: (code: string) => string;
5656
private readonly resolveRuntimeSourceSpecifierFn: (
5757
specifier: string,
5858
moduleManifest: RuntimeModuleManifest | undefined,
@@ -63,40 +63,27 @@ export class RuntimeSourceModuleLoader {
6363
constructor(options: RuntimeSourceModuleLoaderOptions) {
6464
this.moduleManifest = options.moduleManifest;
6565
this.diagnostics = options.diagnostics;
66-
this.browserModuleUrlCache = options.browserModuleUrlCache;
67-
this.browserModuleInflight = options.browserModuleInflight;
66+
this.materializedModuleUrlCache = options.materializedModuleUrlCache;
67+
this.materializedModuleInflight = options.materializedModuleInflight;
6868
this.remoteFallbackCdnBases = options.remoteFallbackCdnBases;
6969
this.remoteFetchTimeoutMs = options.remoteFetchTimeoutMs;
7070
this.remoteFetchRetries = options.remoteFetchRetries;
7171
this.remoteFetchBackoffMs = options.remoteFetchBackoffMs;
72-
this.canMaterializeBrowserModulesFn = options.canMaterializeBrowserModules;
72+
this.canMaterializeRuntimeModulesFn = options.canMaterializeRuntimeModules;
7373
this.rewriteImportsAsyncFn = options.rewriteImportsAsync;
74-
this.createBrowserBlobModuleUrlFn = options.createBrowserBlobModuleUrl;
74+
this.createInlineModuleUrlFn = options.createInlineModuleUrl;
7575
this.resolveRuntimeSourceSpecifierFn =
7676
options.resolveRuntimeSourceSpecifier;
7777
}
7878

7979
async importSourceModuleFromCode(code: string): Promise<unknown> {
80-
const isNodeRuntime =
81-
typeof process !== "undefined" &&
82-
process !== null &&
83-
typeof process.versions === "object" &&
84-
process.versions !== null &&
85-
typeof process.versions.node === "string";
86-
87-
if (isNodeRuntime && typeof Buffer !== "undefined") {
88-
const encoded = Buffer.from(code, "utf8").toString("base64");
89-
const dataUrl = `data:text/javascript;base64,${encoded}`;
90-
return import(/* webpackIgnore: true */ dataUrl);
91-
}
92-
93-
if (this.canMaterializeBrowserModulesFn()) {
80+
if (this.canMaterializeRuntimeModulesFn()) {
9481
const rewrittenEntry = await this.rewriteImportsAsyncFn(
9582
code,
9683
async (specifier) =>
97-
this.resolveBrowserImportSpecifier(specifier, undefined),
84+
this.resolveRuntimeImportSpecifier(specifier, undefined),
9885
);
99-
const entryUrl = this.createBrowserBlobModuleUrlFn(rewrittenEntry);
86+
const entryUrl = this.createInlineModuleUrlFn(rewrittenEntry);
10087
return import(/* webpackIgnore: true */ entryUrl);
10188
}
10289

@@ -109,7 +96,7 @@ export class RuntimeSourceModuleLoader {
10996
throw new Error("No runtime module import strategy is available");
11097
}
11198

112-
async resolveBrowserImportSpecifier(
99+
async resolveRuntimeImportSpecifier(
113100
specifier: string,
114101
parentUrl: string | undefined,
115102
): Promise<string> {
@@ -123,7 +110,7 @@ export class RuntimeSourceModuleLoader {
123110
}
124111

125112
if (isHttpUrl(trimmed)) {
126-
return this.materializeBrowserRemoteModule(trimmed);
113+
return this.materializeRemoteModule(trimmed);
127114
}
128115

129116
if (
@@ -145,7 +132,7 @@ export class RuntimeSourceModuleLoader {
145132
return absolute;
146133
}
147134

148-
return this.materializeBrowserRemoteModule(absolute);
135+
return this.materializeRemoteModule(absolute);
149136
}
150137

151138
const resolved = this.resolveRuntimeSourceSpecifierFn(
@@ -156,7 +143,7 @@ export class RuntimeSourceModuleLoader {
156143
);
157144

158145
if (isHttpUrl(resolved)) {
159-
return this.materializeBrowserRemoteModule(resolved);
146+
return this.materializeRemoteModule(resolved);
160147
}
161148

162149
if (
@@ -171,24 +158,24 @@ export class RuntimeSourceModuleLoader {
171158
return absolute;
172159
}
173160

174-
return this.materializeBrowserRemoteModule(absolute);
161+
return this.materializeRemoteModule(absolute);
175162
}
176163

177164
return resolved;
178165
}
179166

180-
async materializeBrowserRemoteModule(url: string): Promise<string> {
167+
async materializeRemoteModule(url: string): Promise<string> {
181168
const normalizedUrl = url.trim();
182169
if (normalizedUrl.length === 0) {
183170
return normalizedUrl;
184171
}
185172

186-
const cachedUrl = this.browserModuleUrlCache.get(normalizedUrl);
173+
const cachedUrl = this.materializedModuleUrlCache.get(normalizedUrl);
187174
if (cachedUrl) {
188175
return cachedUrl;
189176
}
190177

191-
const inflight = this.browserModuleInflight.get(normalizedUrl);
178+
const inflight = this.materializedModuleInflight.get(normalizedUrl);
192179
if (inflight) {
193180
return inflight;
194181
}
@@ -198,17 +185,17 @@ export class RuntimeSourceModuleLoader {
198185
await this.fetchRemoteModuleCodeWithFallback(normalizedUrl);
199186
const rewritten = await this.materializeFetchedModuleSource(fetched);
200187

201-
const blobUrl = this.createBrowserBlobModuleUrlFn(rewritten);
202-
this.browserModuleUrlCache.set(normalizedUrl, blobUrl);
203-
this.browserModuleUrlCache.set(fetched.url, blobUrl);
204-
return blobUrl;
188+
const inlineUrl = this.createInlineModuleUrlFn(rewritten);
189+
this.materializedModuleUrlCache.set(normalizedUrl, inlineUrl);
190+
this.materializedModuleUrlCache.set(fetched.url, inlineUrl);
191+
return inlineUrl;
205192
})();
206193

207-
this.browserModuleInflight.set(normalizedUrl, loading);
194+
this.materializedModuleInflight.set(normalizedUrl, loading);
208195
try {
209196
return await loading;
210197
} finally {
211-
this.browserModuleInflight.delete(normalizedUrl);
198+
this.materializedModuleInflight.delete(normalizedUrl);
212199
}
213200
}
214201

@@ -238,7 +225,7 @@ export class RuntimeSourceModuleLoader {
238225
}
239226

240227
return this.rewriteImportsAsyncFn(fetched.code, async (childSpecifier) =>
241-
this.resolveBrowserImportSpecifier(childSpecifier, fetched.url),
228+
this.resolveRuntimeImportSpecifier(childSpecifier, fetched.url),
242229
);
243230
}
244231

packages/runtime/src/runtime-source-utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
type RuntimeNode,
77
} from "@renderify/ir";
88

9+
import { isBrowserRuntime } from "./runtime-environment";
910
export function canMaterializeBrowserModules(): boolean {
1011
return (
12+
isBrowserRuntime() &&
1113
typeof URL !== "undefined" &&
1214
typeof URL.createObjectURL === "function" &&
1315
typeof Blob !== "undefined" &&

0 commit comments

Comments
 (0)