Skip to content

Commit 18e8e17

Browse files
authored
test: add scope test (#219)
1 parent 7b1f6a1 commit 18e8e17

File tree

3 files changed

+165
-172
lines changed

3 files changed

+165
-172
lines changed

scripts/update-fixtures-ast.js

+6-138
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@
1212
const fs = require("fs")
1313
const path = require("path")
1414
const parser = require("../src")
15-
const escope = require("eslint-scope")
1615
const semver = require("semver")
16+
const {
17+
scopeToJSON,
18+
analyze,
19+
replacer,
20+
getAllTokens,
21+
} = require("../test/test-utils")
1722

1823
//------------------------------------------------------------------------------
1924
// Helpers
@@ -30,40 +35,6 @@ const PARSER_OPTIONS = {
3035
eslintScopeManager: true,
3136
}
3237

33-
/**
34-
* Remove `parent` proeprties from the given AST.
35-
* @param {string} key The key.
36-
* @param {any} value The value of the key.
37-
* @returns {any} The value of the key to output.
38-
*/
39-
function replacer(key, value) {
40-
if (key === "parent") {
41-
return undefined
42-
}
43-
if (key === "errors" && Array.isArray(value)) {
44-
return value.map((e) => ({
45-
message: e.message,
46-
index: e.index,
47-
lineNumber: e.lineNumber,
48-
column: e.column,
49-
}))
50-
}
51-
return value
52-
}
53-
54-
/**
55-
* Get all tokens of the given AST.
56-
* @param {ASTNode} ast The root node of AST.
57-
* @returns {Token[]} Tokens.
58-
*/
59-
function getAllTokens(ast) {
60-
const tokenArrays = [ast.tokens, ast.comments]
61-
if (ast.templateBody != null) {
62-
tokenArrays.push(ast.templateBody.tokens, ast.templateBody.comments)
63-
}
64-
return Array.prototype.concat.apply([], tokenArrays)
65-
}
66-
6738
/**
6839
* Create simple tree.
6940
* @param {string} source The source code.
@@ -98,109 +69,6 @@ function getTree(source, ast) {
9869
return root.children
9970
}
10071

101-
function scopeToJSON(scopeManager) {
102-
return JSON.stringify(normalizeScope(scopeManager.globalScope), replacer, 4)
103-
104-
function normalizeScope(scope) {
105-
return {
106-
type: scope.type,
107-
variables: scope.variables.map(normalizeVar),
108-
references: scope.references.map(normalizeReference),
109-
childScopes: scope.childScopes.map(normalizeScope),
110-
through: scope.through.map(normalizeReference),
111-
}
112-
}
113-
114-
function normalizeVar(v) {
115-
return {
116-
name: v.name,
117-
identifiers: v.identifiers.map(normalizeId),
118-
defs: v.defs.map(normalizeDef),
119-
references: v.references.map(normalizeReference),
120-
}
121-
}
122-
123-
function normalizeReference(reference) {
124-
return {
125-
identifier: normalizeId(reference.identifier),
126-
from: reference.from.type,
127-
resolved: normalizeId(
128-
reference.resolved &&
129-
reference.resolved.defs &&
130-
reference.resolved.defs[0] &&
131-
reference.resolved.defs[0].name,
132-
),
133-
init: reference.init || null,
134-
vueUsedInTemplate: reference.vueUsedInTemplate
135-
? reference.vueUsedInTemplate
136-
: undefined,
137-
}
138-
}
139-
140-
function normalizeDef(def) {
141-
return {
142-
type: def.type,
143-
node: normalizeDefNode(def.node),
144-
name: def.name.name,
145-
}
146-
}
147-
148-
function normalizeId(identifier) {
149-
return (
150-
identifier && {
151-
type: identifier.type,
152-
name: identifier.name,
153-
loc: identifier.loc,
154-
}
155-
)
156-
}
157-
158-
function normalizeDefNode(node) {
159-
return {
160-
type: node.type,
161-
loc: node.loc,
162-
}
163-
}
164-
}
165-
166-
/**
167-
* Analyze scope
168-
*/
169-
function analyze(ast, parserOptions) {
170-
const ecmaVersion = parserOptions.ecmaVersion || 2017
171-
const ecmaFeatures = parserOptions.ecmaFeatures || {}
172-
const sourceType = parserOptions.sourceType || "script"
173-
const result = escope.analyze(ast, {
174-
ignoreEval: true,
175-
nodejsScope: false,
176-
impliedStrict: ecmaFeatures.impliedStrict,
177-
ecmaVersion,
178-
sourceType,
179-
fallback: getFallbackKeys,
180-
})
181-
182-
return result
183-
184-
function getFallbackKeys(node) {
185-
return Object.keys(node).filter(fallbackKeysFilter, node)
186-
}
187-
188-
function fallbackKeysFilter(key) {
189-
const value = null
190-
return (
191-
key !== "comments" &&
192-
key !== "leadingComments" &&
193-
key !== "loc" &&
194-
key !== "parent" &&
195-
key !== "range" &&
196-
key !== "tokens" &&
197-
key !== "trailingComments" &&
198-
typeof value === "object" &&
199-
(typeof value.type === "string" || Array.isArray(value))
200-
)
201-
}
202-
}
203-
20472
//------------------------------------------------------------------------------
20573
// Main
20674
//------------------------------------------------------------------------------

test/ast.js

+19-34
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const lodash = require("lodash")
1616
const parser = require("../src")
1717
const Linter = require("./fixtures/eslint").Linter
1818
const semver = require("semver")
19+
const { scopeToJSON, analyze, replacer, getAllTokens } = require("./test-utils")
1920

2021
//------------------------------------------------------------------------------
2122
// Helpers
@@ -30,40 +31,7 @@ const PARSER_OPTIONS = {
3031
loc: true,
3132
range: true,
3233
tokens: true,
33-
}
34-
35-
/**
36-
* Remove `parent` proeprties from the given AST.
37-
* @param {string} key The key.
38-
* @param {any} value The value of the key.
39-
* @returns {any} The value of the key to output.
40-
*/
41-
function replacer(key, value) {
42-
if (key === "parent") {
43-
return undefined
44-
}
45-
if (key === "errors" && Array.isArray(value)) {
46-
return value.map((e) => ({
47-
message: e.message,
48-
index: e.index,
49-
lineNumber: e.lineNumber,
50-
column: e.column,
51-
}))
52-
}
53-
return value
54-
}
55-
56-
/**
57-
* Get all tokens of the given AST.
58-
* @param {ASTNode} ast The root node of AST.
59-
* @returns {Token[]} Tokens.
60-
*/
61-
function getAllTokens(ast) {
62-
const tokenArrays = [ast.tokens, ast.comments]
63-
if (ast.templateBody != null) {
64-
tokenArrays.push(ast.templateBody.tokens, ast.templateBody.comments)
65-
}
66-
return Array.prototype.concat.apply([], tokenArrays)
34+
eslintScopeManager: true,
6735
}
6836

6937
/**
@@ -305,6 +273,23 @@ describe("Template AST", () => {
305273
assert.strictEqual(actualText, expectedText)
306274
})
307275

276+
it("should scope in the correct.", () => {
277+
const version = require(`eslint/package.json`).version
278+
if (!semver.satisfies(version, ">=8")) {
279+
return
280+
}
281+
const resultPath = path.join(ROOT, `${name}/scope.json`)
282+
if (!fs.existsSync(resultPath)) {
283+
return
284+
}
285+
const expectedText = fs.readFileSync(resultPath, "utf8")
286+
const actualText = scopeToJSON(
287+
actual.scopeManager || analyze(actual.ast, options),
288+
)
289+
290+
assert.strictEqual(actualText, expectedText)
291+
})
292+
308293
it("should have correct parent properties.", () => {
309294
validateParent(source, parserOptions)
310295
})

test/test-utils.js

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
const escope = require("eslint-scope")
2+
3+
module.exports = { replacer, getAllTokens, scopeToJSON, analyze }
4+
5+
/**
6+
* Remove `parent` properties from the given AST.
7+
* @param {string} key The key.
8+
* @param {any} value The value of the key.
9+
* @returns {any} The value of the key to output.
10+
*/
11+
function replacer(key, value) {
12+
if (key === "parent") {
13+
return undefined
14+
}
15+
if (key === "errors" && Array.isArray(value)) {
16+
return value.map((e) => ({
17+
message: e.message,
18+
index: e.index,
19+
lineNumber: e.lineNumber,
20+
column: e.column,
21+
}))
22+
}
23+
return value
24+
}
25+
26+
/**
27+
* Get all tokens of the given AST.
28+
* @param {ASTNode} ast The root node of AST.
29+
* @returns {Token[]} Tokens.
30+
*/
31+
function getAllTokens(ast) {
32+
const tokenArrays = [ast.tokens, ast.comments]
33+
if (ast.templateBody != null) {
34+
tokenArrays.push(ast.templateBody.tokens, ast.templateBody.comments)
35+
}
36+
return Array.prototype.concat.apply([], tokenArrays)
37+
}
38+
39+
function scopeToJSON(scopeManager) {
40+
return JSON.stringify(normalizeScope(scopeManager.globalScope), replacer, 4)
41+
42+
function normalizeScope(scope) {
43+
return {
44+
type: scope.type,
45+
variables: scope.variables.map(normalizeVar),
46+
references: scope.references.map(normalizeReference),
47+
childScopes: scope.childScopes.map(normalizeScope),
48+
through: scope.through.map(normalizeReference),
49+
}
50+
}
51+
52+
function normalizeVar(v) {
53+
return {
54+
name: v.name,
55+
identifiers: v.identifiers.map(normalizeId),
56+
defs: v.defs.map(normalizeDef),
57+
references: v.references.map(normalizeReference),
58+
}
59+
}
60+
61+
function normalizeReference(reference) {
62+
return {
63+
identifier: normalizeId(reference.identifier),
64+
from: reference.from.type,
65+
resolved: normalizeId(
66+
reference.resolved &&
67+
reference.resolved.defs &&
68+
reference.resolved.defs[0] &&
69+
reference.resolved.defs[0].name,
70+
),
71+
init: reference.init || null,
72+
vueUsedInTemplate: reference.vueUsedInTemplate
73+
? reference.vueUsedInTemplate
74+
: undefined,
75+
}
76+
}
77+
78+
function normalizeDef(def) {
79+
return {
80+
type: def.type,
81+
node: normalizeDefNode(def.node),
82+
name: def.name.name,
83+
}
84+
}
85+
86+
function normalizeId(identifier) {
87+
return (
88+
identifier && {
89+
type: identifier.type,
90+
name: identifier.name,
91+
loc: identifier.loc,
92+
}
93+
)
94+
}
95+
96+
function normalizeDefNode(node) {
97+
return {
98+
type: node.type,
99+
loc: node.loc,
100+
}
101+
}
102+
}
103+
104+
/**
105+
* Analyze scope
106+
*/
107+
function analyze(ast, parserOptions) {
108+
const ecmaVersion = parserOptions.ecmaVersion || 2017
109+
const ecmaFeatures = parserOptions.ecmaFeatures || {}
110+
const sourceType = parserOptions.sourceType || "script"
111+
const result = escope.analyze(ast, {
112+
ignoreEval: true,
113+
nodejsScope: false,
114+
impliedStrict: ecmaFeatures.impliedStrict,
115+
ecmaVersion,
116+
sourceType,
117+
fallback: getFallbackKeys,
118+
})
119+
120+
return result
121+
122+
function getFallbackKeys(node) {
123+
return Object.keys(node).filter(fallbackKeysFilter, node)
124+
}
125+
126+
function fallbackKeysFilter(key) {
127+
const value = null
128+
return (
129+
key !== "comments" &&
130+
key !== "leadingComments" &&
131+
key !== "loc" &&
132+
key !== "parent" &&
133+
key !== "range" &&
134+
key !== "tokens" &&
135+
key !== "trailingComments" &&
136+
typeof value === "object" &&
137+
(typeof value.type === "string" || Array.isArray(value))
138+
)
139+
}
140+
}

0 commit comments

Comments
 (0)