Иван Стрелков
let sum = 0;
for (let i = 0; i < 10; i++) {
sum += Math.random();
}
console.log(sum);
https://goo.gl/whbc2n
let sum = 0;
for (let i = 0; i < 10; i++) { ... }
console.log(sum);
for (let i = 0; i < 10; i++) { ... }
init
VariableDeclaration
let i = 0
test
BinaryExpression
i < 10
update
UpdateExpression
i++
body
BlockStatement
{ sum += Math.random(); }
{ sum += Math.random(); }
body[0]
ExpressionStatement
sum += Math.random();
expression
AssignmentExpression
left
Identifier
sum
right
CallExpression
Math.random()
Math.random()
arguments
[]
()
callee
MemberExpression
Math.random
object
Identifier
Math
property
Identifier
random
const esprima = require('esprima');
const sourceCode = 'require("./somefile")';
console.log(esprima.parse(sourceCode));
// { type: 'Program',
// body:
// [ { type: 'ExpressionStatement',
// expression: [Object]
// } ],
// sourceType: 'script' }
http://esprima.org/
const React = require('react');
const Header = require('./Header');
class App extends React.Component {
render() {
return (
<div className="App">
<Header />
</div>
);
}
}
module.exports = App;
require('./somefile')
require('./somefile')
callee
Identifier
require
name
'require'
arguments[0]
Literal
'./somefile'
value
'./somefile'
callee
Identifier с именем 'require'
esprima.parse(
'require("./somesthing")',
{},
node => console.log(node.type)
)
// Identifier
// Literal
// CallExpression
// ExpressionStatement
// Program
esprima.parse(sourceCode, { jsx: true }, node => {
if (node.type !== 'CallExpression') {
return;
}
// ...
});
// ...
if (node.callee.type !== 'Identifier') {
return;
}
if (node.callee.name !== 'require') {
return;
}
// ...
esprima.parse(sourceCode, { jsx: true }, node => {
// ...
console.log(node.arguments[0].value)
});
/require\('\.[^')]+'\)/g
const styles = require('./App.css');
class App extends React.Component {
render() {
return (
<div className={styles.App}>
Hello
</div>
);
}
}
.App-logo span {
color: blue;
}
const cssAst = csstree.parse(cssSource);
csstree.walk(cssAst, node => {
if (node.type === 'ClassSelector') {
console.log(node.name);
}
});
require('*.css')
nameToPathMap
{
styles: './style.css',
sharedStyles: '../shared.css',
itemStyles: './item/style.css'
}
require('*.css')
nameToPathMap
left
идентификатор из
nameToPathMap
right
(идентификатор или литерал) как использованный класс
const styles = require('./App.css');
function pseudo(styles) {
console.log(styles['pseudo-used']);
}
const tree = esprima.parse(sourceCode, { jsx: true });
const scopeManager = escope.analyze(tree, {
ecmaVersion: 6
});
const moduleScope = scopeManager.acquire(tree);
moduleScope.variables.forEach(v => {
console.log(`Переменная с именем ${v.name}`);
v.references.forEach(ref => {
console.log(ref.identifier);
});
});
require('*.css')
nameToPathMap
right
(идентификатор или литерал) как использованный класс
if (identifierParent.type !== 'MemberExpression' ||
identifierParent.object !== ref.identifier) {
console.log(identifierParent);
console.log(ref.identifier);
throw new Error('Not expected usage');
}
Иван Стрелков, Avito
Презентация: istrel.github.io/ast-getting-started/
Ссылки и демки: https://goo.gl/2ktLN6