Skip to content

Commit a864dd5

Browse files
committed
Chore: extract traverse function to independent file
1 parent 5e8fbf4 commit a864dd5

File tree

4 files changed

+173
-137
lines changed

4 files changed

+173
-137
lines changed

lib/register-template-body-visitor.js

Lines changed: 2 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -11,139 +11,13 @@
1111

1212
const EventEmitter = require("events")
1313
const NodeEventGenerator = require("eslint/lib/util/node-event-generator")
14+
const traverseNodes = require("./traverse-nodes")
1415

1516
//------------------------------------------------------------------------------
1617
// Helpers
1718
//------------------------------------------------------------------------------
1819

1920
const emitters = new WeakMap()
20-
const KEYS = {
21-
AssignmentExpression: ["left", "right"],
22-
AssignmentPattern: ["left", "right"],
23-
ArrayExpression: ["elements"],
24-
ArrayPattern: ["elements"],
25-
ArrowFunctionExpression: ["params", "body"],
26-
AwaitExpression: ["argument"],
27-
BlockStatement: ["body"],
28-
BinaryExpression: ["left", "right"],
29-
BreakStatement: ["label"],
30-
CallExpression: ["callee", "arguments"],
31-
CatchClause: ["param", "body"],
32-
ClassBody: ["body"],
33-
ClassDeclaration: ["id", "superClass", "body"],
34-
ClassExpression: ["id", "superClass", "body"],
35-
ConditionalExpression: ["test", "consequent", "alternate"],
36-
ContinueStatement: ["label"],
37-
DebuggerStatement: [],
38-
DirectiveStatement: [],
39-
DoWhileStatement: ["body", "test"],
40-
EmptyStatement: [],
41-
ExportAllDeclaration: ["source"],
42-
ExportDefaultDeclaration: ["declaration"],
43-
ExportNamedDeclaration: ["declaration", "specifiers", "source"],
44-
ExportSpecifier: ["exported", "local"],
45-
ExpressionStatement: ["expression"],
46-
ForStatement: ["init", "test", "update", "body"],
47-
ForInStatement: ["left", "right", "body"],
48-
ForOfStatement: ["left", "right", "body"],
49-
FunctionDeclaration: ["id", "params", "body"],
50-
FunctionExpression: ["id", "params", "body"],
51-
Identifier: [],
52-
IfStatement: ["test", "consequent", "alternate"],
53-
ImportDeclaration: ["specifiers", "source"],
54-
ImportDefaultSpecifier: ["local"],
55-
ImportNamespaceSpecifier: ["local"],
56-
ImportSpecifier: ["imported", "local"],
57-
Literal: [],
58-
LabeledStatement: ["label", "body"],
59-
LogicalExpression: ["left", "right"],
60-
MemberExpression: ["object", "property"],
61-
MetaProperty: ["meta", "property"],
62-
MethodDefinition: ["key", "value"],
63-
ModuleSpecifier: [],
64-
NewExpression: ["callee", "arguments"],
65-
ObjectExpression: ["properties"],
66-
ObjectPattern: ["properties"],
67-
Program: ["body"],
68-
Property: ["key", "value"],
69-
RestElement: ["argument"],
70-
ReturnStatement: ["argument"],
71-
SequenceExpression: ["expressions"],
72-
SpreadElement: ["argument"],
73-
Super: [],
74-
SwitchStatement: ["discriminant", "cases"],
75-
SwitchCase: ["test", "consequent"],
76-
TaggedTemplateExpression: ["tag", "quasi"],
77-
TemplateElement: [],
78-
TemplateLiteral: ["quasis", "expressions"],
79-
ThisExpression: [],
80-
ThrowStatement: ["argument"],
81-
TryStatement: ["block", "handler", "finalizer"],
82-
UnaryExpression: ["argument"],
83-
UpdateExpression: ["argument"],
84-
VariableDeclaration: ["declarations"],
85-
VariableDeclarator: ["id", "init"],
86-
WhileStatement: ["test", "body"],
87-
WithStatement: ["object", "body"],
88-
YieldExpression: ["argument"],
89-
90-
VIdentifier: [],
91-
VText: [],
92-
VExpressionContainer: ["expression"],
93-
VDirectiveKey: [],
94-
VAttributeValue: [],
95-
VAttribute: ["key", "value"],
96-
VStartTag: ["id", "attributes"],
97-
VEndTag: ["id"],
98-
VElement: ["startTag", "children", "endTag"],
99-
}
100-
101-
/**
102-
* Get the keys of the given node to traverse it.
103-
* @param {ASTNode} node The node to get.
104-
* @returns {string[]} The keys to traverse.
105-
*/
106-
function fallback(node) {
107-
return Object.keys(node).filter(k =>
108-
k !== "parent" &&
109-
k !== "leadingComments" &&
110-
k !== "trailingComments" &&
111-
node[k] !== null &&
112-
typeof node[k] === "object"
113-
)
114-
}
115-
116-
/**
117-
* Traverse the given node.
118-
* `NodeEventGenerator` supports AST selectors!
119-
* @param {ASTNode} node The node to traverse.
120-
* @param {NodeEventGenerator} generator The event generator.
121-
* @returns {void}
122-
*/
123-
function traverse(node, generator) {
124-
let i = 0
125-
let j = 0
126-
127-
generator.enterNode(node)
128-
129-
const keys = KEYS[node.type] || fallback(node)
130-
for (i = 0; i < keys.length; ++i) {
131-
const child = node[keys[i]]
132-
133-
if (Array.isArray(child)) {
134-
for (j = 0; j < child.length; ++j) {
135-
if (child[j]) {
136-
traverse(child[j], generator)
137-
}
138-
}
139-
}
140-
else if (child) {
141-
traverse(child, generator)
142-
}
143-
}
144-
145-
generator.leaveNode(node)
146-
}
14721

14822
/**
14923
* Get or create the event emitter to traverse.
@@ -162,7 +36,7 @@ function ensureEmitter(context) {
16236
context.eslint.on("Program:exit", (node) => {
16337
if (node.templateBody != null) {
16438
const generator = new NodeEventGenerator(emitter)
165-
traverse(node.templateBody, generator)
39+
traverseNodes(node.templateBody, generator)
16640
}
16741
})
16842

@@ -180,4 +54,3 @@ module.exports = (context, visitor) => {
18054
emitter.on(selector, visitor[selector])
18155
}
18256
}
183-
module.exports.traverse = traverse

lib/traverse-nodes.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/**
2+
* @author Toru Nagashima <https://github.com/mysticatea>
3+
* @copyright 2017 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 KEYS = {
13+
AssignmentExpression: ["left", "right"],
14+
AssignmentPattern: ["left", "right"],
15+
ArrayExpression: ["elements"],
16+
ArrayPattern: ["elements"],
17+
ArrowFunctionExpression: ["params", "body"],
18+
AwaitExpression: ["argument"],
19+
BlockStatement: ["body"],
20+
BinaryExpression: ["left", "right"],
21+
BreakStatement: ["label"],
22+
CallExpression: ["callee", "arguments"],
23+
CatchClause: ["param", "body"],
24+
ClassBody: ["body"],
25+
ClassDeclaration: ["id", "superClass", "body"],
26+
ClassExpression: ["id", "superClass", "body"],
27+
ConditionalExpression: ["test", "consequent", "alternate"],
28+
ContinueStatement: ["label"],
29+
DebuggerStatement: [],
30+
DirectiveStatement: [],
31+
DoWhileStatement: ["body", "test"],
32+
EmptyStatement: [],
33+
ExportAllDeclaration: ["source"],
34+
ExportDefaultDeclaration: ["declaration"],
35+
ExportNamedDeclaration: ["declaration", "specifiers", "source"],
36+
ExportSpecifier: ["exported", "local"],
37+
ExpressionStatement: ["expression"],
38+
ForStatement: ["init", "test", "update", "body"],
39+
ForInStatement: ["left", "right", "body"],
40+
ForOfStatement: ["left", "right", "body"],
41+
FunctionDeclaration: ["id", "params", "body"],
42+
FunctionExpression: ["id", "params", "body"],
43+
Identifier: [],
44+
IfStatement: ["test", "consequent", "alternate"],
45+
ImportDeclaration: ["specifiers", "source"],
46+
ImportDefaultSpecifier: ["local"],
47+
ImportNamespaceSpecifier: ["local"],
48+
ImportSpecifier: ["imported", "local"],
49+
Literal: [],
50+
LabeledStatement: ["label", "body"],
51+
LogicalExpression: ["left", "right"],
52+
MemberExpression: ["object", "property"],
53+
MetaProperty: ["meta", "property"],
54+
MethodDefinition: ["key", "value"],
55+
ModuleSpecifier: [],
56+
NewExpression: ["callee", "arguments"],
57+
ObjectExpression: ["properties"],
58+
ObjectPattern: ["properties"],
59+
Program: ["body"],
60+
Property: ["key", "value"],
61+
RestElement: ["argument"],
62+
ReturnStatement: ["argument"],
63+
SequenceExpression: ["expressions"],
64+
SpreadElement: ["argument"],
65+
Super: [],
66+
SwitchStatement: ["discriminant", "cases"],
67+
SwitchCase: ["test", "consequent"],
68+
TaggedTemplateExpression: ["tag", "quasi"],
69+
TemplateElement: [],
70+
TemplateLiteral: ["quasis", "expressions"],
71+
ThisExpression: [],
72+
ThrowStatement: ["argument"],
73+
TryStatement: ["block", "handler", "finalizer"],
74+
UnaryExpression: ["argument"],
75+
UpdateExpression: ["argument"],
76+
VariableDeclaration: ["declarations"],
77+
VariableDeclarator: ["id", "init"],
78+
WhileStatement: ["test", "body"],
79+
WithStatement: ["object", "body"],
80+
YieldExpression: ["argument"],
81+
82+
VIdentifier: [],
83+
VText: [],
84+
VExpressionContainer: ["expression"],
85+
VDirectiveKey: [],
86+
VAttributeValue: [],
87+
VAttribute: ["key", "value"],
88+
VStartTag: ["id", "attributes"],
89+
VEndTag: ["id"],
90+
VElement: ["startTag", "children", "endTag"],
91+
}
92+
93+
/**
94+
* Check that the given key should be traversed or not.
95+
* @this {ASTNode}
96+
* @param {string} key The key to check.
97+
* @returns {boolean} `true` if the key should be traversed.
98+
*/
99+
function fallbackKeysFilter(key) {
100+
let value = null
101+
return (
102+
key !== "comments" &&
103+
key !== "leadingComments" &&
104+
key !== "loc" &&
105+
key !== "parent" &&
106+
key !== "range" &&
107+
key !== "tokens" &&
108+
key !== "trailingComments" &&
109+
(value = this[key]) !== null &&
110+
typeof value === "object" &&
111+
(typeof value.type === "string" || Array.isArray(value))
112+
)
113+
}
114+
115+
/**
116+
* Get the keys of the given node to traverse it.
117+
* @param {ASTNode} node The node to get.
118+
* @returns {string[]} The keys to traverse.
119+
*/
120+
function getFallbackKeys(node) {
121+
return Object.keys(node).filter(fallbackKeysFilter, node)
122+
}
123+
124+
/**
125+
* Traverse the given node.
126+
* `NodeEventGenerator` supports AST selectors!
127+
* @param {ASTNode} node The node to traverse.
128+
* @param {NodeEventGenerator} generator The event generator.
129+
* @returns {void}
130+
*/
131+
function traverse(node, parent, generator) {
132+
let i = 0
133+
let j = 0
134+
135+
generator.enterNode(node, parent)
136+
137+
const keys = KEYS[node.type] || getFallbackKeys(node)
138+
for (i = 0; i < keys.length; ++i) {
139+
const child = node[keys[i]]
140+
141+
if (Array.isArray(child)) {
142+
for (j = 0; j < child.length; ++j) {
143+
if (child[j]) {
144+
traverse(child[j], node, generator)
145+
}
146+
}
147+
}
148+
else if (child) {
149+
traverse(child, node, generator)
150+
}
151+
}
152+
153+
generator.leaveNode(node, parent)
154+
}
155+
156+
//------------------------------------------------------------------------------
157+
// Exports
158+
//------------------------------------------------------------------------------
159+
160+
module.exports = (node, generator) => traverse(node, null, generator)

test/template-ast.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const EventEmitter = require("events")
1414
const fs = require("fs")
1515
const path = require("path")
1616
const parse = require("..").parse
17-
const traverse = require("../lib/register-template-body-visitor").traverse
17+
const traverseNodes = require("../lib/traverse-nodes")
1818

1919
//------------------------------------------------------------------------------
2020
// Helpers
@@ -39,7 +39,7 @@ const PARSER_OPTIONS = {
3939
*/
4040
function removeParent(ast) {
4141
if (ast.templateBody != null) {
42-
traverse(ast.templateBody, {
42+
traverseNodes(ast.templateBody, {
4343
enterNode(node) {
4444
delete node.parent
4545
},

test/tools/update-template-ast.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
const fs = require("fs")
1313
const path = require("path")
1414
const parse = require("../..").parse
15-
const traverse = require("../../lib/register-template-body-visitor").traverse
15+
const traverseNodes = require("../../lib/traverse-nodes")
1616

1717
//------------------------------------------------------------------------------
1818
// Helpers
@@ -37,7 +37,7 @@ const PARSER_OPTIONS = {
3737
*/
3838
function removeParent(ast) {
3939
if (ast.templateBody != null) {
40-
traverse(ast.templateBody, {
40+
traverseNodes(ast.templateBody, {
4141
enterNode(node) {
4242
delete node.parent
4343
},
@@ -48,15 +48,18 @@ function removeParent(ast) {
4848
}
4949
}
5050

51+
/**
52+
* Get the traversal order.
53+
* @param {ASTNode} ast The node to get.
54+
* @param {string} code The whole source code to check ranges.
55+
* @returns {(string[])[]} The traversal order.
56+
*/
5157
function getTraversalOrder(ast, code) {
5258
const retv = []
5359

5460
if (ast.templateBody != null) {
55-
traverse(ast.templateBody, {
61+
traverseNodes(ast.templateBody, {
5662
enterNode(node) {
57-
if (node.range == null) {
58-
console.log(node)
59-
}
6063
retv.push(["enter", node.type, code.slice(node.range[0], node.range[1])])
6164
},
6265
leaveNode(node) {

0 commit comments

Comments
 (0)