Files
bun.sh/test/bundler/esbuild/css.test.ts
2024-06-27 14:56:07 -07:00

528 lines
19 KiB
TypeScript

import { itBundled } from "../expectBundled";
import { describe, expect } from "bun:test";
// Tests ported from:
// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_css_test.go
// For debug, all files are written to $TEMP/bun-bundle-tests/css
describe.todo("bundler", () => {
itBundled("css/CSSEntryPoint", {
// GENERATED
files: {
"/entry.css": /* css */ `
body {
background: white;
color: black }
`,
},
});
itBundled("css/CSSAtImportMissing", {
files: {
"/entry.css": `@import "./missing.css";`,
},
bundleErrors: {
"/entry.css": ['Could not resolve "./missing.css"'],
},
});
itBundled("css/CSSAtImportExternal", {
// GENERATED
files: {
"/entry.css": /* css */ `
@import "./internal.css";
@import "./external1.css";
@import "./external2.css";
@import "./charset1.css";
@import "./charset2.css";
@import "./external5.css" screen;
`,
"/internal.css": /* css */ `
@import "./external5.css" print;
.before { color: red }
`,
"/charset1.css": /* css */ `
@charset "UTF-8";
@import "./external3.css";
@import "./external4.css";
@import "./external5.css";
@import "https://www.example.com/style1.css";
@import "https://www.example.com/style2.css";
@import "https://www.example.com/style3.css" print;
.middle { color: green }
`,
"/charset2.css": /* css */ `
@charset "UTF-8";
@import "./external3.css";
@import "./external5.css" screen;
@import "https://www.example.com/style1.css";
@import "https://www.example.com/style3.css";
.after { color: blue }
`,
},
});
itBundled("css/CSSAtImport", {
// GENERATED
files: {
"/entry.css": /* css */ `
@import "./a.css";
@import "./b.css";
.entry { color: red }
`,
"/a.css": /* css */ `
@import "./shared.css";
.a { color: green }
`,
"/b.css": /* css */ `
@import "./shared.css";
.b { color: blue }
`,
"/shared.css": `.shared { color: black }`,
},
});
itBundled("css/CSSFromJSMissingImport", {
// GENERATED
files: {
"/entry.js": /* js */ `
import {missing} from "./a.css"
console.log(missing)
`,
"/a.css": `.a { color: red }`,
},
/* TODO FIX expectedCompileLog: `entry.js: ERROR: No matching export in "a.css" for import "missing"
`, */
});
itBundled("css/CSSFromJSMissingStarImport", {
// GENERATED
files: {
"/entry.js": /* js */ `
import * as ns from "./a.css"
console.log(ns.missing)
`,
"/a.css": `.a { color: red }`,
},
});
itBundled("css/ImportCSSFromJS", {
// GENERATED
files: {
"/entry.js": /* js */ `
import "./a.js"
import "./b.js"
`,
"/a.js": /* js */ `
import "./a.css";
console.log('a')
`,
"/a.css": `.a { color: red }`,
"/b.js": /* js */ `
import "./b.css";
console.log('b')
`,
"/b.css": `.b { color: blue }`,
},
});
itBundled("css/ImportCSSFromJSWriteToStdout", {
// GENERATED
files: {
"/entry.js": `import "./entry.css"`,
"/entry.css": `.entry { color: red }`,
},
/* TODO FIX expectedScanLog: `entry.js: ERROR: Cannot import "entry.css" into a JavaScript file without an output path configured
`, */
});
itBundled("css/ImportJSFromCSS", {
// GENERATED
files: {
"/entry.js": `export default 123`,
"/entry.css": `@import "./entry.js";`,
},
entryPoints: ["/entry.css"],
/* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.js" into a CSS file
NOTE: An "@import" rule can only be used to import another CSS file, and "entry.js" is not a CSS file (it was loaded with the "js" loader).
`, */
});
itBundled("css/ImportJSONFromCSS", {
// GENERATED
files: {
"/entry.json": `{}`,
"/entry.css": `@import "./entry.json";`,
},
entryPoints: ["/entry.css"],
/* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.json" into a CSS file
NOTE: An "@import" rule can only be used to import another CSS file, and "entry.json" is not a CSS file (it was loaded with the "json" loader).
`, */
});
itBundled("css/MissingImportURLInCSS", {
// GENERATED
files: {
"/src/entry.css": /* css */ `
a { background: url(./one.png); }
b { background: url("./two.png"); }
`,
},
/* TODO FIX expectedScanLog: `src/entry.css: ERROR: Could not resolve "./one.png"
src/entry.css: ERROR: Could not resolve "./two.png"
`, */
});
itBundled("css/ExternalImportURLInCSS", {
// GENERATED
files: {
"/src/entry.css": /* css */ `
div:after {
content: 'If this is recognized, the path should become "../src/external.png"';
background: url(./external.png);
}
/* These URLs should be external automatically */
a { background: url(http://example.com/images/image.png) }
b { background: url(https://example.com/images/image.png) }
c { background: url(//example.com/images/image.png) }
d { background: url(data:image/png;base64,iVBORw0KGgo=) }
path { fill: url(#filter) }
`,
},
});
itBundled("css/InvalidImportURLInCSS", {
// GENERATED
files: {
"/entry.css": /* css */ `
a {
background: url(./js.js);
background: url("./jsx.jsx");
background: url(./ts.ts);
background: url('./tsx.tsx');
background: url(./json.json);
background: url(./css.css);
}
`,
"/js.js": `export default 123`,
"/jsx.jsx": `export default 123`,
"/ts.ts": `export default 123`,
"/tsx.tsx": `export default 123`,
"/json.json": `{ "test": true }`,
"/css.css": `a { color: red }`,
},
/* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot use "js.js" as a URL
NOTE: You can't use a "url()" token to reference the file "js.js" because it was loaded with the "js" loader, which doesn't provide a URL to embed in the resulting CSS.
entry.css: ERROR: Cannot use "jsx.jsx" as a URL
NOTE: You can't use a "url()" token to reference the file "jsx.jsx" because it was loaded with the "jsx" loader, which doesn't provide a URL to embed in the resulting CSS.
entry.css: ERROR: Cannot use "ts.ts" as a URL
NOTE: You can't use a "url()" token to reference the file "ts.ts" because it was loaded with the "ts" loader, which doesn't provide a URL to embed in the resulting CSS.
entry.css: ERROR: Cannot use "tsx.tsx" as a URL
NOTE: You can't use a "url()" token to reference the file "tsx.tsx" because it was loaded with the "tsx" loader, which doesn't provide a URL to embed in the resulting CSS.
entry.css: ERROR: Cannot use "json.json" as a URL
NOTE: You can't use a "url()" token to reference the file "json.json" because it was loaded with the "json" loader, which doesn't provide a URL to embed in the resulting CSS.
entry.css: ERROR: Cannot use "css.css" as a URL
NOTE: You can't use a "url()" token to reference a CSS file, and "css.css" is a CSS file (it was loaded with the "css" loader).
`, */
});
itBundled("css/TextImportURLInCSSText", {
// GENERATED
files: {
"/entry.css": /* css */ `
a {
background: url(./example.txt);
}
`,
"/example.txt": `This is some text.`,
},
});
itBundled("css/DataURLImportURLInCSS", {
// GENERATED
files: {
"/entry.css": /* css */ `
a {
background: url(./example.png);
}
`,
"/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`,
},
});
itBundled("css/BinaryImportURLInCSS", {
// GENERATED
files: {
"/entry.css": /* css */ `
a {
background: url(./example.png);
}
`,
"/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`,
},
});
itBundled("css/Base64ImportURLInCSS", {
// GENERATED
files: {
"/entry.css": /* css */ `
a {
background: url(./example.png);
}
`,
"/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`,
},
});
itBundled("css/FileImportURLInCSS", {
// GENERATED
files: {
"/entry.css": /* css */ `
@import "./one.css";
@import "./two.css";
`,
"/one.css": `a { background: url(./example.data) }`,
"/two.css": `b { background: url(./example.data) }`,
"/example.data": `This is some data.`,
},
});
itBundled("css/IgnoreURLsInAtRulePrelude", {
// GENERATED
files: {
"/entry.css": /* css */ `
/* This should not generate a path resolution error */
@supports (background: url(ignored.png)) {
a { color: red }
}
`,
},
});
itBundled("css/PackageURLsInCSS", {
// GENERATED
files: {
"/entry.css": /* css */ `
@import "test.css";
a { background: url(a/1.png); }
b { background: url(b/2.png); }
c { background: url(c/3.png); }
`,
"/test.css": `.css { color: red }`,
"/a/1.png": `a-1`,
"/node_modules/b/2.png": `b-2-node_modules`,
"/c/3.png": `c-3`,
"/node_modules/c/3.png": `c-3-node_modules`,
},
});
itBundled("css/CSSAtImportExtensionOrderCollision", {
// GENERATED
files: {
"/entry.css": `@import "./test";`,
"/test.js": `console.log('js')`,
"/test.css": `.css { color: red }`,
},
outfile: "/out.css",
extensionOrder: [".js", ".css"],
});
itBundled("css/CSSAtImportExtensionOrderCollisionUnsupported", {
// GENERATED
files: {
"/entry.css": `@import "./test";`,
"/test.js": `console.log('js')`,
"/test.sass": `// some code`,
},
outfile: "/out.css",
extensionOrder: [".js", ".sass"],
bundleErrors: {
"/entry.css": ['ERROR: No loader is configured for ".sass" files: test.sass'],
},
});
itBundled("css/CSSAtImportConditionsNoBundle", {
// GENERATED
files: {
"/entry.css": `@import "./print.css" print;`,
},
mode: "passthrough",
});
itBundled("css/CSSAtImportConditionsBundleExternal", {
// GENERATED
files: {
"/entry.css": `@import "https://example.com/print.css" print;`,
},
});
itBundled("css/CSSAtImportConditionsBundleExternalConditionWithURL", {
// GENERATED
files: {
"/entry.css": `@import "https://example.com/foo.css" (foo: url("foo.png")) and (bar: url("bar.png"));`,
},
});
itBundled("css/CSSAtImportConditionsBundle", {
// GENERATED
files: {
"/entry.css": `@import "./print.css" print;`,
"/print.css": `body { color: red }`,
},
/* TODO FIX expectedScanLog: `entry.css: ERROR: Bundling with conditional "@import" rules is not currently supported
`, */
});
itBundled("css/CSSAndJavaScriptCodeSplittingESBuildIssue1064", {
// GENERATED
files: {
"/a.js": /* js */ `
import shared from './shared.js'
console.log(shared() + 1)
`,
"/b.js": /* js */ `
import shared from './shared.js'
console.log(shared() + 2)
`,
"/c.css": /* css */ `
@import "./shared.css";
body { color: red }
`,
"/d.css": /* css */ `
@import "./shared.css";
body { color: blue }
`,
"/shared.js": `export default function() { return 3 }`,
"/shared.css": `body { background: black }`,
},
entryPoints: ["/a.js", "/b.js", "/c.css", "/d.css"],
format: "esm",
splitting: true,
});
itBundled("css/CSSExternalQueryAndHashNoMatchESBuildIssue1822", {
// GENERATED
files: {
"/entry.css": /* css */ `
a { background: url(foo/bar.png?baz) }
b { background: url(foo/bar.png#baz) }
`,
},
outfile: "/out.css",
/* TODO FIX expectedScanLog: `entry.css: ERROR: Could not resolve "foo/bar.png?baz"
NOTE: You can mark the path "foo/bar.png?baz" as external to exclude it from the bundle, which will remove this error.
entry.css: ERROR: Could not resolve "foo/bar.png#baz"
NOTE: You can mark the path "foo/bar.png#baz" as external to exclude it from the bundle, which will remove this error.
`, */
});
itBundled("css/CSSExternalQueryAndHashMatchESBuildIssue1822", {
// GENERATED
files: {
"/entry.css": /* css */ `
a { background: url(foo/bar.png?baz) }
b { background: url(foo/bar.png#baz) }
`,
},
outfile: "/out.css",
});
itBundled("css/CSSNestingOldBrowser", {
// GENERATED
files: {
"/nested-@layer.css": `a { @layer base { color: red; } }`,
"/nested-@media.css": `a { @media screen { color: red; } }`,
"/nested-ampersand-twice.css": `a { &, & { color: red; } }`,
"/nested-ampersand-first.css": `a { &, b { color: red; } }`,
"/nested-attribute.css": `a { [href] { color: red; } }`,
"/nested-colon.css": `a { :hover { color: red; } }`,
"/nested-dot.css": `a { .cls { color: red; } }`,
"/nested-greaterthan.css": `a { > b { color: red; } }`,
"/nested-hash.css": `a { #id { color: red; } }`,
"/nested-plus.css": `a { + b { color: red; } }`,
"/nested-tilde.css": `a { ~ b { color: red; } }`,
"/toplevel-ampersand-twice.css": `&, & { color: red; }`,
"/toplevel-ampersand-first.css": `&, a { color: red; }`,
"/toplevel-ampersand-second.css": `a, & { color: red; }`,
"/toplevel-attribute.css": `[href] { color: red; }`,
"/toplevel-colon.css": `:hover { color: red; }`,
"/toplevel-dot.css": `.cls { color: red; }`,
"/toplevel-greaterthan.css": `> b { color: red; }`,
"/toplevel-hash.css": `#id { color: red; }`,
"/toplevel-plus.css": `+ b { color: red; }`,
"/toplevel-tilde.css": `~ b { color: red; }`,
},
entryPoints: [
"/nested-@layer.css",
"/nested-@media.css",
"/nested-ampersand-twice.css",
"/nested-ampersand-first.css",
"/nested-attribute.css",
"/nested-colon.css",
"/nested-dot.css",
"/nested-greaterthan.css",
"/nested-hash.css",
"/nested-plus.css",
"/nested-tilde.css",
"/toplevel-ampersand-twice.css",
"/toplevel-ampersand-first.css",
"/toplevel-ampersand-second.css",
"/toplevel-attribute.css",
"/toplevel-colon.css",
"/toplevel-dot.css",
"/toplevel-greaterthan.css",
"/toplevel-hash.css",
"/toplevel-plus.css",
"/toplevel-tilde.css",
],
unsupportedCSSFeatures: "Nesting",
/* TODO FIX expectedScanLog: `nested-@layer.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-@media.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-attribute.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-colon.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-dot.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-hash.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
nested-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
toplevel-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
toplevel-ampersand-second.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
toplevel-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
toplevel-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
toplevel-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
`, */
});
itBundled("css/MetafileCSSBundleTwoToOne", {
files: {
"/foo/entry.js": /* js */ `
import '../common.css'
console.log('foo')
`,
"/bar/entry.js": /* js */ `
import '../common.css'
console.log('bar')
`,
"/common.css": `body { color: red }`,
},
metafile: true,
entryPoints: ["/foo/entry.js", "/bar/entry.js"],
entryNaming: "[ext]/[hash]",
outdir: "/",
});
itBundled("css/DeduplicateRules", {
// GENERATED
files: {
"/yes0.css": `a { color: red; color: green; color: red }`,
"/yes1.css": `a { color: red } a { color: green } a { color: red }`,
"/yes2.css": `@media screen { a { color: red } } @media screen { a { color: red } }`,
"/no0.css": `@media screen { a { color: red } } @media screen { & a { color: red } }`,
"/no1.css": `@media screen { a { color: red } } @media screen { a[x] { color: red } }`,
"/no2.css": `@media screen { a { color: red } } @media screen { a.x { color: red } }`,
"/no3.css": `@media screen { a { color: red } } @media screen { a#x { color: red } }`,
"/no4.css": `@media screen { a { color: red } } @media screen { a:x { color: red } }`,
"/no5.css": `@media screen { a:x { color: red } } @media screen { a:x(y) { color: red } }`,
"/no6.css": `@media screen { a b { color: red } } @media screen { a + b { color: red } }`,
"/across-files.css": `@import 'across-files-0.css'; @import 'across-files-1.css'; @import 'across-files-2.css';`,
"/across-files-0.css": `a { color: red; color: red }`,
"/across-files-1.css": `a { color: green }`,
"/across-files-2.css": `a { color: red }`,
"/across-files-url.css": `@import 'across-files-url-0.css'; @import 'across-files-url-1.css'; @import 'across-files-url-2.css';`,
"/across-files-url-0.css": `@import 'http://example.com/some.css'; @font-face { src: url(http://example.com/some.font); }`,
"/across-files-url-1.css": `@font-face { src: url(http://example.com/some.other.font); }`,
"/across-files-url-2.css": `@font-face { src: url(http://example.com/some.font); }`,
},
entryPoints: [
"/yes0.css",
"/yes1.css",
"/yes2.css",
"/no0.css",
"/no1.css",
"/no2.css",
"/no3.css",
"/no4.css",
"/no5.css",
"/no6.css",
"/across-files.css",
"/across-files-url.css",
],
});
});