Skip to content

Commit 7044a32

Browse files
authored
[fix] adjust style directive AST (#7127)
- 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
1 parent 4ae20d7 commit 7044a32

File tree

11 files changed

+474
-116
lines changed

11 files changed

+474
-116
lines changed

src/compiler/compile/nodes/Element.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Transition from './Transition';
77
import Animation from './Animation';
88
import Action from './Action';
99
import Class from './Class';
10-
import Style from './Style';
10+
import StyleDirective from './StyleDirective';
1111
import Text from './Text';
1212
import { namespaces } from '../../utils/namespaces';
1313
import map_children from './shared/map_children';
@@ -181,7 +181,7 @@ export default class Element extends Node {
181181
actions: Action[] = [];
182182
bindings: Binding[] = [];
183183
classes: Class[] = [];
184-
styles: Style[] = [];
184+
styles: StyleDirective[] = [];
185185
handlers: EventHandler[] = [];
186186
lets: Let[] = [];
187187
intro?: Transition = null;
@@ -265,8 +265,8 @@ export default class Element extends Node {
265265
this.classes.push(new Class(component, this, scope, node));
266266
break;
267267

268-
case 'Style':
269-
this.styles.push(new Style(component, this, scope, node));
268+
case 'StyleDirective':
269+
this.styles.push(new StyleDirective(component, this, scope, node));
270270
break;
271271

272272
case 'EventHandler':

src/compiler/compile/nodes/Style.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { TemplateNode } from '../../interfaces';
2+
import Component from '../Component';
3+
import { nodes_to_template_literal } from '../utils/nodes_to_template_literal';
4+
import Expression from './shared/Expression';
5+
import Node from './shared/Node';
6+
import TemplateScope from './shared/TemplateScope';
7+
8+
export default class StyleDirective extends Node {
9+
type: 'StyleDirective';
10+
name: string;
11+
expression: Expression;
12+
should_cache: boolean;
13+
14+
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
15+
super(component, parent, scope, info);
16+
17+
this.name = info.name;
18+
19+
// Convert the value array to an expression so it's easier to handle
20+
// the StyleDirective going forward.
21+
if (info.value === true || (info.value.length === 1 && info.value[0].type === 'MustacheTag')) {
22+
const identifier = info.value === true
23+
? {
24+
type: 'Identifier',
25+
start: info.end - info.name.length,
26+
end: info.end,
27+
name: info.name
28+
} as any
29+
: info.value[0].expression;
30+
this.expression = new Expression(component, this, scope, identifier);
31+
this.should_cache = false;
32+
} else {
33+
const raw_expression = nodes_to_template_literal(info.value);
34+
this.expression = new Expression(component, this, scope, raw_expression);
35+
this.should_cache = raw_expression.expressions.length > 0;
36+
}
37+
38+
}
39+
}

src/compiler/compile/nodes/interfaces.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Binding from './Binding';
88
import Body from './Body';
99
import CatchBlock from './CatchBlock';
1010
import Class from './Class';
11-
import Style from './Style';
11+
import StyleDirective from './StyleDirective';
1212
import Comment from './Comment';
1313
import ConstTag from './ConstTag';
1414
import DebugTag from './DebugTag';
@@ -63,7 +63,7 @@ export type INode = Action
6363
| RawMustacheTag
6464
| Slot
6565
| SlotTemplate
66-
| Style
66+
| StyleDirective
6767
| Tag
6868
| Text
6969
| ThenBlock
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { TemplateElement, TemplateLiteral } from 'estree';
2+
import { MustacheTag, Text } from '../../interfaces';
3+
4+
/**
5+
* Transforms a list of Text and MustacheTags into a TemplateLiteral expression.
6+
* Start/End positions on the elements of the expression are not set.
7+
*/
8+
export function nodes_to_template_literal(value: Array<Text | MustacheTag>): TemplateLiteral {
9+
const literal: TemplateLiteral = {
10+
type: 'TemplateLiteral',
11+
expressions: [],
12+
quasis: []
13+
};
14+
15+
let quasi: TemplateElement = {
16+
type: 'TemplateElement',
17+
value: { raw: '', cooked: null },
18+
tail: false
19+
};
20+
21+
value.forEach((node) => {
22+
if (node.type === 'Text') {
23+
quasi.value.raw += node.raw;
24+
} else if (node.type === 'MustacheTag') {
25+
literal.quasis.push(quasi);
26+
literal.expressions.push(node.expression as any);
27+
quasi = {
28+
type: 'TemplateElement',
29+
value: { raw: '', cooked: null },
30+
tail: false
31+
};
32+
}
33+
});
34+
quasi.tail = true;
35+
literal.quasis.push(quasi);
36+
return literal;
37+
}

src/compiler/interfaces.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,18 @@ export type DirectiveType = 'Action'
4444
| 'Animation'
4545
| 'Binding'
4646
| 'Class'
47-
| 'Style'
47+
| 'StyleDirective'
4848
| 'EventHandler'
4949
| 'Let'
5050
| 'Ref'
5151
| 'Transition';
5252

5353
interface BaseDirective extends BaseNode {
54+
type: DirectiveType;
55+
name: string;
56+
}
57+
58+
interface BaseExpressionDirective extends BaseDirective {
5459
type: DirectiveType;
5560
expression: null | Node;
5661
name: string;
@@ -74,13 +79,13 @@ export interface SpreadAttribute extends BaseNode {
7479
expression: Node;
7580
}
7681

77-
export interface Transition extends BaseDirective {
82+
export interface Transition extends BaseExpressionDirective {
7883
type: 'Transition';
7984
intro: boolean;
8085
outro: boolean;
8186
}
8287

83-
export type Directive = BaseDirective | Transition;
88+
export type Directive = BaseDirective | BaseExpressionDirective | Transition;
8489

8590
export type TemplateNode = Text
8691
| ConstTag

src/compiler/parse/state/tag.ts

Lines changed: 20 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
import { TemplateLiteral, TemplateElement, Expression } from 'estree';
1+
import { Directive, DirectiveType, TemplateNode, Text } from '../../interfaces';
2+
import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore';
3+
import fuzzymatch from '../../utils/fuzzymatch';
4+
import { is_void } from '../../utils/names';
5+
import parser_errors from '../errors';
6+
import { Parser } from '../index';
27
import read_expression from '../read/expression';
38
import read_script from '../read/script';
49
import read_style from '../read/style';
5-
import { decode_character_references, closing_tag_omitted } from '../utils/html';
6-
import { is_void } from '../../utils/names';
7-
import { Parser } from '../index';
8-
import { Directive, DirectiveType, TemplateNode, Text, MustacheTag } from '../../interfaces';
9-
import fuzzymatch from '../../utils/fuzzymatch';
10-
import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore';
11-
import parser_errors from '../errors';
10+
import { closing_tag_omitted, decode_character_references } from '../utils/html';
1211

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

274-
function node_to_template_literal(value: Array<Text | MustacheTag>): TemplateLiteral {
275-
let quasi: TemplateElement = {
276-
type: 'TemplateElement',
277-
value: { raw: '', cooked: null },
278-
tail: false
279-
};
280-
const literal: TemplateLiteral = {
281-
type: 'TemplateLiteral',
282-
expressions: [],
283-
quasis: []
284-
};
285-
286-
value.forEach((node) => {
287-
if (node.type === 'Text') {
288-
quasi.value.raw += node.raw;
289-
} else if (node.type === 'MustacheTag') {
290-
literal.quasis.push(quasi);
291-
literal.expressions.push(node.expression as Expression);
292-
quasi = {
293-
type: 'TemplateElement',
294-
value: { raw: '', cooked: null },
295-
tail: false
296-
};
297-
}
298-
});
299-
quasi.tail = true;
300-
literal.quasis.push(quasi);
301-
return literal;
302-
}
303-
304273
function read_attribute(parser: Parser, unique_names: Set<string>) {
305274
const start = parser.index;
306275

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

368+
if (type === 'StyleDirective') {
369+
return {
370+
start,
371+
end,
372+
type,
373+
name: directive_name,
374+
value
375+
};
376+
}
377+
399378
const first_value = value[0];
400379
let expression = null;
401380

402381
if (first_value) {
403382
const attribute_contains_text = (value as any[]).length > 1 || first_value.type === 'Text';
404-
if (type === 'Style') {
405-
expression = attribute_contains_text ? node_to_template_literal(value as Array<Text | MustacheTag>) : first_value.expression;
406-
} else if (attribute_contains_text) {
383+
if (attribute_contains_text) {
407384
parser.error(parser_errors.invalid_directive_value, first_value.start);
408385
} else {
409386
expression = first_value.expression;
@@ -426,7 +403,7 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
426403
}
427404

428405
// Directive name is expression, e.g. <p class:isRed />
429-
if (!directive.expression && (type === 'Binding' || type === 'Class' || type === 'Style')) {
406+
if (!directive.expression && (type === 'Binding' || type === 'Class')) {
430407
directive.expression = {
431408
start: directive.start + colon_index + 1,
432409
end: directive.end,
@@ -454,7 +431,7 @@ function get_directive_type(name: string): DirectiveType {
454431
if (name === 'animate') return 'Animation';
455432
if (name === 'bind') return 'Binding';
456433
if (name === 'class') return 'Class';
457-
if (name === 'style') return 'Style';
434+
if (name === 'style') return 'StyleDirective';
458435
if (name === 'on') return 'EventHandler';
459436
if (name === 'let') return 'Let';
460437
if (name === 'ref') return 'Ref';

test/parser/samples/attribute-style-directive-shorthand/output.json

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,13 @@
1313
{
1414
"start": 5,
1515
"end": 16,
16-
"type": "Style",
16+
"type": "StyleDirective",
1717
"name": "color",
18-
"modifiers": [],
19-
"expression": {
20-
"start": 11,
21-
"end": 16,
22-
"name": "color",
23-
"type": "Identifier"
24-
}
18+
"value": true
2519
}
2620
],
2721
"children": []
2822
}
2923
]
3024
}
31-
}
25+
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
<div style:color="red"></div>
1+
<div style:color="red"></div>
2+
<div style:color='red'></div>
3+
<div style:color=red></div>
4+
<div style:color="red{variable}"></div>
5+
<div style:color='red{variable}'></div>
6+
<div style:color=red{variable}></div>
7+
<div style:color={`template${literal}`}></div>

0 commit comments

Comments
 (0)