diff --git a/examples/css/out/entry.css b/examples/css/out/entry.css index ece363e..da82e12 100644 --- a/examples/css/out/entry.css +++ b/examples/css/out/entry.css @@ -3,7 +3,7 @@ body { color: white; } -/* postcss:reset.css */ +/* postcss:./reset.css */ body { color: black; background: white; diff --git a/postcss.test.ts b/postcss.test.ts index e25b0bf..59b1401 100644 --- a/postcss.test.ts +++ b/postcss.test.ts @@ -99,30 +99,9 @@ describe("css", () => { describe("modules", () => { const rootDir = path.resolve("./examples/modules"); - it("modules set to false", async () => { - const result = await build("modules", ["./disabled.css"], { - plugins: [postCSSPlugin({ modules: false })], - }); - assertObjectMatch(result, { - errors: [], - warnings: [], - }); - assertGreaterOrEqual(result.outputFiles.length, 1); - const outFilePath = path.resolve(rootDir, "out/disabled.css"); - assertEquals( - result.outputFiles[0].path, - outFilePath, - ); - assertEquals( - result.outputFiles[0].text, - await Deno.readTextFile(outFilePath), - ); - assertEquals(result.outputFiles.length, 1); - }); - it("modules defaults to true", async (t) => { const result = await build("modules", ["./main.module.css"], { - plugins: [postCSSPlugin({ modules: true })], + plugins: [postCSSPlugin()], }); assertObjectMatch(result, { errors: [], diff --git a/postcss.ts b/postcss.ts index 75853f3..eee1d5f 100644 --- a/postcss.ts +++ b/postcss.ts @@ -253,11 +253,19 @@ export const postCSSPlugin = ( } const absolutePath = path.resolve(args.resolveDir, args.path); + const relativePath = path.relative( + build.initialOptions.absWorkingDir ?? Deno.cwd(), + absolutePath, + ); + const stylesheetId = Deno.build.os === "windows" + ? relativePath.replaceAll("\\", "/") + : relativePath; const ext = path.extname(absolutePath); const sourceBaseName = path.basename(absolutePath, ext); - const module = isModule - ? isModule(absolutePath) - : sourceBaseName.match(/\.module$/); + const module = modules !== false && + (isModule + ? isModule(absolutePath) + : /\.module$/.test(sourceBaseName)); const fileContent = await Deno.readTextFile(absolutePath); let css = ext === ".css" ? fileContent : ""; @@ -287,7 +295,7 @@ export const postCSSPlugin = ( return { namespace: module ? "postcss-module" : "postcss", - path: args.path, + path: `./${stylesheetId}`, watchFiles, pluginData: { resolveDir: args.resolveDir, diff --git a/resolution.test.ts b/resolution.test.ts new file mode 100644 index 0000000..174c5e4 --- /dev/null +++ b/resolution.test.ts @@ -0,0 +1,91 @@ +import { + assertEquals, + assertNotEquals, + assertRejects, + assertStringIncludes, +} from "@std/assert"; +import * as path from "@std/path"; +import { describe, it } from "@std/testing/bdd"; + +import { postCSSPlugin } from "./postcss.ts"; +import { build } from "./test-utils.ts"; + +describe("stylesheet identity and module selection", () => { + for (const filename of ["styles.css", "styles.module.css"]) { + it(`keeps both ${filename} imports when they belong to different directories`, async () => { + const root = await Deno.makeTempDir({ prefix: "postcss identity " }); + try { + for ( + const [directory, color] of [["first", "red"], ["second", "blue"]] + ) { + await Deno.mkdir(path.join(root, directory)); + await Deno.writeTextFile( + path.join(root, directory, filename), + `.box { color: ${color}; }`, + ); + await Deno.writeTextFile( + path.join(root, directory, "index.ts"), + `export * from "./${filename}";`, + ); + } + await Deno.writeTextFile( + path.join(root, "main.ts"), + ` + import * as first from "./first/index.ts"; + import * as second from "./second/index.ts"; + export const styles = [first.css, second.css]; + ${ + filename.includes(".module") + ? "export const classes = [first.box, second.box];" + : "" + } + `, + ); + const result = await build("", ["main.ts"], { + absWorkingDir: root, + bundle: true, + plugins: [postCSSPlugin()], + }); + const output = await import( + `data:application/javascript,${ + encodeURIComponent(result.outputFiles[0].text) + }` + ); + assertStringIncludes(output.styles[0], "red"); + assertStringIncludes(output.styles[1], "blue"); + if (filename.includes(".module")) { + assertNotEquals(output.classes[0], output.classes[1]); + } + } finally { + await Deno.remove(root, { recursive: true }); + } + }); + } + + for (const customSelector of [false, true]) { + it(`disables module rewriting and JSON output with ${customSelector ? "a custom selector" : "the filename convention"}`, async () => { + const root = await Deno.makeTempDir({ prefix: "postcss disabled " }); + try { + const file = path.join(root, "styles.module.css"); + await Deno.writeTextFile(file, ".original { color: red; }"); + const result = await build("", [file], { + absWorkingDir: root, + plugins: [ + postCSSPlugin({ + modules: false, + ...(customSelector ? { isModule: () => true } : {}), + }), + ], + }); + assertStringIncludes(result.outputFiles[0].text, ".original {"); + assertEquals(result.errors, []); + await assertRejects( + () => Deno.stat(`${file}.json`), + Deno.errors.NotFound, + ); + } finally { + await Deno.remove(root, { recursive: true }); + } + }); + } +});