UNPKG

@atomist/automation-client

Version:

Atomist API for software low-level client

142 lines 4.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tree_path_1 = require("@atomist/tree-path"); const _ = require("lodash"); const ts = require("typescript"); const logger_1 = require("../../../util/logger"); /** * Allow path expressions against ASTs from the TypeScript parser. * For reference material on the grammar, and which productions are legal * names in path expressions, see the grammar at * https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#A. * See also the ES6 grammar of which TypeScript is a superset: * http://www.ecma-international.org/ecma-262/6.0/#sec-grammar-summary. * and the SyntaxKind type defined by * the TypeScript compiler. Invalid production names will be rejected * with a runtime error. * * Will try to determine TypeScript ScriptKind from the file extension. */ class TypeScriptFileParser { constructor(scriptTarget) { this.scriptTarget = scriptTarget; this.rootName = ts.SyntaxKind[ts.SyntaxKind.SourceFile]; } toAst(f) { return f.getContent() .then(content => { const sourceFile = ts.createSourceFile(f.name, content, this.scriptTarget, false, scriptKindFor(f)); const root = new TypeScriptAstNodeTreeNode(sourceFile, sourceFile, undefined); return root; }); } /** * Check that this path expression uses only valid TypeScript constructs * @param {PathExpression} pex */ validate(pex) { for (const ls of locationSteps(pex)) { if (tree_path_1.isNamedNodeTest(ls.test) && ls.test !== tree_path_1.AllNodeTest) { if (!ts.SyntaxKind[ls.test.name]) { throw new Error(`Invalid path expression '${tree_path_1.stringify(pex)}': ` + `No such TypeScript element: '${ls.test.name}'`); } } } } } exports.TypeScriptFileParser = TypeScriptFileParser; /** * Determine the script kind of the file from its extension * @param {File} f * @return {ts.ScriptKind} */ function scriptKindFor(f) { switch (f.extension) { case "js": return ts.ScriptKind.JS; case "jsx": return ts.ScriptKind.JSX; case "ts": return ts.ScriptKind.TS; case "tsx": return ts.ScriptKind.TSX; default: return ts.ScriptKind.Unknown; } } function locationSteps(pex) { return tree_path_1.isUnionPathExpression(pex) ? _.flatten(pex.unions.map(locationSteps)) : pex.locationSteps; } /** * TreeNode implementation backed by a node from the TypeScript parser */ class TypeScriptAstNodeTreeNode { constructor(sourceFile, node, $parent) { this.sourceFile = sourceFile; this.node = node; this.$parent = $parent; this.$children = []; this.$name = extractName(node); try { this.$offset = node.getStart(sourceFile, true); } catch (e) { // Ignore and continue if (!!node.pos) { this.$offset = node.pos; } else { logger_1.logger.debug("Cannot get start for node with kind %s", ts.SyntaxKind[node.kind]); } } for (const n of node.getChildren(sourceFile)) { if (!!n) { this.$children.push(new TypeScriptAstNodeTreeNode(sourceFile, n, this)); } } if (this.$children.length === 0) { // Get it off the JSON if it doesn't matter this.$children = undefined; } else { // It's a non-terminal, so the name needs to be the kind this.$name = ts.SyntaxKind[node.kind]; } } get $value() { return extractValue(this.sourceFile, this.node); } } function extractName(node) { if (!!node.name && node.name.escapedText) { return node.name.escapedText; } else { return ts.SyntaxKind[node.kind]; } } function extractValue(sourceFile, node) { if (!!node.text) { return node.text; } try { return node.getText(sourceFile); } catch (te) { const start = node.getStart(sourceFile, true); const end = node.getEnd(sourceFile, true); if (!!start && !!end) { return sourceFile.text.substr(start, end - start); } return undefined; } } /** * Parser for TypeScript and JavaScript * @type {TypeScriptFileParser} */ exports.TypeScriptES6FileParser = new TypeScriptFileParser(ts.ScriptTarget.ES2016); //# sourceMappingURL=TypeScriptFileParser.js.map