From 50aa595e7e511da1b13f77a684a5804e63fd231d Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Wed, 5 Feb 2020 21:20:44 -0300 Subject: [PATCH 1/8] Allow to customize the css scope class --- src/compiler/compile/Component.ts | 11 +++++---- src/compiler/compile/css/Stylesheet.ts | 23 ++++++++++++++++--- src/compiler/compile/index.ts | 3 ++- src/compiler/interfaces.ts | 9 +++++++- .../css/samples/custom-scope-class/_config.js | 7 ++++++ .../samples/custom-scope-class/expected.css | 1 + .../samples/custom-scope-class/input.svelte | 7 ++++++ 7 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 test/css/samples/custom-scope-class/_config.js create mode 100644 test/css/samples/custom-scope-class/expected.css create mode 100644 test/css/samples/custom-scope-class/input.svelte diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 8aab2b4898dd..22dec220b74e 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -133,12 +133,15 @@ 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, + options: { + dev: compile_options.dev, + scope_class_getter: compile_options.scopeClass + } + }); 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..1332192b3a29 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, CssScopeClassGetter } from '../../interfaces'; import Component from '../Component'; import { CssNode } from './interfaces'; import hash from '../utils/hash'; @@ -275,6 +275,10 @@ class Atrule { } } +const getDefaultScopeClass: CssScopeClassGetter = ({ hash }) => { + return `svelte-${hash}`; +}; + export default class Stylesheet { source: string; ast: Ast; @@ -289,14 +293,27 @@ export default class Stylesheet { nodes_with_css_class: Set = new Set(); - constructor(source: string, ast: Ast, filename: string, dev: boolean) { + constructor({ + source, + ast, + filename, + options: { dev, scope_class_getter = getDefaultScopeClass }, + }: { + source: string; + ast: Ast; + filename: string; + options: { + dev: boolean; + scope_class_getter: CssScopeClassGetter; + }; + }) { 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 = scope_class_getter({ filename, hash: hash(ast.css.content.styles) }); this.has_styles = true; diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index 9f9b31917e38..17266cafc5ec 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', + 'scopeClass', ]; function validate_options(options: CompileOptions, warnings: Warning[]) { diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index b9c95ae06db4..86da4f32a098 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -104,6 +104,12 @@ export interface Warning { export type ModuleFormat = 'esm' | 'cjs'; + +export type CssScopeClassGetter = (args: { + filename: string; + hash: string; +}) => string; + export interface CompileOptions { format?: ModuleFormat; name?: string; @@ -125,6 +131,7 @@ export interface CompileOptions { css?: boolean; loopGuardTimeout?: number; namespace?: string; + scopeClass?: CssScopeClassGetter; preserveComments?: boolean; preserveWhitespace?: boolean; @@ -166,7 +173,7 @@ export interface Var { imported?: boolean; } -export interface CssResult { +export interface CssResult { code: string; map: SourceMap; } diff --git a/test/css/samples/custom-scope-class/_config.js b/test/css/samples/custom-scope-class/_config.js new file mode 100644 index 000000000000..6e4386ecda24 --- /dev/null +++ b/test/css/samples/custom-scope-class/_config.js @@ -0,0 +1,7 @@ +export default { + compileOptions: { + scopeClass({ hash }) { + return `sv-${hash}`; + } + }, +}; diff --git a/test/css/samples/custom-scope-class/expected.css b/test/css/samples/custom-scope-class/expected.css new file mode 100644 index 000000000000..5eecaebfcaf2 --- /dev/null +++ b/test/css/samples/custom-scope-class/expected.css @@ -0,0 +1 @@ +div.sv-bzh57p{color:red} \ No newline at end of file diff --git a/test/css/samples/custom-scope-class/input.svelte b/test/css/samples/custom-scope-class/input.svelte new file mode 100644 index 000000000000..bfe5a232140e --- /dev/null +++ b/test/css/samples/custom-scope-class/input.svelte @@ -0,0 +1,7 @@ +
red
+ + From 2f55fc4fe7bf9012d479e4ab7e9f8f5d4a083c13 Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Sun, 9 Feb 2020 13:25:56 -0300 Subject: [PATCH 2/8] Pass component name to scope class generator --- src/compiler/compile/Component.ts | 3 ++- src/compiler/compile/css/Stylesheet.ts | 17 +++++++++++++---- src/compiler/interfaces.ts | 4 ++-- test/css/samples/custom-scope-class/_config.js | 11 ++++++++--- .../css/samples/custom-scope-class/expected.css | 2 +- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 22dec220b74e..b3d133606fb5 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -136,8 +136,9 @@ export default class Component { this.stylesheet = new Stylesheet({ source, ast, - filename: compile_options.filename, options: { + filename: compile_options.filename, + component_name: name, dev: compile_options.dev, scope_class_getter: compile_options.scopeClass } diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index 1332192b3a29..ad063a526e21 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -296,13 +296,18 @@ export default class Stylesheet { constructor({ source, ast, - filename, - options: { dev, scope_class_getter = getDefaultScopeClass }, + options: { + component_name, + filename, + dev, + scope_class_getter = getDefaultScopeClass, + }, }: { source: string; ast: Ast; - filename: string; options: { + filename: string | undefined; + component_name: string | undefined; dev: boolean; scope_class_getter: CssScopeClassGetter; }; @@ -313,7 +318,11 @@ export default class Stylesheet { this.dev = dev; if (ast.css && ast.css.children.length) { - this.id = scope_class_getter({ filename, hash: hash(ast.css.content.styles) }); + this.id = scope_class_getter({ + filename, + name: component_name, + hash: hash(ast.css.content.styles), + }); this.has_styles = true; diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 86da4f32a098..c23c6ec8f0f8 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -104,9 +104,9 @@ export interface Warning { export type ModuleFormat = 'esm' | 'cjs'; - export type CssScopeClassGetter = (args: { - filename: string; + name: string | undefined; + filename: string | undefined; hash: string; }) => string; diff --git a/test/css/samples/custom-scope-class/_config.js b/test/css/samples/custom-scope-class/_config.js index 6e4386ecda24..404e22f56195 100644 --- a/test/css/samples/custom-scope-class/_config.js +++ b/test/css/samples/custom-scope-class/_config.js @@ -1,7 +1,12 @@ export default { compileOptions: { - scopeClass({ hash }) { - return `sv-${hash}`; - } + filename: 'src/components/FooSwitcher.svelte', + scopeClass({ hash, name, filename }) { + const minFilename = filename + .split('/') + .map(i => i.charAt(0).toLowerCase()) + .join(''); + return `sv-${name}-${minFilename}-${hash}`; + }, }, }; diff --git a/test/css/samples/custom-scope-class/expected.css b/test/css/samples/custom-scope-class/expected.css index 5eecaebfcaf2..fe710597ea2f 100644 --- a/test/css/samples/custom-scope-class/expected.css +++ b/test/css/samples/custom-scope-class/expected.css @@ -1 +1 @@ -div.sv-bzh57p{color:red} \ No newline at end of file +div.sv-FooSwitcher-scf-bzh57p{color:red} \ No newline at end of file From a038f72fb3f0a6619a1301a612fe8b67ab48c90a Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Sun, 9 Feb 2020 13:48:12 -0300 Subject: [PATCH 3/8] Move Stylesheet arguments into an object --- src/compiler/compile/Component.ts | 10 ++++------ src/compiler/compile/css/Stylesheet.ts | 20 ++++++++------------ src/compiler/interfaces.ts | 2 +- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index b3d133606fb5..dc51b0fefdac 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -136,12 +136,10 @@ export default class Component { this.stylesheet = new Stylesheet({ source, ast, - options: { - filename: compile_options.filename, - component_name: name, - dev: compile_options.dev, - scope_class_getter: compile_options.scopeClass - } + filename: compile_options.filename, + component_name: name, + dev: compile_options.dev, + scope_class_getter: compile_options.scopeClass }); this.stylesheet.validate(this); diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index ad063a526e21..d1917420b426 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -296,21 +296,17 @@ export default class Stylesheet { constructor({ source, ast, - options: { - component_name, - filename, - dev, - scope_class_getter = getDefaultScopeClass, - }, + component_name, + filename, + dev, + scope_class_getter = getDefaultScopeClass, }: { source: string; ast: Ast; - options: { - filename: string | undefined; - component_name: string | undefined; - dev: boolean; - scope_class_getter: CssScopeClassGetter; - }; + filename: string | undefined; + component_name: string | undefined; + dev: boolean; + scope_class_getter: CssScopeClassGetter; }) { this.source = source; this.ast = ast; diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index c23c6ec8f0f8..39130592319e 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -105,7 +105,7 @@ export interface Warning { export type ModuleFormat = 'esm' | 'cjs'; export type CssScopeClassGetter = (args: { - name: string | undefined; + name: string; filename: string | undefined; hash: string; }) => string; From 66caecd57368c4e5ec8c331419fb8bc987adad36 Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Wed, 17 Feb 2021 17:33:09 -0300 Subject: [PATCH 4/8] Refactor to cssHash --- src/compiler/compile/Component.ts | 2 +- src/compiler/compile/css/Stylesheet.ts | 10 +++++----- src/compiler/compile/index.ts | 2 +- src/compiler/interfaces.ts | 4 ++-- .../{custom-scope-class => custom-css-hash}/_config.js | 2 +- .../expected.css | 0 .../input.svelte | 0 7 files changed, 10 insertions(+), 10 deletions(-) rename test/css/samples/{custom-scope-class => custom-css-hash}/_config.js (85%) rename test/css/samples/{custom-scope-class => custom-css-hash}/expected.css (100%) rename test/css/samples/{custom-scope-class => custom-css-hash}/input.svelte (100%) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index dc51b0fefdac..bf22e0e6b0ae 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -139,7 +139,7 @@ export default class Component { filename: compile_options.filename, component_name: name, dev: compile_options.dev, - scope_class_getter: compile_options.scopeClass + get_css_hash: compile_options.cssHash }); this.stylesheet.validate(this); diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index d1917420b426..1b19e6cca930 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, CssScopeClassGetter } from '../../interfaces'; +import { Ast, CssHashGetter } from '../../interfaces'; import Component from '../Component'; import { CssNode } from './interfaces'; import hash from '../utils/hash'; @@ -275,7 +275,7 @@ class Atrule { } } -const getDefaultScopeClass: CssScopeClassGetter = ({ hash }) => { +const get_default_css_hash: CssHashGetter = ({ hash }) => { return `svelte-${hash}`; }; @@ -299,14 +299,14 @@ export default class Stylesheet { component_name, filename, dev, - scope_class_getter = getDefaultScopeClass, + get_css_hash = get_default_css_hash, }: { source: string; ast: Ast; filename: string | undefined; component_name: string | undefined; dev: boolean; - scope_class_getter: CssScopeClassGetter; + get_css_hash: CssHashGetter; }) { this.source = source; this.ast = ast; @@ -314,7 +314,7 @@ export default class Stylesheet { this.dev = dev; if (ast.css && ast.css.children.length) { - this.id = scope_class_getter({ + this.id = get_css_hash({ filename, name: component_name, hash: hash(ast.css.content.styles), diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index 17266cafc5ec..d4d4e544cbc2 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -29,7 +29,7 @@ const valid_options = [ 'loopGuardTimeout', 'preserveComments', 'preserveWhitespace', - 'scopeClass', + 'cssHash', ]; function validate_options(options: CompileOptions, warnings: Warning[]) { diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 39130592319e..c86aed0e061c 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -104,7 +104,7 @@ export interface Warning { export type ModuleFormat = 'esm' | 'cjs'; -export type CssScopeClassGetter = (args: { +export type CssHashGetter = (args: { name: string; filename: string | undefined; hash: string; @@ -131,7 +131,7 @@ export interface CompileOptions { css?: boolean; loopGuardTimeout?: number; namespace?: string; - scopeClass?: CssScopeClassGetter; + cssHash?: CssHashGetter; preserveComments?: boolean; preserveWhitespace?: boolean; diff --git a/test/css/samples/custom-scope-class/_config.js b/test/css/samples/custom-css-hash/_config.js similarity index 85% rename from test/css/samples/custom-scope-class/_config.js rename to test/css/samples/custom-css-hash/_config.js index 404e22f56195..f862cb21a713 100644 --- a/test/css/samples/custom-scope-class/_config.js +++ b/test/css/samples/custom-css-hash/_config.js @@ -1,7 +1,7 @@ export default { compileOptions: { filename: 'src/components/FooSwitcher.svelte', - scopeClass({ hash, name, filename }) { + cssHash({ hash, name, filename }) { const minFilename = filename .split('/') .map(i => i.charAt(0).toLowerCase()) diff --git a/test/css/samples/custom-scope-class/expected.css b/test/css/samples/custom-css-hash/expected.css similarity index 100% rename from test/css/samples/custom-scope-class/expected.css rename to test/css/samples/custom-css-hash/expected.css diff --git a/test/css/samples/custom-scope-class/input.svelte b/test/css/samples/custom-css-hash/input.svelte similarity index 100% rename from test/css/samples/custom-scope-class/input.svelte rename to test/css/samples/custom-css-hash/input.svelte From 21504449d994f7fad14daa90223fa95eab79c056 Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Wed, 17 Feb 2021 17:38:12 -0300 Subject: [PATCH 5/8] Please the almighty linter --- src/compiler/compile/css/Stylesheet.ts | 4 ++-- src/compiler/compile/index.ts | 2 +- test/css/samples/custom-css-hash/_config.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index 1b19e6cca930..312890ad0fc5 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -299,7 +299,7 @@ export default class Stylesheet { component_name, filename, dev, - get_css_hash = get_default_css_hash, + get_css_hash = get_default_css_hash }: { source: string; ast: Ast; @@ -317,7 +317,7 @@ export default class Stylesheet { this.id = get_css_hash({ filename, name: component_name, - hash: hash(ast.css.content.styles), + hash: hash(ast.css.content.styles) }); this.has_styles = true; diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index d4d4e544cbc2..c9b594dbe3dc 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -29,7 +29,7 @@ const valid_options = [ 'loopGuardTimeout', 'preserveComments', 'preserveWhitespace', - 'cssHash', + 'cssHash' ]; function validate_options(options: CompileOptions, warnings: Warning[]) { diff --git a/test/css/samples/custom-css-hash/_config.js b/test/css/samples/custom-css-hash/_config.js index f862cb21a713..0739cc9d8bde 100644 --- a/test/css/samples/custom-css-hash/_config.js +++ b/test/css/samples/custom-css-hash/_config.js @@ -7,6 +7,6 @@ export default { .map(i => i.charAt(0).toLowerCase()) .join(''); return `sv-${name}-${minFilename}-${hash}`; - }, - }, + } + } }; From 8ea3bb2631de8430e4c82d05affa20ad17cadc1a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 25 Feb 2021 12:02:51 -0500 Subject: [PATCH 6/8] pass hash function to cssHash --- src/compiler/compile/css/Stylesheet.ts | 7 ++++--- src/compiler/interfaces.ts | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index 312890ad0fc5..af8d4d9c8552 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -275,8 +275,8 @@ class Atrule { } } -const get_default_css_hash: CssHashGetter = ({ hash }) => { - return `svelte-${hash}`; +const get_default_css_hash: CssHashGetter = ({ css, hash }) => { + return `svelte-${hash(css)}`; }; export default class Stylesheet { @@ -317,7 +317,8 @@ export default class Stylesheet { this.id = get_css_hash({ filename, name: component_name, - hash: hash(ast.css.content.styles) + css: ast.css.content.styles, + hash }); this.has_styles = true; diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index c86aed0e061c..a34f932a9362 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -107,7 +107,8 @@ export type ModuleFormat = 'esm' | 'cjs'; export type CssHashGetter = (args: { name: string; filename: string | undefined; - hash: string; + css: string; + hash: (input: string) => string; }) => string; export interface CompileOptions { From 8724f383c93f0e3e3d8c5f4e48516b639417b0a1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 25 Feb 2021 12:08:57 -0500 Subject: [PATCH 7/8] update test --- test/css/samples/custom-css-hash/_config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/css/samples/custom-css-hash/_config.js b/test/css/samples/custom-css-hash/_config.js index 0739cc9d8bde..6ee2e0293918 100644 --- a/test/css/samples/custom-css-hash/_config.js +++ b/test/css/samples/custom-css-hash/_config.js @@ -1,12 +1,12 @@ export default { compileOptions: { filename: 'src/components/FooSwitcher.svelte', - cssHash({ hash, name, filename }) { + cssHash({ hash, css, name, filename }) { const minFilename = filename .split('/') .map(i => i.charAt(0).toLowerCase()) .join(''); - return `sv-${name}-${minFilename}-${hash}`; + return `sv-${name}-${minFilename}-${hash(css)}`; } } }; From 1966852120ab94dd8112702a230a45e5efb7eac5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 25 Feb 2021 15:43:15 -0500 Subject: [PATCH 8/8] document cssHash option --- site/content/docs/04-compile-time.md | 1 + 1 file changed, 1 insertion(+) 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.