Skip to content

Commit 5dc03c8

Browse files
committed
stash
1 parent b2e0db9 commit 5dc03c8

File tree

3 files changed

+183
-24
lines changed

3 files changed

+183
-24
lines changed

packages/core/src/runtime-plugin.ts

Lines changed: 167 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { readFileSync, realpathSync } from "node:fs"
2+
import { basename, dirname, isAbsolute } from "node:path"
3+
import { fileURLToPath } from "node:url"
14
import { type BunPlugin } from "bun"
25
import * as coreRuntime from "./index.js"
36

@@ -45,6 +48,22 @@ const exactSpecifierFilter = (specifier: string): RegExp => {
4548
return new RegExp(`^${escapeRegExp(specifier)}$`)
4649
}
4750

51+
const exactPathFilter = (path: string): RegExp => {
52+
const variants = new Set<string>([sourcePath(path), normalizeSourcePath(path)])
53+
54+
for (const variant of [...variants]) {
55+
if (variant.startsWith("/var/")) {
56+
variants.add(`/private${variant}`)
57+
}
58+
59+
if (variant.startsWith("/private/var/")) {
60+
variants.add(variant.slice("/private".length))
61+
}
62+
}
63+
64+
return new RegExp(`^(?:${[...variants].map(escapeRegExp).join("|")})(?:[?#].*)?$`)
65+
}
66+
4867
export const runtimeModuleIdForSpecifier = (specifier: string): string => {
4968
return `${RUNTIME_MODULE_PREFIX}${encodeURIComponent(specifier)}`
5069
}
@@ -64,10 +83,41 @@ const sourcePath = (path: string): string => {
6483
return end === undefined ? path : path.slice(0, end)
6584
}
6685

86+
const normalizeSourcePath = (path: string): string => {
87+
const cleanPath = sourcePath(path)
88+
89+
try {
90+
return realpathSync(cleanPath)
91+
} catch {
92+
return cleanPath
93+
}
94+
}
95+
6796
const isNodeModulesPath = (path: string): boolean => {
6897
return /(?:^|[/\\])node_modules(?:[/\\])/.test(path)
6998
}
7099

100+
const nodeModulesPackageRootForPath = (path: string): string | null => {
101+
let currentDir = dirname(path)
102+
103+
while (true) {
104+
const parentDir = dirname(currentDir)
105+
if (parentDir === currentDir) {
106+
return null
107+
}
108+
109+
if (basename(parentDir) === "node_modules") {
110+
return currentDir
111+
}
112+
113+
if (basename(dirname(parentDir)) === "node_modules" && basename(parentDir).startsWith("@")) {
114+
return currentDir
115+
}
116+
117+
currentDir = parentDir
118+
}
119+
}
120+
71121
const resolveRuntimePluginRewriteOptions = (
72122
options: RuntimePluginRewriteOptions | undefined,
73123
): Required<RuntimePluginRewriteOptions> => {
@@ -101,8 +151,6 @@ const runtimeLoaderForPath = (path: string): "js" | "ts" | "jsx" | "tsx" | null
101151
return null
102152
}
103153

104-
const runtimeSourceFilter = /\.(?:[cm]?js|[cm]?ts|jsx|tsx)(?:[?#].*)?$/
105-
106154
const resolveImportSpecifierPatterns = [
107155
/(from\s+["'])([^"']+)(["'])/g,
108156
/(import\s+["'])([^"']+)(["'])/g,
@@ -180,6 +228,42 @@ const resolveFromParent = (specifier: string, parent: string): string | null =>
180228
}
181229
}
182230

231+
const resolveSourcePathFromSpecifier = (specifier: string, importer: string): string | null => {
232+
if (
233+
specifier.startsWith("node:") ||
234+
specifier.startsWith("bun:") ||
235+
specifier.startsWith("http:") ||
236+
specifier.startsWith("https:") ||
237+
specifier.startsWith("data:") ||
238+
specifier.startsWith(RUNTIME_MODULE_PREFIX)
239+
) {
240+
return null
241+
}
242+
243+
if (specifier.startsWith("file:")) {
244+
return normalizeSourcePath(fileURLToPath(specifier))
245+
}
246+
247+
if (isAbsolute(specifier)) {
248+
return normalizeSourcePath(specifier)
249+
}
250+
251+
const resolvedSpecifier = resolveFromParent(specifier, importer)
252+
if (!resolvedSpecifier) {
253+
return null
254+
}
255+
256+
if (resolvedSpecifier.startsWith("file:")) {
257+
return normalizeSourcePath(fileURLToPath(resolvedSpecifier))
258+
}
259+
260+
if (isAbsolute(resolvedSpecifier)) {
261+
return normalizeSourcePath(resolvedSpecifier)
262+
}
263+
264+
return null
265+
}
266+
183267
const rewriteImportsFromResolveParents = (code: string, resolveParentsByRecency: string[]): string => {
184268
if (resolveParentsByRecency.length === 0) {
185269
return code
@@ -230,6 +314,61 @@ export function createRuntimePlugin(input: CreateRuntimePluginOptions = {}): Bun
230314
name: "bun-plugin-opentui-runtime-modules",
231315
setup: (build) => {
232316
const resolveParentsByRecency: string[] = []
317+
const installedRewriteLoaders = new Set<string>()
318+
const nodeModulesBareRewritePackageRoots = new Set<string>()
319+
const runtimeSpecifierRewriteNeededByPath = new Map<string, boolean>()
320+
321+
const installRewriteLoader = (path: string): void => {
322+
const normalizedPath = normalizeSourcePath(path)
323+
if (installedRewriteLoaders.has(normalizedPath)) {
324+
return
325+
}
326+
327+
installedRewriteLoaders.add(normalizedPath)
328+
329+
build.onLoad({ filter: exactPathFilter(normalizedPath) }, async (args) => {
330+
const path = normalizeSourcePath(args.path)
331+
const nodeModulesPath = isNodeModulesPath(path)
332+
const shouldRewriteRuntimeSpecifiers = !nodeModulesPath || rewriteOptions.nodeModulesRuntimeSpecifiers
333+
const shouldRewriteBareSpecifiers = !nodeModulesPath || rewriteOptions.nodeModulesBareSpecifiers
334+
const loader = runtimeLoaderForPath(args.path)
335+
336+
if (!loader) {
337+
throw new Error(`Unable to determine runtime loader for path: ${args.path}`)
338+
}
339+
340+
const contents = await Bun.file(path).text()
341+
const runtimeRewrittenContents = shouldRewriteRuntimeSpecifiers
342+
? rewriteRuntimeSpecifiers(contents, runtimeModuleIdsBySpecifier)
343+
: contents
344+
345+
if (runtimeRewrittenContents !== contents && shouldRewriteBareSpecifiers) {
346+
registerResolveParent(resolveParentsByRecency, path)
347+
}
348+
349+
const transformedContents = shouldRewriteBareSpecifiers
350+
? rewriteImportsFromResolveParents(runtimeRewrittenContents, resolveParentsByRecency)
351+
: runtimeRewrittenContents
352+
353+
return {
354+
contents: transformedContents,
355+
loader,
356+
}
357+
})
358+
}
359+
360+
const needsRuntimeSpecifierRewrite = (path: string): boolean => {
361+
const normalizedPath = normalizeSourcePath(path)
362+
const cached = runtimeSpecifierRewriteNeededByPath.get(normalizedPath)
363+
if (cached !== undefined) {
364+
return cached
365+
}
366+
367+
const contents = readFileSync(normalizedPath, "utf8")
368+
const needsRewrite = rewriteRuntimeSpecifiers(contents, runtimeModuleIdsBySpecifier) !== contents
369+
runtimeSpecifierRewriteNeededByPath.set(normalizedPath, needsRewrite)
370+
return needsRewrite
371+
}
233372

234373
for (const [specifier, moduleEntry] of runtimeModules.entries()) {
235374
const moduleId = runtimeModuleIdsBySpecifier.get(specifier)
@@ -246,39 +385,43 @@ export function createRuntimePlugin(input: CreateRuntimePluginOptions = {}): Bun
246385
build.onResolve({ filter: exactSpecifierFilter(specifier) }, () => ({ path: moduleId }))
247386
}
248387

249-
build.onLoad({ filter: runtimeSourceFilter }, async (args) => {
250-
const path = sourcePath(args.path)
251-
const nodeModulesPath = isNodeModulesPath(path)
252-
const shouldRewriteRuntimeSpecifiers = !nodeModulesPath || rewriteOptions.nodeModulesRuntimeSpecifiers
253-
const shouldRewriteBareSpecifiers = !nodeModulesPath || rewriteOptions.nodeModulesBareSpecifiers
388+
build.onResolve({ filter: /.*/ }, (args) => {
389+
if (runtimeModuleIdsBySpecifier.has(args.path) || args.path.startsWith(RUNTIME_MODULE_PREFIX)) {
390+
return undefined
391+
}
254392

255-
if (!shouldRewriteRuntimeSpecifiers && !shouldRewriteBareSpecifiers) {
393+
const path = resolveSourcePathFromSpecifier(args.path, args.importer)
394+
if (!path || !runtimeLoaderForPath(path)) {
256395
return undefined
257396
}
258397

259-
const loader = runtimeLoaderForPath(args.path)
260-
if (!loader) {
261-
throw new Error(`Unable to determine runtime loader for path: ${args.path}`)
398+
const nodeModulesPath = isNodeModulesPath(path)
399+
400+
if (!nodeModulesPath) {
401+
installRewriteLoader(path)
402+
return undefined
262403
}
263404

264-
const file = Bun.file(path)
265-
const contents = await file.text()
266-
const runtimeRewrittenContents = shouldRewriteRuntimeSpecifiers
267-
? rewriteRuntimeSpecifiers(contents, runtimeModuleIdsBySpecifier)
268-
: contents
405+
if (!rewriteOptions.nodeModulesRuntimeSpecifiers && !rewriteOptions.nodeModulesBareSpecifiers) {
406+
return undefined
407+
}
269408

270-
if (runtimeRewrittenContents !== contents && shouldRewriteBareSpecifiers) {
271-
registerResolveParent(resolveParentsByRecency, path)
409+
const packageRoot = nodeModulesPackageRootForPath(path)
410+
if (rewriteOptions.nodeModulesBareSpecifiers && packageRoot && nodeModulesBareRewritePackageRoots.has(packageRoot)) {
411+
installRewriteLoader(path)
412+
return undefined
272413
}
273414

274-
const transformedContents = shouldRewriteBareSpecifiers
275-
? rewriteImportsFromResolveParents(runtimeRewrittenContents, resolveParentsByRecency)
276-
: runtimeRewrittenContents
415+
if (!rewriteOptions.nodeModulesRuntimeSpecifiers || !needsRuntimeSpecifierRewrite(path)) {
416+
return undefined
417+
}
277418

278-
return {
279-
contents: transformedContents,
280-
loader,
419+
if (rewriteOptions.nodeModulesBareSpecifiers && packageRoot) {
420+
nodeModulesBareRewritePackageRoots.add(packageRoot)
281421
}
422+
423+
installRewriteLoader(path)
424+
return undefined
282425
})
283426
},
284427
}

packages/solid/tests/runtime-plugin-support-node-modules.fixture.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,38 @@ type FixtureState = typeof globalThis & {
1717
}
1818

1919
const tempRoot = mkdtempSync(join(tmpdir(), "solid-runtime-plugin-support-node-modules-fixture-"))
20+
const commonJsDependencyDir = join(tempRoot, "node_modules", "runtime-plugin-support-cjs-dependency")
2021
const externalPackageDir = join(tempRoot, "node_modules", "runtime-plugin-support-node-modules-fixture")
2122
const externalPackageEntryPath = join(externalPackageDir, "index.js")
2223

24+
mkdirSync(commonJsDependencyDir, { recursive: true })
2325
mkdirSync(externalPackageDir, { recursive: true })
2426

27+
writeFileSync(
28+
join(commonJsDependencyDir, "package.json"),
29+
JSON.stringify({
30+
name: "runtime-plugin-support-cjs-dependency",
31+
private: true,
32+
main: "./index.js",
33+
}),
34+
)
35+
36+
writeFileSync(join(commonJsDependencyDir, "index.js"), 'exports.parse = (input) => JSON.parse(input)\n')
37+
2538
const source = [
2639
'import * as solid from "@opentui/solid"',
2740
'import * as core from "@opentui/core"',
2841
'import * as coreTesting from "@opentui/core/testing"',
2942
'import { createSignal } from "solid-js"',
43+
'import { parse } from "runtime-plugin-support-cjs-dependency"',
3044
'import * as solidStore from "solid-js/store"',
3145
"const host = globalThis.__solidRuntimeHost__",
3246
"const checks = [",
3347
" `solid=${solid.extend === host?.solid.extend}`,",
3448
" `core=${core.engine === host?.core.engine}`,",
3549
" `coreTesting=${coreTesting.createTestRenderer === host?.coreTesting.createTestRenderer}`,",
3650
" `solidJs=${createSignal === host?.solidJs.createSignal}`,",
51+
" `cjs=${parse('{\\\"value\\\":1}').value === 1}`,",
3752
" `solidStore=${solidStore.createStore === host?.solidJsStore.createStore}`,",
3853
"]",
3954
"console.log(checks.join(';'))",

packages/solid/tests/runtime-plugin-support-node-modules.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ describe("solid runtime plugin support in node_modules", () => {
2727
expect(stdout).toContain("core=true")
2828
expect(stdout).toContain("coreTesting=true")
2929
expect(stdout).toContain("solidJs=true")
30+
expect(stdout).toContain("cjs=true")
3031
expect(stdout).toContain("solidStore=true")
3132
})
3233
})

0 commit comments

Comments
 (0)