add cssHash option (#6026)

* Allow to customize the css scope class

* Pass component name to scope class generator

* Move Stylesheet arguments into an object

* Refactor to cssHash

* Please the almighty linter

* pass hash function to cssHash

* update test

* document cssHash option

Co-authored-by: Christian Kaisermann <christian@kaisermann.me>
pull/6029/head
Rich Harris 5 years ago committed by GitHub
parent 5c8807e523
commit 2925a00eff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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. | `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"`. | `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. | `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`** | `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. | `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. | `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.

@ -133,12 +133,14 @@ export default class Component {
this.locate = getLocator(this.source, { offsetLine: 1 }); this.locate = getLocator(this.source, { offsetLine: 1 });
// styles // styles
this.stylesheet = new Stylesheet( this.stylesheet = new Stylesheet({
source, source,
ast, ast,
compile_options.filename, filename: compile_options.filename,
compile_options.dev component_name: name,
); dev: compile_options.dev,
get_css_hash: compile_options.cssHash
});
this.stylesheet.validate(this); this.stylesheet.validate(this);
this.component_options = process_component_options( this.component_options = process_component_options(

@ -2,7 +2,7 @@ import MagicString from 'magic-string';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import Selector from './Selector'; import Selector from './Selector';
import Element from '../nodes/Element'; import Element from '../nodes/Element';
import { Ast } from '../../interfaces'; import { Ast, CssHashGetter } from '../../interfaces';
import Component from '../Component'; import Component from '../Component';
import { CssNode } from './interfaces'; import { CssNode } from './interfaces';
import hash from '../utils/hash'; 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 { export default class Stylesheet {
source: string; source: string;
ast: Ast; ast: Ast;
@ -289,14 +293,33 @@ export default class Stylesheet {
nodes_with_css_class: Set<CssNode> = new Set(); nodes_with_css_class: Set<CssNode> = 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.source = source;
this.ast = ast; this.ast = ast;
this.filename = filename; this.filename = filename;
this.dev = dev; this.dev = dev;
if (ast.css && ast.css.children.length) { 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; this.has_styles = true;

@ -28,7 +28,8 @@ const valid_options = [
'css', 'css',
'loopGuardTimeout', 'loopGuardTimeout',
'preserveComments', 'preserveComments',
'preserveWhitespace' 'preserveWhitespace',
'cssHash'
]; ];
function validate_options(options: CompileOptions, warnings: Warning[]) { function validate_options(options: CompileOptions, warnings: Warning[]) {

@ -104,6 +104,13 @@ export interface Warning {
export type ModuleFormat = 'esm' | 'cjs'; export type ModuleFormat = 'esm' | 'cjs';
export type CssHashGetter = (args: {
name: string;
filename: string | undefined;
css: string;
hash: (input: string) => string;
}) => string;
export interface CompileOptions { export interface CompileOptions {
format?: ModuleFormat; format?: ModuleFormat;
name?: string; name?: string;
@ -125,6 +132,7 @@ export interface CompileOptions {
css?: boolean; css?: boolean;
loopGuardTimeout?: number; loopGuardTimeout?: number;
namespace?: string; namespace?: string;
cssHash?: CssHashGetter;
preserveComments?: boolean; preserveComments?: boolean;
preserveWhitespace?: boolean; preserveWhitespace?: boolean;
@ -166,7 +174,7 @@ export interface Var {
imported?: boolean; imported?: boolean;
} }
export interface CssResult { export interface CssResult {
code: string; code: string;
map: SourceMap; map: SourceMap;
} }

@ -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)}`;
}
}
};

@ -0,0 +1 @@
div.sv-FooSwitcher-scf-bzh57p{color:red}

@ -0,0 +1,7 @@
<div>red</div>
<style>
div {
color: red;
}
</style>
Loading…
Cancel
Save