Skip to content

Commit

Permalink
[feat] Compiler option css: 'none' support (#7914)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxiruani committed Nov 9, 2022
1 parent b05b684 commit 773dedf
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 5 deletions.
2 changes: 1 addition & 1 deletion site/content/docs/05-compile-time.md
Expand Up @@ -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.
Expand Down
27 changes: 25 additions & 2 deletions src/compiler/compile/index.ts
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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 = [];
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/render_dom/index.ts
Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/interfaces.ts
Expand Up @@ -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;
Expand All @@ -191,6 +191,7 @@ export interface CompileOptions {
export interface ParserOptions {
filename?: string;
customElement?: boolean;
css?: 'injected' | 'external' | 'none' | boolean;
}

export interface Visitor {
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/parse/index.ts
Expand Up @@ -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[] = [];
Expand All @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/parse/read/style.ts
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions test/parser/samples/css-option-none/input.svelte
@@ -0,0 +1,7 @@
<div>foo</div>

<style>
div {
color: red;
}
</style>
3 changes: 3 additions & 0 deletions test/parser/samples/css-option-none/options.json
@@ -0,0 +1,3 @@
{
"css": "none"
}
32 changes: 32 additions & 0 deletions 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"
}
]
}
}

0 comments on commit 773dedf

Please sign in to comment.