Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions lib/runner/tsconfigPaths.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { normalizeFilePath } from "./normalizeFsMap"
import { joinPath } from "../utils/pathJoin"

type RawTsConfig = {
compilerOptions?: {
Expand Down Expand Up @@ -40,6 +41,13 @@ export function resolveWithTsconfigPaths(opts: {
const { importPath, normalizedFilePathMap, extensions, tsConfig } = opts
if (!tsConfig) return null
const { baseUrl, paths } = tsConfig
const normalizedBaseUrl = baseUrl ? normalizeFilePath(baseUrl) : ""
const effectiveBaseUrl = normalizedBaseUrl === "." ? "" : normalizedBaseUrl

const resolveTargetWithBaseUrl = (target: string) => {
if (!baseUrl || target.startsWith("/")) return target
return joinPath(effectiveBaseUrl, target)
}

const tryResolveCandidate = (candidate: string) => {
const normalizedCandidate = normalizeFilePath(candidate)
Expand Down Expand Up @@ -72,20 +80,14 @@ export function resolveWithTsconfigPaths(opts: {
)
for (const target of targets) {
const replaced = target.replace("*", starMatch)
const candidate =
baseUrl && !replaced.startsWith("./") && !replaced.startsWith("/")
? `${baseUrl}/${replaced}`
: replaced
const candidate = resolveTargetWithBaseUrl(replaced)
const resolved = tryResolveCandidate(candidate)
if (resolved) return resolved
}
} else {
if (importPath !== alias) continue
for (const target of targets) {
const candidate =
baseUrl && !target.startsWith("./") && !target.startsWith("/")
? `${baseUrl}/${target}`
: target
const candidate = resolveTargetWithBaseUrl(target)
const resolved = tryResolveCandidate(candidate)
if (resolved) return resolved
}
Expand Down
38 changes: 38 additions & 0 deletions lib/utils/pathJoin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export function joinPath(...parts: string[]): string {
const segments: string[] = []
let isAbsolute = false

for (const part of parts) {
if (!part) continue
const normalized = part.replace(/\\/g, "/")
if (!normalized) continue

if (normalized.startsWith("/")) {
segments.length = 0
isAbsolute = true
}

for (const segment of normalized.split("/")) {
if (!segment || segment === ".") {
continue
}
if (segment === "..") {
if (segments.length && segments[segments.length - 1] !== "..") {
segments.pop()
continue
}
if (!isAbsolute) {
segments.push("..")
}
continue
}
segments.push(segment)
}
}

const joined = segments.join("/")
if (isAbsolute) {
return joined ? `/${joined}` : "/"
}
return joined
}
31 changes: 31 additions & 0 deletions tests/features/tsconfig-paths-resolution.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,34 @@ test("throws error when tsconfig path alias cannot be resolved (instead of tryin
'Import "@utils/missing" matches a tsconfig path alias but could not be resolved to an existing file',
)
})

test("tsconfig paths honor baseUrl when targets use relative prefixes", async () => {
const circuitJson = await runTscircuitCode(
{
"tsconfig.json": JSON.stringify({
compilerOptions: {
baseUrl: "./src",
paths: {
"@components/*": ["./components/*"],
},
},
}),
"src/components/res.tsx": `
export default () => (<resistor name="Rbase" resistance="1k" />)
`,
"user.tsx": `
import Resistor from "@components/res"
export default () => (<Resistor />)
`,
},
{
mainComponentPath: "user",
},
)

const resistor = circuitJson.find(
(el) => el.type === "source_component" && el.name === "Rbase",
) as any
expect(resistor).toBeDefined()
expect(resistor.resistance).toBe(1000)
})