From 68d27f1c4f64b070f322229e1f51460d1bf934b3 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:52:11 -0700 Subject: [PATCH] chore: generate CSS hash using the filename (#16740) * chore: generate CSS hash using the filename * fix all tests but one * slightly kludgy fix * try this * fix --------- Co-authored-by: Rich Harris --- .changeset/fast-boxes-sort.md | 5 ++++ .../src/compiler/phases/2-analyze/index.js | 20 +++++++++------- packages/svelte/src/compiler/types/index.d.ts | 2 +- .../svelte/src/compiler/validate-options.js | 4 ++-- packages/svelte/tests/html_equal.js | 8 ++++++- .../svelte/tests/runtime-browser/assert.js | 1 + .../_config.js | 24 +++++++++---------- .../_config.js | 20 ++++++++-------- .../_config.js | 24 +++++++++---------- .../_config.js | 20 ++++++++-------- .../custom-element-svelte-class/_config.js | 4 ++-- .../_config.js | 6 ++--- .../_expected.html | 2 +- .../_expected_head.html | 2 +- .../css-injected-options-nested/_expected.css | 2 +- .../_expected.html | 2 +- .../_expected_head.html | 2 +- .../css-injected-options/_expected.css | 2 +- .../css-injected-options/_expected.html | 2 +- .../css-injected-options/_expected_head.html | 2 +- .../samples/css/_expected.css | 2 +- .../samples/css/_expected.html | 2 +- .../samples/css/_expected_head.html | 2 +- .../samples/attached-sourcemap/_config.js | 2 +- .../tests/sourcemaps/samples/css/_config.js | 2 +- packages/svelte/types/index.d.ts | 4 ++-- 26 files changed, 91 insertions(+), 77 deletions(-) create mode 100644 .changeset/fast-boxes-sort.md diff --git a/.changeset/fast-boxes-sort.md b/.changeset/fast-boxes-sort.md new file mode 100644 index 0000000000..1edabf0582 --- /dev/null +++ b/.changeset/fast-boxes-sort.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: generate CSS hash using the filename diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 92b89c588e..de27c4623b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -456,10 +456,19 @@ export function analyze_component(root, source, options) { const is_custom_element = !!options.customElementOptions || options.customElement; + const name = module.scope.generate(options.name ?? component_name); + + state.adjust({ + component_name: name, + dev: options.dev, + rootDir: options.rootDir, + runes + }); + // TODO remove all the ?? stuff, we don't need it now that we're validating the config /** @type {ComponentAnalysis} */ const analysis = { - name: module.scope.generate(options.name ?? component_name), + name, root: scope_root, module, instance, @@ -520,7 +529,7 @@ export function analyze_component(root, source, options) { hash: root.css ? options.cssHash({ css: root.css.content.styles, - filename: options.filename, + filename: state.filename, name: component_name, hash }) @@ -534,13 +543,6 @@ export function analyze_component(root, source, options) { async_deriveds: new Set() }; - state.adjust({ - component_name: analysis.name, - dev: options.dev, - rootDir: options.rootDir, - runes - }); - if (!runes) { // every exported `let` or `var` declaration becomes a prop, everything else becomes an export for (const node of instance.ast.body) { diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index 6211e69bd3..e13c9a9e22 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -105,7 +105,7 @@ export interface CompileOptions extends ModuleCompileOptions { css?: 'injected' | 'external'; /** * A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. - * It defaults to returning `svelte-${hash(css)}`. + * It defaults to returning `svelte-${hash(filename ?? css)}`. * * @default undefined */ diff --git a/packages/svelte/src/compiler/validate-options.js b/packages/svelte/src/compiler/validate-options.js index 2b727ad093..a94a553311 100644 --- a/packages/svelte/src/compiler/validate-options.js +++ b/packages/svelte/src/compiler/validate-options.js @@ -70,8 +70,8 @@ const component_options = { return input; }), - cssHash: fun(({ css, hash }) => { - return `svelte-${hash(css)}`; + cssHash: fun(({ css, filename, hash }) => { + return `svelte-${hash(filename === '(unknown)' ? css : filename ?? css)}`; }), // TODO this is a sourcemap option, would be good to put under a sourcemap namespace diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js index b637e4d538..76a4a957a5 100644 --- a/packages/svelte/tests/html_equal.js +++ b/packages/svelte/tests/html_equal.js @@ -32,7 +32,13 @@ function clean_children(node, opts) { return; } - node.setAttribute(attr.name, attr.value); + let value = attr.value; + + if (attr.name === 'class') { + value = value.replace(/svelte-\w+/, 'svelte-xyz123'); + } + + node.setAttribute(attr.name, value); }); for (let child of [...node.childNodes]) { diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js index 9a294a48c7..e331c8b677 100644 --- a/packages/svelte/tests/runtime-browser/assert.js +++ b/packages/svelte/tests/runtime-browser/assert.js @@ -74,6 +74,7 @@ function normalize_html(window, html) { node.innerHTML = html .replace(//g, '') .replace(/>[\s\r\n]+<') + .replace(/svelte-\w+/g, 'svelte-xyz123') .trim(); normalize_children(node); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js index cbd0456e13..1fc15c7eda 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js @@ -1,43 +1,43 @@ import { ok, test } from '../../test'; export default test({ - html: '
', + html: '
', test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); component.testName = null; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined + ''; - assert.equal(div.className, 'undefined svelte-x1o6ra'); + assert.equal(div.className, 'undefined svelte-70s021'); component.testName = null + ''; - assert.equal(div.className, 'null svelte-x1o6ra'); + assert.equal(div.className, 'null svelte-70s021'); component.testName = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName = false; - assert.equal(div.className, 'false svelte-x1o6ra'); + assert.equal(div.className, 'false svelte-70s021'); component.testName = true; - assert.equal(div.className, 'true svelte-x1o6ra'); + assert.equal(div.className, 'true svelte-70s021'); component.testName = {}; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = ''; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = 'testClassName'; - assert.equal(div.className, 'testClassName svelte-x1o6ra'); + assert.equal(div.className, 'testClassName svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js index d2511a4403..2f4b6242cb 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js @@ -10,43 +10,43 @@ export default test({ }; }, - html: '
', + html: '
', test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); - assert.equal(div.className, 'test1test2 svelte-x1o6ra'); + assert.equal(div.className, 'test1test2 svelte-70s021'); component.testName1 = null; component.testName2 = null; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = null; component.testName2 = 'test'; - assert.equal(div.className, 'nulltest svelte-x1o6ra'); + assert.equal(div.className, 'nulltest svelte-70s021'); component.testName1 = undefined; component.testName2 = 'test'; - assert.equal(div.className, 'undefinedtest svelte-x1o6ra'); + assert.equal(div.className, 'undefinedtest svelte-70s021'); component.testName1 = undefined; component.testName2 = undefined; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName1 = undefined; component.testName2 = 1; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = undefined; component.testName2 = 0; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js index 081fceecf2..b06103992f 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js @@ -8,41 +8,41 @@ export default test({ }; }, - html: '
', + html: '
', test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); - assert.equal(div.className, 'testClassName svelte-x1o6ra'); + assert.equal(div.className, 'testClassName svelte-70s021'); component.testName = null; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined + ''; - assert.equal(div.className, 'undefined svelte-x1o6ra'); + assert.equal(div.className, 'undefined svelte-70s021'); component.testName = null + ''; - assert.equal(div.className, 'null svelte-x1o6ra'); + assert.equal(div.className, 'null svelte-70s021'); component.testName = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName = false; - assert.equal(div.className, 'false svelte-x1o6ra'); + assert.equal(div.className, 'false svelte-70s021'); component.testName = true; - assert.equal(div.className, 'true svelte-x1o6ra'); + assert.equal(div.className, 'true svelte-70s021'); component.testName = {}; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = ''; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js index d55a0079c0..7fc3dd85f4 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js @@ -10,43 +10,43 @@ export default test({ }; }, - html: '
', + html: '
', async test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); - assert.equal(div.className, 'test1test2 svelte-x1o6ra'); + assert.equal(div.className, 'test1test2 svelte-70s021'); component.testName1 = null; component.testName2 = null; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = null; component.testName2 = 'test'; - assert.equal(div.className, 'nulltest svelte-x1o6ra'); + assert.equal(div.className, 'nulltest svelte-70s021'); component.testName1 = undefined; component.testName2 = 'test'; - assert.equal(div.className, 'undefinedtest svelte-x1o6ra'); + assert.equal(div.className, 'undefinedtest svelte-70s021'); component.testName1 = undefined; component.testName2 = undefined; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName1 = undefined; component.testName2 = 1; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = undefined; component.testName2 = 0; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js index 0069a7dfd7..930de12585 100644 --- a/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js @@ -3,7 +3,7 @@ import { test } from '../../test'; export default test({ async test({ assert, target, logs }) { const [my_element, my_element_1] = target.querySelectorAll('my-element'); - assert.equal(my_element.classList.contains('svelte-1koh33s'), true); - assert.equal(my_element_1.classList.contains('svelte-1koh33s'), true); + assert.equal(my_element.classList.contains('svelte-70s021'), true); + assert.equal(my_element_1.classList.contains('svelte-70s021'), true); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js index eae8101e6e..cb532b28b5 100644 --- a/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js @@ -1,14 +1,14 @@ import { ok, test } from '../../test'; export default test({ - html: ``, + html: ``, async test({ assert, target }) { const [el, el2] = target.querySelectorAll('custom-element'); ok(el); ok(el2); - assert.deepEqual(el.className, 'red svelte-p153w3'); - assert.deepEqual(el2.className, 'red svelte-p153w3'); + assert.deepEqual(el.className, 'red svelte-70s021'); + assert.deepEqual(el2.className, 'red svelte-70s021'); } }); diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html index 60e974bd1a..7ba4de394e 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html @@ -1 +1 @@ -
foo
\ No newline at end of file +
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html index 5350e77a49..99db8fc6dc 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css index bddefdd00c..81c1111967 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css @@ -1,4 +1,4 @@ -.foo.svelte-sg04hs { +.foo.svelte-1nvcr6w { color: red; } diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html index 1f0b2b95fe..a6eba00942 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html @@ -1 +1 @@ -
bar
foo
\ No newline at end of file +
bar
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html index 6d795670ff..516a9576b3 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css index 8882c6ec7e..eb1f3e7a9b 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css @@ -1,4 +1,4 @@ - .foo.svelte-sg04hs { + .foo.svelte-okauro { color: red; } diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html index 8ebe1ad73e..01ebd79914 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html @@ -1 +1 @@ -
foo
\ No newline at end of file +
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html index 9c4f8a8538..1a1511f55e 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/server-side-rendering/samples/css/_expected.css b/packages/svelte/tests/server-side-rendering/samples/css/_expected.css index 8882c6ec7e..ec86890478 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css/_expected.css +++ b/packages/svelte/tests/server-side-rendering/samples/css/_expected.css @@ -1,4 +1,4 @@ - .foo.svelte-sg04hs { + .foo.svelte-e9omc { color: red; } diff --git a/packages/svelte/tests/server-side-rendering/samples/css/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css/_expected.html index 8ebe1ad73e..dc9409fa03 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css/_expected.html @@ -1 +1 @@ -
foo
\ No newline at end of file +
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html index 9c4f8a8538..941c1f13b4 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js b/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js index ee9a3d92c4..3a292ff428 100644 --- a/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js +++ b/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js @@ -57,7 +57,7 @@ export default test({ { str: 'replace_me_script', strGenerated: 'done_replace_script_2' }, { str: 'done_replace_script_2', idxGenerated: 1 } ], - css: [{ str: '.replace_me_style', strGenerated: '.done_replace_style_2.svelte-o6vre' }], + css: [{ str: '.replace_me_style', strGenerated: '.done_replace_style_2.svelte-1vsrjd4' }], test({ assert, code_preprocessed, code_css }) { assert.equal( code_preprocessed.includes('\n/*# sourceMappingURL=data:application/json;base64,'), diff --git a/packages/svelte/tests/sourcemaps/samples/css/_config.js b/packages/svelte/tests/sourcemaps/samples/css/_config.js index df3c83c703..fce38d401c 100644 --- a/packages/svelte/tests/sourcemaps/samples/css/_config.js +++ b/packages/svelte/tests/sourcemaps/samples/css/_config.js @@ -1,5 +1,5 @@ import { test } from '../../test'; export default test({ - css: [{ str: '.foo', strGenerated: '.foo.svelte-sg04hs' }] + css: [{ str: '.foo', strGenerated: '.foo.svelte-1eyw86p' }] }); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 97e6f0f5a3..9888de59b2 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -998,7 +998,7 @@ declare module 'svelte/compiler' { css?: 'injected' | 'external'; /** * A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. - * It defaults to returning `svelte-${hash(css)}`. + * It defaults to returning `svelte-${hash(filename ?? css)}`. * * @default undefined */ @@ -2933,7 +2933,7 @@ declare module 'svelte/types/compiler/interfaces' { css?: 'injected' | 'external'; /** * A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. - * It defaults to returning `svelte-${hash(css)}`. + * It defaults to returning `svelte-${hash(filename ?? css)}`. * * @default undefined */