-
-
Notifications
You must be signed in to change notification settings - Fork 596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ast(parser): add #456
ast(parser): add #456
Changes from 13 commits
2f5191d
d238053
1a9c618
eb0d74d
3ed1c80
5aac14f
a4579a0
eb87fb2
418c805
9184cfb
d650e80
1825fff
16af31d
173c136
3b14f77
7d6a6bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
const webpack = require("webpack"); | ||
|
||
module.exports = { | ||
entry: { | ||
objects: "are", | ||
super: [ | ||
"yeah", | ||
{ | ||
test: new RegExp(/\.(js|vue)$/), | ||
loader: "'eslint-loader'", | ||
enforce: "'pre'", | ||
include: ["customObj", "'Stringy'"], | ||
options: { | ||
formatter: "'oki'", | ||
updateMe: "sure" | ||
} | ||
} | ||
], | ||
nice: "':)'", | ||
foo: "Promise.resolve()", | ||
man: "() => duper", | ||
mode: "yaaa" | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,12 @@ | ||
const validateIdentifier = require("./validate-identifier"); | ||
|
||
/** | ||
* | ||
* Traverse safely over a path object for array for paths | ||
* @param {Object} obj - Object on which we traverse | ||
* @param {Array} paths - Array of strings containing the traversal path | ||
* @returns {Any} Value at given traversal path | ||
*/ | ||
function safeTraverse(obj, paths) { | ||
let val = obj; | ||
let idx = 0; | ||
|
@@ -14,6 +21,17 @@ function safeTraverse(obj, paths) { | |
return val; | ||
} | ||
|
||
/** | ||
* | ||
* Traverse safely and return `type` for path object with value.value property | ||
* @param {Node} path - pathNode | ||
* @returns {String|Boolean} type at given path. | ||
*/ | ||
function safeTraverseAndGetType(path) { | ||
const pathValue = safeTraverse(path, ["value", "value"]); | ||
return pathValue ? pathValue.type : false; | ||
} | ||
|
||
// Convert nested MemberExpressions to strings like webpack.optimize.DedupePlugin | ||
function memberExpressionToPathString(path) { | ||
if (path && path.object) { | ||
|
@@ -382,26 +400,37 @@ function getRequire(j, constName, packagePath) { | |
* @param {Node} p - AST node | ||
* @param {String} key - key of a key/val object | ||
* @param {Any} value - Any type of object | ||
* @param {String} action - Action to be done on the given ast | ||
* @returns {Node} - the created ast | ||
*/ | ||
|
||
function addProperty(j, p, key, value) { | ||
let valForNode; | ||
function addProperty(j, p, key, value, action) { | ||
if (!p) { | ||
return; | ||
} | ||
let valForNode; | ||
if (Array.isArray(value)) { | ||
const arr = j.arrayExpression([]); | ||
value.filter(val => val).forEach(val => { | ||
addProperty(j, arr, null, val); | ||
let arrExp = j.arrayExpression([]); | ||
if ( | ||
safeTraverseAndGetType(p) === "ArrayExpression" | ||
) { | ||
arrExp = p.value.value; | ||
} | ||
value.forEach(val => { | ||
addProperty(j, arrExp, null, val); | ||
}); | ||
valForNode = arr; | ||
valForNode = arrExp; | ||
} else if ( | ||
typeof value === "object" && | ||
!(value.__paths || value instanceof RegExp) | ||
) { | ||
// object -> loop through it | ||
let objectExp = j.objectExpression([]); | ||
if ( | ||
safeTraverseAndGetType(p) === "ObjectExpression" | ||
) { | ||
objectExp = p.value.value; | ||
} | ||
// object -> loop through it | ||
Object.keys(value).forEach(prop => { | ||
addProperty(j, objectExp, prop, value[prop]); | ||
}); | ||
|
@@ -415,6 +444,11 @@ function addProperty(j, p, key, value) { | |
} else { | ||
pushVal = valForNode; | ||
} | ||
|
||
// incase of _add_ or while updating a property, | ||
// we only return the generated pushVal which will be replace the node path | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. keep this, discard the other comment |
||
if (action === "add") return pushVal; | ||
|
||
if (p.properties) { | ||
p.properties.push(pushVal); | ||
return p; | ||
|
@@ -507,8 +541,10 @@ function parseMerge(j, ast, value, action) { | |
return ast; | ||
} | ||
} | ||
|
||
module.exports = { | ||
safeTraverse, | ||
safeTraverseAndGetType, | ||
createProperty, | ||
findPluginsByName, | ||
findRootNodesByName, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -210,4 +210,144 @@ const a = { plugs: [] } | |
expect(j(require).toSource()).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
describe("safeTraverse", () => { | ||
it("should safe traverse", () => { | ||
const VALUE = "a value"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. better naming here |
||
const p = { | ||
value: { | ||
value: VALUE | ||
} | ||
}; | ||
const require = utils.safeTraverse(p, ["value", "value"]); | ||
expect(require).toEqual(VALUE); | ||
}); | ||
|
||
it("should safe traverse thrice", () => { | ||
const type = { | ||
type: "NodeType" | ||
}; | ||
const p = { | ||
parent: { | ||
value: { | ||
value: type | ||
} | ||
} | ||
}; | ||
const require = utils.safeTraverse(p, ["parent", "value", "value"]); | ||
expect(require).toEqual(type); | ||
}); | ||
}); | ||
|
||
describe("safeTraverseAndGetType", () => { | ||
it("should safe traverse and get type", () => { | ||
const NODE_TYPE = "NodeType"; | ||
const p = { | ||
value: { | ||
value: { | ||
type: NODE_TYPE | ||
} | ||
} | ||
}; | ||
const require = utils.safeTraverseAndGetType(p); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vague naming, please change to descriptive names |
||
expect(require).toEqual(NODE_TYPE); | ||
}); | ||
|
||
it("should safe traverse and return false if not found", () => { | ||
const NODE_TYPE = "NodeType"; | ||
const p = { | ||
ed: { | ||
sheeran: { | ||
type: NODE_TYPE | ||
} | ||
} | ||
}; | ||
const require = utils.safeTraverseAndGetType(p); | ||
expect(require).toEqual(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not a valid test, you're testing the node against itself. Give it a real path to check. |
||
}); | ||
}); | ||
|
||
describe("addProperty", () => { | ||
it("add entry property while init", () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
const ast = j("module.exports = {}"); | ||
const propertyValue = { | ||
objects: "are", | ||
super: [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can trim this down so it doesn't get overbloated |
||
"yeah", | ||
{ | ||
test: new RegExp(/\.(js|vue)$/), | ||
loader: "'eslint-loader'", | ||
enforce: "'pre'", | ||
include: ["customObj", "'Stringy'"], | ||
options: { | ||
formatter: "'someOption'" | ||
} | ||
} | ||
], | ||
nice: "':)'", | ||
foo: "Promise.resolve()", | ||
man: "() => duper" | ||
}; | ||
|
||
const root = ast | ||
.find(j.ObjectExpression); | ||
|
||
root.forEach(p => { | ||
utils.addProperty(j, p, "entry", propertyValue); | ||
}); | ||
|
||
expect(ast.toSource()).toMatchSnapshot(); | ||
}); | ||
|
||
it("add entry property while add", () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
const ast = j(`module.exports = { | ||
entry: { | ||
objects: are, | ||
|
||
super: [yeah, { | ||
test: /\\\\.(js|vue)$/, | ||
loader: 'eslint-loader', | ||
enforce: 'pre', | ||
include: [customObj, 'Stringy'], | ||
|
||
options: { | ||
formatter: 'someOption' | ||
} | ||
}], | ||
|
||
nice: ':)', | ||
foo: Promise.resolve(), | ||
man: () => duper | ||
} | ||
}`); | ||
const propertyValue = { | ||
objects: "are not", | ||
super: [ | ||
"op", | ||
{ | ||
test: new RegExp(/\.(wasm|c)$/), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. less props |
||
loader: "'pia-loader'", | ||
enforce: "'pre'", | ||
include: ["asd", "'Stringy'"], | ||
options: { | ||
formatter: "'nao'" | ||
} | ||
} | ||
], | ||
nice: "'=)'", | ||
foo: "Promise.resolve()", | ||
man: "() => nice!!", | ||
mode: "super-man" | ||
}; | ||
|
||
const root = ast | ||
.find(j.ObjectExpression); | ||
|
||
utils.findRootNodesByName(j, root, "entry").forEach(p => { | ||
j(p).replaceWith(utils.addProperty(j, p, "entry", propertyValue, "add")); | ||
}); | ||
|
||
expect(ast.toSource()).toMatchSnapshot(); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change to AST node in description