Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 42b5bfb

Browse filesBrowse files
authored
Fix wrong handling when CRLF in <script> (#146)
1 parent 734a87c commit 42b5bfb
Copy full SHA for 42b5bfb

File tree

Expand file treeCollapse file tree

6 files changed

+107
-40
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+107
-40
lines changed

‎src/common/lines-and-columns.ts

Copy file name to clipboardExpand all lines: src/common/lines-and-columns.ts
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sortedLastIndex from "lodash/sortedLastIndex"
22
import type { Location } from "../ast"
3+
import type { LocationCalculator } from "./location-calculator"
34
/**
45
* A class for getting lines and columns location.
56
*/
@@ -24,4 +25,13 @@ export class LinesAndColumns {
2425
const column = index - (line === 1 ? 0 : this.ltOffsets[line - 2])
2526
return { line, column }
2627
}
28+
29+
public createOffsetLocationCalculator(offset: number): LocationCalculator {
30+
return {
31+
getFixOffset() {
32+
return offset
33+
},
34+
getLocFromIndex: this.getLocFromIndex.bind(this),
35+
}
36+
}
2737
}

‎src/html/tokenizer.ts

Copy file name to clipboardExpand all lines: src/html/tokenizer.ts
+9-7Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export class Tokenizer {
170170
public readonly lineTerminators: number[]
171171
private readonly parserOptions: ParserOptions
172172
private lastCodePoint: number
173+
private lastCodePointRaw: number
173174
private offset: number
174175
private column: number
175176
private line: number
@@ -221,7 +222,7 @@ export class Tokenizer {
221222
this.gaps = []
222223
this.lineTerminators = []
223224
this.parserOptions = parserOptions || {}
224-
this.lastCodePoint = NULL
225+
this.lastCodePoint = this.lastCodePointRaw = NULL
225226
this.offset = -1
226227
this.column = -1
227228
this.line = 1
@@ -307,14 +308,14 @@ export class Tokenizer {
307308
*/
308309
private consumeNextCodePoint(): number {
309310
if (this.offset >= this.text.length) {
310-
this.lastCodePoint = EOF
311+
this.lastCodePoint = this.lastCodePointRaw = EOF
311312
return EOF
312313
}
313314

314315
this.offset += this.lastCodePoint >= 0x10000 ? 2 : 1
315316
if (this.offset >= this.text.length) {
316317
this.advanceLocation()
317-
this.lastCodePoint = EOF
318+
this.lastCodePoint = this.lastCodePointRaw = EOF
318319
return EOF
319320
}
320321

@@ -334,18 +335,19 @@ export class Tokenizer {
334335
}
335336

336337
// Skip LF to convert CRLF → LF.
337-
if (this.lastCodePoint === CARRIAGE_RETURN && cp === LINE_FEED) {
338-
this.lastCodePoint = LINE_FEED
338+
if (this.lastCodePointRaw === CARRIAGE_RETURN && cp === LINE_FEED) {
339+
this.lastCodePoint = this.lastCodePointRaw = LINE_FEED
339340
this.gaps.push(this.offset)
340341
return this.consumeNextCodePoint()
341342
}
342343

343344
// Update locations.
344345
this.advanceLocation()
345-
this.lastCodePoint = cp
346+
this.lastCodePoint = this.lastCodePointRaw = cp
346347

347348
// To convert CRLF → LF.
348349
if (cp === CARRIAGE_RETURN) {
350+
this.lastCodePoint = LINE_FEED
349351
return LINE_FEED
350352
}
351353

@@ -356,7 +358,7 @@ export class Tokenizer {
356358
* Advance the current line and column.
357359
*/
358360
private advanceLocation(): void {
359-
if (this.lastCodePoint === LINE_FEED) {
361+
if (this.lastCodePointRaw === LINE_FEED) {
360362
this.lineTerminators.push(this.offset)
361363
this.line += 1
362364
this.column = 0

‎src/index.ts

Copy file name to clipboardExpand all lines: src/index.ts
+9-4Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,15 @@ export function parseForESLint(
132132
},
133133
)
134134
} else {
135-
result = parseScriptElement(scripts[0], locationCalculator, {
136-
...options,
137-
parser: scriptParser,
138-
})
135+
result = parseScriptElement(
136+
scripts[0],
137+
code,
138+
new LinesAndColumns(tokenizer.lineTerminators),
139+
{
140+
...options,
141+
parser: scriptParser,
142+
},
143+
)
139144
}
140145

141146
if (options.vueFeatures?.styleCSSVariableInjection ?? true) {

‎src/script-setup/index.ts

Copy file name to clipboardExpand all lines: src/script-setup/index.ts
+6-21Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,15 @@ function parseScript(
189189
* Parse the source code of the given `<script setup>` and `<script>` elements.
190190
* @param scriptSetupElement The `<script setup>` element to parse.
191191
* @param nodes The `<script>` elements to parse.
192-
* @param code The source code of SFC.
192+
* @param sfcCode The source code of SFC.
193193
* @param linesAndColumns The lines and columns location calculator.
194194
* @param parserOptions The parser options.
195195
* @returns The result of parsing.
196196
*/
197197
export function parseScriptSetupElements(
198198
scriptSetupElement: VElement,
199199
scriptElement: VElement,
200-
code: string,
200+
sfcCode: string,
201201
linesAndColumns: LinesAndColumns,
202202
originalParserOptions: ParserOptions,
203203
): ESLintExtendedProgram {
@@ -207,16 +207,15 @@ export function parseScriptSetupElements(
207207
const scriptSetupModuleCodeBlocks = getScriptSetupModuleCodeBlocks(
208208
scriptSetupElement,
209209
scriptElement,
210-
code,
210+
sfcCode,
211211
linesAndColumns,
212212
parserOptions,
213213
)
214214
if (!scriptSetupModuleCodeBlocks) {
215215
return parseScriptFragment(
216216
"",
217-
simpleOffsetLocationCalculator(
217+
linesAndColumns.createOffsetLocationCalculator(
218218
scriptSetupElement.startTag.range[1],
219-
linesAndColumns,
220219
),
221220
parserOptions,
222221
)
@@ -492,10 +491,8 @@ function getScriptSetupCodeBlocks(
492491
scriptSetupEndOffset,
493492
)
494493

495-
const offsetLocationCalculator = simpleOffsetLocationCalculator(
496-
scriptSetupStartOffset,
497-
linesAndColumns,
498-
)
494+
const offsetLocationCalculator =
495+
linesAndColumns.createOffsetLocationCalculator(scriptSetupStartOffset)
499496

500497
const result = parseScript(
501498
scriptCode,
@@ -925,15 +922,3 @@ function remapLocationAndTokens(
925922

926923
fixLocations(result, locationCalculator)
927924
}
928-
929-
function simpleOffsetLocationCalculator(
930-
offset: number,
931-
linesAndColumns: LinesAndColumns,
932-
): LocationCalculator {
933-
return {
934-
getFixOffset() {
935-
return offset
936-
},
937-
getLocFromIndex: linesAndColumns.getLocFromIndex.bind(linesAndColumns),
938-
}
939-
}

‎src/script/index.ts

Copy file name to clipboardExpand all lines: src/script/index.ts
+17-8Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
getScriptSetupParserOptions,
6060
} from "../script-setup/parser-options"
6161
import { isScriptSetupElement } from "../common/ast-utils"
62+
import type { LinesAndColumns } from "../common/lines-and-columns"
6263

6364
// [1] = aliases.
6465
// [2] = delimiter.
@@ -563,13 +564,15 @@ export function parseScript(
563564
/**
564565
* Parse the source code of the given `<script>` element.
565566
* @param node The `<script>` element to parse.
566-
* @param globalLocationCalculator The location calculator for fixLocations.
567+
* @param sfcCode The source code of SFC.
568+
* @param linesAndColumns The lines and columns location calculator.
567569
* @param parserOptions The parser options.
568570
* @returns The result of parsing.
569571
*/
570572
export function parseScriptElement(
571573
node: VElement,
572-
globalLocationCalculator: LocationCalculatorForHtml,
574+
sfcCode: string,
575+
linesAndColumns: LinesAndColumns,
573576
originalParserOptions: ParserOptions,
574577
): ESLintExtendedProgram {
575578
const parserOptions: ParserOptions = isScriptSetupElement(node)
@@ -580,13 +583,19 @@ export function parseScriptElement(
580583
originalParserOptions.ecmaVersion || DEFAULT_ECMA_VERSION,
581584
}
582585

583-
const text = node.children[0]
584-
const { code, offset } =
585-
text != null && text.type === "VText"
586-
? { code: text.value, offset: text.range[0] }
587-
: { code: "", offset: node.startTag.range[1] }
586+
let code: string
587+
let offset: number
588+
const textNode = node.children[0]
589+
if (textNode != null && textNode.type === "VText") {
590+
const [scriptStartOffset, scriptEndOffset] = textNode.range
591+
code = sfcCode.slice(scriptStartOffset, scriptEndOffset)
592+
offset = scriptStartOffset
593+
} else {
594+
code = ""
595+
offset = node.startTag.range[1]
596+
}
588597
const locationCalculator =
589-
globalLocationCalculator.getSubCalculatorAfter(offset)
598+
linesAndColumns.createOffsetLocationCalculator(offset)
590599
const result = parseScriptFragment(code, locationCalculator, parserOptions)
591600

592601
// Needs the tokens of start/end tags for `lines-around-*` rules to work

‎test/crlf.js

Copy file name to clipboard
+56Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const assert = require("assert")
2+
const parser = require("../src")
3+
4+
describe("About CRLF tests", () => {
5+
it("should not contain CR in `<script>` contents.", () => {
6+
const parsed = parser.parseForESLint(
7+
`<script>\r
8+
export default {\r
9+
computed: {\r
10+
/**\r
11+
* @description TEST\r
12+
* @param {string} arg - Lorem\r
13+
* @return {string} - Some Description\r
14+
*/\r
15+
isForTestingLint (arg) {\r
16+
return arg;\r
17+
},\r
18+
},\r
19+
};\r
20+
</script>\r
21+
`,
22+
{
23+
sourceType: "module",
24+
},
25+
)
26+
const script = parsed.services
27+
.getDocumentFragment()
28+
.children.find(
29+
(child) => child.type === "VElement" && child.name === "script",
30+
)
31+
assert.ok(!script.children[0].value.includes("\r"))
32+
})
33+
it("should contain CRLF in script comment.", async () => {
34+
const parsed = parser.parseForESLint(
35+
`<script>\r
36+
export default {\r
37+
computed: {\r
38+
/**\r
39+
* @description TEST\r
40+
* @param {string} arg - Lorem\r
41+
* @return {string} - Some Description\r
42+
*/\r
43+
isForTestingLint (arg) {\r
44+
return arg;\r
45+
},\r
46+
},\r
47+
};\r
48+
</script>\r
49+
`,
50+
{
51+
sourceType: "module",
52+
},
53+
)
54+
assert.ok(parsed.ast.comments[0].value.includes("\r\n"))
55+
})
56+
})

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.