mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
306 lines
9.7 KiB
TypeScript
306 lines
9.7 KiB
TypeScript
// import.meta properties are inlined at parse time in Bake
|
|
import { expect } from "bun:test";
|
|
import { devTest, emptyHtmlFile, minimalFramework } from "../bake-harness";
|
|
|
|
const platformPath = (path: string) => {
|
|
if (process.platform === "win32") {
|
|
return path.replace(/\//g, "\\");
|
|
}
|
|
return path;
|
|
};
|
|
|
|
devTest("import.meta properties are inlined in bake", {
|
|
framework: minimalFramework,
|
|
files: {
|
|
"routes/index.ts": `
|
|
export default function (req, meta) {
|
|
return Response.json({
|
|
dir: import.meta.dir,
|
|
dirname: import.meta.dirname,
|
|
file: import.meta.file,
|
|
path: import.meta.path,
|
|
url: import.meta.url,
|
|
});
|
|
}
|
|
`,
|
|
},
|
|
async test(dev) {
|
|
const response = await dev.fetch("/");
|
|
const json = await response.json();
|
|
|
|
// Check that all properties are strings, not undefined
|
|
expect(typeof json.dir).toBe("string");
|
|
expect(typeof json.dirname).toBe("string");
|
|
expect(typeof json.file).toBe("string");
|
|
expect(typeof json.path).toBe("string");
|
|
expect(typeof json.url).toBe("string");
|
|
|
|
// Check that dir and dirname are the same
|
|
expect(json.dir).toBe(json.dirname);
|
|
|
|
// Check that file is just the filename
|
|
expect(json.file).toBe("index.ts");
|
|
|
|
// Check that path contains the full path including filename
|
|
expect(json.path).toContain(platformPath("routes/index.ts"));
|
|
expect(json.path).toEndWith("index.ts");
|
|
|
|
// Check that url is a file:// URL
|
|
expect(json.url).toStartWith("file://");
|
|
expect(json.url).toContain("routes/index.ts");
|
|
},
|
|
});
|
|
|
|
devTest("import.meta properties work with dynamic updates", {
|
|
framework: minimalFramework,
|
|
files: {
|
|
"routes/test.ts": `
|
|
export default function (req, meta) {
|
|
const values = [
|
|
"dir: " + import.meta.dir,
|
|
"file: " + import.meta.file,
|
|
"path: " + import.meta.path,
|
|
];
|
|
return new Response(values.join("\\n"));
|
|
}
|
|
`,
|
|
},
|
|
async test(dev) {
|
|
const response = await dev.fetch("/test");
|
|
const text = await response.text();
|
|
|
|
// Verify the values are inlined strings
|
|
expect(text).toContain("dir: ");
|
|
expect(text).toContain("file: test.ts");
|
|
expect(text).toContain("path: ");
|
|
expect(text).toContain(platformPath("routes/test.ts"));
|
|
|
|
// Update the file with a meaningful change
|
|
await dev.patch("routes/test.ts", {
|
|
find: '"dir: "',
|
|
replace: '"directory: "',
|
|
});
|
|
|
|
const response2 = await dev.fetch("/test");
|
|
const text2 = await response2.text();
|
|
|
|
// After the patch, the first line should say "directory:" instead of "dir:"
|
|
expect(text2).toContain("directory: ");
|
|
expect(text2).toContain("file: test.ts");
|
|
expect(text2).toContain("path: ");
|
|
expect(text2).toContain(platformPath("routes/test.ts"));
|
|
},
|
|
});
|
|
|
|
devTest("import.meta properties with nested directories", {
|
|
framework: minimalFramework,
|
|
files: {
|
|
"routes/api/v1/handler.ts": `
|
|
export default function (req, meta) {
|
|
return Response.json({
|
|
dir: import.meta.dir,
|
|
file: import.meta.file,
|
|
path: import.meta.path,
|
|
url: import.meta.url,
|
|
});
|
|
}
|
|
`,
|
|
},
|
|
async test(dev) {
|
|
const response = await dev.fetch("/api/v1/handler");
|
|
const json = await response.json();
|
|
|
|
expect(json.file).toBe("handler.ts");
|
|
expect(json.path).toContain(platformPath("routes/api/v1/handler.ts"));
|
|
expect(json.dir).toContain(platformPath("routes/api/v1"));
|
|
expect(json.url).toMatch(/^file:\/\/.*routes\/api\/v1\/handler\.ts$/);
|
|
},
|
|
});
|
|
|
|
devTest("import.meta properties in client-side code show runtime values", {
|
|
framework: minimalFramework,
|
|
files: {
|
|
"test_import_meta_inline.js": `
|
|
// Test file for import.meta inlining
|
|
console.log("import.meta.dir:", import.meta.dir);
|
|
console.log("import.meta.dirname:", import.meta.dirname);
|
|
console.log("import.meta.file:", import.meta.file);
|
|
console.log("import.meta.path:", import.meta.path);
|
|
console.log("import.meta.url:", import.meta.url);
|
|
`,
|
|
"index.html": emptyHtmlFile({
|
|
scripts: ["test_import_meta_inline.js"],
|
|
}),
|
|
},
|
|
async test(dev) {
|
|
await using c = await dev.client("/");
|
|
|
|
// In client-side code, import.meta properties show runtime values
|
|
// They are NOT inlined because this is not server-side code
|
|
const messages = [
|
|
await c.getStringMessage(),
|
|
await c.getStringMessage(),
|
|
await c.getStringMessage(),
|
|
await c.getStringMessage(),
|
|
await c.getStringMessage(),
|
|
];
|
|
|
|
// Verify all properties are logged
|
|
expect(messages.some(m => m.startsWith("import.meta.dir:"))).toBe(true);
|
|
expect(messages.some(m => m.startsWith("import.meta.dirname:"))).toBe(true);
|
|
expect(messages.some(m => m.startsWith("import.meta.file:"))).toBe(true);
|
|
expect(messages.some(m => m.startsWith("import.meta.path:"))).toBe(true);
|
|
expect(messages.some(m => m.startsWith("import.meta.url:"))).toBe(true);
|
|
},
|
|
});
|
|
|
|
devTest("import.meta properties in catch-all routes", {
|
|
framework: minimalFramework,
|
|
files: {
|
|
"routes/blog/[...slug].ts": `
|
|
export default function BlogPost(req, meta) {
|
|
const url = new URL(req.url);
|
|
const slug = url.pathname.replace('/blog/', '').split('/').filter(Boolean);
|
|
|
|
const metaInfo = {
|
|
file: import.meta.file,
|
|
dir: import.meta.dir,
|
|
path: import.meta.path,
|
|
url: import.meta.url,
|
|
dirname: import.meta.dirname,
|
|
};
|
|
|
|
return Response.json({
|
|
slug: slug,
|
|
title: slug.map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' '),
|
|
meta: metaInfo,
|
|
content: "This is a blog post at: " + slug.join('/'),
|
|
});
|
|
}
|
|
`,
|
|
},
|
|
async test(dev) {
|
|
// Test single segment
|
|
const post1 = await dev.fetch("/blog/hello");
|
|
const json1 = await post1.json();
|
|
|
|
expect(json1.slug).toEqual(["hello"]);
|
|
expect(json1.title).toBe("Hello");
|
|
expect(json1.content).toBe("This is a blog post at: hello");
|
|
|
|
// Verify import.meta properties are inlined
|
|
expect(json1.meta.file).toBe("[...slug].ts");
|
|
expect(json1.meta.dir).toContain(platformPath("routes/blog"));
|
|
expect(json1.meta.dirname).toBe(json1.meta.dir);
|
|
expect(json1.meta.path).toContain(platformPath("routes/blog/[...slug].ts"));
|
|
// url encoded!
|
|
expect(json1.meta.url).toMatch(/^file:\/\/.*routes\/blog\/%5B\.\.\.slug%5D\.ts$/);
|
|
|
|
// Test multiple segments
|
|
const post2 = await dev.fetch("/blog/2024/tech/bun-framework");
|
|
const json2 = await post2.json();
|
|
|
|
expect(json2.slug).toEqual(["2024", "tech", "bun-framework"]);
|
|
expect(json2.title).toBe("2024 Tech Bun-framework");
|
|
expect(json2.content).toBe("This is a blog post at: 2024/tech/bun-framework");
|
|
|
|
// Meta properties should be the same regardless of the route
|
|
expect(json2.meta.file).toBe("[...slug].ts");
|
|
expect(json2.meta.path).toContain(platformPath("routes/blog/[...slug].ts"));
|
|
|
|
// Test empty slug (just /blog/)
|
|
const post3 = await dev.fetch("/blog/");
|
|
const json3 = await post3.json();
|
|
|
|
expect(json3.slug).toEqual([]);
|
|
expect(json3.title).toBe("");
|
|
expect(json3.content).toBe("This is a blog post at: ");
|
|
},
|
|
});
|
|
|
|
devTest("import.meta properties in nested catch-all routes with static siblings", {
|
|
framework: minimalFramework,
|
|
files: {
|
|
"routes/docs/[...path].ts": `
|
|
export default function DocsPage(req, meta) {
|
|
const url = new URL(req.url);
|
|
const path = url.pathname.replace('/docs/', '').split('/').filter(Boolean);
|
|
|
|
return Response.json({
|
|
type: "catch-all",
|
|
path: path,
|
|
file: import.meta.file,
|
|
dir: import.meta.dir,
|
|
fullPath: import.meta.path,
|
|
});
|
|
}
|
|
`,
|
|
"routes/docs/api.ts": `
|
|
export default function ApiDocs(req, meta) {
|
|
return Response.json({
|
|
type: "static",
|
|
page: "API Documentation",
|
|
file: import.meta.file,
|
|
dir: import.meta.dir,
|
|
fullPath: import.meta.path,
|
|
});
|
|
}
|
|
`,
|
|
"routes/docs/getting-started.ts": `
|
|
export default function GettingStarted(req, meta) {
|
|
return Response.json({
|
|
type: "static",
|
|
page: "Getting Started",
|
|
file: import.meta.file,
|
|
dir: import.meta.dir,
|
|
fullPath: import.meta.path,
|
|
});
|
|
}
|
|
`,
|
|
},
|
|
async test(dev) {
|
|
// Test static route - should match api.ts, not catch-all
|
|
const apiResponse = await dev.fetch("/docs/api");
|
|
const apiJson = await apiResponse.json();
|
|
|
|
expect(apiJson.type).toBe("static");
|
|
expect(apiJson.page).toBe("API Documentation");
|
|
expect(apiJson.file).toBe("api.ts");
|
|
expect(apiJson.dir).toContain(platformPath("routes/docs"));
|
|
expect(apiJson.fullPath).toContain(platformPath("routes/docs/api.ts"));
|
|
|
|
// Test another static route
|
|
const startResponse = await dev.fetch("/docs/getting-started");
|
|
const startJson = await startResponse.json();
|
|
|
|
expect(startJson.type).toBe("static");
|
|
expect(startJson.page).toBe("Getting Started");
|
|
expect(startJson.file).toBe("getting-started.ts");
|
|
expect(startJson.fullPath).toContain(platformPath("routes/docs/getting-started.ts"));
|
|
|
|
// Test catch-all route - should match for non-static paths
|
|
const guideResponse = await dev.fetch("/docs/guides/advanced/optimization");
|
|
expect(guideResponse.status).toBe(200);
|
|
const guideJson = await guideResponse.json();
|
|
|
|
expect(guideJson.type).toBe("catch-all");
|
|
expect(guideJson.path).toEqual(["guides", "advanced", "optimization"]);
|
|
expect(guideJson.file).toBe("[...path].ts");
|
|
expect(guideJson.dir).toContain(platformPath("routes/docs"));
|
|
expect(guideJson.fullPath).toContain(platformPath("routes/docs/[...path].ts"));
|
|
|
|
// Update catch-all route and verify import.meta values remain inlined
|
|
await dev.patch("routes/docs/[...path].ts", {
|
|
find: '"catch-all"',
|
|
replace: '"dynamic-catch-all"',
|
|
});
|
|
|
|
const updatedResponse = await dev.fetch("/docs/tutorials/intro");
|
|
const updatedJson = await updatedResponse.json();
|
|
|
|
expect(updatedJson.type).toBe("dynamic-catch-all");
|
|
expect(updatedJson.file).toBe("[...path].ts");
|
|
expect(updatedJson.fullPath).toContain(platformPath("routes/docs/[...path].ts"));
|
|
},
|
|
});
|