Skip to content

Commit 00a1179

Browse files
authored
(fix) use regex for script/style blanks (#440)
#300
1 parent a62525e commit 00a1179

File tree

7 files changed

+75
-105
lines changed

7 files changed

+75
-105
lines changed

packages/language-server/test/lib/documents/utils.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ describe('document/utils', () => {
174174
<scrit>blah</scrit>
175175
<script>top level script</script>
176176
`;
177-
// Note: cannot test <scrit>blah</scriPt> as that breaks parse5 parsing for top level script!
178177

179178
assert.deepStrictEqual(extractScriptTags(text)?.script, {
180179
content: 'top level script',

packages/svelte2tsx/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@
1919
"devDependencies": {
2020
"@types/mocha": "^5.2.7",
2121
"@types/node": "^8.10.53",
22-
"@types/parse5": "^5.0.2",
2322
"@types/unist": "^2.0.3",
2423
"@types/vfile": "^3.0.2",
2524
"magic-string": "^0.25.4",
2625
"mocha": "^6.2.2",
27-
"parse5": "^5.1.0",
2826
"rollup": "^1.12.0",
2927
"rollup-plugin-commonjs": "^10.0.0",
3028
"rollup-plugin-delete": "^1.1.0",

packages/svelte2tsx/rollup.config.test.js

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,7 @@ export default [
2020
json(),
2121
typescript(),
2222
],
23-
external: [
24-
...builtins,
25-
'typescript',
26-
'svelte',
27-
'svelte/compiler',
28-
'parse5',
29-
'magic-string',
30-
],
23+
external: [...builtins, 'typescript', 'svelte', 'svelte/compiler', 'magic-string'],
3124
},
3225
{
3326
input: ['src/htmlxtojsx/index.ts'],
@@ -43,13 +36,6 @@ export default [
4336
json(),
4437
typescript(),
4538
],
46-
external: [
47-
...builtins,
48-
'typescript',
49-
'svelte',
50-
'svelte/compiler',
51-
'parse5',
52-
'magic-string',
53-
],
39+
external: [...builtins, 'typescript', 'svelte', 'svelte/compiler', 'magic-string'],
5440
},
5541
];

packages/svelte2tsx/src/htmlxparser.ts

Lines changed: 59 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,78 @@
1-
import parse5, {
2-
DefaultTreeDocumentFragment,
3-
DefaultTreeElement,
4-
DefaultTreeTextNode,
5-
} from 'parse5';
61
import compiler from 'svelte/compiler';
72
import { Node } from 'estree-walker';
83

9-
function walkAst(doc: DefaultTreeElement, action: (c: DefaultTreeElement) => void) {
10-
action(doc);
11-
if (!doc.childNodes) return;
12-
for (let i = 0; i < doc.childNodes.length; i++) {
13-
walkAst(doc.childNodes[i] as DefaultTreeElement, action);
14-
}
4+
function parseAttributeValue(value: string): string {
5+
return /^['"]/.test(value) ? value.slice(1, -1) : value;
156
}
167

17-
export function findVerbatimElements(htmlx: string) {
18-
const elements: Node[] = [];
19-
const tagNames = ['script', 'style'];
20-
21-
const parseOpts = { sourceCodeLocationInfo: true };
22-
const doc = parse5.parseFragment(htmlx, parseOpts) as DefaultTreeDocumentFragment;
23-
24-
const checkCase = (content: DefaultTreeTextNode, el: parse5.DefaultTreeElement) => {
25-
const orgStart = el.sourceCodeLocation.startOffset || 0;
26-
const orgEnd = el.sourceCodeLocation.endOffset || 0;
27-
const outerHtml = htmlx.substring(orgStart, orgEnd);
28-
const onlyTag = content ? outerHtml.replace(content.value, '') : outerHtml;
29-
30-
return tagNames.some((tag) => onlyTag.match(tag));
31-
};
32-
33-
walkAst(doc as DefaultTreeElement, (el) => {
34-
const parseValue = (attr: parse5.Attribute) => {
35-
const sourceCodeLocation = el.sourceCodeLocation.attrs[attr.name];
36-
const { startOffset, endOffset } = sourceCodeLocation;
37-
const beforeAttrEnd = htmlx.substring(0, endOffset);
38-
const valueStartIndex = beforeAttrEnd.indexOf('=', startOffset);
39-
const isBare = valueStartIndex === -1;
40-
41-
return {
8+
function parseAttributes(str: string, start: number) {
9+
const attrs: Node[] = [];
10+
str.split(/\s+/)
11+
.filter(Boolean)
12+
.forEach((attr) => {
13+
const attrStart = start + str.indexOf(attr);
14+
const [name, value] = attr.split('=');
15+
attrs[name] = value ? parseAttributeValue(value) : name;
16+
attrs.push({
4217
type: 'Attribute',
43-
name: attr.name,
44-
value: isBare || [
18+
name,
19+
value: !value || [
4520
{
4621
type: 'Text',
47-
start: valueStartIndex + 1,
48-
end: endOffset,
49-
raw: attr.value,
22+
start: attrStart + attr.indexOf('=') + 1,
23+
end: attrStart + attr.length,
24+
raw: parseAttributeValue(value),
5025
},
5126
],
52-
start: startOffset,
53-
end: endOffset,
54-
};
55-
};
27+
start: attrStart,
28+
end: attrStart + attr.length,
29+
});
30+
});
31+
32+
return attrs;
33+
}
5634

57-
if (tagNames.includes(el.nodeName)) {
58-
const hasNodes = el.childNodes && el.childNodes.length > 0;
59-
const content = hasNodes ? (el.childNodes[0] as DefaultTreeTextNode) : null;
60-
if (!checkCase(content, el)) {
61-
return;
62-
}
35+
function extractTag(htmlx: string, tag: 'script' | 'style') {
36+
const exp = new RegExp(`(<${tag}([\\S\\s]*?)>)([\\S\\s]*?)<\\/${tag}>`, 'g');
37+
const matches: Node[] = [];
6338

64-
elements.push({
65-
start: el.sourceCodeLocation.startOffset,
66-
end: el.sourceCodeLocation.endOffset,
67-
type: el.nodeName[0].toUpperCase() + el.nodeName.substr(1),
68-
attributes: !el.attrs ? [] : el.attrs.map((a) => parseValue(a)),
69-
content:
70-
content === null
71-
? {
72-
type: 'Text',
73-
start: el.sourceCodeLocation.startTag.endCol,
74-
end: el.sourceCodeLocation.endTag.startCol,
75-
value: '',
76-
raw: '',
77-
}
78-
: {
79-
type: 'Text',
80-
start: content.sourceCodeLocation.startOffset,
81-
end: content.sourceCodeLocation.endOffset,
82-
value: content.value,
83-
raw: content.value,
84-
},
85-
});
39+
let match: RegExpExecArray | null = null;
40+
while ((match = exp.exec(htmlx)) != null) {
41+
const content = match[3];
42+
43+
if (!content) {
44+
// Self-closing/empty tags don't need replacement
45+
continue;
8646
}
87-
});
8847

89-
return elements;
48+
const start = match.index + match[1].length;
49+
const end = start + content.length;
50+
const containerStart = match.index;
51+
const containerEnd = match.index + match[0].length;
52+
53+
matches.push({
54+
start: containerStart,
55+
end: containerEnd,
56+
type: tag === 'style' ? 'Style' : 'Script',
57+
attributes: parseAttributes(match[2], containerStart + `<${tag}`.length),
58+
content: {
59+
type: 'Text',
60+
start,
61+
end,
62+
value: content,
63+
raw: content,
64+
},
65+
});
66+
}
67+
68+
return matches;
69+
}
70+
71+
function findVerbatimElements(htmlx: string) {
72+
return [...extractTag(htmlx, 'script'), ...extractTag(htmlx, 'style')];
9073
}
9174

92-
export function blankVerbatimContent(htmlx: string, verbatimElements: Node[]) {
75+
function blankVerbatimContent(htmlx: string, verbatimElements: Node[]) {
9376
let output = htmlx;
9477
for (const node of verbatimElements) {
9578
const content = node.content;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
///<reference types="svelte" />
2+
<></>;function render() {
3+
<><iframe src="" />
4+
</>
5+
return { props: {}, slots: {}, getters: {}, events: {} }}
6+
7+
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) {
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<iframe src="" />
2+
<style>
3+
.bla {
4+
color: blue
5+
}
6+
</style>

yarn.lock

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,6 @@
181181
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
182182
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
183183

184-
"@types/parse5@^5.0.2":
185-
version "5.0.3"
186-
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109"
187-
integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==
188-
189184
"@types/prettier@^1.13.2":
190185
version "1.19.0"
191186
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.0.tgz#a2502fb7ce9b6626fdbfc2e2a496f472de1bdd05"
@@ -1607,11 +1602,6 @@ parse-json@^5.0.0:
16071602
json-parse-better-errors "^1.0.1"
16081603
lines-and-columns "^1.1.6"
16091604

1610-
parse5@^5.1.0:
1611-
version "5.1.1"
1612-
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
1613-
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
1614-
16151605
pascal-case@^3.1.1:
16161606
version "3.1.1"
16171607
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f"

0 commit comments

Comments
 (0)