tweak-parser
Rich Harris 2 days ago
parent 92ebcaec99
commit 726b764b2b

@ -163,62 +163,52 @@ function get_comment_handlers(source, comments, index = 0) {
/** @param {acorn.Node & { leadingComments?: CommentWithLocation[]; trailingComments?: CommentWithLocation[]; }} ast */
add_comments(ast) {
if (comments.length === 0) return;
comments = comments
.filter((comment) => comment.start >= index)
.map(({ type, value, start, end }) => ({ type, value, start, end }));
walk(ast, null, {
_(node, { next, path }) {
let comment;
while (comments[0] && comments[0].start < node.start) {
comment = /** @type {CommentWithLocation} */ (comments.shift());
(node.leadingComments ||= []).push(comment);
}
next();
if (comments[0]) {
const parent = /** @type {any} */ (path.at(-1));
if (parent === undefined || node.end !== parent.end) {
const slice = source.slice(node.end, comments[0].start);
const is_last_in_body =
((parent?.type === 'BlockStatement' || parent?.type === 'Program') &&
parent.body.indexOf(node) === parent.body.length - 1) ||
(parent?.type === 'ArrayExpression' &&
parent.elements.indexOf(node) === parent.elements.length - 1) ||
(parent?.type === 'ObjectExpression' &&
parent.properties.indexOf(node) === parent.properties.length - 1);
if (is_last_in_body) {
// Special case: There can be multiple trailing comments after the last node in a block,
// and they can be separated by newlines
let end = node.end;
while (comments.length) {
const comment = comments[0];
if (parent && comment.start >= parent.end) break;
(node.trailingComments ||= []).push(comment);
comments.shift();
end = comment.end;
}
} else if (node.end <= comments[0].start && /^[,) \t]*$/.test(slice)) {
node.trailingComments = [/** @type {CommentWithLocation} */ (comments.shift())];
}
}
}
}
});
// Special case: Trailing comments after the root node (which can only happen for expression tags or for Program nodes).
// Adding them ensures that we can later detect the end of the expression tag correctly.
if (comments.length > 0 && (comments[0].start >= ast.end || ast.type === 'Program')) {
(ast.trailingComments ||= []).push(...comments.splice(0));
}
// if (comments.length === 0) return;
// comments = comments
// .filter((comment) => comment.start >= index)
// .map(({ type, value, start, end }) => ({ type, value, start, end }));
// walk(ast, null, {
// _(node, { next, path }) {
// let comment;
// while (comments[0] && comments[0].start < node.start) {
// comment = /** @type {CommentWithLocation} */ (comments.shift());
// (node.leadingComments ||= []).push(comment);
// }
// next();
// if (comments[0]) {
// const parent = /** @type {any} */ (path.at(-1));
// if (parent === undefined || node.end !== parent.end) {
// const slice = source.slice(node.end, comments[0].start);
// const is_last_in_body =
// ((parent?.type === 'BlockStatement' || parent?.type === 'Program') &&
// parent.body.indexOf(node) === parent.body.length - 1) ||
// (parent?.type === 'ArrayExpression' &&
// parent.elements.indexOf(node) === parent.elements.length - 1) ||
// (parent?.type === 'ObjectExpression' &&
// parent.properties.indexOf(node) === parent.properties.length - 1);
// if (is_last_in_body) {
// // Special case: There can be multiple trailing comments after the last node in a block,
// // and they can be separated by newlines
// let end = node.end;
// while (comments.length) {
// const comment = comments[0];
// if (parent && comment.start >= parent.end) break;
// (node.trailingComments ||= []).push(comment);
// comments.shift();
// end = comment.end;
// }
// } else if (node.end <= comments[0].start && /^[,) \t]*$/.test(slice)) {
// node.trailingComments = [/** @type {CommentWithLocation} */ (comments.shift())];
// }
// }
// }
// }
// });
// // Special case: Trailing comments after the root node (which can only happen for expression tags or for Program nodes).
// // Adding them ensures that we can later detect the end of the expression tag correctly.
// if (comments.length > 0 && (comments[0].start >= ast.end || ast.type === 'Program')) {
// (ast.trailingComments ||= []).push(...comments.splice(0));
// }
}
};
}

@ -1,6 +1,7 @@
/** @import { AST } from '#compiler' */
/** @import { Location } from 'locate-character' */
/** @import * as ESTree from 'estree' */
/** @import { CommentWithLocation } from './types' */
// @ts-expect-error acorn type definitions are borked in the release we use
import { isIdentifierStart, isIdentifierChar } from 'acorn';
import fragment from './state/fragment.js';
@ -11,6 +12,7 @@ import { is_reserved } from '../../../utils.js';
import { disallow_children } from '../2-analyze/visitors/shared/special-element.js';
import * as state from '../../state.js';
import { find_end } from './utils/find.js';
import { walk } from 'zimmerframe';
/** @param {number} cc */
function is_whitespace(cc) {
@ -168,6 +170,8 @@ export class Parser {
enumerable: false
});
}
this.#attach_comments();
}
current() {
@ -228,10 +232,23 @@ export class Parser {
}
if (code === 47) {
const start = i;
const next = source.charCodeAt(i + 1);
if (next === 47 || next === 42) {
i = find_end(source, next === 42 ? '*/' : '\n', i);
const is_block = next === 42;
i = find_end(source, is_block ? '*/' : '\n', i);
const end = is_block ? i : i - 1; // line comments don't include the '\n'
this.root.comments.push({
type: is_block ? 'Block' : 'Line',
start,
end,
value: source.slice(start + 2, end - (is_block ? 2 : 1)),
loc: state.get_loc(start, end)
});
continue;
}
}
@ -343,6 +360,72 @@ export class Parser {
this.fragments.at(-1)?.nodes.push(node);
return node;
}
#attach_comments() {
const source = this.template;
const comments = this.root.comments.map(({ type, value, start, end }) => ({
type,
value,
start,
end
}));
const root =
/** @type {AST.BaseNode & { leadingComments: CommentWithLocation[], trailingComments: CommentWithLocation[] }} */ (
/** @type {unknown} */ (this.root)
);
const nodes = [this.root.module, this.root.instance, this.root.css, ...this.root.fragment.nodes]
.filter((node) => !!node)
.sort((a, b) => a.start - b.start);
for (const node of nodes) {
walk(node, null, {
_(node, { next, path }) {
let comment;
while (comments[0] && comments[0].start < node.start) {
comment = /** @type {CommentWithLocation} */ (comments.shift());
(node.leadingComments ||= []).push(comment);
}
next();
if (comments[0]) {
const parent = /** @type {any} */ (path.at(-1));
if (parent === undefined || node.end !== parent.end) {
const slice = source.slice(node.end, comments[0].start);
const is_last_in_body =
((parent?.type === 'BlockStatement' || parent?.type === 'Program') &&
parent.body.indexOf(node) === parent.body.length - 1) ||
(parent?.type === 'ArrayExpression' &&
parent.elements.indexOf(node) === parent.elements.length - 1) ||
(parent?.type === 'ObjectExpression' &&
parent.properties.indexOf(node) === parent.properties.length - 1);
if (is_last_in_body) {
// Special case: There can be multiple trailing comments after the last node in a block,
// and they can be separated by newlines
let end = node.end;
while (comments.length) {
const comment = comments[0];
if (parent && comment.start >= parent.end) break;
(node.trailingComments ||= []).push(comment);
comments.shift();
end = comment.end;
}
} else if (node.end <= comments[0].start && /^[,) \t]*$/.test(slice)) {
node.trailingComments = [/** @type {CommentWithLocation} */ (comments.shift())];
}
}
}
}
});
}
}
}
/**

@ -84,7 +84,7 @@ function read_object_pattern(parser) {
const property = {
type: 'Property',
start,
end: -1,
end: key.end,
key,
value: /** @type {Identifier} */ (key),
method: false,
@ -98,6 +98,7 @@ function read_object_pattern(parser) {
if (parser.eat(':', computed)) {
property.value = read_pattern(parser);
property.shorthand = false;
property.end = property.value.end;
}
parser.advance();
@ -123,14 +124,17 @@ function read_object_pattern(parser) {
right,
loc: get_loc(property.value.start, right.end)
};
property.end = property.value.end;
}
if (parser.ts) {
property.typeAnnotation = read_type_annotation(parser);
if (property.typeAnnotation) {
property.end = property.typeAnnotation.end;
}
}
property.end = parser.index;
property.loc = get_loc(start, property.end);
properties.push(property);
@ -227,9 +231,11 @@ function read_array_pattern(parser) {
if (parser.ts) {
element.typeAnnotation = read_type_annotation(parser);
}
element.end = parser.index;
if (element.typeAnnotation) {
element.end = element.typeAnnotation.end;
}
}
element.loc = get_loc(start, element.end);

@ -0,0 +1,6 @@
import type { Comment } from 'estree';
export type CommentWithLocation = Comment & {
start: number;
end: number;
};

@ -77,6 +77,14 @@
"raw": "2",
"data": "2"
}
],
"leadingComments": [
{
"type": "Line",
"value": " this is a line comment",
"start": 20,
"end": 45
}
]
},
{
@ -104,6 +112,26 @@
"raw": "3",
"data": "3"
}
],
"leadingComments": [
{
"type": "Block",
"value": " this is a\n\t\tm\n\t\tu\n\t\tl\n\t\tt\n\t\ti\n\t\tl\n\t\ti\n\t\tn\n\t\te\n\t\tcomment\n\t",
"start": 61,
"end": 123
},
{
"type": "Line",
"value": " oh look another line comment",
"start": 125,
"end": 156
},
{
"type": "Line",
"value": " (two, in fact)",
"start": 158,
"end": 175
}
]
}
],
@ -162,6 +190,20 @@
"raw": "1",
"data": "1"
}
],
"leadingComments": [
{
"type": "Block",
"value": " inline ",
"start": 207,
"end": 219
},
{
"type": "Block",
"value": " another inline ",
"start": 220,
"end": 240
}
]
}
],

@ -92,11 +92,13 @@
"loc": {
"start": {
"line": 1,
"column": 16
"column": 16,
"character": 16
},
"end": {
"line": 1,
"column": 17
"column": 17,
"character": 17
}
},
"name": "x"
@ -108,11 +110,13 @@
"loc": {
"start": {
"line": 1,
"column": 16
"column": 16,
"character": 16
},
"end": {
"line": 1,
"column": 17
"column": 17,
"character": 17
}
},
"name": "x"
@ -190,11 +194,11 @@
"loc": {
"start": {
"line": 2,
"column": 15
"column": 14
},
"end": {
"line": 2,
"column": 29
"column": 28
}
},
"properties": [
@ -205,11 +209,11 @@
"loc": {
"start": {
"line": 2,
"column": 17
"column": 16
},
"end": {
"line": 2,
"column": 18
"column": 17
}
},
"method": false,
@ -222,11 +226,13 @@
"loc": {
"start": {
"line": 2,
"column": 17
"column": 16,
"character": 55
},
"end": {
"line": 2,
"column": 18
"column": 17,
"character": 56
}
},
"name": "x"
@ -238,11 +244,13 @@
"loc": {
"start": {
"line": 2,
"column": 17
"column": 16,
"character": 55
},
"end": {
"line": 2,
"column": 18
"column": 17,
"character": 56
}
},
"name": "x"
@ -320,11 +328,11 @@
"loc": {
"start": {
"line": 3,
"column": 15
"column": 14
},
"end": {
"line": 3,
"column": 28
"column": 27
}
},
"properties": [
@ -335,11 +343,11 @@
"loc": {
"start": {
"line": 3,
"column": 17
"column": 16
},
"end": {
"line": 3,
"column": 18
"column": 17
}
},
"method": false,
@ -352,11 +360,13 @@
"loc": {
"start": {
"line": 3,
"column": 17
"column": 16,
"character": 95
},
"end": {
"line": 3,
"column": 18
"column": 17,
"character": 96
}
},
"name": "x"
@ -368,11 +378,13 @@
"loc": {
"start": {
"line": 3,
"column": 17
"column": 16,
"character": 95
},
"end": {
"line": 3,
"column": 18
"column": 17,
"character": 96
}
},
"name": "x"
@ -450,11 +462,11 @@
"loc": {
"start": {
"line": 4,
"column": 15
"column": 14
},
"end": {
"line": 4,
"column": 29
"column": 28
}
},
"properties": [
@ -465,11 +477,13 @@
"loc": {
"start": {
"line": 4,
"column": 17
"column": 16,
"character": 134
},
"end": {
"line": 4,
"column": 18
"column": 17,
"character": 135
}
},
"method": false,
@ -482,11 +496,13 @@
"loc": {
"start": {
"line": 4,
"column": 17
"column": 16,
"character": 134
},
"end": {
"line": 4,
"column": 18
"column": 17,
"character": 135
}
},
"name": "x"
@ -498,11 +514,13 @@
"loc": {
"start": {
"line": 4,
"column": 17
"column": 16,
"character": 134
},
"end": {
"line": 4,
"column": 18
"column": 17,
"character": 135
}
},
"name": "x"
@ -548,11 +566,11 @@
"loc": {
"start": {
"line": 2,
"column": 19
"column": 18
},
"end": {
"line": 2,
"column": 27
"column": 26
}
}
},
@ -564,11 +582,11 @@
"loc": {
"start": {
"line": 3,
"column": 19
"column": 18
},
"end": {
"line": 3,
"column": 26
"column": 25
}
}
},
@ -580,13 +598,13 @@
"loc": {
"start": {
"line": 4,
"column": 19
"column": 18
},
"end": {
"line": 4,
"column": 27
"column": 26
}
}
}
]
}
}

Loading…
Cancel
Save