fix(internal): parse let directive as pattern instead of expression

parser-let-destructure
gtmnayan 2 years ago
parent ddfff4df8c
commit c1f143411c

@ -1664,13 +1664,10 @@ export default class Component {
const { let_attributes } = parent; const { let_attributes } = parent;
for (const attr of let_attributes) { for (const attr of let_attributes) {
if ( if (attr.expression) {
// @ts-expect-error // @ts-expect-error
// TODO extract_names only considers patterns but let attributes return expressions if (extract_names(attr.expression).includes(name)) return true;
(attr.expression && extract_names(attr.expression).includes(name)) || } else if (attr.name === name) return true;
attr.name === name
)
return true;
} }
} }
} }

@ -2,7 +2,7 @@ import Node from './shared/Node.js';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import compiler_errors from '../compiler_errors.js'; import compiler_errors from '../compiler_errors.js';
const applicable = new Set(['Identifier', 'ObjectExpression', 'ArrayExpression', 'Property']); const applicable = new Set(['Identifier', 'ObjectPattern', 'ArrayPattern', 'Property']);
/** @extends Node<'Let'> */ /** @extends Node<'Let'> */
export default class Let extends Node { export default class Let extends Node {
@ -36,13 +36,6 @@ export default class Let extends Node {
if (node.type === 'Identifier') { if (node.type === 'Identifier') {
names.push(/** @type {import('estree').Identifier} */ (node).name); names.push(/** @type {import('estree').Identifier} */ (node).name);
} }
// slightly unfortunate hack
if (node.type === 'ArrayExpression') {
node.type = 'ArrayPattern';
}
if (node.type === 'ObjectExpression') {
node.type = 'ObjectPattern';
}
} }
}); });
} else { } else {

@ -6,6 +6,7 @@ import read_expression from '../read/expression.js';
import read_script from '../read/script.js'; import read_script from '../read/script.js';
import read_style from '../read/style.js'; import read_style from '../read/style.js';
import { closing_tag_omitted, decode_character_references } from '../utils/html.js'; import { closing_tag_omitted, decode_character_references } from '../utils/html.js';
import read_context from '../read/context.js';
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
@ -348,7 +349,7 @@ function read_attribute(parser, unique_names) {
let value = true; let value = true;
if (parser.eat('=')) { if (parser.eat('=')) {
parser.allow_whitespace(); parser.allow_whitespace();
value = read_attribute_value(parser); value = read_attribute_value(parser, type === 'Let');
end = parser.index; end = parser.index;
} else if (parser.match_regex(regex_starts_with_quote_characters)) { } else if (parser.match_regex(regex_starts_with_quote_characters)) {
parser.error(parser_errors.unexpected_token('='), parser.index); parser.error(parser_errors.unexpected_token('='), parser.index);
@ -438,8 +439,9 @@ function get_directive_type(name) {
/** /**
* @param {import('../index.js').Parser} parser * @param {import('../index.js').Parser} parser
* @param {boolean} as_pattern
*/ */
function read_attribute_value(parser) { function read_attribute_value(parser, as_pattern = false) {
const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null; const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null;
if (quote_mark && parser.eat(quote_mark)) { if (quote_mark && parser.eat(quote_mark)) {
return [ return [
@ -461,7 +463,8 @@ function read_attribute_value(parser) {
if (quote_mark) return parser.match(quote_mark); if (quote_mark) return parser.match(quote_mark);
return !!parser.match_regex(regex_starts_with_invalid_attr_value); return !!parser.match_regex(regex_starts_with_invalid_attr_value);
}, },
'in attribute value' 'in attribute value',
as_pattern
); );
} catch (error) { } catch (error) {
if (error.code === 'parse-error') { if (error.code === 'parse-error') {
@ -486,9 +489,10 @@ function read_attribute_value(parser) {
* @param {import('../index.js').Parser} parser * @param {import('../index.js').Parser} parser
* @param {() => boolean} done * @param {() => boolean} done
* @param {string} location * @param {string} location
* @param {boolean} as_pattern
* @returns {import('../../interfaces.js').TemplateNode[]} * @returns {import('../../interfaces.js').TemplateNode[]}
*/ */
function read_sequence(parser, done, location) { function read_sequence(parser, done, location, as_pattern = false) {
/** /**
* @type {import('../../interfaces.js').Text} * @type {import('../../interfaces.js').Text}
*/ */
@ -534,7 +538,7 @@ function read_sequence(parser, done, location) {
} }
flush(parser.index - 1); flush(parser.index - 1);
parser.allow_whitespace(); parser.allow_whitespace();
const expression = read_expression(parser); const expression = as_pattern ? read_context(parser) : read_expression(parser);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
chunks.push({ chunks.push({

@ -46,8 +46,8 @@
"start": 35, "start": 35,
"end": 52, "end": 52,
"type": "StyleDirective", "type": "StyleDirective",
"modifiers": [],
"name": "color", "name": "color",
"modifiers": [],
"value": [ "value": [
{ {
"start": 48, "start": 48,

@ -14,11 +14,11 @@
"tag": "div" "tag": "div"
}, },
{ {
"type": "Text",
"start": 44, "start": 44,
"end": 45, "end": 45,
"data": "\n", "type": "Text",
"raw": "\n" "raw": "\n",
"data": "\n"
}, },
{ {
"start": 45, "start": 45,
@ -27,17 +27,17 @@
"name": "svelte:element", "name": "svelte:element",
"attributes": [ "attributes": [
{ {
"type": "Attribute",
"start": 72, "start": 72,
"end": 83, "end": 83,
"type": "Attribute",
"name": "class", "name": "class",
"value": [ "value": [
{ {
"type": "Text",
"start": 79, "start": 79,
"end": 82, "end": 82,
"data": "foo", "type": "Text",
"raw": "foo" "raw": "foo",
"data": "foo"
} }
] ]
} }

@ -12,6 +12,7 @@
"attributes": [], "attributes": [],
"children": [], "children": [],
"tag": { "tag": {
"type": "Identifier",
"start": 22, "start": 22,
"end": 25, "end": 25,
"loc": { "loc": {
@ -24,41 +25,41 @@
"column": 25 "column": 25
} }
}, },
"name": "tag", "name": "tag"
"type": "Identifier"
} }
}, },
{ {
"type": "Text",
"start": 44, "start": 44,
"end": 45, "end": 45,
"data": "\n", "type": "Text",
"raw": "\n" "raw": "\n",
"data": "\n"
}, },
{ {
"start": 45, "start": 45,
"end": 101, "end": 101,
"type": "Element", "type": "Element",
"name": "svelte:element", "name": "svelte:element",
"children": [],
"attributes": [ "attributes": [
{ {
"type": "Attribute",
"start": 72, "start": 72,
"end": 83, "end": 83,
"type": "Attribute",
"name": "class", "name": "class",
"value": [ "value": [
{ {
"type": "Text",
"start": 79, "start": 79,
"end": 82, "end": 82,
"data": "foo", "type": "Text",
"raw": "foo" "raw": "foo",
"data": "foo"
} }
] ]
} }
], ],
"children": [],
"tag": { "tag": {
"type": "Identifier",
"start": 67, "start": 67,
"end": 70, "end": 70,
"loc": { "loc": {
@ -71,8 +72,7 @@
"column": 25 "column": 25
} }
}, },
"name": "tag", "name": "tag"
"type": "Identifier"
} }
} }
] ]

@ -0,0 +1,133 @@
{
"html": {
"start": 0,
"end": 43,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 43,
"type": "InlineComponent",
"name": "Component",
"attributes": [
{
"start": 11,
"end": 28,
"type": "Let",
"name": "x",
"modifiers": [],
"expression": {
"type": "ObjectPattern",
"start": 18,
"end": 27,
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 27
}
},
"properties": [
{
"type": "Property",
"start": 19,
"end": 26,
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 26
}
},
"method": false,
"shorthand": true,
"computed": false,
"key": {
"type": "Identifier",
"start": 19,
"end": 22,
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 22
}
},
"name": "foo"
},
"kind": "init",
"value": {
"type": "AssignmentPattern",
"start": 19,
"end": 26,
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 26
}
},
"left": {
"type": "Identifier",
"start": 19,
"end": 22,
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 22
}
},
"name": "foo"
},
"right": {
"type": "Literal",
"start": 25,
"end": 26,
"loc": {
"start": {
"line": 1,
"column": 25
},
"end": {
"line": 1,
"column": 26
}
},
"value": 1,
"raw": "1"
}
}
}
]
}
}
],
"children": [
{
"start": 29,
"end": 31,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
}
]
}
]
}
}

@ -4,6 +4,8 @@
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import glob from 'tiny-glob/sync.js'; import glob from 'tiny-glob/sync.js';
const __dirname = new URL('.', import.meta.url).pathname;
glob('samples/*/_actual.json', { cwd: __dirname }).forEach((file) => { glob('samples/*/_actual.json', { cwd: __dirname }).forEach((file) => {
const actual = fs.readFileSync(`${__dirname}/${file}`, 'utf-8'); const actual = fs.readFileSync(`${__dirname}/${file}`, 'utf-8');
fs.writeFileSync(`${__dirname}/${file.replace('_actual.json', 'output.json')}`, actual); fs.writeFileSync(`${__dirname}/${file.replace('_actual.json', 'output.json')}`, actual);

Loading…
Cancel
Save