fix: preserve node locations for better sourcemaps (#17269)

* preserve node locations

* preserve component IDs

* update tests

* use IDs for elements

* more

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* tweak

* tidy up

* changeset

* oops
pull/17284/head
Rich Harris 6 days ago committed by GitHub
parent 6d696be170
commit 617e179e68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: preserve node locations for better sourcemaps

@ -1,4 +1,6 @@
/** @import { AST } from '#compiler' */
/** @import { Location } from 'locate-character' */
/** @import * as ESTree from 'estree' */
// @ts-expect-error acorn type definitions are borked in the release we use
import { isIdentifierStart, isIdentifierChar } from 'acorn';
import fragment from './state/fragment.js';
@ -218,31 +220,45 @@ export class Parser {
return result;
}
/** @param {any} allow_reserved */
read_identifier(allow_reserved = false) {
/**
* @returns {ESTree.Identifier & { start: number, end: number, loc: { start: Location, end: Location } }}
*/
read_identifier() {
const start = this.index;
let end = start;
let name = '';
let i = this.index;
const code = /** @type {number} */ (this.template.codePointAt(this.index));
const code = /** @type {number} */ (this.template.codePointAt(i));
if (!isIdentifierStart(code, true)) return null;
if (isIdentifierStart(code, true)) {
let i = this.index;
end += code <= 0xffff ? 1 : 2;
i += code <= 0xffff ? 1 : 2;
while (end < this.template.length) {
const code = /** @type {number} */ (this.template.codePointAt(end));
while (i < this.template.length) {
const code = /** @type {number} */ (this.template.codePointAt(i));
if (!isIdentifierChar(code, true)) break;
i += code <= 0xffff ? 1 : 2;
}
if (!isIdentifierChar(code, true)) break;
end += code <= 0xffff ? 1 : 2;
}
const identifier = this.template.slice(this.index, (this.index = i));
name = this.template.slice(start, end);
this.index = end;
if (!allow_reserved && is_reserved(identifier)) {
e.unexpected_reserved_word(start, identifier);
if (is_reserved(name)) {
e.unexpected_reserved_word(start, name);
}
}
return identifier;
return {
type: 'Identifier',
name,
start,
end,
loc: {
start: state.locator(start),
end: state.locator(end)
}
};
}
/** @param {RegExp} pattern */

@ -1,11 +1,9 @@
/** @import { Location } from 'locate-character' */
/** @import { Pattern } from 'estree' */
/** @import { Parser } from '../index.js' */
import { match_bracket } from '../utils/bracket.js';
import { parse_expression_at } from '../acorn.js';
import { regex_not_newline_characters } from '../../patterns.js';
import * as e from '../../../errors.js';
import { locator } from '../../../state.js';
/**
* @param {Parser} parser
@ -15,20 +13,13 @@ export default function read_pattern(parser) {
const start = parser.index;
let i = parser.index;
const name = parser.read_identifier();
const id = parser.read_identifier();
if (name !== null) {
if (id.name !== '') {
const annotation = read_type_annotation(parser);
return {
type: 'Identifier',
name,
start,
loc: {
start: /** @type {Location} */ (locator(start)),
end: /** @type {Location} */ (locator(parser.index))
},
end: parser.index,
...id,
typeAnnotation: annotation
};
}

@ -1,4 +1,5 @@
/** @import { Expression } from 'estree' */
/** @import { Expression, Identifier, SourceLocation } from 'estree' */
/** @import { Location } from 'locate-character' */
/** @import { AST } from '#compiler' */
/** @import { Parser } from '../index.js' */
import { is_void } from '../../../../utils.js';
@ -13,6 +14,8 @@ import { create_attribute, ExpressionMetadata, is_element_node } from '../../nod
import { get_attribute_expression, is_expression_attribute } from '../../../utils/ast.js';
import { closing_tag_omitted } from '../../../../html-tree-validation.js';
import { list } from '../../../utils/string.js';
import { locator } from '../../../state.js';
import * as b from '#compiler/builders';
const regex_invalid_unquoted_attribute_value = /^(\/>|[\s"'=<>`])/;
const regex_closing_textarea_tag = /^<\/textarea(\s[^>]*)?>/i;
@ -67,10 +70,9 @@ export default function element(parser) {
return;
}
const is_closing_tag = parser.eat('/');
const name = parser.read_until(regex_whitespace_or_slash_or_closing_tag);
if (parser.eat('/')) {
const name = parser.read_until(regex_whitespace_or_slash_or_closing_tag);
if (is_closing_tag) {
parser.allow_whitespace();
parser.eat('>', true);
@ -125,39 +127,41 @@ export default function element(parser) {
return;
}
if (name.startsWith('svelte:') && !meta_tags.has(name)) {
const bounds = { start: start + 1, end: start + 1 + name.length };
const tag = read_tag(parser, regex_whitespace_or_slash_or_closing_tag);
if (tag.name.startsWith('svelte:') && !meta_tags.has(tag.name)) {
const bounds = { start: start + 1, end: start + 1 + tag.name.length };
e.svelte_meta_invalid_tag(bounds, list(Array.from(meta_tags.keys())));
}
if (!regex_valid_element_name.test(name) && !regex_valid_component_name.test(name)) {
if (!regex_valid_element_name.test(tag.name) && !regex_valid_component_name.test(tag.name)) {
// <div. -> in the middle of typing -> allow in loose mode
if (!parser.loose || !name.endsWith('.')) {
const bounds = { start: start + 1, end: start + 1 + name.length };
if (!parser.loose || !tag.name.endsWith('.')) {
const bounds = { start: start + 1, end: start + 1 + tag.name.length };
e.tag_invalid_name(bounds);
}
}
if (root_only_meta_tags.has(name)) {
if (name in parser.meta_tags) {
e.svelte_meta_duplicate(start, name);
if (root_only_meta_tags.has(tag.name)) {
if (tag.name in parser.meta_tags) {
e.svelte_meta_duplicate(start, tag.name);
}
if (parent.type !== 'Root') {
e.svelte_meta_invalid_placement(start, name);
e.svelte_meta_invalid_placement(start, tag.name);
}
parser.meta_tags[name] = true;
parser.meta_tags[tag.name] = true;
}
const type = meta_tags.has(name)
? meta_tags.get(name)
: regex_valid_component_name.test(name) || (parser.loose && name.endsWith('.'))
const type = meta_tags.has(tag.name)
? meta_tags.get(tag.name)
: regex_valid_component_name.test(tag.name) || (parser.loose && tag.name.endsWith('.'))
? 'Component'
: name === 'title' && parent_is_head(parser.stack)
: tag.name === 'title' && parent_is_head(parser.stack)
? 'TitleElement'
: // TODO Svelte 6/7: once slots are removed in favor of snippets, always keep slot as a regular element
name === 'slot' && !parent_is_shadowroot_template(parser.stack)
tag.name === 'slot' && !parent_is_shadowroot_template(parser.stack)
? 'SlotElement'
: 'RegularElement';
@ -168,7 +172,8 @@ export default function element(parser) {
type,
start,
end: -1,
name,
name: tag.name,
name_loc: tag.loc,
attributes: [],
fragment: create_fragment(true),
metadata: {
@ -184,7 +189,8 @@ export default function element(parser) {
type,
start,
end: -1,
name,
name: tag.name,
name_loc: tag.loc,
attributes: [],
fragment: create_fragment(true),
metadata: {
@ -194,14 +200,14 @@ export default function element(parser) {
parser.allow_whitespace();
if (parent.type === 'RegularElement' && closing_tag_omitted(parent.name, name)) {
if (parent.type === 'RegularElement' && closing_tag_omitted(parent.name, tag.name)) {
const end = parent.fragment.nodes[0]?.start ?? start;
w.element_implicitly_closed({ start: parent.start, end }, `<${name}>`, `</${parent.name}>`);
w.element_implicitly_closed({ start: parent.start, end }, `<${tag.name}>`, `</${parent.name}>`);
parent.end = start;
parser.pop();
parser.last_auto_closed_tag = {
tag: parent.name,
reason: name,
reason: tag.name,
depth: parser.stack.length
};
}
@ -211,7 +217,7 @@ export default function element(parser) {
const current = parser.current();
const is_top_level_script_or_style =
(name === 'script' || name === 'style') && current.type === 'Root';
(tag.name === 'script' || tag.name === 'style') && current.type === 'Root';
const read = is_top_level_script_or_style ? read_static_attribute : read_attribute;
@ -324,7 +330,7 @@ export default function element(parser) {
}
}
if (name === 'script') {
if (tag.name === 'script') {
const content = read_script(parser, start, element.attributes);
if (prev_comment) {
// We take advantage of the fact that the root will never have leadingComments set,
@ -352,7 +358,7 @@ export default function element(parser) {
parser.append(element);
const self_closing = parser.eat('/') || is_void(name);
const self_closing = parser.eat('/') || is_void(tag.name);
const closed = parser.eat('>', true, false);
// Loose parsing mode
@ -382,7 +388,7 @@ export default function element(parser) {
if (self_closing || !closed) {
// don't push self-closing elements onto the stack
element.end = parser.index;
} else if (name === 'textarea') {
} else if (tag.name === 'textarea') {
// special case
element.fragment.nodes = read_sequence(
parser,
@ -391,10 +397,10 @@ export default function element(parser) {
);
parser.read(regex_closing_textarea_tag);
element.end = parser.index;
} else if (name === 'script' || name === 'style') {
} else if (tag.name === 'script' || tag.name === 'style') {
// special case
const start = parser.index;
const data = parser.read_until(new RegExp(`</${name}>`));
const data = parser.read_until(new RegExp(`</${tag.name}>`));
const end = parser.index;
/** @type {AST.Text} */
@ -407,7 +413,7 @@ export default function element(parser) {
};
element.fragment.nodes.push(node);
parser.eat(`</${name}>`, true);
parser.eat(`</${tag.name}>`, true);
element.end = parser.index;
} else {
parser.stack.push(element);
@ -450,8 +456,8 @@ function parent_is_shadowroot_template(stack) {
function read_static_attribute(parser) {
const start = parser.index;
const name = parser.read_until(regex_token_ending_character);
if (!name) return null;
const tag = read_tag(parser, regex_token_ending_character);
if (!tag.name) return null;
/** @type {true | Array<AST.Text | AST.ExpressionTag>} */
let value = true;
@ -485,7 +491,7 @@ function read_static_attribute(parser) {
e.expected_token(parser.index, '=');
}
return create_attribute(name, start, parser.index, value);
return create_attribute(tag.name, tag.loc, start, parser.index, value);
}
/**
@ -538,10 +544,9 @@ function read_attribute(parser) {
return spread;
} else {
const value_start = parser.index;
let name = parser.read_identifier();
const id = parser.read_identifier();
if (name === null) {
if (id.name === '') {
if (
parser.loose &&
(parser.match('#') || parser.match('/') || parser.match('@') || parser.match(':'))
@ -551,7 +556,6 @@ function read_attribute(parser) {
return null;
} else if (parser.loose && parser.match('}')) {
// Likely in the middle of typing, just created the shorthand
name = '';
} else {
e.attribute_empty_shorthand(start);
}
@ -563,32 +567,28 @@ function read_attribute(parser) {
/** @type {AST.ExpressionTag} */
const expression = {
type: 'ExpressionTag',
start: value_start,
end: value_start + name.length,
expression: {
start: value_start,
end: value_start + name.length,
type: 'Identifier',
name
},
start: id.start,
end: id.end,
expression: id,
metadata: {
expression: new ExpressionMetadata()
}
};
return create_attribute(name, start, parser.index, expression);
return create_attribute(id.name, id.loc, start, parser.index, expression);
}
}
const name = parser.read_until(regex_token_ending_character);
if (!name) return null;
const tag = read_tag(parser, regex_token_ending_character);
if (!tag.name) return null;
let end = parser.index;
parser.allow_whitespace();
const colon_index = name.indexOf(':');
const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index));
const colon_index = tag.name.indexOf(':');
const type = colon_index !== -1 && get_directive_type(tag.name.slice(0, colon_index));
/** @type {true | AST.ExpressionTag | Array<AST.Text | AST.ExpressionTag>} */
let value = true;
@ -617,10 +617,10 @@ function read_attribute(parser) {
}
if (type) {
const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|');
const [directive_name, ...modifiers] = tag.name.slice(colon_index + 1).split('|');
if (directive_name === '') {
e.directive_missing_name({ start, end: start + colon_index + 1 }, name);
e.directive_missing_name({ start, end: start + colon_index + 1 }, tag.name);
}
if (type === 'StyleDirective') {
@ -629,6 +629,7 @@ function read_attribute(parser) {
end,
type,
name: directive_name,
name_loc: tag.loc,
modifiers: /** @type {Array<'important'>} */ (modifiers),
value,
metadata: {
@ -659,6 +660,7 @@ function read_attribute(parser) {
end,
type,
name: directive_name,
name_loc: tag.loc,
expression,
metadata: {
expression: new ExpressionMetadata()
@ -669,7 +671,7 @@ function read_attribute(parser) {
directive.modifiers = modifiers;
if (directive.type === 'TransitionDirective') {
const direction = name.slice(0, colon_index);
const direction = tag.name.slice(0, colon_index);
directive.intro = direction === 'in' || direction === 'transition';
directive.outro = direction === 'out' || direction === 'transition';
}
@ -690,7 +692,7 @@ function read_attribute(parser) {
return directive;
}
return create_attribute(name, start, end, value);
return create_attribute(tag.name, tag.loc, start, end, value);
}
/**
@ -851,3 +853,25 @@ function read_sequence(parser, done, location) {
e.unexpected_eof(parser.template.length);
}
}
/**
* @param {Parser} parser
* @param {RegExp} regex
* @returns {Identifier & { start: number, end: number, loc: SourceLocation }}
*/
function read_tag(parser, regex) {
const start = parser.index;
const name = parser.read_until(regex);
const end = parser.index;
return {
type: 'Identifier',
name,
start,
end,
loc: {
start: locator(start),
end: locator(end)
}
};
}

@ -177,7 +177,7 @@ function open(parser) {
if (parser.eat(',')) {
parser.allow_whitespace();
index = parser.read_identifier();
index = parser.read_identifier().name;
if (!index) {
e.expected_identifier(parser.index);
}
@ -347,16 +347,10 @@ function open(parser) {
if (parser.eat('snippet')) {
parser.require_whitespace();
const name_start = parser.index;
let name = parser.read_identifier();
const name_end = parser.index;
const id = parser.read_identifier();
if (name === null) {
if (parser.loose) {
name = '';
} else {
e.expected_identifier(parser.index);
}
if (id.name === '' && !parser.loose) {
e.expected_identifier(parser.index);
}
parser.allow_whitespace();
@ -415,12 +409,7 @@ function open(parser) {
type: 'SnippetBlock',
start,
end: -1,
expression: {
type: 'Identifier',
start: name_start,
end: name_end,
name
},
expression: id,
typeParams: type_params,
parameters: function_expression.params,
body: create_fragment(),

@ -900,7 +900,7 @@ export function analyze_component(root, source, options) {
// We need an empty class to generate the set_class() or class="" correctly
if (!has_spread && !has_class && (node.metadata.scoped || has_class_directive)) {
node.attributes.push(
create_attribute('class', -1, -1, [
create_attribute('class', null, -1, -1, [
{
type: 'Text',
data: '',
@ -915,7 +915,7 @@ export function analyze_component(root, source, options) {
// We need an empty style to generate the set_style() correctly
if (!has_spread && !has_style && has_style_directive) {
node.attributes.push(
create_attribute('style', -1, -1, [
create_attribute('style', null, -1, -1, [
{
type: 'Text',
data: '',

@ -48,8 +48,9 @@ export function RegularElement(node, context) {
node.attributes.push(
create_attribute(
'value',
/** @type {AST.Text} */ (node.fragment.nodes.at(0)).start,
/** @type {AST.Text} */ (node.fragment.nodes.at(-1)).end,
null,
-1,
-1,
// @ts-ignore
node.fragment.nodes
)

@ -1,4 +1,3 @@
/** @import { Location } from 'locate-character' */
/** @import { Namespace } from '#compiler' */
/** @import { ComponentClientTransformState } from '../types.js' */
/** @import { Node } from './types.js' */
@ -15,7 +14,7 @@ function build_locations(nodes) {
for (const node of nodes) {
if (node.type !== 'element') continue;
const { line, column } = /** @type {Location} */ (locator(node.start));
const { line, column } = locator(node.start);
const expression = b.array([b.literal(line), b.literal(column)]);
const children = build_locations(node.children);

@ -40,22 +40,38 @@ export function BindDirective(node, context) {
validate_binding(context.state, node, expression);
}
get = b.thunk(expression);
const assignment = /** @type {Expression} */ (
context.visit(b.assignment('=', /** @type {Pattern} */ (node.expression), b.id('$$value')))
);
/** @type {Expression | undefined} */
set = b.unthunk(
b.arrow(
if (dev) {
// in dev, create named functions, so that `$inspect(...)` delivers
// useful stack traces
get = b.function(b.id('get', node.name_loc), [], b.block([b.return(expression)]));
set = b.function(
b.id('set', node.name_loc),
[b.id('$$value')],
/** @type {Expression} */ (
context.visit(
b.assignment('=', /** @type {Pattern} */ (node.expression), b.id('$$value'))
b.block([b.stmt(assignment)])
);
} else {
// in prod, optimise for brevity
get = b.thunk(expression);
/** @type {Expression | undefined} */
set = b.unthunk(
b.arrow(
[b.id('$$value')],
/** @type {Expression} */ (
context.visit(
b.assignment('=', /** @type {Pattern} */ (node.expression), b.id('$$value'))
)
)
)
)
);
);
if (get === set) {
set = undefined;
if (get === set) {
set = undefined;
}
}
}

@ -39,7 +39,8 @@ export function CallExpression(node, context) {
}
}
return b.call('$.state', value);
const callee = b.id('$.state', node.callee.loc);
return b.call(callee, value);
}
case '$derived':

@ -1,6 +1,5 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import { regex_is_valid_identifier } from '../../../patterns.js';
import { build_component } from './shared/component.js';
/**
@ -8,6 +7,6 @@ import { build_component } from './shared/component.js';
* @param {ComponentContext} context
*/
export function Component(node, context) {
const component = build_component(node, node.name, context);
const component = build_component(node, node.name, node.name_loc, context);
context.state.init.push(component);
}

@ -81,7 +81,7 @@ export function Fragment(node, context) {
if (is_single_element) {
const element = /** @type {AST.RegularElement} */ (trimmed[0]);
const id = b.id(context.state.scope.generate(element.name));
const id = b.id(context.state.scope.generate(element.name), element.name_loc);
context.visit(element, {
...state,

@ -407,6 +407,7 @@ export function RegularElement(node, context) {
const synthetic_node = node.metadata.synthetic_value_node;
const synthetic_attribute = create_attribute(
'value',
null,
synthetic_node.start,
synthetic_node.end,
[synthetic_node]

@ -1,12 +1,13 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import { build_component } from './shared/component.js';
import * as b from '#compiler/builders';
/**
* @param {AST.SvelteComponent} node
* @param {ComponentContext} context
*/
export function SvelteComponent(node, context) {
const component = build_component(node, '$$component', context);
const component = build_component(node, '$$component', null, context);
context.state.init.push(component);
}

@ -14,7 +14,7 @@ export function SvelteHead(node, context) {
context.state.init.push(
b.stmt(
b.call(
'$.head',
b.id('$.head', node.name_loc),
b.literal(hash(filename)),
b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.fragment)))
)

@ -1,5 +1,6 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import { component_name } from '../../../../state.js';
import { build_component } from './shared/component.js';
/**
@ -7,6 +8,6 @@ import { build_component } from './shared/component.js';
* @param {ComponentContext} context
*/
export function SvelteSelf(node, context) {
const component = build_component(node, context.state.analysis.name, context);
const component = build_component(node, component_name, node.name_loc, context);
context.state.init.push(component);
}

@ -20,7 +20,7 @@ export function TitleElement(node, context) {
const statement = b.stmt(
b.assignment(
'=',
b.id('$.document.title'),
b.member(b.id('$.document'), b.id('title', node.name_loc)),
evaluated.is_known
? b.literal(evaluated.value)
: evaluated.is_defined

@ -136,7 +136,8 @@ export function VariableDeclaration(node, context) {
}
if (is_state) {
value = b.call('$.state', value);
const callee = b.id('$.state', /** @type {CallExpression} */ (init).callee.loc);
value = b.call(callee, value);
if (dev) {
value = b.call('$.tag', value, b.literal(id.name));

@ -1,4 +1,4 @@
/** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, Statement } from 'estree' */
/** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, SourceLocation, Statement } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../../types.js' */
import { dev, is_ignored } from '../../../../../state.js';
@ -12,10 +12,11 @@ import { determine_slot } from '../../../../../utils/slot.js';
/**
* @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf} node
* @param {string} component_name
* @param {SourceLocation | null} loc
* @param {ComponentContext} context
* @returns {Statement}
*/
export function build_component(node, component_name, context) {
export function build_component(node, component_name, loc, context) {
/** @type {Expression} */
const anchor = context.state.node;
@ -259,15 +260,9 @@ export function build_component(node, component_name, context) {
attribute.expression.type === 'Identifier' &&
context.state.scope.get(attribute.expression.name)?.kind === 'store_sub';
// Delay prop pushes so bindings come at the end, to avoid spreads overwriting them
if (is_store_sub) {
push_prop(
b.get(attribute.name, [b.stmt(b.call('$.mark_store_binding')), b.return(expression)]),
true
);
} else {
push_prop(b.get(attribute.name, [b.return(expression)]), true);
}
const get = is_store_sub
? b.get(attribute.name, [b.stmt(b.call('$.mark_store_binding')), b.return(expression)])
: b.get(attribute.name, [b.return(expression)]);
const assignment = b.assignment(
'=',
@ -275,10 +270,16 @@ export function build_component(node, component_name, context) {
b.id('$$value')
);
push_prop(
b.set(attribute.name, [b.stmt(/** @type {Expression} */ (context.visit(assignment)))]),
true
);
const set = b.set(attribute.name, [
b.stmt(/** @type {Expression} */ (context.visit(assignment)))
]);
get.key.loc = attribute.name_loc;
set.key.loc = attribute.name_loc;
// Delay prop pushes so bindings come at the end, to avoid spreads overwriting them
push_prop(get, true);
push_prop(set, true);
}
}
} else if (attribute.type === 'AttachTag') {
@ -434,16 +435,17 @@ export function build_component(node, component_name, context) {
/** @param {Expression} node_id */
let fn = (node_id) => {
return b.call(
// TODO We can remove this ternary once we remove legacy mode, since in runes mode dynamic components
// will be handled separately through the `$.component` function, and then the component name will
// always be referenced through just the identifier here.
is_component_dynamic
? intermediate_name
: /** @type {Expression} */ (context.visit(b.member_id(component_name))),
node_id,
props_expression
);
// TODO We can remove this ternary once we remove legacy mode, since in runes mode dynamic components
// will be handled separately through the `$.component` function, and then the component name will
// always be referenced through just the identifier here.
const callee = is_component_dynamic
? b.id(intermediate_name)
: /** @type {Expression} */ (context.visit(b.member_id(component_name)));
// line up the `Foo` in `Foo(...)` and `<Foo>` for usable stack traces
callee.loc = loc;
return b.call(callee, node_id, props_expression);
};
if (bind_this !== null) {

@ -32,7 +32,13 @@ export function visit_event_attribute(node, context) {
}
context.state.init.push(
b.stmt(b.assignment('=', b.member(context.state.node, '__' + event_name), handler))
b.stmt(
b.assignment(
'=',
b.member(context.state.node, b.id('__' + event_name, node.name_loc)),
handler
)
)
);
} else {
const statement = b.stmt(
@ -140,7 +146,7 @@ export function build_event_handler(node, metadata, context) {
b.this,
b.id('$$args'),
b.id(context.state.analysis.name),
loc && b.array([b.literal(loc.line), b.literal(loc.column)]),
b.array([b.literal(loc.line), b.literal(loc.column)]),
has_side_effects(node) && b.true,
remove_parens && b.true
);

@ -1,4 +1,4 @@
/** @import { Expression } from 'estree' */
/** @import { Expression, Identifier, SourceLocation } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../../types' */
import { cannot_be_set_statically } from '../../../../../../utils.js';
@ -42,13 +42,14 @@ export function process_children(nodes, initial, is_element, context) {
/**
* @param {boolean} is_text
* @param {string} name
* @param {SourceLocation | null} [loc]
*/
function flush_node(is_text, name) {
function flush_node(is_text, name, loc) {
const expression = get_node(is_text);
let id = expression;
if (id.type !== 'Identifier') {
id = b.id(context.state.scope.generate(name));
id = b.id(context.state.scope.generate(name), loc);
context.state.init.push(b.var(id, expression));
}
@ -109,7 +110,12 @@ export function process_children(nodes, initial, is_element, context) {
) {
node.metadata.is_controlled = true;
} else {
const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node');
const id = flush_node(
false,
node.type === 'RegularElement' ? node.name : 'node',
node.type === 'RegularElement' ? node.name_loc : null
);
child_state = { ...context.state, node: id };
}

@ -366,8 +366,8 @@ export function validate_binding(state, binding, expression) {
: b.literal(/** @type {Identifier} */ (expression.property).name)
)
),
loc && b.literal(loc.line),
loc && b.literal(loc.column)
b.literal(loc.line),
b.literal(loc.column)
)
)
);

@ -99,7 +99,7 @@ export function RegularElement(node, context) {
}
if (dev) {
const location = /** @type {Location} */ (locator(node.start));
const location = locator(node.start);
state.template.push(
b.stmt(
b.call(

@ -50,7 +50,7 @@ export function SvelteElement(node, context) {
build_element_attributes(node, { ...context, state }, optimiser.transform);
if (dev) {
const location = /** @type {Location} */ (locator(node.start));
const location = locator(node.start);
statements.push(
b.stmt(
b.call(

@ -142,7 +142,7 @@ export function build_element_attributes(node, context, transform) {
);
attributes.push(
create_attribute('checked', -1, -1, [
create_attribute('checked', null, -1, -1, [
{
type: 'ExpressionTag',
start: -1,
@ -165,7 +165,7 @@ export function build_element_attributes(node, context, transform) {
);
} else {
attributes.push(
create_attribute(attribute.name, -1, -1, [
create_attribute(attribute.name, null, -1, -1, [
{
type: 'ExpressionTag',
start: -1,

@ -1,4 +1,4 @@
/** @import { Expression, PrivateIdentifier } from 'estree' */
/** @import { Expression, PrivateIdentifier, SourceLocation } from 'estree' */
/** @import { AST, Binding } from '#compiler' */
import * as b from '#compiler/builders';
@ -47,17 +47,19 @@ export function is_custom_element_node(node) {
/**
* @param {string} name
* @param {SourceLocation | null} name_loc
* @param {number} start
* @param {number} end
* @param {AST.Attribute['value']} value
* @returns {AST.Attribute}
*/
export function create_attribute(name, start, end, value) {
export function create_attribute(name, name_loc, start, end, value) {
return {
type: 'Attribute',
start,
end,
name,
name_loc,
value,
metadata: {
delegated: false,

@ -40,19 +40,28 @@ export let dev;
export let runes = false;
export let locator = getLocator('', { offsetLine: 1 });
/** @type {(index: number) => Location} */
export let locator;
/** @param {string} value */
export function set_source(value) {
source = value;
locator = getLocator(source, { offsetLine: 1 });
const l = getLocator(source, { offsetLine: 1 });
locator = (i) => {
const loc = l(i);
if (!loc) throw new Error('An impossible situation occurred');
return loc;
};
}
/**
* @param {AST.SvelteNode & { start?: number | undefined }} node
*/
export function locate_node(node) {
const loc = /** @type {Location} */ (locator(/** @type {number} */ (node.start)));
const loc = locator(/** @type {number} */ (node.start));
return `${sanitize_location(filename)}:${loc?.line}:${loc.column}`;
}
@ -103,7 +112,6 @@ export function reset(state) {
runes = false;
component_name = UNKNOWN_FILENAME;
source = '';
locator = () => undefined;
filename = (state.filename ?? UNKNOWN_FILENAME).replace(/\\/g, '/');
warning_filter = state.warning ?? (() => true);
warnings = [];

@ -13,7 +13,8 @@ import type {
Program,
ChainExpression,
SimpleCallExpression,
SequenceExpression
SequenceExpression,
SourceLocation
} from 'estree';
import type { Scope } from '../phases/scope';
import type { _CSS } from './css';
@ -188,7 +189,7 @@ export namespace AST {
}
/** An `animate:` directive */
export interface AnimateDirective extends BaseNode {
export interface AnimateDirective extends BaseAttribute {
type: 'AnimateDirective';
/** The 'x' in `animate:x` */
name: string;
@ -201,7 +202,7 @@ export namespace AST {
}
/** A `bind:` directive */
export interface BindDirective extends BaseNode {
export interface BindDirective extends BaseAttribute {
type: 'BindDirective';
/** The 'x' in `bind:x` */
name: string;
@ -217,7 +218,7 @@ export namespace AST {
}
/** A `class:` directive */
export interface ClassDirective extends BaseNode {
export interface ClassDirective extends BaseAttribute {
type: 'ClassDirective';
/** The 'x' in `class:x` */
name: 'class';
@ -230,7 +231,7 @@ export namespace AST {
}
/** A `let:` directive */
export interface LetDirective extends BaseNode {
export interface LetDirective extends BaseAttribute {
type: 'LetDirective';
/** The 'x' in `let:x` */
name: string;
@ -239,7 +240,7 @@ export namespace AST {
}
/** An `on:` directive */
export interface OnDirective extends BaseNode {
export interface OnDirective extends BaseAttribute {
type: 'OnDirective';
/** The 'x' in `on:x` */
name: string;
@ -263,7 +264,7 @@ export namespace AST {
}
/** A `style:` directive */
export interface StyleDirective extends BaseNode {
export interface StyleDirective extends BaseAttribute {
type: 'StyleDirective';
/** The 'x' in `style:x` */
name: string;
@ -278,7 +279,7 @@ export namespace AST {
// TODO have separate in/out/transition directives
/** A `transition:`, `in:` or `out:` directive */
export interface TransitionDirective extends BaseNode {
export interface TransitionDirective extends BaseAttribute {
type: 'TransitionDirective';
/** The 'x' in `transition:x` */
name: string;
@ -296,7 +297,7 @@ export namespace AST {
}
/** A `use:` directive */
export interface UseDirective extends BaseNode {
export interface UseDirective extends BaseAttribute {
type: 'UseDirective';
/** The 'x' in `use:x` */
name: string;
@ -310,6 +311,7 @@ export namespace AST {
export interface BaseElement extends BaseNode {
name: string;
name_loc: SourceLocation;
attributes: Array<Attribute | SpreadAttribute | Directive | AttachTag>;
fragment: Fragment;
}
@ -528,9 +530,13 @@ export namespace AST {
};
}
export interface Attribute extends BaseNode {
type: 'Attribute';
export interface BaseAttribute extends BaseNode {
name: string;
name_loc: SourceLocation | null;
}
export interface Attribute extends BaseAttribute {
type: 'Attribute';
/**
* Quoted/string values are represented by an array, even if they contain a single expression like `"{x}"`
*/

@ -262,10 +262,14 @@ export function get(name, body) {
/**
* @param {string} name
* @param {ESTree.SourceLocation | null} [loc]
* @returns {ESTree.Identifier}
*/
export function id(name) {
return { type: 'Identifier', name };
export function id(name, loc) {
const node = /** @type {ESTree.Identifier} */ ({ type: 'Identifier', name });
if (loc) node.loc = loc;
return node;
}
/**

@ -15,6 +15,18 @@
"end": 20,
"type": "Action",
"name": "autofocus",
"name_loc": {
"start": {
"line": 1,
"column": 7,
"character": 7
},
"end": {
"line": 1,
"column": 20,
"character": 20
}
},
"expression": null,
"modifiers": []
},
@ -23,6 +35,18 @@
"end": 34,
"type": "Action",
"name": "autofocus",
"name_loc": {
"start": {
"line": 1,
"column": 21,
"character": 21
},
"end": {
"line": 1,
"column": 34,
"character": 34
}
},
"expression": null,
"modifiers": []
}
@ -31,4 +55,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 39,
"type": "Action",
"name": "tooltip",
"name_loc": {
"start": {
"line": 1,
"column": 7,
"character": 7
},
"end": {
"line": 1,
"column": 18,
"character": 18
}
},
"expression": {
"type": "CallExpression",
"start": 21,
@ -73,4 +85,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 28,
"type": "Action",
"name": "tooltip",
"name_loc": {
"start": {
"line": 1,
"column": 7,
"character": 7
},
"end": {
"line": 1,
"column": 18,
"character": 18
}
},
"expression": {
"type": "Identifier",
"start": 20,
@ -38,4 +50,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 36,
"type": "Action",
"name": "tooltip",
"name_loc": {
"start": {
"line": 1,
"column": 7,
"character": 7
},
"end": {
"line": 1,
"column": 18,
"character": 18
}
},
"expression": {
"type": "Literal",
"start": 21,
@ -39,4 +51,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 20,
"type": "Action",
"name": "autofocus",
"name_loc": {
"start": {
"line": 1,
"column": 7,
"character": 7
},
"end": {
"line": 1,
"column": 20,
"character": 20
}
},
"expression": null,
"modifiers": []
}
@ -23,4 +35,4 @@
}
]
}
}
}

@ -20,6 +20,18 @@
"end": 50,
"type": "Animation",
"name": "flip",
"name_loc": {
"start": {
"line": 2,
"column": 6,
"character": 38
},
"end": {
"line": 2,
"column": 18,
"character": 50
}
},
"expression": null,
"modifiers": []
}
@ -39,6 +51,7 @@
"type": "Identifier",
"name": "thing",
"start": 17,
"end": 22,
"loc": {
"start": {
"line": 1,
@ -50,8 +63,7 @@
"column": 22,
"character": 22
}
},
"end": 22
}
},
"expression": {
"type": "Identifier",
@ -88,4 +100,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 22,
"type": "Class",
"name": "foo",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 14,
"character": 14
}
},
"expression": {
"type": "Identifier",
"start": 16,
@ -38,4 +50,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 3,
"end": 30,
"name": "href",
"name_loc": {
"start": {
"line": 1,
"column": 3,
"character": 3
},
"end": {
"line": 1,
"column": 7,
"character": 7
}
},
"value": [
{
"start": 8,
@ -38,4 +50,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 7,
"end": 15,
"name": "foo",
"name_loc": {
"start": {
"line": 1,
"column": 7,
"character": 7
},
"end": {
"line": 1,
"column": 10,
"character": 10
}
},
"value": [
{
"start": 11,
@ -52,4 +64,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 10,
"end": 29,
"name": "readonly",
"name_loc": {
"start": {
"line": 1,
"column": 10,
"character": 10
},
"end": {
"line": 1,
"column": 18,
"character": 18
}
},
"value": [
{
"type": "MustacheTag",
@ -44,4 +56,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 5,
"end": 28,
"name": "style",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 10,
"character": 10
}
},
"value": [
{
"start": 12,
@ -80,4 +92,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 5,
"end": 9,
"name": "a",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 6,
"character": 6
}
},
"value": [
{
"start": 8,
@ -30,6 +42,18 @@
"start": 10,
"end": 16,
"name": "b",
"name_loc": {
"start": {
"line": 1,
"column": 10,
"character": 10
},
"end": {
"line": 1,
"column": 11,
"character": 11
}
},
"value": [
{
"type": "MustacheTag",
@ -60,6 +84,18 @@
"start": 17,
"end": 21,
"name": "c",
"name_loc": {
"start": {
"line": 1,
"column": 17,
"character": 17
},
"end": {
"line": 1,
"column": 18,
"character": 18
}
},
"value": [
{
"start": 20,
@ -75,6 +111,18 @@
"start": 22,
"end": 30,
"name": "d",
"name_loc": {
"start": {
"line": 1,
"column": 22,
"character": 22
},
"end": {
"line": 1,
"column": 23,
"character": 23
}
},
"value": [
{
"type": "MustacheTag",
@ -105,4 +153,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 5,
"end": 76,
"name": "data-foo",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 13,
"character": 13
}
},
"value": [
{
"start": 15,
@ -30,4 +42,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 5,
"end": 11,
"name": "id",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 7,
"character": 7
}
},
"value": [
{
"start": 9,
@ -30,6 +42,18 @@
"start": 12,
"end": 21,
"name": "class",
"name_loc": {
"start": {
"line": 1,
"column": 12,
"character": 12
},
"end": {
"line": 1,
"column": 17,
"character": 17
}
},
"value": [
{
"start": 19,
@ -45,4 +69,4 @@
}
]
}
}
}

@ -15,16 +15,40 @@
"start": 5,
"end": 9,
"name": "id",
"name_loc": {
"start": {
"line": 1,
"column": 6,
"character": 6
},
"end": {
"line": 1,
"column": 8,
"character": 8
}
},
"value": [
{
"type": "AttributeShorthand",
"start": 6,
"end": 8,
"expression": {
"type": "Identifier",
"name": "id",
"start": 6,
"end": 8,
"type": "Identifier",
"name": "id"
"loc": {
"start": {
"line": 1,
"column": 6,
"character": 6
},
"end": {
"line": 1,
"column": 8,
"character": 8
}
}
}
}
]
@ -34,4 +58,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 10,
"end": 18,
"name": "readonly",
"name_loc": {
"start": {
"line": 1,
"column": 10,
"character": 10
},
"end": {
"line": 1,
"column": 18,
"character": 18
}
},
"value": true
}
],
@ -22,4 +34,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 5,
"end": 16,
"name": "class",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 10,
"character": 10
}
},
"value": [
{
"start": 12,
@ -30,4 +42,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 36,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 26,
"character": 26
}
},
"modifiers": [
"important"
],
@ -47,4 +59,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 16,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 16,
"character": 16
}
},
"modifiers": [],
"value": true
}
@ -23,4 +35,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 22,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 16,
"character": 16
}
},
"modifiers": [],
"value": [
{
@ -47,6 +59,18 @@
"end": 52,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 2,
"column": 5,
"character": 35
},
"end": {
"line": 2,
"column": 16,
"character": 46
}
},
"modifiers": [],
"value": [
{
@ -79,6 +103,18 @@
"end": 80,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 3,
"column": 5,
"character": 65
},
"end": {
"line": 3,
"column": 16,
"character": 76
}
},
"modifiers": [],
"value": [
{
@ -111,6 +147,18 @@
"end": 120,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 4,
"column": 5,
"character": 93
},
"end": {
"line": 4,
"column": 16,
"character": 104
}
},
"modifiers": [],
"value": [
{
@ -164,6 +212,18 @@
"end": 160,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 5,
"column": 5,
"character": 133
},
"end": {
"line": 5,
"column": 16,
"character": 144
}
},
"modifiers": [],
"value": [
{
@ -217,6 +277,18 @@
"end": 198,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 6,
"column": 5,
"character": 173
},
"end": {
"line": 6,
"column": 16,
"character": 184
}
},
"modifiers": [],
"value": [
{
@ -270,6 +342,18 @@
"end": 245,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 7,
"column": 5,
"character": 211
},
"end": {
"line": 7,
"column": 16,
"character": 222
}
},
"modifiers": [],
"value": [
{
@ -359,4 +443,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 26,
"type": "StyleDirective",
"name": "color",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 16,
"character": 16
}
},
"modifiers": [],
"value": [
{
@ -45,4 +57,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 5,
"end": 24,
"name": "style",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 10,
"character": 10
}
},
"value": [
{
"start": 12,
@ -38,4 +50,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 5,
"end": 14,
"name": "class",
"name_loc": {
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 10,
"character": 10
}
},
"value": [
{
"start": 11,
@ -46,6 +58,18 @@
"start": 25,
"end": 31,
"name": "href",
"name_loc": {
"start": {
"line": 2,
"column": 3,
"character": 25
},
"end": {
"line": 2,
"column": 7,
"character": 29
}
},
"value": [
{
"start": 30,
@ -85,6 +109,18 @@
"start": 44,
"end": 53,
"name": "href",
"name_loc": {
"start": {
"line": 3,
"column": 3,
"character": 44
},
"end": {
"line": 3,
"column": 7,
"character": 48
}
},
"value": [
{
"start": 49,
@ -108,4 +144,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 23,
"type": "EventHandler",
"name": "click",
"name_loc": {
"start": {
"line": 1,
"column": 8,
"character": 8
},
"end": {
"line": 1,
"column": 16,
"character": 16
}
},
"expression": {
"type": "Identifier",
"start": 19,
@ -46,4 +58,4 @@
}
]
}
}
}

@ -29,6 +29,7 @@
"type": "Identifier",
"name": "theError",
"start": 47,
"end": 55,
"loc": {
"start": {
"line": 3,
@ -40,8 +41,7 @@
"column": 16,
"character": 55
}
},
"end": 55
}
},
"pending": {
"type": "PendingBlock",
@ -183,4 +183,4 @@
}
]
}
}
}

@ -28,6 +28,7 @@
"type": "Identifier",
"name": "theValue",
"start": 46,
"end": 54,
"loc": {
"start": {
"line": 3,
@ -39,13 +40,13 @@
"column": 15,
"character": 54
}
},
"end": 54
}
},
"error": {
"type": "Identifier",
"name": "theError",
"start": 96,
"end": 104,
"loc": {
"start": {
"line": 5,
@ -57,8 +58,7 @@
"column": 16,
"character": 104
}
},
"end": 104
}
},
"pending": {
"type": "PendingBlock",
@ -252,4 +252,4 @@
}
]
}
}
}

@ -22,6 +22,18 @@
"end": 46,
"type": "Binding",
"name": "foo",
"name_loc": {
"start": {
"line": 5,
"column": 8,
"character": 38
},
"end": {
"line": 5,
"column": 16,
"character": 46
}
},
"expression": {
"start": 43,
"end": 46,
@ -109,4 +121,4 @@
"sourceType": "module"
}
}
}
}

@ -22,6 +22,18 @@
"end": 55,
"type": "Binding",
"name": "value",
"name_loc": {
"start": {
"line": 5,
"column": 7,
"character": 38
},
"end": {
"line": 5,
"column": 17,
"character": 48
}
},
"expression": {
"type": "Identifier",
"start": 50,
@ -119,4 +131,4 @@
"sourceType": "module"
}
}
}
}

@ -156,6 +156,18 @@
"start": 263,
"end": 274,
"name": "class",
"name_loc": {
"start": {
"line": 7,
"column": 29,
"character": 263
},
"end": {
"line": 7,
"column": 34,
"character": 268
}
},
"value": [
{
"start": 270,
@ -171,4 +183,4 @@
}
]
}
}
}

@ -62,6 +62,18 @@
"start": 72,
"end": 83,
"name": "class",
"name_loc": {
"start": {
"line": 2,
"column": 27,
"character": 72
},
"end": {
"line": 2,
"column": 32,
"character": 77
}
},
"value": [
{
"start": 79,
@ -77,4 +89,4 @@
}
]
}
}
}

@ -44,6 +44,7 @@
"type": "Identifier",
"name": "animal",
"start": 18,
"end": 24,
"loc": {
"start": {
"line": 1,
@ -55,8 +56,7 @@
"column": 24,
"character": 24
}
},
"end": 24
}
},
"expression": {
"type": "Identifier",
@ -100,4 +100,4 @@
}
]
}
}
}

@ -72,6 +72,7 @@
"type": "Identifier",
"name": "animal",
"start": 18,
"end": 24,
"loc": {
"start": {
"line": 1,
@ -83,8 +84,7 @@
"column": 24,
"character": 24
}
},
"end": 24
}
},
"expression": {
"type": "Identifier",
@ -106,4 +106,4 @@
}
]
}
}
}

@ -44,6 +44,7 @@
"type": "Identifier",
"name": "todo",
"start": 16,
"end": 20,
"loc": {
"start": {
"line": 1,
@ -55,8 +56,7 @@
"column": 20,
"character": 20
}
},
"end": 20
}
},
"expression": {
"type": "Identifier",
@ -126,4 +126,4 @@
}
]
}
}
}

@ -44,6 +44,7 @@
"type": "Identifier",
"name": "animal",
"start": 18,
"end": 24,
"loc": {
"start": {
"line": 1,
@ -55,8 +56,7 @@
"column": 24,
"character": 24
}
},
"end": 24
}
},
"expression": {
"type": "Identifier",
@ -77,4 +77,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 6,
"end": 13,
"name": "attr",
"name_loc": {
"start": {
"line": 1,
"column": 6,
"character": 6
},
"end": {
"line": 1,
"column": 10,
"character": 10
}
},
"value": [
{
"start": 12,
@ -46,6 +58,18 @@
"start": 28,
"end": 35,
"name": "attr",
"name_loc": {
"start": {
"line": 2,
"column": 6,
"character": 28
},
"end": {
"line": 2,
"column": 10,
"character": 32
}
},
"value": [
{
"start": 34,
@ -61,4 +85,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 6,
"end": 16,
"name": "attr",
"name_loc": {
"start": {
"line": 1,
"column": 6,
"character": 6
},
"end": {
"line": 1,
"column": 10,
"character": 10
}
},
"value": [
{
"start": 12,
@ -46,6 +58,18 @@
"start": 31,
"end": 41,
"name": "attr",
"name_loc": {
"start": {
"line": 2,
"column": 6,
"character": 31
},
"end": {
"line": 2,
"column": 10,
"character": 35
}
},
"value": [
{
"start": 37,
@ -61,4 +85,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"start": 10,
"end": 14,
"name": "html",
"name_loc": {
"start": {
"line": 1,
"column": 10,
"character": 10
},
"end": {
"line": 1,
"column": 14,
"character": 14
}
},
"value": true
}
],
@ -22,4 +34,4 @@
}
]
}
}
}

@ -15,6 +15,18 @@
"end": 45,
"type": "EventHandler",
"name": "click",
"name_loc": {
"start": {
"line": 1,
"column": 8,
"character": 8
},
"end": {
"line": 1,
"column": 16,
"character": 16
}
},
"expression": {
"type": "ArrowFunctionExpression",
"start": 19,
@ -161,4 +173,4 @@
}
]
}
}
}

@ -17,9 +17,21 @@
"end": 92,
"expression": {
"type": "Identifier",
"name": "generic",
"start": 40,
"end": 47,
"name": "generic"
"loc": {
"start": {
"line": 4,
"column": 10,
"character": 40
},
"end": {
"line": 4,
"column": 17,
"character": 47
}
}
},
"parameters": [
{
@ -123,9 +135,21 @@
"end": 192,
"expression": {
"type": "Identifier",
"name": "complex_generic",
"start": 104,
"end": 119,
"name": "complex_generic"
"loc": {
"start": {
"line": 8,
"column": 10,
"character": 104
},
"end": {
"line": 8,
"column": 25,
"character": 119
}
}
},
"parameters": [
{
@ -241,4 +265,4 @@
"sourceType": "module"
}
}
}
}

@ -22,6 +22,18 @@
"end": 692,
"type": "EventHandler",
"name": "click",
"name_loc": {
"start": {
"line": 34,
"column": 1,
"character": 574
},
"end": {
"line": 34,
"column": 9,
"character": 582
}
},
"expression": {
"type": "ArrowFunctionExpression",
"start": 596,
@ -869,4 +881,4 @@
"sourceType": "module"
}
}
}
}

@ -75,9 +75,21 @@
"end": 75,
"expression": {
"type": "Identifier",
"name": "",
"start": 63,
"end": 63,
"name": ""
"loc": {
"start": {
"line": 9,
"column": 10,
"character": 63
},
"end": {
"line": 9,
"column": 10,
"character": 63
}
}
},
"parameters": [],
"children": []
@ -95,13 +107,25 @@
"end": 102,
"expression": {
"type": "Identifier",
"name": "foo",
"start": 87,
"end": 90,
"name": "foo"
"loc": {
"start": {
"line": 12,
"column": 10,
"character": 87
},
"end": {
"line": 12,
"column": 13,
"character": 90
}
}
},
"parameters": [],
"children": []
}
]
}
}
}

@ -15,16 +15,40 @@
"start": 5,
"end": 7,
"name": "",
"name_loc": {
"start": {
"line": 1,
"column": 6,
"character": 6
},
"end": {
"line": 1,
"column": 6,
"character": 6
}
},
"value": [
{
"type": "AttributeShorthand",
"start": 6,
"end": 6,
"expression": {
"type": "Identifier",
"name": "",
"start": 6,
"end": 6,
"type": "Identifier",
"name": ""
"loc": {
"start": {
"line": 1,
"column": 6,
"character": 6
},
"end": {
"line": 1,
"column": 6,
"character": 6
}
}
}
}
]
@ -50,6 +74,18 @@
"start": 20,
"end": 26,
"name": "foo",
"name_loc": {
"start": {
"line": 2,
"column": 5,
"character": 20
},
"end": {
"line": 2,
"column": 8,
"character": 23
}
},
"value": [
{
"type": "MustacheTag",
@ -85,6 +121,18 @@
"start": 40,
"end": 48,
"name": "foo",
"name_loc": {
"start": {
"line": 4,
"column": 5,
"character": 40
},
"end": {
"line": 4,
"column": 8,
"character": 43
}
},
"value": [
{
"type": "MustacheTag",
@ -120,6 +168,18 @@
"start": 61,
"end": 73,
"name": "foo",
"name_loc": {
"start": {
"line": 5,
"column": 5,
"character": 61
},
"end": {
"line": 5,
"column": 8,
"character": 64
}
},
"value": [
{
"type": "MustacheTag",
@ -155,6 +215,18 @@
"start": 92,
"end": 110,
"name": "onclick",
"name_loc": {
"start": {
"line": 6,
"column": 11,
"character": 92
},
"end": {
"line": 6,
"column": 18,
"character": 99
}
},
"value": [
{
"type": "MustacheTag",
@ -190,6 +262,18 @@
"end": 137,
"type": "Binding",
"name": "value",
"name_loc": {
"start": {
"line": 8,
"column": 7,
"character": 122
},
"end": {
"line": 8,
"column": 17,
"character": 132
}
},
"expression": {
"type": "Identifier",
"start": 134,
@ -272,6 +356,7 @@
"type": "Identifier",
"name": "item",
"start": 197,
"end": 201,
"loc": {
"start": {
"line": 15,
@ -283,8 +368,7 @@
"column": 20,
"character": 201
}
},
"end": 201
}
},
"expression": {
"type": "Identifier",
@ -325,6 +409,7 @@
"type": "Identifier",
"name": "item",
"start": 234,
"end": 238,
"loc": {
"start": {
"line": 17,
@ -336,8 +421,7 @@
"column": 19,
"character": 238
}
},
"end": 238
}
},
"expression": {
"type": "Identifier",
@ -408,6 +492,7 @@
"type": "Identifier",
"name": "y",
"start": 285,
"end": 286,
"loc": {
"start": {
"line": 21,
@ -419,8 +504,7 @@
"column": 17,
"character": 286
}
},
"end": 286
}
},
"error": null,
"pending": {
@ -467,6 +551,7 @@
"type": "Identifier",
"name": "y",
"start": 314,
"end": 315,
"loc": {
"start": {
"line": 23,
@ -478,8 +563,7 @@
"column": 18,
"character": 315
}
},
"end": 315
}
},
"pending": {
"type": "PendingBlock",
@ -505,4 +589,4 @@
}
]
}
}
}

@ -240,6 +240,7 @@
"type": "Identifier",
"name": "y",
"start": 138,
"end": 139,
"loc": {
"start": {
"line": 19,
@ -251,8 +252,7 @@
"column": 13,
"character": 139
}
},
"end": 139
}
},
"expression": {
"type": "Identifier",
@ -273,4 +273,4 @@
}
]
}
}
}

@ -29,6 +29,18 @@
"start": 13,
"end": 22,
"name": "foo",
"name_loc": {
"start": {
"line": 2,
"column": 7,
"character": 13
},
"end": {
"line": 2,
"column": 10,
"character": 16
}
},
"value": [
{
"type": "MustacheTag",
@ -90,6 +102,18 @@
"start": 44,
"end": 53,
"name": "foo",
"name_loc": {
"start": {
"line": 6,
"column": 7,
"character": 44
},
"end": {
"line": 6,
"column": 10,
"character": 47
}
},
"value": [
{
"type": "MustacheTag",
@ -158,6 +182,18 @@
"start": 79,
"end": 88,
"name": "foo",
"name_loc": {
"start": {
"line": 10,
"column": 7,
"character": 79
},
"end": {
"line": 10,
"column": 10,
"character": 82
}
},
"value": [
{
"type": "MustacheTag",
@ -226,6 +262,18 @@
"start": 113,
"end": 122,
"name": "foo",
"name_loc": {
"start": {
"line": 14,
"column": 7,
"character": 113
},
"end": {
"line": 14,
"column": 10,
"character": 116
}
},
"value": [
{
"type": "MustacheTag",
@ -317,6 +365,18 @@
"start": 161,
"end": 170,
"name": "foo",
"name_loc": {
"start": {
"line": 20,
"column": 5,
"character": 161
},
"end": {
"line": 20,
"column": 8,
"character": 164
}
},
"value": [
{
"type": "MustacheTag",
@ -346,4 +406,4 @@
}
]
}
}
}

@ -68,6 +68,18 @@
"start": 35,
"end": 44,
"name": "foo",
"name_loc": {
"start": {
"line": 6,
"column": 7,
"character": 35
},
"end": {
"line": 6,
"column": 10,
"character": 38
}
},
"value": [
{
"type": "MustacheTag",
@ -275,6 +287,18 @@
"start": 159,
"end": 168,
"name": "foo",
"name_loc": {
"start": {
"line": 26,
"column": 7,
"character": 159
},
"end": {
"line": 26,
"column": 10,
"character": 162
}
},
"value": [
{
"type": "MustacheTag",
@ -360,4 +384,4 @@
}
]
}
}
}

@ -119,6 +119,7 @@
"type": "Identifier",
"name": "f",
"start": 97,
"end": 98,
"loc": {
"start": {
"line": 13,
@ -130,8 +131,7 @@
"column": 8,
"character": 98
}
},
"end": 98
}
},
"error": null,
"pending": {
@ -219,6 +219,7 @@
"type": "Identifier",
"name": "f",
"start": 137,
"end": 138,
"loc": {
"start": {
"line": 18,
@ -230,8 +231,7 @@
"column": 8,
"character": 138
}
},
"end": 138
}
},
"error": null,
"pending": {
@ -289,4 +289,4 @@
}
]
}
}
}

@ -22,6 +22,18 @@
"end": 53,
"type": "Binding",
"name": "this",
"name_loc": {
"start": {
"line": 5,
"column": 8,
"character": 38
},
"end": {
"line": 5,
"column": 17,
"character": 47
}
},
"expression": {
"type": "Identifier",
"start": 49,
@ -119,4 +131,4 @@
"sourceType": "module"
}
}
}
}

@ -69,6 +69,18 @@
"start": 30,
"end": 49,
"name": "depth",
"name_loc": {
"start": {
"line": 2,
"column": 14,
"character": 30
},
"end": {
"line": 2,
"column": 19,
"character": 35
}
},
"value": [
{
"type": "MustacheTag",
@ -133,4 +145,4 @@
}
]
}
}
}

@ -22,6 +22,18 @@
"start": 16,
"end": 26,
"name": "slot",
"name_loc": {
"start": {
"line": 1,
"column": 16,
"character": 16
},
"end": {
"line": 1,
"column": 20,
"character": 20
}
},
"value": [
{
"start": 22,
@ -39,4 +51,4 @@
}
]
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save