ast-module-types
Version:
Collection of useful helper functions when trying to determine module type (CommonJS or AMD) properties of an AST node.
231 lines (181 loc) • 6.8 kB
JavaScript
'use strict';
// Deprecated
// Whether or not the node represents a generic define() call
// Note: this should not be used as it will have false positives.
// It is mostly used to guide sniffs for other methods and will be made private eventually.
module.exports.isDefine = function(node) {
if (!node) return false;
const c = node.callee;
return c &&
node.type === 'CallExpression' &&
c.type === 'Identifier' &&
c.name === 'define';
};
// Whether or not the node represents any of the AMD define() forms
module.exports.isDefineAMD = function(node) {
if (!node) return false;
const e = module.exports;
return e.isNamedForm(node) || e.isDependencyForm(node) ||
e.isFactoryForm(node) || e.isNoDependencyForm(node) ||
e.isREMForm(node);
};
// Whether or not the node represents a require function call
module.exports.isRequire = function(node) {
return this.isPlainRequire(node) || this.isMainScopedRequire(node);
};
// Whether or not the node represents a plain require function call [require(...)]
module.exports.isPlainRequire = function(node) {
if (!node) return false;
const c = node.callee;
return c &&
node.type === 'CallExpression' &&
c.type === 'Identifier' &&
c.name === 'require';
};
// Whether or not the node represents main-scoped require function call [require.main.require(...)]
module.exports.isMainScopedRequire = function(node) {
if (!node) return false;
const c = node.callee;
return c &&
node.type === 'CallExpression' &&
c.type === 'MemberExpression' &&
c.object.type === 'MemberExpression' &&
c.object.object.type === 'Identifier' &&
c.object.object.name === 'require' &&
c.object.property.type === 'Identifier' &&
c.object.property.name === 'main' &&
c.property.type === 'Identifier' &&
c.property.name === 'require';
};
// Whether or not the node represents a require at the top of the module
// Instead of trying to find the require then backtrack to the top,
// just take the root and check its immediate child
module.exports.isTopLevelRequire = function(node) {
if (node.type !== 'Program' || !node.body ||
node.body.length === 0 || !node.body[0].expression) {
return false;
}
return this.isRequire(node.body[0].expression);
};
// Whether or not the node represents an AMD-style driver script's require
// Example: require(deps, function)
module.exports.isAMDDriverScriptRequire = function(node) {
return this.isRequire(node) &&
node.arguments &&
node.arguments[0] && node.arguments[0].type &&
node.arguments[0].type === 'ArrayExpression';
};
function isExportsIdentifier(obj) {
return obj.type && obj.type === 'Identifier' && obj.name === 'exports';
}
function isModuleIdentifier(obj) {
return obj.type && obj.type === 'Identifier' && obj.name === 'module';
}
// module.exports.foo
function isModuleExportsAttach(node) {
if (!node.object || !node.object.object || !node.object.property) return false;
return node.type === 'MemberExpression' &&
isModuleIdentifier(node.object.object) &&
isExportsIdentifier(node.object.property);
}
// module.exports
function isModuleExportsAssign(node) {
if (!node.object || !node.property) return false;
return node.type === 'MemberExpression' &&
isModuleIdentifier(node.object) &&
isExportsIdentifier(node.property);
}
// exports
function isExportsAssign(node) {
return isExportsIdentifier(node);
}
// exports.foo
function isExportsAttach(node) {
return node.type === 'MemberExpression' && isExportsIdentifier(node.object);
}
// Whether or not the node represents the use of
// assigning (and possibly attaching) something to module.exports or exports
module.exports.isExports = function(node) {
if (node.type !== 'AssignmentExpression') return;
// Only the left side matters
const leftNode = node.left;
return isModuleExportsAttach(leftNode) || isModuleExportsAssign(leftNode) ||
isExportsAttach(leftNode) || isExportsAssign(leftNode);
};
// define('name', [deps], func)
module.exports.isNamedForm = function(node) {
if (!this.isDefine(node)) return false;
const args = node.arguments;
return args && args.length === 3 &&
(args[0].type === 'Literal' || args[0].type === 'StringLiteral') &&
args[1].type === 'ArrayExpression' &&
args[2].type === 'FunctionExpression';
};
// define([deps], func)
module.exports.isDependencyForm = function(node) {
if (!this.isDefine(node)) return false;
const args = node.arguments;
return args && args.length === 2 &&
args[0].type === 'ArrayExpression' &&
args[1].type === 'FunctionExpression';
};
// define(func(require))
module.exports.isFactoryForm = function(node) {
if (!this.isDefine(node)) return false;
const args = node.arguments;
const firstParamNode = args.length > 0 && args[0].params ? args[0].params[0] : null;
// Node should have a function whose first param is 'require'
return args && args.length === 1 &&
args[0].type === 'FunctionExpression' &&
firstParamNode && firstParamNode.type === 'Identifier' &&
firstParamNode.name === 'require';
};
// define({})
module.exports.isNoDependencyForm = function(node) {
if (!this.isDefine(node)) return false;
const args = node.arguments;
return args && args.length === 1 && args[0].type === 'ObjectExpression';
};
// define(function(require, exports, module)
module.exports.isREMForm = function(node) {
if (!this.isDefine(node)) return false;
const args = node.arguments;
const params = args.length > 0 ? args[0].params : null;
if (!args || args.length === 0 || args[0].type !== 'FunctionExpression' || params.length !== 3) {
return false;
}
const [first, second, third] = params;
return first.type === 'Identifier' && first.name === 'require' &&
second.type === 'Identifier' && second.name === 'exports' &&
third.type === 'Identifier' && third.name === 'module';
};
module.exports.isES6Import = function(node) {
switch (node.type) {
case 'Import':
case 'ImportDeclaration':
case 'ImportDefaultSpecifier':
case 'ImportNamespaceSpecifier': {
return true;
}
default: {
return false;
}
}
};
module.exports.isES6Export = function(node) {
switch (node.type) {
case 'ExportDeclaration':
case 'ExportNamedDeclaration':
case 'ExportSpecifier':
case 'ExportDefaultDeclaration':
case 'ExportAllDeclaration': {
return true;
}
default: {
return false;
}
}
};
module.exports.isDynamicImport = function(node) {
return node.callee && node.callee.type === 'Import' && node.arguments.length > 0;
};