Skip to content

Commit 63eb62f

Browse files
committed
Chore: move parsing logic into lib directory
1 parent 94ee8fd commit 63eb62f

File tree

3 files changed

+156
-128
lines changed

3 files changed

+156
-128
lines changed

index.js

Lines changed: 31 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010
//------------------------------------------------------------------------------
1111

1212
const path = require("path")
13-
const SAXParser = require("parse5").SAXParser
13+
const parse = require("./lib/parse")
1414

1515
//------------------------------------------------------------------------------
1616
// Helpers
1717
//------------------------------------------------------------------------------
1818

19-
const LINE_TERMINATORS = /\r\n|\r|\n|\u2028|\u2029/g
20-
2119
/**
2220
* Gets the specified parser.
2321
* If it's unspecified, this returns espree.
@@ -30,135 +28,44 @@ function getParser(options) {
3028
return require(options.parser || "espree")
3129
}
3230

33-
/**
34-
* Calculates the end location.
35-
*
36-
* @param {string} raw - The text of the target token.
37-
* @param {number} startLine - The start line of the target token.
38-
* @param {number} startColumn - The start column of the target token.
39-
* @returns {{line: number, column: number}} The end location.
40-
* @private
41-
*/
42-
function calcLocEnd(raw, startLine, startColumn) {
43-
const lines = raw.split(LINE_TERMINATORS)
44-
const line = startLine + lines.length - 1
45-
const column = (lines.length === 1)
46-
? startColumn + raw.length
47-
: lines[lines.length - 1].length
48-
49-
return {line, column}
50-
}
51-
52-
/**
53-
* Creates the token with the given parameters.
54-
*
55-
* @param {string} value - The token value to create.
56-
* @param {string} text - The whole text.
57-
* @param {object} location - The location object of `parse5` module.
58-
* @returns {object} The created token object.
59-
* @private
60-
*/
61-
function createToken(value, text, location) {
62-
const type = "Punctuator"
63-
const start = location.startOffset
64-
const end = location.endOffset
65-
const line = location.line
66-
const column = location.col - 1
67-
const range = [start, end]
68-
const raw = text.slice(start, end)
69-
const loc = {
70-
start: {line, column},
71-
end: calcLocEnd(raw, line, column),
72-
}
73-
74-
return {type, value, raw, start, end, range, loc}
75-
}
76-
77-
/**
78-
* Extracts the text of the 1st script element in the given text.
79-
*
80-
* @param {string} originalText - The whole text to extract.
81-
* @returns {{text: string, offset: number}} The information of the 1st script.
82-
* @private
83-
*/
84-
function extractFirstScript(originalText) {
85-
const parser = new SAXParser({locationInfo: true})
86-
let inTemplate = 0
87-
let startToken = null
88-
let endToken = null
89-
let text = ""
90-
let offset = 0
91-
92-
parser.on("startTag", (name, attrs, selfClosing, location) => {
93-
if (selfClosing) {
94-
return
95-
}
96-
if (name === "template") {
97-
inTemplate += 1
98-
}
99-
if (inTemplate === 0 && name === "script") {
100-
startToken = createToken("<script>", originalText, location)
101-
}
102-
})
103-
parser.on("endTag", (name, location) => {
104-
if (inTemplate > 0 && name === "template") {
105-
inTemplate -= 1
106-
}
107-
if (startToken != null && name === "script") {
108-
endToken = createToken("</script>", originalText, location)
109-
parser.stop()
110-
}
111-
})
112-
parser.on("text", (_, location) => {
113-
if (startToken != null) {
114-
const start = location.startOffset
115-
const countLines = location.line - 1
116-
const lineTerminators = "\n".repeat(countLines)
117-
const spaces = " ".repeat(start - countLines)
118-
const scriptText = originalText.slice(start, location.endOffset)
119-
120-
text = `${spaces}${lineTerminators}${scriptText}`
121-
offset = start
122-
}
123-
})
124-
parser.end(originalText)
125-
126-
return {startToken, endToken, text, offset}
127-
}
128-
12931
//------------------------------------------------------------------------------
13032
// Exports
13133
//------------------------------------------------------------------------------
13234

13335
/**
134-
* Parses the source code.
135-
*
136-
* If `options.filePath` is a `.vue` file, this extracts the first `<script>`
137-
* element then parses it.
36+
* Provides the `parse` method for `.vue` files.
13837
*
139-
* @memberof module:vue-eslint-parser
140-
* @function parse
141-
* @param {string} text - The source code to be parsed.
142-
* @param {object} options - The option object for espree.
143-
* @returns {ASTNode} The AST object as the result of parsing.
38+
* @module vue-eslint-parser
14439
*/
145-
module.exports.parse = function parse(text, options) {
146-
const parser = getParser(options)
147-
148-
if (path.extname(options.filePath || "unknown.js") !== ".vue") {
149-
return parser.parse(text, options)
150-
}
40+
module.exports = {
41+
/**
42+
* Parses the source code.
43+
*
44+
* If `options.filePath` is a `.vue` file, this extracts the first `<script>`
45+
* element then parses it.
46+
*
47+
* @param {string} text - The source code to be parsed.
48+
* @param {object} options - The option object for espree.
49+
* @returns {{ast: ASTNode}} The AST object as the result of parsing.
50+
*/
51+
parse(text, options) {
52+
const parser = getParser(options)
53+
54+
if (path.extname(options.filePath || "unknown.js") !== ".vue") {
55+
return parser.parse(text, options)
56+
}
15157

152-
const script = extractFirstScript(text)
153-
const ast = parser.parse(script.text, options)
58+
const script = parse(text)
59+
const ast = parser.parse(script.text, options)
15460

155-
ast.start = script.offset
156-
if (script.startToken) {
157-
ast.tokens.unshift(script.startToken)
158-
}
159-
if (script.endToken) {
160-
ast.tokens.push(script.endToken)
161-
}
61+
ast.start = script.offset
62+
if (script.startToken) {
63+
ast.tokens.unshift(script.startToken)
64+
}
65+
if (script.endToken) {
66+
ast.tokens.push(script.endToken)
67+
}
16268

163-
return ast
69+
return ast
70+
},
16471
}

lib/parse.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* @author Toru Nagashima <https://github.com/mysticatea>
3+
* @copyright 2016 Toru Nagashima. All rights reserved.
4+
* See LICENSE file in root directory for full license.
5+
*/
6+
"use strict"
7+
8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
const SAXParser = require("parse5").SAXParser
13+
14+
//------------------------------------------------------------------------------
15+
// Helpers
16+
//------------------------------------------------------------------------------
17+
18+
const LINE_TERMINATORS = /\r\n|\r|\n|\u2028|\u2029/g
19+
20+
/**
21+
* Calculates the end location.
22+
*
23+
* @param {string} raw - The text of the target token.
24+
* @param {number} startLine - The start line of the target token.
25+
* @param {number} startColumn - The start column of the target token.
26+
* @returns {{line: number, column: number}} The end location.
27+
* @private
28+
*/
29+
function calcLocEnd(raw, startLine, startColumn) {
30+
const lines = raw.split(LINE_TERMINATORS)
31+
const line = startLine + lines.length - 1
32+
const column = (lines.length === 1)
33+
? startColumn + raw.length
34+
: lines[lines.length - 1].length
35+
36+
return {line, column}
37+
}
38+
39+
/**
40+
* Creates the token with the given parameters.
41+
*
42+
* @param {string} value - The token value to create.
43+
* @param {string} text - The whole text.
44+
* @param {object} location - The location object of `parse5` module.
45+
* @returns {object} The created token object.
46+
* @private
47+
*/
48+
function createToken(value, text, location) {
49+
const type = "Punctuator"
50+
const start = location.startOffset
51+
const end = location.endOffset
52+
const line = location.line
53+
const column = location.col - 1
54+
const range = [start, end]
55+
const raw = text.slice(start, end)
56+
const loc = {
57+
start: {line, column},
58+
end: calcLocEnd(raw, line, column),
59+
}
60+
61+
return {type, value, raw, start, end, range, loc}
62+
}
63+
64+
//------------------------------------------------------------------------------
65+
// Exports
66+
//------------------------------------------------------------------------------
67+
68+
/**
69+
* Extracts the text of the 1st script element in the given text.
70+
*
71+
* @param {string} originalText - The whole text to extract.
72+
* @returns {{text: string, offset: number}} The information of the 1st script.
73+
* @private
74+
*/
75+
module.exports = function parse(originalText) {
76+
const parser = new SAXParser({locationInfo: true})
77+
let inTemplate = 0
78+
let startToken = null
79+
let endToken = null
80+
let text = ""
81+
let offset = 0
82+
83+
parser.on("startTag", (name, attrs, selfClosing, location) => {
84+
if (selfClosing) {
85+
return
86+
}
87+
if (name === "template") {
88+
inTemplate += 1
89+
}
90+
if (inTemplate === 0 && name === "script") {
91+
startToken = createToken("<script>", originalText, location)
92+
}
93+
})
94+
parser.on("endTag", (name, location) => {
95+
if (inTemplate > 0 && name === "template") {
96+
inTemplate -= 1
97+
}
98+
if (startToken != null && name === "script") {
99+
endToken = createToken("</script>", originalText, location)
100+
parser.stop()
101+
}
102+
})
103+
parser.on("text", (_, location) => {
104+
if (startToken != null) {
105+
const start = location.startOffset
106+
const countLines = location.line - 1
107+
const lineTerminators = "\n".repeat(countLines)
108+
const spaces = " ".repeat(start - countLines)
109+
const scriptText = originalText.slice(start, location.endOffset)
110+
111+
text = `${spaces}${lineTerminators}${scriptText}`
112+
offset = start
113+
}
114+
})
115+
parser.end(originalText)
116+
117+
return {startToken, endToken, text, offset}
118+
}

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@
66
"node": ">=4"
77
},
88
"main": "index.js",
9-
"files": [],
9+
"files": [
10+
"lib"
11+
],
1012
"scripts": {
13+
"_mocha": "_mocha \"test/*.js\" --compilers js:babel-register --reporter progress --timeout 30000",
1114
"clean": "rimraf .nyc_output coverage",
1215
"coverage": "nyc report --reporter lcov && opener ./coverage/lcov-report/index.html",
13-
"lint": "eslint index.js \"test/*.js\"",
16+
"lint": "eslint index.js lib \"test/*.js\"",
1417
"postversion": "git push && git push --tags",
1518
"pretest": "npm run lint",
1619
"preversion": "npm test",
1720
"setup": "git submodule update --init && cd test/fixtures/eslint && npm install",
18-
"test": "nyc mocha \"test/*.js\" --compilers js:babel-register --reporter progress --timeout 30000",
19-
"watch": "mocha \"test/*.js\" --compilers js:babel-register --growl --reporter progress --watch",
21+
"test": "nyc npm run _mocha",
22+
"watch": "npm run _mocha -- --growl --watch",
2023
"codecov": "nyc report --reporter lcovonly && codecov"
2124
},
2225
"dependencies": {

0 commit comments

Comments
 (0)