Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge
babel-check-duplicated-nodes
into monorepo (#14420)
* Working helper * Added pkg.json description * 1.0.0 * cleanup build scripts * refactor: convert to TS * add checkDuplicateNodes to parser tests * remove babel core injection * ignore check-duplicate-nodes and readme generator * add test cases * refactor: simplify duplicate nodes tracking Co-authored-by: Mateusz Burzy艅ski <mateuszburzynski@gmail.com>
- Loading branch information
Showing
15 changed files
with
254 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
lib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
src | ||
test | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# @babel/helper-check-duplicate-nodes | ||
|
||
Duplicated AST nodes often lead to obscure bugs. This module checks your AST and | ||
throws a helpful error if you include a duplicated node in your output. It's | ||
useful when authoring babel transforms. | ||
|
||
This piece of code was originally written by @nicolo-ribaudo and is included in | ||
[@babel/helper-transform-fixture-test-runnner](https://github.com/babel/babel/blob/d383659ca6adec54b6054f77cdaa16da88e8a171/packages/babel-helper-transform-fixture-test-runner/src/index.js#L128). | ||
|
||
## API | ||
|
||
```js | ||
import checkDuplicateNodes from "@babel/helper-check-duplicate-nodes"; | ||
checkDuplicateNodes(ast); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"name": "@babel/helper-check-duplicate-nodes", | ||
"version": "1.0.0", | ||
"description": "Babel helper module for babel transforms authors to check the AST against duplicated nodes.", | ||
"main": "./lib/index.js", | ||
"exports": { | ||
".": "./lib/index.js", | ||
"./package.json": "./package.json" | ||
}, | ||
"files": [ | ||
"lib" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/babel/babel.git", | ||
"directory": "packages/babel-helper-check-duplicate-nodes" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"keywords": [ | ||
"babel" | ||
], | ||
"author": "The Babel Team (https://babel.dev/team)", | ||
"license": "MIT", | ||
"homepage": "https://babel.dev/docs/en/next/babel-helper-check-duplicate-nodes", | ||
"engines": { | ||
"node": ">=6.9.0" | ||
}, | ||
"dependencies": { | ||
"@babel/types": "workspace:^" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "workspace:^" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { VISITOR_KEYS } from "@babel/types"; | ||
|
||
export default function checkDuplicateNodes(ast) { | ||
if (arguments.length !== 1) { | ||
throw new Error("checkDuplicateNodes accepts only one argument: ast"); | ||
} | ||
// A Map from node to its parent | ||
const parentsMap = new WeakMap(); | ||
|
||
const hidePrivateProperties = (key, val) => { | ||
// Hides properties like _shadowedFunctionLiteral, | ||
// which makes the AST circular | ||
if (key[0] === "_") return "[Private]"; | ||
return val; | ||
}; | ||
|
||
const stack = [{ node: ast, parent: null }]; | ||
let item; | ||
|
||
while ((item = stack.pop()) !== undefined) { | ||
const { node, parent } = item; | ||
if (!node) continue; | ||
|
||
const keys = VISITOR_KEYS[node.type]; | ||
if (!keys) continue; | ||
|
||
if (parentsMap.has(node)) { | ||
const parents = [parentsMap.get(node), parent]; | ||
throw new Error( | ||
"Do not reuse nodes. Use `t.cloneNode` (or `t.clone`/`t.cloneDeep` if using babel@6) to copy them.\n" + | ||
JSON.stringify(node, hidePrivateProperties, 2) + | ||
"\nParent:\n" + | ||
JSON.stringify(parents, hidePrivateProperties, 2), | ||
); | ||
} | ||
parentsMap.set(node, parent); | ||
|
||
for (const key of keys) { | ||
const subNode = node[key]; | ||
|
||
if (Array.isArray(subNode)) { | ||
for (const child of subNode) { | ||
stack.push({ node: child, parent: node }); | ||
} | ||
} else { | ||
stack.push({ node: subNode, parent: node }); | ||
} | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
packages/babel-helper-check-duplicate-nodes/test/__snapshots__/index.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`checkDuplicateNodes should throw on duplicate AST nodes within different parent 1`] = ` | ||
"Do not reuse nodes. Use \`t.cloneNode\` (or \`t.clone\`/\`t.cloneDeep\` if using babel@6) to copy them. | ||
{ | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
} | ||
Parent: | ||
[ | ||
{ | ||
\\"type\\": \\"VariableDeclarator\\", | ||
\\"id\\": { | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
}, | ||
\\"init\\": null | ||
}, | ||
{ | ||
\\"type\\": \\"AssignmentExpression\\", | ||
\\"operator\\": \\"=\\", | ||
\\"left\\": { | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
}, | ||
\\"right\\": { | ||
\\"type\\": \\"NullLiteral\\" | ||
} | ||
} | ||
]" | ||
`; | ||
|
||
exports[`checkDuplicateNodes should throw on duplicate AST nodes within same parent 1`] = ` | ||
"Do not reuse nodes. Use \`t.cloneNode\` (or \`t.clone\`/\`t.cloneDeep\` if using babel@6) to copy them. | ||
{ | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
} | ||
Parent: | ||
[ | ||
{ | ||
\\"type\\": \\"VariableDeclarator\\", | ||
\\"id\\": { | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
}, | ||
\\"init\\": { | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
} | ||
}, | ||
{ | ||
\\"type\\": \\"VariableDeclarator\\", | ||
\\"id\\": { | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
}, | ||
\\"init\\": { | ||
\\"type\\": \\"Identifier\\", | ||
\\"name\\": \\"ref\\" | ||
} | ||
} | ||
]" | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import _checkDuplicateNodes from "../lib/index.js"; | ||
import babel from "@babel/core"; | ||
const { parseSync, traverse, types: t } = babel; | ||
const checkDuplicateNodes = _checkDuplicateNodes.default; | ||
|
||
describe("checkDuplicateNodes", () => { | ||
it("should throw on duplicate AST nodes within same parent", () => { | ||
const ast = parseSync("{}", { | ||
filename: "example.js", | ||
configFile: false, | ||
}); | ||
traverse(ast, { | ||
BlockStatement(path) { | ||
const { node } = path; | ||
const duplicate = t.identifier("ref"); | ||
const statementBody = node.body; | ||
statementBody.unshift( | ||
t.variableDeclaration("var", [ | ||
t.variableDeclarator(duplicate, duplicate), | ||
]), | ||
); | ||
path.stop(); | ||
}, | ||
}); | ||
expect(() => { | ||
checkDuplicateNodes(ast); | ||
}).toThrowErrorMatchingSnapshot(); | ||
}); | ||
it("should throw on duplicate AST nodes within different parent", () => { | ||
const ast = parseSync("{}", { | ||
filename: "example.js", | ||
configFile: false, | ||
}); | ||
traverse(ast, { | ||
BlockStatement(path) { | ||
const { node } = path; | ||
const duplicate = t.identifier("ref"); | ||
const statementBody = node.body; | ||
statementBody.unshift( | ||
t.variableDeclaration("var", [t.variableDeclarator(duplicate)]), | ||
); | ||
statementBody.unshift( | ||
t.expressionStatement( | ||
t.assignmentExpression("=", duplicate, t.nullLiteral()), | ||
), | ||
); | ||
path.stop(); | ||
}, | ||
}); | ||
expect(() => { | ||
checkDuplicateNodes(ast); | ||
}).toThrowErrorMatchingSnapshot(); | ||
}); | ||
it("should throw when more than one arguments are passed", () => { | ||
expect(() => { | ||
checkDuplicateNodes(babel, {}); | ||
}).toThrow("checkDuplicateNodes accepts only one argument: ast"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ "type": "module" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters