From f93ad2a8f0f5f048bfbd2bad9ba3237d30e323af Mon Sep 17 00:00:00 2001 From: Nic <162764842+Nic-Polumeyv@users.noreply.github.com> Date: Fri, 17 Jul 2026 20:45:48 -0400 Subject: [PATCH 1/2] Allow skipping only same-file duplicates with skipDuplicates: "same-file", fixes #601 --- README.md | 7 ++++++- lib/parse-styles.js | 5 +++-- test/fixtures/imports/same-file-dedup/a/shared.css | 1 + test/fixtures/imports/same-file-dedup/b/shared.css | 1 + test/fixtures/same-file-dedup.css | 3 +++ test/fixtures/same-file-dedup.expected.css | 2 ++ test/import.js | 9 +++++++++ 7 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/imports/same-file-dedup/a/shared.css create mode 100644 test/fixtures/imports/same-file-dedup/b/shared.css create mode 100644 test/fixtures/same-file-dedup.css create mode 100644 test/fixtures/same-file-dedup.expected.css diff --git a/README.md b/README.md index 9c96cf2..0bf2617 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ promised content. #### `skipDuplicates` -Type: `Boolean` +Type: `Boolean` or `"same-file"` Default: `true` By default, similar files (based on the same content) are being skipped. @@ -188,6 +188,11 @@ It's to optimize output and skip similar files like `normalize.css` for example. If this behavior is not what you want, just set this option to `false` to disable it. +Setting this option to `"same-file"` skips repeated imports of the same file, +but not different files sharing the same content. Use it when imported files +contain at-rules whose paths resolve relative to the file's own location, so +that byte-identical files at different paths are not actually duplicates. + #### `addModulesDirectories` Type: `Array` diff --git a/lib/parse-styles.js b/lib/parse-styles.js index 766de9c..ec0e44b 100644 --- a/lib/parse-styles.js +++ b/lib/parse-styles.js @@ -178,8 +178,9 @@ async function loadImportContent( } // skip previous imported files not containing @import rules + // (content dedup does not apply in "same-file" mode) if ( - options.skipDuplicates && + options.skipDuplicates === true && state.hashFiles[content]?.[stmtDuplicateCheckKey] ) { return @@ -196,7 +197,7 @@ async function loadImportContent( const styles = importedResult.root result.messages = result.messages.concat(importedResult.messages) - if (options.skipDuplicates) { + if (options.skipDuplicates === true) { const hasImport = styles.some(child => { return child.type === "atrule" && child.name === "import" }) diff --git a/test/fixtures/imports/same-file-dedup/a/shared.css b/test/fixtures/imports/same-file-dedup/a/shared.css new file mode 100644 index 0000000..6abac48 --- /dev/null +++ b/test/fixtures/imports/same-file-dedup/a/shared.css @@ -0,0 +1 @@ +shared {} diff --git a/test/fixtures/imports/same-file-dedup/b/shared.css b/test/fixtures/imports/same-file-dedup/b/shared.css new file mode 100644 index 0000000..6abac48 --- /dev/null +++ b/test/fixtures/imports/same-file-dedup/b/shared.css @@ -0,0 +1 @@ +shared {} diff --git a/test/fixtures/same-file-dedup.css b/test/fixtures/same-file-dedup.css new file mode 100644 index 0000000..ef02919 --- /dev/null +++ b/test/fixtures/same-file-dedup.css @@ -0,0 +1,3 @@ +@import "same-file-dedup/a/shared.css"; +@import "same-file-dedup/b/shared.css"; +@import "same-file-dedup/a/shared.css"; diff --git a/test/fixtures/same-file-dedup.expected.css b/test/fixtures/same-file-dedup.expected.css new file mode 100644 index 0000000..572363e --- /dev/null +++ b/test/fixtures/same-file-dedup.expected.css @@ -0,0 +1,2 @@ +shared {} +shared {} diff --git a/test/import.js b/test/import.js index e2a2bd1..6fc22c5 100644 --- a/test/import.js +++ b/test/import.js @@ -46,6 +46,15 @@ test( test("should import stylesheets with same content", checkFixture, "same") +test( + "should only skip same-file duplicates with skipDuplicates: same-file", + checkFixture, + "same-file-dedup", + { + skipDuplicates: "same-file", + }, +) + test("should ignore & adjust external import", checkFixture, "ignore") test("should not fail with only one absolute import", t => { From 9d48439a6df3d206c7900a57d2dcb9747a03a9aa Mon Sep 17 00:00:00 2001 From: Nic <162764842+Nic-Polumeyv@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:39:58 -0400 Subject: [PATCH 2/2] Make same-file duplicate skipping the default, move content dedup behind skipDuplicates: "content" --- README.md | 18 +++++++++--------- lib/parse-styles.js | 5 ++--- test/fixtures/content-dedup.css | 3 +++ test/fixtures/content-dedup.expected.css | 1 + test/import.js | 10 ++++++++-- 5 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 test/fixtures/content-dedup.css create mode 100644 test/fixtures/content-dedup.expected.css diff --git a/README.md b/README.md index 0bf2617..92f91c1 100644 --- a/README.md +++ b/README.md @@ -180,18 +180,18 @@ promised content. #### `skipDuplicates` -Type: `Boolean` or `"same-file"` +Type: `Boolean` or `"content"` Default: `true` -By default, similar files (based on the same content) are being skipped. -It's to optimize output and skip similar files like `normalize.css` for example. -If this behavior is not what you want, just set this option to `false` to -disable it. +By default, repeated imports of the same file are being skipped. -Setting this option to `"same-file"` skips repeated imports of the same file, -but not different files sharing the same content. Use it when imported files -contain at-rules whose paths resolve relative to the file's own location, so -that byte-identical files at different paths are not actually duplicates. +Set this option to `"content"` to skip similar files based on their content +instead, so that copies of the same stylesheet (`normalize.css` for example) +imported from different paths are also skipped. Files can contain at-rules +whose paths resolve relative to the file's own location, which is why this +is not the default. + +If you don't want any duplicates to be skipped, set this option to `false`. #### `addModulesDirectories` diff --git a/lib/parse-styles.js b/lib/parse-styles.js index ec0e44b..d1896dc 100644 --- a/lib/parse-styles.js +++ b/lib/parse-styles.js @@ -178,9 +178,8 @@ async function loadImportContent( } // skip previous imported files not containing @import rules - // (content dedup does not apply in "same-file" mode) if ( - options.skipDuplicates === true && + options.skipDuplicates === "content" && state.hashFiles[content]?.[stmtDuplicateCheckKey] ) { return @@ -197,7 +196,7 @@ async function loadImportContent( const styles = importedResult.root result.messages = result.messages.concat(importedResult.messages) - if (options.skipDuplicates === true) { + if (options.skipDuplicates === "content") { const hasImport = styles.some(child => { return child.type === "atrule" && child.name === "import" }) diff --git a/test/fixtures/content-dedup.css b/test/fixtures/content-dedup.css new file mode 100644 index 0000000..ef02919 --- /dev/null +++ b/test/fixtures/content-dedup.css @@ -0,0 +1,3 @@ +@import "same-file-dedup/a/shared.css"; +@import "same-file-dedup/b/shared.css"; +@import "same-file-dedup/a/shared.css"; diff --git a/test/fixtures/content-dedup.expected.css b/test/fixtures/content-dedup.expected.css new file mode 100644 index 0000000..6abac48 --- /dev/null +++ b/test/fixtures/content-dedup.expected.css @@ -0,0 +1 @@ +shared {} diff --git a/test/import.js b/test/import.js index 6fc22c5..a099db7 100644 --- a/test/import.js +++ b/test/import.js @@ -47,11 +47,17 @@ test( test("should import stylesheets with same content", checkFixture, "same") test( - "should only skip same-file duplicates with skipDuplicates: same-file", + "should not skip different files with the same content by default", checkFixture, "same-file-dedup", +) + +test( + "should skip files with the same content with skipDuplicates: content", + checkFixture, + "content-dedup", { - skipDuplicates: "same-file", + skipDuplicates: "content", }, )