Skip to content
Closed
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
11 changes: 9 additions & 2 deletions src/build/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export async function writeTypes(nitro: Nitro) {
continue;
}
let path = resolveAlias(from, nitro.options.alias);
let keepResolvedExtension = false;
if (!isAbsolute(path)) {
const resolvedPath = resolveModulePath(from, {
try: true,
Expand All @@ -80,11 +81,17 @@ export async function writeTypes(nitro: Nitro) {
path = resolvedPath;
} else {
const subpath = await lookupNodeModuleSubpath(resolvedPath);
path = subpath && subpath !== "./" ? join(dir, name, subpath) : resolvedPath;
const subpathPath = subpath && subpath !== "./" && join(dir, name, subpath);
if (subpathPath && existsSync(subpathPath) && !(await isDirectory(subpathPath))) {
path = subpathPath;
} else {
path = resolvedPath;
keepResolvedExtension = Boolean(subpathPath);
}
}
}
}
if (existsSync(path) && !(await isDirectory(path))) {
if (!keepResolvedExtension && existsSync(path) && !(await isDirectory(path))) {
path = path.replace(/\.[a-z]+$/, "");
}
if (isAbsolute(path)) {
Expand Down
76 changes: 75 additions & 1 deletion test/unit/types-imports.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "pathe";
import { fileURLToPath } from "node:url";
import { dirname, join } from "pathe";
import { describe, expect, it } from "vitest";
import { createNitro, writeTypes } from "nitro/builder";

const tscPath = join(
dirname(fileURLToPath(import.meta.resolve("typescript/package.json"))),
"bin/tsc"
);

describe("writeTypes auto-import resolution", () => {
const fixtureDir = mkdtempSync(join(tmpdir(), "nitro-types-"));

Expand Down Expand Up @@ -63,4 +70,71 @@ describe("writeTypes auto-import resolution", () => {
`specifier should not end at the package directory, got ${specifier}`
).toBe(false);
});

it("emits a resolvable path for package export subpaths", async () => {
const pkgDir = join(fixtureDir, "node_modules", "export-subpath-pkg");
mkdirSync(join(pkgDir, "lib"), { recursive: true });
writeFileSync(
join(pkgDir, "package.json"),
JSON.stringify({
name: "export-subpath-pkg",
type: "module",
exports: {
"./h3": "./lib/h3.mjs",
},
})
);
writeFileSync(join(pkgDir, "lib", "h3.mjs"), "export function useH3() {}\n");
writeFileSync(join(pkgDir, "lib", "h3.d.mts"), 'export declare function useH3(): "resolved"\n');

const nitro = await createNitro({
rootDir: fixtureDir,
builder: "rolldown",
imports: {
presets: [
{
from: "export-subpath-pkg/h3",
imports: ["useH3"],
},
],
},
});

await writeTypes(nitro);

const declarationPath = join(
fixtureDir,
"node_modules",
".nitro",
"types",
"nitro-imports.d.ts"
);
const generated = readFileSync(declarationPath, "utf8");
const match = generated.match(/typeof import\('([^']*export-subpath-pkg[^']*)'\)/);
expect(
match,
`expected import() referencing export-subpath-pkg in:\n${generated}`
).toBeTruthy();

expect(match![1]).toBe("../../export-subpath-pkg/lib/h3.mjs");

const checkPath = join(fixtureDir, "check.ts");
writeFileSync(checkPath, 'const result: "resolved" = useH3()\n');

execFileSync(
process.execPath,
[
tscPath,
"--noEmit",
"--module",
"preserve",
"--moduleResolution",
"bundler",
"--noImplicitAny",
declarationPath,
checkPath,
],
{ cwd: fixtureDir }
Comment thread
coderabbitai[bot] marked this conversation as resolved.
);
});
});
Loading