atom-nuclide
Version:
A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.
327 lines (293 loc) • 12.5 kB
JavaScript
Object.defineProperty(exports, '__esModule', {
value: true
});
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.astToOutline = astToOutline;
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _commonsNodeCollection2;
function _commonsNodeCollection() {
return _commonsNodeCollection2 = require('../../commons-node/collection');
}
var _commonsNodeTokenizedText2;
function _commonsNodeTokenizedText() {
return _commonsNodeTokenizedText2 = require('../../commons-node/tokenizedText');
}
var _assert2;
function _assert() {
return _assert2 = _interopRequireDefault(require('assert'));
}
function astToOutline(ast) {
return itemsToTrees(ast.body);
}
function itemsToTrees(items) {
return (0, (_commonsNodeCollection2 || _commonsNodeCollection()).arrayCompact)(items.map(itemToTree));
}
function itemToTree(item) {
if (item == null) {
return null;
}
var extent = getExtent(item);
switch (item.type) {
case 'FunctionDeclaration':
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).keyword)('function'), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).whitespace)(' '), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).method)(item.id.name), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('(')].concat(_toConsumableArray(paramsTokenizedText(item.params)), [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)(')')]),
representativeName: item.id.name,
children: []
}, extent);
case 'ClassDeclaration':
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).keyword)('class'), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).whitespace)(' '), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).className)(item.id.name)],
representativeName: item.id.name,
children: itemsToTrees(item.body.body)
}, extent);
case 'ClassProperty':
var paramTokens = [];
if (item.value && item.value.type === 'ArrowFunctionExpression') {
paramTokens = [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('(')].concat(_toConsumableArray(paramsTokenizedText(item.value.params)), [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)(')')]);
}
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).method)(item.key.name), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('=')].concat(_toConsumableArray(paramTokens)),
representativeName: item.key.name,
children: []
}, extent);
case 'MethodDefinition':
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).method)(item.key.name), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('(')].concat(_toConsumableArray(paramsTokenizedText(item.value.params)), [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)(')')]),
representativeName: item.key.name,
children: []
}, extent);
case 'ExportDeclaration':
var tree = itemToTree(item.declaration);
if (tree == null) {
return null;
}
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).keyword)('export'), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).whitespace)(' ')].concat(_toConsumableArray(tree.tokenizedText)),
representativeName: tree.representativeName,
children: tree.children
}, extent);
case 'ExpressionStatement':
return topLevelExpressionOutline(item);
case 'TypeAlias':
return typeAliasOutline(item);
default:
return null;
}
}
function paramsTokenizedText(params) {
var textElements = [];
params.forEach(function (p, index) {
switch (p.type) {
case 'Identifier':
textElements.push((0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).param)(p.name));
break;
case 'ObjectPattern':
textElements.push((0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('{'));
textElements.push.apply(textElements, _toConsumableArray(paramsTokenizedText(p.properties.map(function (obj) {
return obj.key;
}))));
textElements.push((0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('}'));
break;
case 'ArrayPattern':
textElements.push((0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('['));
textElements.push.apply(textElements, _toConsumableArray(paramsTokenizedText(p.elements)));
textElements.push((0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)(']'));
break;
default:
throw new Error('encountered unexpected argument type ' + p.type);
}
if (index < params.length - 1) {
textElements.push((0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)(','));
textElements.push((0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).whitespace)(' '));
}
});
return textElements;
}
function getExtent(item) {
return {
startPosition: {
// It definitely makes sense that the lines we get are 1-based and the columns are
// 0-based... convert to 0-based all around.
line: item.loc.start.line - 1,
column: item.loc.start.column
},
endPosition: {
line: item.loc.end.line - 1,
column: item.loc.end.column
}
};
}
function typeAliasOutline(typeAliasExpression) {
(0, (_assert2 || _assert()).default)(typeAliasExpression.type === 'TypeAlias');
var name = typeAliasExpression.id.name;
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).keyword)('type'), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).whitespace)(' '), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).type)(name)],
representativeName: name,
children: []
}, getExtent(typeAliasExpression));
}
function topLevelExpressionOutline(expressionStatement) {
switch (expressionStatement.expression.type) {
case 'CallExpression':
return specOutline(expressionStatement, /* describeOnly */true);
case 'AssignmentExpression':
return moduleExportsOutline(expressionStatement.expression);
default:
return null;
}
}
function moduleExportsOutline(assignmentStatement) {
(0, (_assert2 || _assert()).default)(assignmentStatement.type === 'AssignmentExpression');
var left = assignmentStatement.left;
if (!isModuleExports(left)) {
return null;
}
var right = assignmentStatement.right;
if (right.type !== 'ObjectExpression') {
return null;
}
var properties = right.properties;
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('module.exports')],
children: (0, (_commonsNodeCollection2 || _commonsNodeCollection()).arrayCompact)(properties.map(moduleExportsPropertyOutline))
}, getExtent(assignmentStatement));
}
function isModuleExports(left) {
return left.type === 'MemberExpression' && left.object.type === 'Identifier' && left.object.name === 'module' && left.property.type === 'Identifier' && left.property.name === 'exports';
}
function moduleExportsPropertyOutline(property) {
(0, (_assert2 || _assert()).default)(property.type === 'Property');
if (property.key.type !== 'Identifier') {
return null;
}
var propName = property.key.name;
if (property.shorthand) {
// This happens when the shorthand `{ foo }` is used for `{ foo: foo }`
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).string)(propName)],
representativeName: propName,
children: []
}, getExtent(property));
}
if (property.value.type === 'FunctionExpression' || property.value.type === 'ArrowFunctionExpression') {
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).method)(propName), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)('(')].concat(_toConsumableArray(paramsTokenizedText(property.value.params)), [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)(')')]),
representativeName: propName,
children: []
}, getExtent(property));
}
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).string)(propName), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).plain)(':')],
representativeName: propName,
children: []
}, getExtent(property));
}
function specOutline(expressionStatement) {
var describeOnly = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
var expression = expressionStatement.expression;
if (expression.type !== 'CallExpression') {
return null;
}
var functionName = getFunctionName(expression.callee);
if (functionName == null) {
return null;
}
if (!isDescribe(functionName)) {
if (describeOnly || !isIt(functionName)) {
return null;
}
}
var description = getStringLiteralValue(expression.arguments[0]);
var specBody = getFunctionBody(expression.arguments[1]);
if (description == null || specBody == null) {
return null;
}
var children = undefined;
if (isIt(functionName)) {
children = [];
} else {
children = (0, (_commonsNodeCollection2 || _commonsNodeCollection()).arrayCompact)(specBody.filter(function (item) {
return item.type === 'ExpressionStatement';
}).map(function (item) {
return specOutline(item);
}));
}
return _extends({
tokenizedText: [(0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).method)(functionName), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).whitespace)(' '), (0, (_commonsNodeTokenizedText2 || _commonsNodeTokenizedText()).string)(description)],
representativeName: description,
children: children
}, getExtent(expressionStatement));
}
// Return the function name as written as a string. Intended to stringify patterns like `describe`
// and `describe.only` even though `describe.only` is a MemberExpression rather than an Identifier.
function getFunctionName(callee) {
switch (callee.type) {
case 'Identifier':
return callee.name;
case 'MemberExpression':
if (callee.object.type !== 'Identifier' || callee.property.type !== 'Identifier') {
return null;
}
return callee.object.name + '.' + callee.property.name;
default:
return null;
}
}
function isDescribe(functionName) {
switch (functionName) {
case 'describe':
case 'fdescribe':
case 'ddescribe':
case 'xdescribe':
case 'describe.only':
case 'describe.skip':
return true;
default:
return false;
}
}
function isIt(functionName) {
switch (functionName) {
case 'it':
case 'fit':
case 'iit':
case 'pit':
case 'xit':
case 'it.only':
case 'it.skip':
return true;
default:
return false;
}
}
/** If the given AST Node is a string literal, return its literal value. Otherwise return null */
function getStringLiteralValue(literal) {
if (literal == null) {
return null;
}
if (literal.type !== 'Literal') {
return null;
}
var value = literal.value;
if (typeof value !== 'string') {
return null;
}
return value;
}
function getFunctionBody(fn) {
if (fn == null) {
return null;
}
if (fn.type !== 'ArrowFunctionExpression' && fn.type !== 'FunctionExpression') {
return null;
}
return fn.body.body;
}