Skip to content

Commit

Permalink
[fix] adjust style directive AST (sveltejs#7127)
Browse files Browse the repository at this point in the history
- deduplicate type name: Is now "StyleDirective"
- Don't transform value array to template literal in the AST phase but in the compiler phase. This ensures other tools know what the raw output was and that start/end positions are available
  • Loading branch information
dummdidumm authored and nevilm-lt committed Apr 22, 2022
1 parent 9ae94eb commit a796b91
Show file tree
Hide file tree
Showing 11 changed files with 474 additions and 116 deletions.
8 changes: 4 additions & 4 deletions src/compiler/compile/nodes/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Transition from './Transition';
import Animation from './Animation';
import Action from './Action';
import Class from './Class';
import Style from './Style';
import StyleDirective from './StyleDirective';
import Text from './Text';
import { namespaces } from '../../utils/namespaces';
import map_children from './shared/map_children';
Expand Down Expand Up @@ -181,7 +181,7 @@ export default class Element extends Node {
actions: Action[] = [];
bindings: Binding[] = [];
classes: Class[] = [];
styles: Style[] = [];
styles: StyleDirective[] = [];
handlers: EventHandler[] = [];
lets: Let[] = [];
intro?: Transition = null;
Expand Down Expand Up @@ -265,8 +265,8 @@ export default class Element extends Node {
this.classes.push(new Class(component, this, scope, node));
break;

case 'Style':
this.styles.push(new Style(component, this, scope, node));
case 'StyleDirective':
this.styles.push(new StyleDirective(component, this, scope, node));
break;

case 'EventHandler':
Expand Down
22 changes: 0 additions & 22 deletions src/compiler/compile/nodes/Style.ts

This file was deleted.

39 changes: 39 additions & 0 deletions src/compiler/compile/nodes/StyleDirective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { TemplateNode } from '../../interfaces';
import Component from '../Component';
import { nodes_to_template_literal } from '../utils/nodes_to_template_literal';
import Expression from './shared/Expression';
import Node from './shared/Node';
import TemplateScope from './shared/TemplateScope';

export default class StyleDirective extends Node {
type: 'StyleDirective';
name: string;
expression: Expression;
should_cache: boolean;

constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);

this.name = info.name;

// Convert the value array to an expression so it's easier to handle
// the StyleDirective going forward.
if (info.value === true || (info.value.length === 1 && info.value[0].type === 'MustacheTag')) {
const identifier = info.value === true
? {
type: 'Identifier',
start: info.end - info.name.length,
end: info.end,
name: info.name
} as any
: info.value[0].expression;
this.expression = new Expression(component, this, scope, identifier);
this.should_cache = false;
} else {
const raw_expression = nodes_to_template_literal(info.value);
this.expression = new Expression(component, this, scope, raw_expression);
this.should_cache = raw_expression.expressions.length > 0;
}

}
}
4 changes: 2 additions & 2 deletions src/compiler/compile/nodes/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Binding from './Binding';
import Body from './Body';
import CatchBlock from './CatchBlock';
import Class from './Class';
import Style from './Style';
import StyleDirective from './StyleDirective';
import Comment from './Comment';
import ConstTag from './ConstTag';
import DebugTag from './DebugTag';
Expand Down Expand Up @@ -63,7 +63,7 @@ export type INode = Action
| RawMustacheTag
| Slot
| SlotTemplate
| Style
| StyleDirective
| Tag
| Text
| ThenBlock
Expand Down
37 changes: 37 additions & 0 deletions src/compiler/compile/utils/nodes_to_template_literal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { TemplateElement, TemplateLiteral } from 'estree';
import { MustacheTag, Text } from '../../interfaces';

/**
* Transforms a list of Text and MustacheTags into a TemplateLiteral expression.
* Start/End positions on the elements of the expression are not set.
*/
export function nodes_to_template_literal(value: Array<Text | MustacheTag>): TemplateLiteral {
const literal: TemplateLiteral = {
type: 'TemplateLiteral',
expressions: [],
quasis: []
};

let quasi: TemplateElement = {
type: 'TemplateElement',
value: { raw: '', cooked: null },
tail: false
};

value.forEach((node) => {
if (node.type === 'Text') {
quasi.value.raw += node.raw;
} else if (node.type === 'MustacheTag') {
literal.quasis.push(quasi);
literal.expressions.push(node.expression as any);
quasi = {
type: 'TemplateElement',
value: { raw: '', cooked: null },
tail: false
};
}
});
quasi.tail = true;
literal.quasis.push(quasi);
return literal;
}
11 changes: 8 additions & 3 deletions src/compiler/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,18 @@ export type DirectiveType = 'Action'
| 'Animation'
| 'Binding'
| 'Class'
| 'Style'
| 'StyleDirective'
| 'EventHandler'
| 'Let'
| 'Ref'
| 'Transition';

interface BaseDirective extends BaseNode {
type: DirectiveType;
name: string;
}

interface BaseExpressionDirective extends BaseDirective {
type: DirectiveType;
expression: null | Node;
name: string;
Expand All @@ -74,13 +79,13 @@ export interface SpreadAttribute extends BaseNode {
expression: Node;
}

export interface Transition extends BaseDirective {
export interface Transition extends BaseExpressionDirective {
type: 'Transition';
intro: boolean;
outro: boolean;
}

export type Directive = BaseDirective | Transition;
export type Directive = BaseDirective | BaseExpressionDirective | Transition;

export type TemplateNode = Text
| ConstTag
Expand Down
63 changes: 20 additions & 43 deletions src/compiler/parse/state/tag.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { TemplateLiteral, TemplateElement, Expression } from 'estree';
import { Directive, DirectiveType, TemplateNode, Text } from '../../interfaces';
import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore';
import fuzzymatch from '../../utils/fuzzymatch';
import { is_void } from '../../utils/names';
import parser_errors from '../errors';
import { Parser } from '../index';
import read_expression from '../read/expression';
import read_script from '../read/script';
import read_style from '../read/style';
import { decode_character_references, closing_tag_omitted } from '../utils/html';
import { is_void } from '../../utils/names';
import { Parser } from '../index';
import { Directive, DirectiveType, TemplateNode, Text, MustacheTag } from '../../interfaces';
import fuzzymatch from '../../utils/fuzzymatch';
import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore';
import parser_errors from '../errors';
import { closing_tag_omitted, decode_character_references } from '../utils/html';

// eslint-disable-next-line no-useless-escape
const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
Expand Down Expand Up @@ -271,36 +270,6 @@ function read_tag_name(parser: Parser) {
return name;
}

function node_to_template_literal(value: Array<Text | MustacheTag>): TemplateLiteral {
let quasi: TemplateElement = {
type: 'TemplateElement',
value: { raw: '', cooked: null },
tail: false
};
const literal: TemplateLiteral = {
type: 'TemplateLiteral',
expressions: [],
quasis: []
};

value.forEach((node) => {
if (node.type === 'Text') {
quasi.value.raw += node.raw;
} else if (node.type === 'MustacheTag') {
literal.quasis.push(quasi);
literal.expressions.push(node.expression as Expression);
quasi = {
type: 'TemplateElement',
value: { raw: '', cooked: null },
tail: false
};
}
});
quasi.tail = true;
literal.quasis.push(quasi);
return literal;
}

function read_attribute(parser: Parser, unique_names: Set<string>) {
const start = parser.index;

Expand Down Expand Up @@ -396,14 +365,22 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
parser.error(parser_errors.invalid_ref_directive(directive_name), start);
}

if (type === 'StyleDirective') {
return {
start,
end,
type,
name: directive_name,
value
};
}

const first_value = value[0];
let expression = null;

if (first_value) {
const attribute_contains_text = (value as any[]).length > 1 || first_value.type === 'Text';
if (type === 'Style') {
expression = attribute_contains_text ? node_to_template_literal(value as Array<Text | MustacheTag>) : first_value.expression;
} else if (attribute_contains_text) {
if (attribute_contains_text) {
parser.error(parser_errors.invalid_directive_value, first_value.start);
} else {
expression = first_value.expression;
Expand All @@ -426,7 +403,7 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
}

// Directive name is expression, e.g. <p class:isRed />
if (!directive.expression && (type === 'Binding' || type === 'Class' || type === 'Style')) {
if (!directive.expression && (type === 'Binding' || type === 'Class')) {
directive.expression = {
start: directive.start + colon_index + 1,
end: directive.end,
Expand Down Expand Up @@ -454,7 +431,7 @@ function get_directive_type(name: string): DirectiveType {
if (name === 'animate') return 'Animation';
if (name === 'bind') return 'Binding';
if (name === 'class') return 'Class';
if (name === 'style') return 'Style';
if (name === 'style') return 'StyleDirective';
if (name === 'on') return 'EventHandler';
if (name === 'let') return 'Let';
if (name === 'ref') return 'Ref';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,13 @@
{
"start": 5,
"end": 16,
"type": "Style",
"type": "StyleDirective",
"name": "color",
"modifiers": [],
"expression": {
"start": 11,
"end": 16,
"name": "color",
"type": "Identifier"
}
"value": true
}
],
"children": []
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
<div style:color="red"></div>
<div style:color="red"></div>
<div style:color='red'></div>
<div style:color=red></div>
<div style:color="red{variable}"></div>
<div style:color='red{variable}'></div>
<div style:color=red{variable}></div>
<div style:color={`template${literal}`}></div>
Loading

0 comments on commit a796b91

Please sign in to comment.