Skip to content
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

Merged
merged 16 commits into from
Jun 2, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions packages/utils/__snapshots__/ast-utils.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`utils addProperty add entry property while add 1`] = `
"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,
objects: are not,

super: [op, {
test: /\\\\.(wasm|c)$/,
loader: 'pia-loader',
enforce: 'pre',
include: [asd, 'Stringy'],

options: {
formatter: 'nao'
}
}],

nice: '=)',
foo: Promise.resolve(),
man: () => nice!!,
mode: super-man
}
}"
`;

exports[`utils addProperty add entry property while init 1`] = `
"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
}
}"
`;

exports[`utils createIdentifierOrLiteral should create basic literal 1`] = `"'stringLiteral'"`;

exports[`utils createIdentifierOrLiteral should create boolean 1`] = `"true"`;
Expand Down
48 changes: 48 additions & 0 deletions packages/utils/__snapshots__/recursive-parser.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`add transforms correctly using "fixture-2" data 1`] = `
"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\\",
objects: are not,

super: [op, {
test: /\\\\.(wasm|c)$/,
loader: 'pia-loader',
enforce: 'pre',
include: [asd, 'Stringy'],

options: {
formatter: 'nao'
}
}],

nice: '=)',
foo: Promise.resolve(),
man: () => nice!!,
mode: super-man
}
}
"
`;

exports[`init transforms correctly using "fixture-1" data 1`] = `
"module.exports = {
entry: {
Expand All @@ -17,6 +64,7 @@ exports[`init transforms correctly using "fixture-1" data 1`] = `
}],

nice: ':)',
foo: Promise.resolve(),
man: () => duper
}
};"
Expand Down
25 changes: 25 additions & 0 deletions packages/utils/__testfixtures__/fixture-2.input.js
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"

}
}
50 changes: 43 additions & 7 deletions packages/utils/ast-utils.js
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;
Expand All @@ -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
Copy link
Member

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

* @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) {
Expand Down Expand Up @@ -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]);
});
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The 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;
Expand Down Expand Up @@ -507,8 +541,10 @@ function parseMerge(j, ast, value, action) {
return ast;
}
}

module.exports = {
safeTraverse,
safeTraverseAndGetType,
createProperty,
findPluginsByName,
findRootNodesByName,
Expand Down
140 changes: 140 additions & 0 deletions packages/utils/ast-utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,144 @@ const a = { plugs: [] }
expect(j(require).toSource()).toMatchSnapshot();
});
});

describe("safeTraverse", () => {
it("should safe traverse", () => {
const VALUE = "a value";
Copy link
Member

Choose a reason for hiding this comment

The 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);
Copy link
Member

Choose a reason for hiding this comment

The 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);
Copy link
Member

Choose a reason for hiding this comment

The 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. p, foo, bar.

});
});

describe("addProperty", () => {
it("add entry property while init", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while -> using

const ast = j("module.exports = {}");
const propertyValue = {
objects: "are",
super: [
Copy link
Member

Choose a reason for hiding this comment

The 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", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using not while

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)$/),
Copy link
Member

Choose a reason for hiding this comment

The 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();
});
});
});
Loading