diff --git a/site/content/docs/04-compile-time.md b/site/content/docs/04-compile-time.md index 48f3ae2c4eaa..755985520d98 100644 --- a/site/content/docs/04-compile-time.md +++ b/site/content/docs/04-compile-time.md @@ -74,6 +74,7 @@ The following options can be passed to the compiler. None are required: | `customElement` | `false` | If `true`, tells the compiler to generate a custom element constructor instead of a regular Svelte component. | `tag` | `null` | A `string` that tells Svelte what tag name to register the custom element with. It must be a lowercase alphanumeric string with at least one hyphen, e.g. `"my-element"`. | `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime. It's recommended that you set this to `false` and use the CSS that is statically generated, as it will result in smaller JavaScript bundles and better performance. +| `cssHash` | See right | 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)}` | `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`** | `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out. | `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than removed or collapsed to a single space where possible. diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 64e658c04966..70c25460b549 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -133,12 +133,14 @@ export default class Component { this.locate = getLocator(this.source, { offsetLine: 1 }); // styles - this.stylesheet = new Stylesheet( + this.stylesheet = new Stylesheet({ source, ast, - compile_options.filename, - compile_options.dev - ); + filename: compile_options.filename, + component_name: name, + dev: compile_options.dev, + get_css_hash: compile_options.cssHash + }); this.stylesheet.validate(this); this.component_options = process_component_options( diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index b730079a8998..af8d4d9c8552 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -2,7 +2,7 @@ import MagicString from 'magic-string'; import { walk } from 'estree-walker'; import Selector from './Selector'; import Element from '../nodes/Element'; -import { Ast } from '../../interfaces'; +import { Ast, CssHashGetter } from '../../interfaces'; import Component from '../Component'; import { CssNode } from './interfaces'; import hash from '../utils/hash'; @@ -275,6 +275,10 @@ class Atrule { } } +const get_default_css_hash: CssHashGetter = ({ css, hash }) => { + return `svelte-${hash(css)}`; +}; + export default class Stylesheet { source: string; ast: Ast; @@ -289,14 +293,33 @@ export default class Stylesheet { nodes_with_css_class: Set = new Set(); - constructor(source: string, ast: Ast, filename: string, dev: boolean) { + constructor({ + source, + ast, + component_name, + filename, + dev, + get_css_hash = get_default_css_hash + }: { + source: string; + ast: Ast; + filename: string | undefined; + component_name: string | undefined; + dev: boolean; + get_css_hash: CssHashGetter; + }) { this.source = source; this.ast = ast; this.filename = filename; this.dev = dev; if (ast.css && ast.css.children.length) { - this.id = `svelte-${hash(ast.css.content.styles)}`; + this.id = get_css_hash({ + filename, + name: component_name, + css: ast.css.content.styles, + hash + }); this.has_styles = true; diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index 9f9b31917e38..c9b594dbe3dc 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -28,7 +28,8 @@ const valid_options = [ 'css', 'loopGuardTimeout', 'preserveComments', - 'preserveWhitespace' + 'preserveWhitespace', + 'cssHash' ]; function validate_options(options: CompileOptions, warnings: Warning[]) { diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index b9c95ae06db4..a34f932a9362 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -104,6 +104,13 @@ export interface Warning { export type ModuleFormat = 'esm' | 'cjs'; +export type CssHashGetter = (args: { + name: string; + filename: string | undefined; + css: string; + hash: (input: string) => string; +}) => string; + export interface CompileOptions { format?: ModuleFormat; name?: string; @@ -125,6 +132,7 @@ export interface CompileOptions { css?: boolean; loopGuardTimeout?: number; namespace?: string; + cssHash?: CssHashGetter; preserveComments?: boolean; preserveWhitespace?: boolean; @@ -166,7 +174,7 @@ export interface Var { imported?: boolean; } -export interface CssResult { +export interface CssResult { code: string; map: SourceMap; } diff --git a/test/css/samples/custom-css-hash/_config.js b/test/css/samples/custom-css-hash/_config.js new file mode 100644 index 000000000000..6ee2e0293918 --- /dev/null +++ b/test/css/samples/custom-css-hash/_config.js @@ -0,0 +1,12 @@ +export default { + compileOptions: { + filename: 'src/components/FooSwitcher.svelte', + cssHash({ hash, css, name, filename }) { + const minFilename = filename + .split('/') + .map(i => i.charAt(0).toLowerCase()) + .join(''); + return `sv-${name}-${minFilename}-${hash(css)}`; + } + } +}; diff --git a/test/css/samples/custom-css-hash/expected.css b/test/css/samples/custom-css-hash/expected.css new file mode 100644 index 000000000000..fe710597ea2f --- /dev/null +++ b/test/css/samples/custom-css-hash/expected.css @@ -0,0 +1 @@ +div.sv-FooSwitcher-scf-bzh57p{color:red} \ No newline at end of file diff --git a/test/css/samples/custom-css-hash/input.svelte b/test/css/samples/custom-css-hash/input.svelte new file mode 100644 index 000000000000..bfe5a232140e --- /dev/null +++ b/test/css/samples/custom-css-hash/input.svelte @@ -0,0 +1,7 @@ +
red
+ +