diff --git a/site/content/docs/05-compile-time.md b/site/content/docs/05-compile-time.md index 35a57ced85b..45dd454ea01 100644 --- a/site/content/docs/05-compile-time.md +++ b/site/content/docs/05-compile-time.md @@ -73,7 +73,7 @@ The following options can be passed to the compiler. None are required: | `accessors` | `false` | If `true`, getters and setters will be created for the component's props. If `false`, they will only be created for readonly exported values (i.e. those declared with `const`, `class` and `function`). If compiling with `customElement: true` this option defaults to `true`. | `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 for the components actually rendered. If `false`, the CSS will be returned in the `css` field of the compilation result. Most Svelte bundler plugins will set this to `false` and use the CSS that is statically generated for better performance, as it will result in smaller JavaScript bundles and the output can be served as cacheable `.css` files. +| `css` | `'injected'` | If `'injected'` (formerly `true`), styles will be included in the JavaScript class and injected at runtime for the components actually rendered. If `'external'` (formerly `false`), the CSS will be returned in the `css` field of the compilation result. Most Svelte bundler plugins will set this to `'external'` and use the CSS that is statically generated for better performance, as it will result in smaller JavaScript bundles and the output can be served as cacheable `.css` files. If `'none'`, styles are completely avoided and no CSS output is generated. | `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. diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index a5edc3a6f72..38a3da057cf 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -35,11 +35,19 @@ const valid_options = [ 'cssHash' ]; +const valid_css_values = [ + true, + false, + 'injected', + 'external', + 'none' +]; + const regex_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/; const regex_starts_with_lowercase_character = /^[a-z]/; function validate_options(options: CompileOptions, warnings: Warning[]) { - const { name, filename, loopGuardTimeout, dev, namespace } = options; + const { name, filename, loopGuardTimeout, dev, namespace, css } = options; Object.keys(options).forEach(key => { if (!valid_options.includes(key)) { @@ -75,6 +83,21 @@ function validate_options(options: CompileOptions, warnings: Warning[]) { }); } + if (valid_css_values.indexOf(css) === -1) { + throw new Error(`options.css must be true, false, 'injected', 'external', or 'none' (got '${css}')`); + } + + if (css === true || css === false) { + options.css = css === true ? 'injected' : 'external'; + const message = `options.css as a boolean is deprecated. Use '${options.css}' instead of ${css}.`; + warnings.push({ + code: 'options-css-boolean-deprecated', + message, + filename, + toString: () => message + }); + } + if (namespace && valid_namespaces.indexOf(namespace) === -1) { const match = fuzzymatch(namespace, valid_namespaces); if (match) { @@ -86,7 +109,7 @@ function validate_options(options: CompileOptions, warnings: Warning[]) { } export default function compile(source: string, options: CompileOptions = {}) { - options = Object.assign({ generate: 'dom', dev: false, enableSourcemap: true }, options); + options = Object.assign({ generate: 'dom', dev: false, enableSourcemap: true, css: 'injected' }, options); const stats = new Stats(); const warnings = []; diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 3055715b4cc..44155f8464c 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -54,7 +54,7 @@ export default function dom( const should_add_css = ( !options.customElement && !!styles && - options.css !== false + options.css === 'injected' ); if (should_add_css) { diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 248175da599..eb2cc336ede 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -179,7 +179,7 @@ export interface CompileOptions { legacy?: boolean; customElement?: boolean; tag?: string; - css?: boolean; + css?: 'injected' | 'external' | 'none' | boolean; loopGuardTimeout?: number; namespace?: string; cssHash?: CssHashGetter; @@ -191,6 +191,7 @@ export interface CompileOptions { export interface ParserOptions { filename?: string; customElement?: boolean; + css?: 'injected' | 'external' | 'none' | boolean; } export interface Visitor { diff --git a/src/compiler/parse/index.ts b/src/compiler/parse/index.ts index 079be0e20c2..8de6563c8b1 100644 --- a/src/compiler/parse/index.ts +++ b/src/compiler/parse/index.ts @@ -21,6 +21,7 @@ export class Parser { readonly template: string; readonly filename?: string; readonly customElement: boolean; + readonly css_mode: 'injected' | 'external' | 'none' | boolean; index = 0; stack: TemplateNode[] = []; @@ -39,6 +40,7 @@ export class Parser { this.template = template.trimRight(); this.filename = options.filename; this.customElement = options.customElement; + this.css_mode = options.css; this.html = { start: null, diff --git a/src/compiler/parse/read/style.ts b/src/compiler/parse/read/style.ts index d7ba83c6e50..560d377cec5 100644 --- a/src/compiler/parse/read/style.ts +++ b/src/compiler/parse/read/style.ts @@ -19,6 +19,12 @@ export default function read_style(parser: Parser, start: number, attributes: No const content_end = parser.index; + // discard styles when css is disabled + if (parser.css_mode === 'none') { + parser.read(regex_closing_style_tag); + return null; + } + let ast; try { @@ -71,6 +77,7 @@ export default function read_style(parser: Parser, start: number, attributes: No }); parser.read(regex_closing_style_tag); + const end = parser.index; return { diff --git a/test/parser/samples/css-option-none/input.svelte b/test/parser/samples/css-option-none/input.svelte new file mode 100644 index 00000000000..659c4343f63 --- /dev/null +++ b/test/parser/samples/css-option-none/input.svelte @@ -0,0 +1,7 @@ +
foo
+ + diff --git a/test/parser/samples/css-option-none/options.json b/test/parser/samples/css-option-none/options.json new file mode 100644 index 00000000000..b10d517cc99 --- /dev/null +++ b/test/parser/samples/css-option-none/options.json @@ -0,0 +1,3 @@ +{ + "css": "none" +} diff --git a/test/parser/samples/css-option-none/output.json b/test/parser/samples/css-option-none/output.json new file mode 100644 index 00000000000..ef1f9c9a371 --- /dev/null +++ b/test/parser/samples/css-option-none/output.json @@ -0,0 +1,32 @@ +{ + "html": { + "start": 0, + "end": 14, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 14, + "type": "Element", + "name": "div", + "attributes": [], + "children": [ + { + "start": 5, + "end": 8, + "type": "Text", + "raw": "foo", + "data": "foo" + } + ] + }, + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "\n\n", + "data": "\n\n" + } + ] + } +}