Skip to content

Commit fd8adbd

Browse files
authored
fix: self-closing style with external source (#252)
* fix: self-closing style with external source * Create thirty-ants-run.md
1 parent 1bb68f6 commit fd8adbd

9 files changed

+1529
-21
lines changed

.changeset/thirty-ants-run.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": patch
3+
---
4+
5+
fix: self-closing style with external source

src/context/index.ts

+81-21
Original file line numberDiff line numberDiff line change
@@ -148,26 +148,47 @@ export class Context {
148148
let start = 0;
149149
for (const block of extractBlocks(code)) {
150150
if (block.tag === "template") {
151+
if (block.selfClosing) {
152+
continue;
153+
}
151154
const lang = block.attrs.find((attr) => attr.key.name === "lang");
152155
if (!lang || !lang.value || lang.value.value === "html") {
153156
continue;
154157
}
155158
}
156159
this.blocks.push(block);
157-
templateCode +=
158-
code.slice(start, block.contentRange[0]) +
159-
spaces.slice(block.contentRange[0], block.contentRange[1]);
160-
if (block.tag === "script") {
161-
scriptCode +=
162-
spaces.slice(start, block.contentRange[0]) +
163-
code.slice(...block.contentRange);
164-
for (const attr of block.attrs) {
165-
scriptAttrs[attr.key.name] = attr.value?.value;
166-
}
160+
161+
if (block.selfClosing) {
162+
// Self-closing blocks are temporarily replaced with `<s---->` or `<t---->` tag
163+
// because the svelte compiler cannot parse self-closing block(script, style) tags.
164+
// It will be restored later in `convertHTMLElement()` processing.
165+
templateCode += `${code.slice(
166+
start,
167+
block.startTagRange[0] + 2 /* `<` and first letter */
168+
)}${"-".repeat(
169+
block.tag.length - 1 /* skip first letter */
170+
)}${code.slice(
171+
block.startTagRange[0] + 1 /* skip `<` */ + block.tag.length,
172+
block.startTagRange[1]
173+
)}`;
174+
scriptCode += spaces.slice(start, block.startTagRange[1]);
175+
start = block.startTagRange[1];
167176
} else {
168-
scriptCode += spaces.slice(start, block.contentRange[1]);
177+
templateCode +=
178+
code.slice(start, block.contentRange[0]) +
179+
spaces.slice(block.contentRange[0], block.contentRange[1]);
180+
if (block.tag === "script") {
181+
scriptCode +=
182+
spaces.slice(start, block.contentRange[0]) +
183+
code.slice(...block.contentRange);
184+
for (const attr of block.attrs) {
185+
scriptAttrs[attr.key.name] = attr.value?.value;
186+
}
187+
} else {
188+
scriptCode += spaces.slice(start, block.contentRange[1]);
189+
}
190+
start = block.contentRange[1];
169191
}
170-
start = block.contentRange[1];
171192
}
172193
templateCode += code.slice(start);
173194
scriptCode += spaces.slice(start);
@@ -301,16 +322,43 @@ export class Context {
301322
return this.blocks.find(
302323
(block) =>
303324
block.tag === tag &&
325+
!block.selfClosing &&
304326
element.range[0] <= block.contentRange[0] &&
305327
block.contentRange[1] <= element.range[1]
306328
);
307329
}
330+
331+
public findSelfClosingBlock(
332+
element: SvelteElement
333+
): SelfClosingBlock | undefined {
334+
return this.blocks.find((block): block is SelfClosingBlock =>
335+
Boolean(
336+
block.selfClosing &&
337+
element.startTag.range[0] <= block.startTagRange[0] &&
338+
block.startTagRange[1] <= element.startTag.range[1]
339+
)
340+
);
341+
}
308342
}
309343

310-
type Block = {
344+
type Block =
345+
| {
346+
tag: "script" | "style" | "template";
347+
originalTag: string;
348+
attrs: AttributeToken[];
349+
selfClosing?: false;
350+
contentRange: [number, number];
351+
startTagRange: [number, number];
352+
endTagRange: [number, number];
353+
}
354+
| SelfClosingBlock;
355+
356+
type SelfClosingBlock = {
311357
tag: "script" | "style" | "template";
358+
originalTag: string;
312359
attrs: AttributeToken[];
313-
contentRange: [number, number];
360+
selfClosing: true;
361+
startTagRange: [number, number];
314362
};
315363

316364
/** Extract <script> blocks */
@@ -325,23 +373,32 @@ function* extractBlocks(code: string): IterableIterator<Block> {
325373
if (!tag) {
326374
continue;
327375
}
376+
const startTagStart = startTagOpenMatch.index;
328377
let startTagEnd = startTagOpenRe.lastIndex;
329378

379+
const lowerTag = tag.toLowerCase() as "script" | "style" | "template";
380+
330381
let attrs: AttributeToken[] = [];
331382
if (!nextChar.trim()) {
332383
const attrsData = parseAttributes(code, startTagOpenRe.lastIndex);
333384
attrs = attrsData.attributes;
334385
startTagEnd = attrsData.index;
335-
if (code[startTagEnd] === "/") {
336-
startTagEnd++;
386+
if (code[startTagEnd] === "/" && code[startTagEnd + 1] === ">") {
387+
yield {
388+
tag: lowerTag,
389+
originalTag: tag,
390+
attrs,
391+
selfClosing: true,
392+
startTagRange: [startTagStart, startTagEnd + 2],
393+
};
394+
continue;
337395
}
338396
if (code[startTagEnd] === ">") {
339397
startTagEnd++;
340398
} else {
341399
continue;
342400
}
343401
}
344-
const lowerTag = tag.toLowerCase() as "script" | "style" | "template";
345402
const endTagRe =
346403
lowerTag === "script"
347404
? endScriptTagRe
@@ -352,13 +409,16 @@ function* extractBlocks(code: string): IterableIterator<Block> {
352409
const endTagMatch = endTagRe.exec(code);
353410
if (endTagMatch) {
354411
const endTagStart = endTagMatch.index;
355-
const contentRange: [number, number] = [startTagEnd, endTagStart];
412+
const endTagEnd = endTagRe.lastIndex;
356413
yield {
357-
contentRange,
358-
attrs,
359414
tag: lowerTag,
415+
originalTag: tag,
416+
attrs,
417+
startTagRange: [startTagStart, startTagEnd],
418+
contentRange: [startTagEnd, endTagStart],
419+
endTagRange: [endTagStart, endTagEnd],
360420
};
361-
startTagOpenRe.lastIndex = endTagRe.lastIndex;
421+
startTagOpenRe.lastIndex = endTagEnd;
362422
}
363423
}
364424
}

src/parser/converts/element.ts

+10
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ function convertHTMLElement(
268268
element.name.name === "style" ||
269269
(element.name.name === "template" && ctx.findBlock(element))
270270
) {
271+
// Restore the block-like element.
271272
for (const child of element.children) {
272273
if (child.type === "SvelteText") {
273274
child.value = ctx.code.slice(...child.range);
@@ -280,6 +281,15 @@ function convertHTMLElement(
280281
);
281282
}
282283
}
284+
if (element.startTag.selfClosing && element.name.name.endsWith("-")) {
285+
// Restore the self-closing block.
286+
const selfClosingBlock =
287+
/^[a-z]-+$/iu.test(element.name.name) &&
288+
ctx.findSelfClosingBlock(element);
289+
if (selfClosingBlock) {
290+
element.name.name = selfClosingBlock.originalTag;
291+
}
292+
}
283293

284294
return element;
285295
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<template src="./fixtures/template.pug" />
2+
<script>
3+
console.log('foo')
4+
</script>

0 commit comments

Comments
 (0)