UNPKG

6.63 kBJavaScriptView Raw
1/**
2 * @fileoverview Parser that converts TypeScript into ESTree format.
3 * @author Nicholas C. Zakas
4 * @author James Henry <https://github.com/JamesHenry>
5 * @copyright jQuery Foundation and other contributors, https://jquery.org/
6 * MIT License
7 */
8
9"use strict";
10
11const astNodeTypes = require("./lib/ast-node-types"),
12 ts = require("typescript"),
13 convert = require("./lib/ast-converter"),
14 semver = require("semver");
15
16const SUPPORTED_TYPESCRIPT_VERSIONS = require("./package.json").devDependencies.typescript;
17const ACTIVE_TYPESCRIPT_VERSION = ts.version;
18const isRunningSupportedTypeScriptVersion = semver.satisfies(ACTIVE_TYPESCRIPT_VERSION, SUPPORTED_TYPESCRIPT_VERSIONS);
19
20let extra;
21let warnedAboutTSVersion = false;
22
23/**
24 * Resets the extra config object
25 * @returns {void}
26 */
27function resetExtra() {
28 extra = {
29 tokens: null,
30 range: false,
31 loc: false,
32 comment: false,
33 comments: [],
34 tolerant: false,
35 errors: [],
36 strict: false,
37 ecmaFeatures: {},
38 useJSXTextNode: false,
39 log: console.log // eslint-disable-line no-console
40 };
41}
42
43//------------------------------------------------------------------------------
44// Parser
45//------------------------------------------------------------------------------
46
47/**
48 * Parses the given source code to produce a valid AST
49 * @param {mixed} code TypeScript code
50 * @param {Object} options configuration object for the parser
51 * @param {Object} additionalParsingContext additional internal configuration
52 * @returns {Object} the AST
53 */
54function generateAST(code, options, additionalParsingContext) {
55 additionalParsingContext = additionalParsingContext || {};
56
57 const toString = String;
58
59 if (typeof code !== "string" && !(code instanceof String)) {
60 code = toString(code);
61 }
62
63 resetExtra();
64
65 if (typeof options !== "undefined") {
66 extra.range = (typeof options.range === "boolean") && options.range;
67 extra.loc = (typeof options.loc === "boolean") && options.loc;
68
69 if (extra.loc && options.source !== null && options.source !== undefined) {
70 extra.source = toString(options.source);
71 }
72
73 if (typeof options.tokens === "boolean" && options.tokens) {
74 extra.tokens = [];
75 }
76 if (typeof options.comment === "boolean" && options.comment) {
77 extra.comment = true;
78 extra.comments = [];
79 }
80 if (typeof options.tolerant === "boolean" && options.tolerant) {
81 extra.errors = [];
82 }
83
84 if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
85 // pass through jsx option
86 extra.ecmaFeatures.jsx = options.ecmaFeatures.jsx;
87 }
88
89 /**
90 * Allow the user to cause the parser to error if it encounters an unknown AST Node Type
91 * (used in testing).
92 */
93 if (options.errorOnUnknownASTType) {
94 extra.errorOnUnknownASTType = true;
95 }
96
97 if (typeof options.useJSXTextNode === "boolean" && options.useJSXTextNode) {
98 extra.useJSXTextNode = true;
99 }
100
101 /**
102 * Allow the user to override the function used for logging
103 */
104 if (typeof options.loggerFn === "function") {
105 extra.log = options.loggerFn;
106 } else if (options.loggerFn === false) {
107 extra.log = Function.prototype;
108 }
109
110 /**
111 * Provide the context as to whether or not we are parsing for ESLint,
112 * specifically
113 */
114 if (additionalParsingContext.isParseForESLint) {
115 extra.parseForESLint = true;
116 }
117 }
118
119 if (!isRunningSupportedTypeScriptVersion && !warnedAboutTSVersion) {
120 const border = "=============";
121 const versionWarning = [
122 border,
123 "WARNING: You are currently running a version of TypeScript which is not officially supported by typescript-eslint-parser.",
124 "You may find that it works just fine, or you may not.",
125 `SUPPORTED TYPESCRIPT VERSIONS: ${SUPPORTED_TYPESCRIPT_VERSIONS}`,
126 `YOUR TYPESCRIPT VERSION: ${ACTIVE_TYPESCRIPT_VERSION}`,
127 "Please only submit bug reports when using the officially supported version.",
128 border
129 ];
130 extra.log(versionWarning.join("\n\n"));
131 warnedAboutTSVersion = true;
132 }
133
134 // Even if jsx option is set in typescript compiler, filename still has to
135 // contain .tsx file extension
136 const FILENAME = (extra.ecmaFeatures.jsx) ? "eslint.tsx" : "eslint.ts";
137
138 const compilerHost = {
139 fileExists() {
140 return true;
141 },
142 getCanonicalFileName() {
143 return FILENAME;
144 },
145 getCurrentDirectory() {
146 return "";
147 },
148 getDefaultLibFileName() {
149 return "lib.d.ts";
150 },
151
152 // TODO: Support Windows CRLF
153 getNewLine() {
154 return "\n";
155 },
156 getSourceFile(filename) {
157 return ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true);
158 },
159 readFile() {
160 return null;
161 },
162 useCaseSensitiveFileNames() {
163 return true;
164 },
165 writeFile() {
166 return null;
167 }
168 };
169
170 const program = ts.createProgram([FILENAME], {
171 noResolve: true,
172 target: ts.ScriptTarget.Latest,
173 jsx: extra.ecmaFeatures.jsx ? "preserve" : undefined
174 }, compilerHost);
175
176 const ast = program.getSourceFile(FILENAME);
177
178 extra.code = code;
179 return convert(ast, extra);
180}
181
182//------------------------------------------------------------------------------
183// Public
184//------------------------------------------------------------------------------
185
186exports.version = require("./package.json").version;
187
188exports.parse = function parse(code, options) {
189 return generateAST(code, options, { isParseForESLint: false });
190};
191
192exports.parseForESLint = function parseForESLint(code, options) {
193 const ast = generateAST(code, options, { isParseForESLint: true });
194 return { ast };
195};
196
197// Deep copy.
198/* istanbul ignore next */
199exports.Syntax = (function() {
200 let name,
201 types = {};
202
203 if (typeof Object.create === "function") {
204 types = Object.create(null);
205 }
206
207 for (name in astNodeTypes) {
208 if (astNodeTypes.hasOwnProperty(name)) {
209 types[name] = astNodeTypes[name];
210 }
211 }
212
213 if (typeof Object.freeze === "function") {
214 Object.freeze(types);
215 }
216
217 return types;
218}());