astro-eslint-parser 0.0.6 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import type { AttributeNode, CommentNode, Node, ParentNode, TagLikeNode } from "@astrojs/compiler/types";
2
+ import type { Context } from "../context";
2
3
  /**
3
4
  * Checks if the given node is TagLikeNode
4
5
  */
@@ -14,19 +15,19 @@ export declare function walk(parent: ParentNode, code: string, enter: (n: Node |
14
15
  /**
15
16
  * Get end offset of start tag
16
17
  */
17
- export declare function getStartTagEndOffset(node: TagLikeNode, code: string): number;
18
+ export declare function getStartTagEndOffset(node: TagLikeNode, ctx: Context): number;
18
19
  /**
19
20
  * Get end offset of attribute
20
21
  */
21
- export declare function getAttributeEndOffset(node: AttributeNode, code: string): number;
22
+ export declare function getAttributeEndOffset(node: AttributeNode, ctx: Context): number;
22
23
  /**
23
24
  * Get start offset of attribute value
24
25
  */
25
- export declare function getAttributeValueStartOffset(node: AttributeNode, code: string): number;
26
+ export declare function getAttributeValueStartOffset(node: AttributeNode, ctx: Context): number;
26
27
  /**
27
28
  * Get end offset of comment
28
29
  */
29
- export declare function getCommentEndOffset(node: CommentNode, code: string): number;
30
+ export declare function getCommentEndOffset(node: CommentNode, ctx: Context): number;
30
31
  /**
31
32
  * Skip spaces
32
33
  */
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.skipSpaces = exports.getCommentEndOffset = exports.getAttributeValueStartOffset = exports.getAttributeEndOffset = exports.getStartTagEndOffset = exports.walk = exports.walkElements = exports.isParent = exports.isTag = void 0;
4
+ const errors_1 = require("../errors");
4
5
  /**
5
6
  * Checks if the given node is TagLikeNode
6
7
  */
@@ -50,45 +51,45 @@ exports.walk = walk;
50
51
  /**
51
52
  * Get end offset of start tag
52
53
  */
53
- function getStartTagEndOffset(node, code) {
54
+ function getStartTagEndOffset(node, ctx) {
54
55
  const lastAttr = node.attributes[node.attributes.length - 1];
55
56
  let beforeCloseIndex;
56
57
  if (lastAttr) {
57
- beforeCloseIndex = getAttributeEndOffset(lastAttr, code);
58
+ beforeCloseIndex = getAttributeEndOffset(lastAttr, ctx);
58
59
  }
59
60
  else {
60
- const info = getTokenInfo(code, [`<${node.name}`], node.position.start.offset);
61
+ const info = getTokenInfo(ctx, [`<${node.name}`], node.position.start.offset);
61
62
  beforeCloseIndex = info.index + info.match.length;
62
63
  }
63
- const info = getTokenInfo(code, [[">", "/>"]], beforeCloseIndex);
64
+ const info = getTokenInfo(ctx, [[">", "/>"]], beforeCloseIndex);
64
65
  return info.index + info.match.length;
65
66
  }
66
67
  exports.getStartTagEndOffset = getStartTagEndOffset;
67
68
  /**
68
69
  * Get end offset of attribute
69
70
  */
70
- function getAttributeEndOffset(node, code) {
71
+ function getAttributeEndOffset(node, ctx) {
71
72
  let info;
72
73
  if (node.kind === "empty") {
73
- info = getTokenInfo(code, [node.name], node.position.start.offset);
74
+ info = getTokenInfo(ctx, [node.name], node.position.start.offset);
74
75
  }
75
76
  else if (node.kind === "quoted") {
76
- info = getTokenInfo(code, [[`"${node.value}"`, `'${node.value}'`, node.value]], getAttributeValueStartOffset(node, code));
77
+ info = getTokenInfo(ctx, [[`"${node.value}"`, `'${node.value}'`, node.value]], getAttributeValueStartOffset(node, ctx));
77
78
  }
78
79
  else if (node.kind === "expression") {
79
- info = getTokenInfo(code, ["{", node.value, "}"], getAttributeValueStartOffset(node, code));
80
+ info = getTokenInfo(ctx, ["{", node.value, "}"], getAttributeValueStartOffset(node, ctx));
80
81
  }
81
82
  else if (node.kind === "shorthand") {
82
- info = getTokenInfo(code, ["{", node.name, "}"], node.position.start.offset);
83
+ info = getTokenInfo(ctx, ["{", node.name, "}"], node.position.start.offset);
83
84
  }
84
85
  else if (node.kind === "spread") {
85
- info = getTokenInfo(code, ["{", "...", node.name, "}"], node.position.start.offset);
86
+ info = getTokenInfo(ctx, ["{", "...", node.name, "}"], node.position.start.offset);
86
87
  }
87
88
  else if (node.kind === "template-literal") {
88
- info = getTokenInfo(code, [`\`${node.value}\``], getAttributeValueStartOffset(node, code));
89
+ info = getTokenInfo(ctx, [`\`${node.value}\``], getAttributeValueStartOffset(node, ctx));
89
90
  }
90
91
  else {
91
- throw new Error(`Unknown attr kind: ${node.kind}`);
92
+ throw new errors_1.ParseError(`Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx);
92
93
  }
93
94
  return info.index + info.match.length;
94
95
  }
@@ -96,19 +97,19 @@ exports.getAttributeEndOffset = getAttributeEndOffset;
96
97
  /**
97
98
  * Get start offset of attribute value
98
99
  */
99
- function getAttributeValueStartOffset(node, code) {
100
+ function getAttributeValueStartOffset(node, ctx) {
100
101
  let info;
101
102
  if (node.kind === "quoted") {
102
- info = getTokenInfo(code, [node.name, "=", [`"`, `'`, node.value]], node.position.start.offset);
103
+ info = getTokenInfo(ctx, [node.name, "=", [`"`, `'`, node.value]], node.position.start.offset);
103
104
  }
104
105
  else if (node.kind === "expression") {
105
- info = getTokenInfo(code, [node.name, "=", "{"], node.position.start.offset);
106
+ info = getTokenInfo(ctx, [node.name, "=", "{"], node.position.start.offset);
106
107
  }
107
108
  else if (node.kind === "template-literal") {
108
- info = getTokenInfo(code, [node.name, "=", "`"], node.position.start.offset);
109
+ info = getTokenInfo(ctx, [node.name, "=", "`"], node.position.start.offset);
109
110
  }
110
111
  else {
111
- throw new Error(`Unknown attr kind: ${node.kind}`);
112
+ throw new errors_1.ParseError(`Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx);
112
113
  }
113
114
  return info.index;
114
115
  }
@@ -116,15 +117,15 @@ exports.getAttributeValueStartOffset = getAttributeValueStartOffset;
116
117
  /**
117
118
  * Get end offset of comment
118
119
  */
119
- function getCommentEndOffset(node, code) {
120
- const info = getTokenInfo(code, ["<!--", node.value, "-->"], node.position.start.offset);
120
+ function getCommentEndOffset(node, ctx) {
121
+ const info = getTokenInfo(ctx, ["<!--", node.value, "-->"], node.position.start.offset);
121
122
  return info.index + info.match.length;
122
123
  }
123
124
  exports.getCommentEndOffset = getCommentEndOffset;
124
125
  /**
125
126
  * Get token info
126
127
  */
127
- function getTokenInfo(string, tokens, position) {
128
+ function getTokenInfo(ctx, tokens, position) {
128
129
  let lastMatch;
129
130
  for (const t of tokens) {
130
131
  const index = lastMatch
@@ -134,7 +135,7 @@ function getTokenInfo(string, tokens, position) {
134
135
  ? matchOfStr(t, index)
135
136
  : matchOfForMulti(t, index);
136
137
  if (m == null) {
137
- throw new Error(`Unknown token at ${index}, expected: ${JSON.stringify(t)}, actual: ${JSON.stringify(string.slice(index, index + 10))}`);
138
+ throw new errors_1.ParseError(`Unknown token at ${index}, expected: ${JSON.stringify(t)}, actual: ${JSON.stringify(ctx.code.slice(index, index + 10))}`, index, ctx);
138
139
  }
139
140
  lastMatch = m;
140
141
  }
@@ -143,8 +144,8 @@ function getTokenInfo(string, tokens, position) {
143
144
  * For string
144
145
  */
145
146
  function matchOfStr(search, position) {
146
- const index = search.trim() === search ? skipSpaces(string, position) : position;
147
- if (string.startsWith(search, index)) {
147
+ const index = search.trim() === search ? skipSpaces(ctx.code, position) : position;
148
+ if (ctx.code.startsWith(search, index)) {
148
149
  return {
149
150
  match: search,
150
151
  index,
@@ -1,5 +1,6 @@
1
1
  import type { ParseResult } from "@astrojs/compiler";
2
+ import type { Context } from "../../context";
2
3
  /**
3
4
  * Parse code by `@astrojs/compiler`
4
5
  */
5
- export declare function parse(code: string): ParseResult;
6
+ export declare function parse(code: string, ctx: Context): ParseResult;
@@ -26,26 +26,79 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.parse = void 0;
27
27
  const service = __importStar(require("./astrojs-compiler-service"));
28
28
  const astro_1 = require("../../astro");
29
+ const errors_1 = require("../../errors");
29
30
  /**
30
31
  * Parse code by `@astrojs/compiler`
31
32
  */
32
- function parse(code) {
33
+ function parse(code, ctx) {
33
34
  const ast = service.parse(code, { position: true }).ast;
34
- fixLocations(ast, code);
35
+ const htmlElement = ast.children.find((n) => n.type === "element" && n.name === "html");
36
+ if (htmlElement) {
37
+ adjustHTML(ast, htmlElement, ctx);
38
+ }
39
+ fixLocations(ast, ctx);
35
40
  return { ast };
36
41
  }
37
42
  exports.parse = parse;
43
+ /**
44
+ * Adjust <html> element node
45
+ */
46
+ function adjustHTML(ast, htmlElement, ctx) {
47
+ var _a;
48
+ const htmlEnd = ctx.code.indexOf("</html");
49
+ if (htmlEnd == null) {
50
+ return;
51
+ }
52
+ const children = [...htmlElement.children];
53
+ for (const child of children) {
54
+ const offset = (_a = child.position) === null || _a === void 0 ? void 0 : _a.start.offset;
55
+ if (offset != null) {
56
+ if (htmlEnd <= offset) {
57
+ htmlElement.children.splice(htmlElement.children.indexOf(child), 1);
58
+ ast.children.push(child);
59
+ }
60
+ }
61
+ if (child.type === "element" && child.name === "body") {
62
+ adjustHTMLBody(ast, htmlElement, htmlEnd, child, ctx);
63
+ }
64
+ }
65
+ }
66
+ /**
67
+ * Adjust <body> element node
68
+ */
69
+ function adjustHTMLBody(ast, htmlElement, htmlEnd, bodyElement, ctx) {
70
+ var _a;
71
+ const bodyEnd = ctx.code.indexOf("</body");
72
+ if (bodyEnd == null) {
73
+ return;
74
+ }
75
+ const children = [...bodyElement.children];
76
+ for (const child of children) {
77
+ const offset = (_a = child.position) === null || _a === void 0 ? void 0 : _a.start.offset;
78
+ if (offset != null) {
79
+ if (bodyEnd <= offset) {
80
+ bodyElement.children.splice(bodyElement.children.indexOf(child), 1);
81
+ if (htmlEnd <= offset) {
82
+ ast.children.push(child);
83
+ }
84
+ else {
85
+ htmlElement.children.push(child);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
38
91
  /**
39
92
  * Fix locations
40
93
  */
41
- function fixLocations(node, code) {
94
+ function fixLocations(node, ctx) {
42
95
  // FIXME: Adjust because the parser does not return the correct location.
43
96
  let start = 0;
44
- (0, astro_1.walk)(node, code, (node) => {
97
+ (0, astro_1.walk)(node, ctx.code, (node) => {
45
98
  if (node.type === "frontmatter") {
46
- start = node.position.start.offset = tokenIndex(code, "---", start);
99
+ start = node.position.start.offset = tokenIndex(ctx, "---", start);
47
100
  start = node.position.end.offset =
48
- tokenIndex(code, "---", start + 3 + node.value.length) + 3;
101
+ tokenIndex(ctx, "---", start + 3 + node.value.length) + 3;
49
102
  }
50
103
  else if (node.type === "fragment" ||
51
104
  node.type === "element" ||
@@ -54,27 +107,27 @@ function fixLocations(node, code) {
54
107
  if (!node.position) {
55
108
  node.position = { start: {}, end: {} };
56
109
  }
57
- start = node.position.start.offset = tokenIndex(code, "<", start);
110
+ start = node.position.start.offset = tokenIndex(ctx, "<", start);
58
111
  start += 1;
59
112
  start += node.name.length;
60
113
  if (!node.attributes.length) {
61
- start = (0, astro_1.getStartTagEndOffset)(node, code);
114
+ start = (0, astro_1.getStartTagEndOffset)(node, ctx);
62
115
  }
63
116
  }
64
117
  else if (node.type === "attribute") {
65
- fixLocationForAttr(node, code, start);
66
- start = (0, astro_1.getAttributeEndOffset)(node, code);
118
+ fixLocationForAttr(node, ctx, start);
119
+ start = (0, astro_1.getAttributeEndOffset)(node, ctx);
67
120
  }
68
121
  else if (node.type === "comment") {
69
- node.position.start.offset = tokenIndex(code, "<!--", start);
70
- start = (0, astro_1.getCommentEndOffset)(node, code);
122
+ node.position.start.offset = tokenIndex(ctx, "<!--", start);
123
+ start = (0, astro_1.getCommentEndOffset)(node, ctx);
71
124
  }
72
125
  else if (node.type === "text") {
73
- start = node.position.start.offset = tokenIndex(code, node.value, start);
126
+ start = node.position.start.offset = tokenIndex(ctx, node.value, start);
74
127
  start += node.value.length;
75
128
  }
76
129
  else if (node.type === "expression") {
77
- start = node.position.start.offset = tokenIndex(code, "{", start);
130
+ start = node.position.start.offset = tokenIndex(ctx, "{", start);
78
131
  start += 1;
79
132
  }
80
133
  else if (node.type === "doctype") {
@@ -84,10 +137,10 @@ function fixLocations(node, code) {
84
137
  if (!node.position.end) {
85
138
  node.position.end = {};
86
139
  }
87
- start = node.position.start.offset = tokenIndex(code, "<!", start);
140
+ start = node.position.start.offset = tokenIndex(ctx, "<!", start);
88
141
  start += 2;
89
142
  start = node.position.end.offset =
90
- code.indexOf(">", start) + 1;
143
+ ctx.code.indexOf(">", start) + 1;
91
144
  }
92
145
  else if (node.type === "root") {
93
146
  // noop
@@ -96,21 +149,21 @@ function fixLocations(node, code) {
96
149
  if (node.type === "attribute") {
97
150
  const attributes = parent.attributes;
98
151
  if (attributes[attributes.length - 1] === node) {
99
- start = (0, astro_1.getStartTagEndOffset)(parent, code);
152
+ start = (0, astro_1.getStartTagEndOffset)(parent, ctx);
100
153
  }
101
154
  return;
102
155
  }
103
156
  if (node.type === "expression") {
104
- start = tokenIndex(code, "}", start) + 1;
157
+ start = tokenIndex(ctx, "}", start) + 1;
105
158
  }
106
159
  else if (node.type === "fragment" ||
107
160
  node.type === "element" ||
108
161
  node.type === "component" ||
109
162
  node.type === "custom-element") {
110
- const closeTagStart = tokenIndexSafe(code, `</${node.name}`, start);
163
+ const closeTagStart = tokenIndexSafe(ctx.code, `</${node.name}`, start);
111
164
  if (closeTagStart != null) {
112
165
  start = closeTagStart + 2 + node.name.length;
113
- start = tokenIndex(code, ">", start) + 1;
166
+ start = tokenIndex(ctx, ">", start) + 1;
114
167
  }
115
168
  }
116
169
  else {
@@ -124,37 +177,37 @@ function fixLocations(node, code) {
124
177
  /**
125
178
  * Fix locations
126
179
  */
127
- function fixLocationForAttr(node, code, start) {
180
+ function fixLocationForAttr(node, ctx, start) {
128
181
  if (node.kind === "empty") {
129
- node.position.start.offset = tokenIndex(code, node.name, start);
182
+ node.position.start.offset = tokenIndex(ctx, node.name, start);
130
183
  }
131
184
  else if (node.kind === "quoted") {
132
- node.position.start.offset = tokenIndex(code, node.name, start);
185
+ node.position.start.offset = tokenIndex(ctx, node.name, start);
133
186
  }
134
187
  else if (node.kind === "expression") {
135
- node.position.start.offset = tokenIndex(code, node.name, start);
188
+ node.position.start.offset = tokenIndex(ctx, node.name, start);
136
189
  }
137
190
  else if (node.kind === "shorthand") {
138
- node.position.start.offset = tokenIndex(code, "{", start);
191
+ node.position.start.offset = tokenIndex(ctx, "{", start);
139
192
  }
140
193
  else if (node.kind === "spread") {
141
- node.position.start.offset = tokenIndex(code, "{", start);
194
+ node.position.start.offset = tokenIndex(ctx, "{", start);
142
195
  }
143
196
  else if (node.kind === "template-literal") {
144
- node.position.start.offset = tokenIndex(code, node.name, start);
197
+ node.position.start.offset = tokenIndex(ctx, node.name, start);
145
198
  }
146
199
  else {
147
- throw new Error(`Unknown attr kind: ${node.kind}`);
200
+ throw new errors_1.ParseError(`Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx);
148
201
  }
149
202
  }
150
203
  /**
151
204
  * Get token index
152
205
  */
153
- function tokenIndex(string, token, position) {
154
- const index = tokenIndexSafe(string, token, position);
206
+ function tokenIndex(ctx, token, position) {
207
+ const index = tokenIndexSafe(ctx.code, token, position);
155
208
  if (index == null) {
156
- const start = token.trim() === token ? (0, astro_1.skipSpaces)(string, position) : position;
157
- throw new Error(`Unknown token at ${start}, expected: ${JSON.stringify(token)}, actual: ${JSON.stringify(string.slice(start, start + 10))}`);
209
+ const start = token.trim() === token ? (0, astro_1.skipSpaces)(ctx.code, position) : position;
210
+ throw new errors_1.ParseError(`Unknown token at ${start}, expected: ${JSON.stringify(token)}, actual: ${JSON.stringify(ctx.code.slice(start, start + 10))}`, start, ctx);
158
211
  }
159
212
  return index;
160
213
  }
@@ -82,7 +82,7 @@ function extractTokens(ast, ctx) {
82
82
  */
83
83
  function parseTemplate(code, ctx) {
84
84
  try {
85
- return (0, parse_1.parse)(code);
85
+ return (0, parse_1.parse)(code, ctx);
86
86
  }
87
87
  catch (e) {
88
88
  if (typeof e.pos === "number") {
@@ -8,6 +8,8 @@ const script_1 = require("../context/script");
8
8
  * Process the template to generate a ScriptContext.
9
9
  */
10
10
  function processTemplate(ctx, resultTemplate) {
11
+ let uniqueIdSeq = 0;
12
+ const usedUniqueIds = new Set();
11
13
  const script = new script_1.ScriptContext(ctx);
12
14
  const frontmatter = resultTemplate.ast.children.find((n) => n.type === "frontmatter");
13
15
  let fragmentOpened = false;
@@ -99,13 +101,19 @@ function processTemplate(ctx, resultTemplate) {
99
101
  if (attr.kind === "shorthand") {
100
102
  const start = attr.position.start.offset;
101
103
  script.appendOriginal(start);
102
- script.appendScript(`${attr.name}=`);
104
+ const jsxName = /[\s"'[\]{}]/u.test(attr.name)
105
+ ? generateUniqueId(attr.name)
106
+ : attr.name;
107
+ script.appendScript(`${jsxName}=`);
103
108
  script.addRestoreNodeProcess((scriptNode) => {
104
109
  if (scriptNode.type === types_1.AST_NODE_TYPES.JSXAttribute &&
105
110
  scriptNode.range[0] === start) {
106
111
  const attrNode = scriptNode;
107
112
  attrNode.type = "AstroShorthandAttribute";
108
113
  const locs = ctx.getLocations(...attrNode.value.expression.range);
114
+ if (jsxName !== attr.name) {
115
+ attrNode.name.name = attr.name;
116
+ }
109
117
  attrNode.name.range = locs.range;
110
118
  attrNode.name.loc = locs.loc;
111
119
  return true;
@@ -115,8 +123,8 @@ function processTemplate(ctx, resultTemplate) {
115
123
  }
116
124
  else if (attr.kind === "template-literal") {
117
125
  const attrStart = attr.position.start.offset;
118
- const start = (0, astro_1.getAttributeValueStartOffset)(attr, ctx.code);
119
- const end = (0, astro_1.getAttributeEndOffset)(attr, ctx.code);
126
+ const start = (0, astro_1.getAttributeValueStartOffset)(attr, ctx);
127
+ const end = (0, astro_1.getAttributeEndOffset)(attr, ctx);
120
128
  script.appendOriginal(start);
121
129
  script.appendScript("{");
122
130
  script.appendOriginal(end);
@@ -228,6 +236,17 @@ function processTemplate(ctx, resultTemplate) {
228
236
  script.appendOriginal(ctx.code.length);
229
237
  script.appendScript("</>");
230
238
  return script;
239
+ /**
240
+ * Generate unique id
241
+ */
242
+ function generateUniqueId(base) {
243
+ let candidate = `$_${base.replace(/\W/g, "_")}${uniqueIdSeq++}`;
244
+ while (usedUniqueIds.has(candidate) || ctx.code.includes(candidate)) {
245
+ candidate = `$_${base.replace(/\W/g, "_")}${uniqueIdSeq++}`;
246
+ }
247
+ usedUniqueIds.add(candidate);
248
+ return candidate;
249
+ }
231
250
  }
232
251
  exports.processTemplate = processTemplate;
233
252
  /**
@@ -253,7 +272,7 @@ function getVoidSelfClosingTag(node, parent, ctx) {
253
272
  const next = parent.children[childIndex + 1];
254
273
  nextElementIndex = next.position.start.offset;
255
274
  }
256
- const endOffset = (0, astro_1.getStartTagEndOffset)(node, code);
275
+ const endOffset = (0, astro_1.getStartTagEndOffset)(node, ctx);
257
276
  if (code.slice(endOffset, nextElementIndex).trim()) {
258
277
  // has end tag
259
278
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-eslint-parser",
3
- "version": "0.0.6",
3
+ "version": "0.0.9",
4
4
  "description": "Astro parser for ESLint",
5
5
  "main": "lib/index.js",
6
6
  "files": [