@markuplint/astro-parser 4.0.0-dev.10 → 4.0.0-dev.12

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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2017-2023 Yusuke Hirao
3
+ Copyright (c) 2017-2024 Yusuke Hirao
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/lib/index.d.ts CHANGED
@@ -1,2 +1 @@
1
- export { parse } from './parse.js';
2
- export declare const endTag = "xml";
1
+ export { parser } from './parser.js';
package/lib/index.js CHANGED
@@ -1,2 +1 @@
1
- export { parse } from './parse.js';
2
- export const endTag = 'xml';
1
+ export { parser } from './parser.js';
@@ -0,0 +1,51 @@
1
+ import type { Node } from './astro-parser.js';
2
+ import type { MLASTParentNode, MLASTNodeTreeItem } from '@markuplint/ml-ast';
3
+ import type { ChildToken, Token } from '@markuplint/parser-utils';
4
+ import { Parser } from '@markuplint/parser-utils';
5
+ type State = {
6
+ scopeNS: string;
7
+ };
8
+ declare class AstroParser extends Parser<Node, State> {
9
+ #private;
10
+ constructor();
11
+ tokenize(): {
12
+ ast: Node[];
13
+ isFragment: boolean;
14
+ };
15
+ nodeize(originNode: Node, parentNode: MLASTParentNode | null, depth: number): readonly MLASTNodeTreeItem[];
16
+ afterFlattenNodes(nodeList: readonly MLASTNodeTreeItem[]): readonly MLASTNodeTreeItem[];
17
+ visitElement(token: ChildToken, childNodes: readonly Node[]): readonly MLASTNodeTreeItem[];
18
+ visitChildren(children: readonly Node[], parentNode: MLASTParentNode | null): never[];
19
+ visitAttr(token: Token): (import("@markuplint/ml-ast").MLASTSpreadAttr & {
20
+ __rightText?: string | undefined;
21
+ }) | {
22
+ isDynamicValue: true | undefined;
23
+ isDirective: true | undefined;
24
+ potentialName: string | undefined;
25
+ type: "attr";
26
+ nodeName: string;
27
+ spacesBeforeName: import("@markuplint/ml-ast").MLASTToken;
28
+ name: import("@markuplint/ml-ast").MLASTToken;
29
+ spacesBeforeEqual: import("@markuplint/ml-ast").MLASTToken;
30
+ equal: import("@markuplint/ml-ast").MLASTToken;
31
+ spacesAfterEqual: import("@markuplint/ml-ast").MLASTToken;
32
+ startQuote: import("@markuplint/ml-ast").MLASTToken;
33
+ value: import("@markuplint/ml-ast").MLASTToken;
34
+ endQuote: import("@markuplint/ml-ast").MLASTToken;
35
+ potentialValue?: string | undefined;
36
+ valueType?: "string" | "number" | "boolean" | "code" | undefined;
37
+ candidate?: string | undefined;
38
+ isDuplicatable: boolean;
39
+ uuid: string;
40
+ raw: string;
41
+ startOffset: number;
42
+ endOffset: number;
43
+ startLine: number;
44
+ endLine: number;
45
+ startCol: number;
46
+ endCol: number;
47
+ __rightText?: string | undefined;
48
+ };
49
+ }
50
+ export declare const parser: AstroParser;
51
+ export {};
package/lib/parser.js ADDED
@@ -0,0 +1,214 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _AstroParser_instances, _AstroParser_updateScopeNS;
7
+ import { AttrState, Parser, ParserError } from '@markuplint/parser-utils';
8
+ import { astroParse } from './astro-parser.js';
9
+ class AstroParser extends Parser {
10
+ constructor() {
11
+ super({
12
+ endTagType: 'xml',
13
+ selfCloseType: 'html+xml',
14
+ }, {
15
+ scopeNS: 'http://www.w3.org/1999/xhtml',
16
+ });
17
+ _AstroParser_instances.add(this);
18
+ }
19
+ tokenize() {
20
+ return {
21
+ ast: astroParse(this.rawCode).children,
22
+ isFragment: true,
23
+ };
24
+ }
25
+ nodeize(
26
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
27
+ originNode, parentNode, depth) {
28
+ if (!originNode.position) {
29
+ throw new TypeError("Node doesn't have position");
30
+ }
31
+ const startOffset = originNode.position.start.offset;
32
+ const endOffset = originNode.position.end?.offset;
33
+ const token = this.sliceFragment(startOffset, endOffset);
34
+ __classPrivateFieldGet(this, _AstroParser_instances, "m", _AstroParser_updateScopeNS).call(this, originNode, parentNode);
35
+ switch (originNode.type) {
36
+ case 'doctype': {
37
+ return this.visitDoctype({
38
+ ...token,
39
+ depth,
40
+ parentNode,
41
+ name: originNode.value,
42
+ publicId: '',
43
+ systemId: '',
44
+ });
45
+ }
46
+ case 'text': {
47
+ if (parentNode?.type === 'psblock') {
48
+ return [];
49
+ }
50
+ return this.visitText({
51
+ ...token,
52
+ depth,
53
+ parentNode,
54
+ });
55
+ }
56
+ case 'comment': {
57
+ return this.visitComment({
58
+ ...token,
59
+ depth,
60
+ parentNode,
61
+ });
62
+ }
63
+ case 'component':
64
+ case 'custom-element':
65
+ case 'fragment':
66
+ case 'element': {
67
+ const tagToken = token.raw ? token : this.sliceFragment(0);
68
+ return this.visitElement({
69
+ ...tagToken,
70
+ depth,
71
+ parentNode,
72
+ }, originNode.children);
73
+ }
74
+ case 'expression': {
75
+ const firstChild = originNode.children.at(0);
76
+ const lastChild = originNode.children.at(-1);
77
+ let startExpressionRaw = token.raw;
78
+ let startExpressionStartLine = token.startLine;
79
+ let startExpressionStartCol = token.startCol;
80
+ const nodes = [];
81
+ if (firstChild && lastChild && firstChild !== lastChild) {
82
+ const startExpressionEndOffset = firstChild.position?.end?.offset ?? endOffset ?? startOffset;
83
+ const startExpressionLocation = this.sliceFragment(startOffset, startExpressionEndOffset);
84
+ startExpressionRaw = startExpressionLocation.raw;
85
+ startExpressionStartLine = startExpressionLocation.startLine;
86
+ startExpressionStartCol = startExpressionLocation.startCol;
87
+ const closeExpressionLocation = this.sliceFragment(lastChild.position?.start.offset ?? startOffset, endOffset);
88
+ nodes.push(...this.visitPsBlock({
89
+ ...closeExpressionLocation,
90
+ depth,
91
+ parentNode,
92
+ nodeName: 'MustacheTag',
93
+ }));
94
+ }
95
+ nodes.push(...this.visitPsBlock({
96
+ raw: startExpressionRaw,
97
+ startOffset,
98
+ startLine: startExpressionStartLine,
99
+ startCol: startExpressionStartCol,
100
+ depth,
101
+ parentNode,
102
+ nodeName: 'MustacheTag',
103
+ }, originNode.children));
104
+ return nodes;
105
+ }
106
+ default: {
107
+ return [];
108
+ }
109
+ }
110
+ }
111
+ afterFlattenNodes(nodeList) {
112
+ return super.afterFlattenNodes(nodeList, {
113
+ exposeInvalidNode: false,
114
+ });
115
+ }
116
+ visitElement(token,
117
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
118
+ childNodes) {
119
+ const parsedNodes = this.parseCodeFragment(token);
120
+ const startTagNode = parsedNodes.at(0);
121
+ if (!startTagNode || startTagNode.type !== 'starttag') {
122
+ throw new ParserError('Not found start tag', startTagNode ?? token);
123
+ }
124
+ return super.visitElement(startTagNode, childNodes, {
125
+ overwriteProps: {
126
+ namespace: this.state.scopeNS,
127
+ },
128
+ createEndTagToken: () => {
129
+ if (startTagNode.selfClosingSolidus?.raw === '/') {
130
+ return null;
131
+ }
132
+ const endTagNode = parsedNodes.at(-1);
133
+ if (endTagNode?.type !== 'endtag') {
134
+ return null;
135
+ }
136
+ return endTagNode ?? null;
137
+ },
138
+ });
139
+ }
140
+ visitChildren(
141
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
142
+ children, parentNode) {
143
+ const siblings = super.visitChildren(children, parentNode);
144
+ if (siblings.length > 0) {
145
+ throw new ParserError('Discovered child nodes with differing hierarchy levels', siblings[0]);
146
+ }
147
+ return [];
148
+ }
149
+ visitAttr(token) {
150
+ const attr = super.visitAttr(token, {
151
+ quoteSet: [
152
+ { start: '"', end: '"' },
153
+ { start: "'", end: "'" },
154
+ { start: '{', end: '}' },
155
+ ],
156
+ quoteInValueChars: [
157
+ { start: '"', end: '"' },
158
+ { start: "'", end: "'" },
159
+ { start: '`', end: '`' },
160
+ { start: '${', end: '}' },
161
+ ],
162
+ startState:
163
+ // is shorthand attribute
164
+ token.raw.trim().startsWith('{') ? AttrState.BeforeValue : AttrState.BeforeName,
165
+ });
166
+ if (attr.type === 'spread') {
167
+ return attr;
168
+ }
169
+ const isDynamicValue = attr.startQuote.raw === '{' || undefined;
170
+ let potentialName;
171
+ let isDirective;
172
+ if (isDynamicValue && attr.name.raw === '') {
173
+ potentialName = attr.value.raw;
174
+ }
175
+ /**
176
+ * Detects Template Directive
177
+ *
178
+ * @see https://docs.astro.build/en/reference/directives-reference/
179
+ */
180
+ const [, directive] = attr.name.raw.match(/^([^:]+):([^:]+)$/) ?? [];
181
+ if (directive) {
182
+ const lowerCaseDirectiveName = directive.toLowerCase();
183
+ switch (lowerCaseDirectiveName) {
184
+ case 'class': {
185
+ potentialName = lowerCaseDirectiveName;
186
+ break;
187
+ }
188
+ default: {
189
+ isDirective = true;
190
+ }
191
+ }
192
+ }
193
+ return {
194
+ ...attr,
195
+ isDynamicValue,
196
+ isDirective,
197
+ potentialName,
198
+ };
199
+ }
200
+ }
201
+ _AstroParser_instances = new WeakSet(), _AstroParser_updateScopeNS = function _AstroParser_updateScopeNS(
202
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
203
+ originNode, parentNode) {
204
+ const parentNS = this.state.scopeNS;
205
+ if (parentNS === 'http://www.w3.org/1999/xhtml' &&
206
+ originNode.type === 'element' &&
207
+ originNode.name?.toLowerCase() === 'svg') {
208
+ this.state.scopeNS = 'http://www.w3.org/2000/svg';
209
+ }
210
+ else if (parentNS === 'http://www.w3.org/2000/svg' && parentNode && parentNode.nodeName === 'foreignObject') {
211
+ this.state.scopeNS = 'http://www.w3.org/1999/xhtml';
212
+ }
213
+ };
214
+ export const parser = new AstroParser();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/astro-parser",
3
- "version": "4.0.0-dev.10+b28398ab",
3
+ "version": "4.0.0-dev.12+2275fbeb0",
4
4
  "description": "astro parser for markuplint",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -21,11 +21,12 @@
21
21
  "clean": "tsc --build --clean"
22
22
  },
23
23
  "dependencies": {
24
- "@astrojs/parser": "0.22.2",
25
- "@markuplint/html-parser": "4.0.0-dev.10+b28398ab",
26
- "@markuplint/ml-ast": "4.0.0-dev.10+b28398ab",
27
- "@markuplint/parser-utils": "4.0.0-dev.10+b28398ab",
28
- "astro-eslint-parser": "^0.16.0"
24
+ "@markuplint/ml-ast": "4.0.0-dev.12+2275fbeb0",
25
+ "@markuplint/parser-utils": "4.0.0-dev.12+2275fbeb0",
26
+ "astro-eslint-parser": "^0.16.2"
29
27
  },
30
- "gitHead": "b28398ab9c8f0ad790f2915ad5da8f3a80e9b8d6"
28
+ "devDependencies": {
29
+ "@astrojs/compiler": "^2.5.0"
30
+ },
31
+ "gitHead": "2275fbeb053605b636f080f4fafd7cd4fc57a9a3"
31
32
  }
@@ -1,3 +0,0 @@
1
- import type { AttributeNode } from './astro-parser.js';
2
- import type { MLASTHTMLAttr } from '@markuplint/ml-ast';
3
- export declare function attrTokenizer(attr: AttributeNode, nextAttr: AttributeNode | null, rawHtml: string, startTag: string, startTagEndOffset: number): MLASTHTMLAttr;
@@ -1,57 +0,0 @@
1
- import { defaultValueDelimiters, getSpaceBefore, parseAttr, sliceFragment } from '@markuplint/parser-utils';
2
- const mustacheTag = {
3
- start: '{',
4
- end: '}',
5
- };
6
- export function attrTokenizer(
7
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
8
- attr,
9
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
10
- nextAttr, rawHtml, startTag, startTagEndOffset) {
11
- if (!attr.position) {
12
- throw new TypeError("Attr doesn't have position");
13
- }
14
- if (attr.position.end) {
15
- throw new TypeError('Node may is an attribute because it has end position');
16
- }
17
- let nextAttrBeforeSpaceOffset;
18
- if (nextAttr) {
19
- if (!nextAttr.position) {
20
- throw new TypeError("NextAttr doesn't have position");
21
- }
22
- if (nextAttr.position.end) {
23
- throw new TypeError('NextAttr Node may is an attribute because it has end position');
24
- }
25
- nextAttrBeforeSpaceOffset = getSpaceBefore(nextAttr.position.start.offset, rawHtml).startOffset;
26
- }
27
- else {
28
- const close = /(\/?>)$/.exec(startTag)?.[1] ?? '';
29
- nextAttrBeforeSpaceOffset = startTagEndOffset - close.length;
30
- }
31
- const { raw, startOffset } = sliceFragment(rawHtml, attr.position.start.offset, nextAttrBeforeSpaceOffset);
32
- const result = parseAttr(raw, startOffset, rawHtml, {
33
- valueDelimiters: [...defaultValueDelimiters, mustacheTag],
34
- });
35
- if (result.startQuote.raw === mustacheTag.start) {
36
- result.isDynamicValue = true;
37
- }
38
- /**
39
- * Detects Template Directive
40
- *
41
- * @see https://docs.astro.build/en/reference/directives-reference/
42
- */
43
- const [, directive] = result.name.raw.match(/^([^:]+):([^:]+)$/) ?? [];
44
- if (directive) {
45
- const lowerCaseDirectiveName = directive.toLowerCase();
46
- switch (lowerCaseDirectiveName) {
47
- case 'class': {
48
- result.potentialName = lowerCaseDirectiveName;
49
- break;
50
- }
51
- default: {
52
- result.isDirective = true;
53
- }
54
- }
55
- }
56
- return result;
57
- }
package/lib/parse.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import type { Parse } from '@markuplint/ml-ast';
2
- export declare const parse: Parse;
package/lib/parse.js DELETED
@@ -1,337 +0,0 @@
1
- import { parse as htmlParse } from '@markuplint/html-parser';
2
- import { flattenNodes, detectElementType, getEndCol, getEndLine, sliceFragment, uuid, tagParser, } from '@markuplint/parser-utils';
3
- import { astroParse } from './astro-parser.js';
4
- import { attrTokenizer } from './attr-tokenizer.js';
5
- export const parse = (rawCode, options = {}) => {
6
- const ast = astroParse(rawCode);
7
- const htmlRawNodeList = traverse(ast, null, 'http://www.w3.org/1999/xhtml', rawCode, 0, options);
8
- const firstOffset = htmlRawNodeList[0]?.startOffset ?? 0;
9
- if (firstOffset > 0) {
10
- const head = rawCode.slice(0, firstOffset);
11
- const ast = htmlParse(head, {
12
- ...options,
13
- ignoreFrontMatter: true,
14
- });
15
- const headNodes = ast.nodeList.filter(node => {
16
- return !node.isGhost;
17
- });
18
- htmlRawNodeList.unshift(...headNodes);
19
- }
20
- const nodeList = flattenNodes(htmlRawNodeList, rawCode);
21
- return {
22
- nodeList,
23
- isFragment: true,
24
- };
25
- };
26
- function traverse(
27
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
28
- rootNode, parentNode = null, scopeNS, rawHtml, offset, options, inExpression) {
29
- const nodeList = [];
30
- if (!('children' in rootNode)) {
31
- return nodeList;
32
- }
33
- if (rootNode.children.length === 0) {
34
- return nodeList;
35
- }
36
- let prevNode = null;
37
- for (const astNode of rootNode.children) {
38
- const node = nodeize(astNode, prevNode, parentNode, scopeNS, rawHtml, offset, options, inExpression);
39
- if (!node) {
40
- continue;
41
- }
42
- const nodes = Array.isArray(node) ? node : [node];
43
- for (const node of nodes) {
44
- if (prevNode) {
45
- if (node.type !== 'endtag') {
46
- prevNode.nextNode = node;
47
- }
48
- node.prevNode = prevNode;
49
- }
50
- prevNode = node;
51
- nodeList.push(node);
52
- }
53
- }
54
- return nodeList;
55
- }
56
- function nodeize(
57
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
58
- originNode,
59
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
60
- prevNode,
61
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
62
- parentNode, scopeNS, rawHtml, offset = 0, options, inExpression) {
63
- if (!originNode.position) {
64
- throw new TypeError("Node doesn't have position");
65
- }
66
- const nextNode = null;
67
- const startOffset = originNode.position.start.offset + offset;
68
- const endOffset = (originNode.position.end?.offset ?? originNode.position.start.offset) + offset;
69
- const { startLine, endLine, startCol, endCol, raw } = sliceFragment(rawHtml, startOffset, endOffset);
70
- if (scopeNS === 'http://www.w3.org/1999/xhtml' &&
71
- originNode.type === 'element' &&
72
- originNode.name?.toLowerCase() === 'svg') {
73
- scopeNS = 'http://www.w3.org/2000/svg';
74
- }
75
- else if (scopeNS === 'http://www.w3.org/2000/svg' && parentNode && parentNode.nodeName === 'foreignObject') {
76
- scopeNS = 'http://www.w3.org/1999/xhtml';
77
- }
78
- switch (originNode.type) {
79
- case 'text': {
80
- if (inExpression) {
81
- return null;
82
- }
83
- const node = {
84
- uuid: uuid(),
85
- raw,
86
- startOffset,
87
- endOffset,
88
- startLine,
89
- endLine,
90
- startCol,
91
- endCol,
92
- nodeName: '#text',
93
- type: 'text',
94
- parentNode,
95
- prevNode,
96
- nextNode,
97
- isFragment: false,
98
- isGhost: false,
99
- };
100
- return node;
101
- }
102
- case 'expression': {
103
- let _endOffset = endOffset;
104
- let _startLine = startLine;
105
- let _endLine = endLine;
106
- let _startCol = startCol;
107
- let _endCol = endCol;
108
- let _raw = raw;
109
- let closeExpression = null;
110
- const firstChild = originNode.children[0];
111
- const lastChild = originNode.children.at(-1);
112
- if (firstChild && lastChild && firstChild !== lastChild) {
113
- _endOffset = firstChild.position?.end?.offset ?? endOffset;
114
- const startLoc = sliceFragment(rawHtml, startOffset, _endOffset);
115
- _startLine = startLoc.startLine;
116
- _endLine = startLoc.endLine;
117
- _startCol = startLoc.startCol;
118
- _endCol = startLoc.endCol;
119
- _raw = startLoc.raw;
120
- const closeStartOffset = lastChild.position?.start.offset ?? startOffset;
121
- const closeLoc = sliceFragment(rawHtml, closeStartOffset, endOffset);
122
- closeExpression = {
123
- uuid: uuid(),
124
- raw: closeLoc.raw,
125
- startOffset: closeStartOffset,
126
- endOffset: closeLoc.endOffset,
127
- startLine: closeLoc.startLine,
128
- endLine: closeLoc.endLine,
129
- startCol: closeLoc.startCol,
130
- endCol: closeLoc.endCol,
131
- nodeName: 'MustacheTag',
132
- type: 'psblock',
133
- parentNode,
134
- prevNode,
135
- nextNode,
136
- isFragment: false,
137
- isGhost: false,
138
- };
139
- }
140
- const node = {
141
- uuid: uuid(),
142
- raw: _raw,
143
- startOffset,
144
- endOffset: _endOffset,
145
- startLine: _startLine,
146
- endLine: _endLine,
147
- startCol: _startCol,
148
- endCol: _endCol,
149
- nodeName: 'MustacheTag',
150
- type: 'psblock',
151
- parentNode,
152
- prevNode,
153
- nextNode,
154
- isFragment: false,
155
- isGhost: false,
156
- };
157
- if (originNode.children.length > 0) {
158
- node.childNodes = traverse(originNode, parentNode, scopeNS, rawHtml, offset, options, true);
159
- }
160
- if (closeExpression) {
161
- return [node, closeExpression];
162
- }
163
- return node;
164
- }
165
- case 'comment': {
166
- return {
167
- uuid: uuid(),
168
- raw,
169
- startOffset,
170
- endOffset,
171
- startLine,
172
- endLine,
173
- startCol,
174
- endCol,
175
- nodeName: '#comment',
176
- type: 'comment',
177
- parentNode,
178
- prevNode,
179
- nextNode,
180
- isFragment: false,
181
- isGhost: false,
182
- };
183
- }
184
- case 'component':
185
- case 'custom-element':
186
- case 'fragment':
187
- case 'element': {
188
- if (originNode.name?.toLowerCase() === '!doctype') {
189
- return {
190
- uuid: uuid(),
191
- raw,
192
- name: originNode.name,
193
- publicId: '',
194
- systemId: '',
195
- startOffset,
196
- endOffset,
197
- startLine,
198
- endLine,
199
- startCol,
200
- endCol,
201
- nodeName: '#doctype',
202
- type: 'doctype',
203
- parentNode,
204
- prevNode,
205
- nextNode,
206
- isFragment: false,
207
- isGhost: false,
208
- };
209
- }
210
- return parseElement(originNode, scopeNS, rawHtml, startLine, startCol, startOffset, parentNode, prevNode, nextNode, offset, options);
211
- }
212
- default: {
213
- return null;
214
- }
215
- }
216
- }
217
- function parseElement(
218
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
219
- originNode, scopeNS, rawHtml, startLine, startCol, startOffset,
220
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
221
- parentNode,
222
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
223
- prevNode,
224
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
225
- nextNode, offset, options) {
226
- if (!originNode.position) {
227
- throw new TypeError("Node doesn't have position");
228
- }
229
- let startTagRaw;
230
- let childrenStart;
231
- let childrenEnd;
232
- if (originNode.children[0]) {
233
- childrenStart = (originNode.children[0].position?.start?.offset ?? 0) + offset;
234
- childrenEnd = (originNode.children.at(-1)?.position?.end?.offset ?? 0) + offset;
235
- const startTagStartOffset = originNode.position.start.offset + offset;
236
- const startTagEndOffset = childrenStart;
237
- startTagRaw = rawHtml.slice(startTagStartOffset, startTagEndOffset);
238
- }
239
- else {
240
- childrenStart = offset + (originNode.position.end?.offset ?? nextNode?.endOffset ?? rawHtml.length - offset);
241
- childrenEnd = childrenStart;
242
- const startTagStartOffset = originNode.position.start.offset + offset;
243
- let startTagEndOffset = childrenStart;
244
- startTagRaw = rawHtml.slice(startTagStartOffset, startTagEndOffset);
245
- const expectedCloseTag = `</${originNode.name}>`;
246
- if (startTagRaw.includes(expectedCloseTag)) {
247
- childrenStart -= expectedCloseTag.length;
248
- childrenEnd = childrenStart;
249
- startTagRaw = startTagRaw.replace(expectedCloseTag, '');
250
- }
251
- else {
252
- let startTagRawMasked = startTagRaw;
253
- for (const attr of originNode.attributes) {
254
- startTagRawMasked = startTagRawMasked.replace(attr.value, ' '.repeat(attr.value.length));
255
- }
256
- const closeChars = '>';
257
- const closeOffset = startTagRawMasked.indexOf(closeChars) + closeChars.length;
258
- startTagEndOffset = startTagStartOffset + closeOffset;
259
- startTagRaw = rawHtml.slice(startTagStartOffset, startTagEndOffset);
260
- }
261
- // console.log({
262
- // originNode,
263
- // attrs: originNode.attributes,
264
- // startTagRaw,
265
- // startTagStartOffset,
266
- // startTagEndOffset,
267
- // expectedCloseTag,
268
- // childrenStart,
269
- // childrenEnd,
270
- // });
271
- }
272
- const tagTokens = tagParser(startTagRaw, startLine, startCol, startOffset);
273
- const tagName = tagTokens.tagName;
274
- let endTag = null;
275
- if (childrenEnd < (originNode.position.end?.offset ?? 0) + offset) {
276
- const endTagLoc = sliceFragment(rawHtml, childrenEnd, (originNode.position.end?.offset ?? 0) + offset);
277
- const endTagTokens = tagParser(endTagLoc.raw, endTagLoc.startLine, endTagLoc.startCol, endTagLoc.startOffset);
278
- const endTagName = endTagTokens.tagName;
279
- endTag = {
280
- uuid: uuid(),
281
- raw: endTagLoc.raw,
282
- startOffset: endTagLoc.startOffset,
283
- endOffset: endTagLoc.endOffset,
284
- startLine: endTagLoc.startLine,
285
- endLine: endTagLoc.endLine,
286
- startCol: endTagLoc.startCol,
287
- endCol: endTagLoc.endCol,
288
- nodeName: endTagName,
289
- type: 'endtag',
290
- namespace: scopeNS,
291
- attributes: endTagTokens.attrs,
292
- parentNode,
293
- prevNode,
294
- nextNode,
295
- pearNode: null,
296
- isFragment: false,
297
- isGhost: false,
298
- tagOpenChar: '</',
299
- tagCloseChar: '>',
300
- };
301
- }
302
- const startTag = {
303
- uuid: uuid(),
304
- raw: startTagRaw,
305
- startOffset,
306
- endOffset: startOffset + startTagRaw.length,
307
- startLine,
308
- endLine: getEndLine(startTagRaw, startLine),
309
- startCol,
310
- endCol: getEndCol(startTagRaw, startCol),
311
- nodeName: tagName,
312
- type: 'starttag',
313
- namespace: scopeNS,
314
- elementType: detectElementType(tagName, options?.authoredElementName, /^[A-Z]|\./),
315
- attributes: originNode.attributes.map((
316
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
317
- attr, i) => attrTokenizer(attr, originNode.attributes[i + 1] ?? null, rawHtml, startTagRaw, startOffset + startTagRaw.length)),
318
- hasSpreadAttr: false,
319
- parentNode,
320
- prevNode,
321
- nextNode,
322
- pearNode: endTag,
323
- selfClosingSolidus: tagTokens.selfClosingSolidus,
324
- endSpace: tagTokens.afterAttrSpaces,
325
- isFragment: false,
326
- isGhost: false,
327
- tagOpenChar: '<',
328
- tagCloseChar: '>',
329
- };
330
- if (endTag) {
331
- endTag.pearNode = startTag;
332
- }
333
- startTag.childNodes = ['style', 'script'].includes(tagName)
334
- ? undefined
335
- : traverse(originNode, startTag, scopeNS, rawHtml, offset, options);
336
- return startTag;
337
- }