mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
### What does this PR do? ### How did you verify your code works? --------- Co-authored-by: pfg <pfg@pfg.pw>
394 lines
12 KiB
TypeScript
394 lines
12 KiB
TypeScript
import { describe, expect } from "bun:test";
|
|
import { SourceMap } from "node:module";
|
|
import { itBundled } from "./expectBundled";
|
|
|
|
describe("single-line comments", () => {
|
|
itBundled("unix newlines", {
|
|
files: {
|
|
"/entry.js": `// This is a comment\nconsole.log("hello");\n// Another comment\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
const output = api.readFile("/out.js");
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("windows newlines", {
|
|
files: {
|
|
"/entry.js": `// This is a comment\r\nconsole.log("hello");\r\n// Another comment\r\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("no trailing newline", {
|
|
files: {
|
|
"/entry.js": `// This is a comment\nconsole.log("hello");\n// No newline at end`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("non-ascii characters", {
|
|
files: {
|
|
"/entry.js": `// 你好,世界\n// Привет, мир\n// こんにちは世界\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("emoji", {
|
|
files: {
|
|
"/entry.js": `// 🚀 🔥 💯\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("invalid surrogate pair at beginning", {
|
|
files: {
|
|
"/entry.js": `// \uDC00 invalid surrogate\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("invalid surrogate pair at end", {
|
|
files: {
|
|
"/entry.js": `// invalid surrogate \uD800\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("invalid surrogate pair in middle", {
|
|
files: {
|
|
"/entry.js": `// invalid \uD800\uDC00\uD800 surrogate\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("multiple comments on same line", {
|
|
files: {
|
|
"/entry.js": `const x = 5; // first comment // second comment\nconsole.log(x);\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("console.log(x)");
|
|
},
|
|
});
|
|
|
|
itBundled("comment with ASI", {
|
|
files: {
|
|
"/entry.js": `const x = 5// first comment // second comment\nconsole.log(x)`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("console.log(x)");
|
|
},
|
|
});
|
|
|
|
itBundled("comment at end of file without newline", {
|
|
files: {
|
|
"/entry.js": `console.log("hello"); //`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("empty comments", {
|
|
files: {
|
|
"/entry.js": `//\n//\nconsole.log("hello");\n//`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("comments with special characters", {
|
|
files: {
|
|
"/entry.js": `// Comment with \\ backslash\n// Comment with \" quote\n// Comment with \t tab\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("comments with control characters", {
|
|
files: {
|
|
"/entry.js": `// Comment with \u0000 NULL\n// Comment with \u0001 SOH\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("comments with minification", {
|
|
files: {
|
|
"/entry.js": `// This should be removed\nconsole.log("hello");\n// This too`,
|
|
},
|
|
minifyWhitespace: true,
|
|
minifySyntax: true,
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toEqualIgnoringWhitespace('console.log("hello");');
|
|
},
|
|
});
|
|
|
|
for (const minify of [true, false]) {
|
|
itBundled(
|
|
`some code and an empty comment without newline preceding ${minify ? "with minification" : "without minification"}`,
|
|
{
|
|
files: {
|
|
"/entry.js": `console.log("hello");//`,
|
|
},
|
|
minifyWhitespace: minify,
|
|
minifySyntax: minify,
|
|
run: {
|
|
stdout: "hello",
|
|
},
|
|
},
|
|
);
|
|
itBundled(`some code and then only an empty comment ${minify ? "with minification" : "without minification"}`, {
|
|
files: {
|
|
"/entry.js": `console.log("hello");\n//`,
|
|
},
|
|
minifyWhitespace: minify,
|
|
minifySyntax: minify,
|
|
run: {
|
|
stdout: "hello",
|
|
},
|
|
});
|
|
itBundled(`only an empty comment ${minify ? "with minification" : "without minification"}`, {
|
|
files: {
|
|
"/entry.js": `//`,
|
|
},
|
|
minifyWhitespace: minify,
|
|
minifySyntax: minify,
|
|
run: {
|
|
stdout: "",
|
|
},
|
|
});
|
|
itBundled("only a comment", {
|
|
files: {
|
|
"/entry.js": `// This is a comment`,
|
|
},
|
|
minifyWhitespace: true,
|
|
minifySyntax: true,
|
|
run: {
|
|
stdout: "",
|
|
},
|
|
});
|
|
}
|
|
|
|
itBundled("trailing //# sourceMappingURL=", {
|
|
files: {
|
|
"/entry.js": `// This is a comment\nconsole.log("hello");\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZS5qcyIsInNvdXJjZSI6Ii8vZXhhbXBsZS5qcyJ9`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("trailing //# sourceMappingURL= with == at end", {
|
|
files: {
|
|
"/entry.js": `// This is a comment\nconsole.log("hello");\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZS5qcyIsInNvdXJjZSI6Ii8vZXhhbXBsZS5qcyJ9==`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("trailing //# sourceMappingURL= with = at end", {
|
|
files: {
|
|
"/entry.js": `// This is a comment\nconsole.log("hello");\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZS5qcyIsInNvdXJjZSI6Ii8vZXhhbXBsZS5qcyJ9=`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("leading //# sourceMappingURL= with = at end", {
|
|
files: {
|
|
"/entry.js": `//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZS5qcyIsInNvdXJjZSI6Ii8vZXhhbXBsZS5qcyJ9=\n// This is a comment\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("leading trailing newline //# sourceMappingURL= with = at end", {
|
|
files: {
|
|
"/entry.js": `//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZS5qcyIsInNvdXJjZSI6Ii8vZXhhbXBsZS5qcyJ9=\n// This is a comment\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("leading newline and sourcemap, trailing newline //# sourceMappingURL= with = at end", {
|
|
files: {
|
|
"/entry.js": `\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZS5qcyIsInNvdXJjZSI6Ii8vZXhhbXBsZS5qcyJ9=\n// This is a comment\nconsole.log("hello");\n`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment basic", {
|
|
files: {
|
|
"/entry.js": `//#__PURE__\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with spaces", {
|
|
files: {
|
|
"/entry.js": `// #__PURE__ \nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with text before", {
|
|
files: {
|
|
"/entry.js": `// some text #__PURE__\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with text after", {
|
|
files: {
|
|
"/entry.js": `// #__PURE__ some text\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with unicode characters", {
|
|
files: {
|
|
"/entry.js": `// 你好 #__PURE__ 世界\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with emoji", {
|
|
files: {
|
|
"/entry.js": `// 🚀 #__PURE__ 🔥\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with invalid surrogate pair", {
|
|
files: {
|
|
"/entry.js": `// \uD800 #__PURE__ \uDC00\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("multiple __PURE__ comments in single-line comments", {
|
|
files: {
|
|
"/entry.js": `//#__PURE__\nconsole.log("hello");\n//#__PURE__\nconsole.log("world");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
api.expectFile("/out.js").not.toContain("world");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with minification", {
|
|
files: {
|
|
"/entry.js": `//#__PURE__\nconsole.log("hello");`,
|
|
},
|
|
minifyWhitespace: true,
|
|
minifySyntax: true,
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment with windows newlines", {
|
|
files: {
|
|
"/entry.js": `//#__PURE__\r\nconsole.log("hello");`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").not.toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment at end of file", {
|
|
files: {
|
|
"/entry.js": `console.log("hello");\n//#__PURE__`,
|
|
},
|
|
onAfterBundle(api) {
|
|
api.expectFile("/out.js").toContain("hello");
|
|
},
|
|
});
|
|
|
|
itBundled("__PURE__ comment in single-line comment in middle of a statement", {
|
|
files: {
|
|
"/entry.js": `console.log(//#__PURE__\n123);`,
|
|
},
|
|
run: {
|
|
stdout: "123",
|
|
},
|
|
});
|
|
});
|
|
|
|
describe("multi-line comments", () => {
|
|
itBundled("comment with \\r\\n has sourcemap", {
|
|
files: {
|
|
"/entry.js": "/*!\r\n * Legal comment line 1\r\n * Legal comment line 2\r\n */\r\nexport const x = 1;",
|
|
},
|
|
sourceMap: "external",
|
|
onAfterBundle(api) {
|
|
const output = api.readFile("/out.js");
|
|
const sourcemapContent = api.readFile("/out.js.map");
|
|
const sourcemap = JSON.parse(sourcemapContent);
|
|
const sm = new SourceMap(sourcemap);
|
|
|
|
// Find the multi-line legal comment in the output
|
|
const outputLines = output.split("\n");
|
|
let commentLineIndex = -1;
|
|
for (let i = 0; i < outputLines.length; i++) {
|
|
if (outputLines[i].includes("Legal comment")) {
|
|
commentLineIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
expect(commentLineIndex).toBeGreaterThanOrEqual(0);
|
|
|
|
// The multi-line legal comment should have a sourcemap entry
|
|
const entry = sm.findEntry(commentLineIndex, 0);
|
|
|
|
// Verify we found a mapping for the comment
|
|
expect(entry).toBeTruthy();
|
|
expect(Object.keys(entry).length).toBeGreaterThan(0);
|
|
|
|
// The mapping should point back to the original source
|
|
expect(entry!.originalSource!).toContain("entry.js");
|
|
expect(typeof entry.originalLine).toBe("number");
|
|
expect(entry.originalLine).toBeGreaterThanOrEqual(0);
|
|
},
|
|
});
|
|
});
|