polymer-analyzer
Version:
Static analysis for Web Components
160 lines • 5.71 kB
JavaScript
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const path = require("path");
const ts = require("typescript");
const _compilerOptions = {
allowJs: true,
emit: false,
lib: ['ES2017', 'DOM'],
};
class TypeScriptAnalyzer {
constructor(analysisContext) {
this._context = analysisContext;
}
analyze(url) {
const host = new AnalyzerCompilerHost(this._context);
return ts.createProgram([url], _compilerOptions, host);
}
}
exports.TypeScriptAnalyzer = TypeScriptAnalyzer;
// This is mainly for telling the compiler the directory that the
// lib files are in.
const defaultLib = '/$lib/lib.es2017.d.ts';
const tsLibPath = path.dirname(require.resolve('typescript'));
function isLibraryPath(filename) {
return filename.startsWith('/$lib/');
}
const libraryCache = new Map();
function getLibrarySource(filePath) {
if (libraryCache.has(filePath)) {
return libraryCache.get(filePath);
}
let libFileName = filePath.substring('/$lib/'.length).toLowerCase();
if (!libFileName.startsWith('lib.')) {
libFileName = `lib.${libFileName}`;
}
const libPath = path.resolve(tsLibPath, libFileName);
let source;
try {
source = fs.readFileSync(libPath, { encoding: 'utf-8' });
}
catch (e) {
// not found
}
libraryCache.set(filePath, source);
return source;
}
/**
* A TypeScript CompilerHost that reads files from an AnalysisContext.
*/
class AnalyzerCompilerHost {
constructor(context) {
this.context = context;
}
getSourceFile(fileName, languageVersion, onError) {
if (isLibraryPath(fileName)) {
const libSource = getLibrarySource(fileName);
if (libSource != null) {
return ts.createSourceFile(fileName, libSource, languageVersion);
}
// We don't call onError for library paths because the compiler asks for
// many paths speculatively. Returning null is sufficient.
}
else {
// This method will be called during analysis, but after all files
// in the dependency graph have been loaded, so it can call a synchronous
// method to get the source of a file.
const scannedDocument = this.context._getScannedDocument(this._failSafeResolveUrl(fileName));
if (scannedDocument != null) {
const typescriptDocument = scannedDocument.document;
return typescriptDocument.ast;
}
if (onError) {
onError('not found');
}
}
// tslint:disable-next-line: no-any
return null;
}
getDefaultLibFileName() {
return defaultLib;
}
get writeFile() {
return (_fileName, _data, _writeByteOrderMark, _onError, _sourceFiles) => {
throw new Error('unsupported operation');
};
}
getCurrentDirectory() {
// Seems to work best
return '';
}
getDirectories(_path) {
// Seems to work best
return [''];
}
getCanonicalFileName(fileName) {
return this._failSafeResolveUrl(fileName);
}
getNewLine() {
return ts.sys.newLine;
}
useCaseSensitiveFileNames() {
return true;
}
fileExists(fileName) {
const resolvedUrl = this._failSafeResolveUrl(fileName);
return isLibraryPath(resolvedUrl) &&
getLibrarySource(resolvedUrl) != null ||
this.context._getScannedDocument(resolvedUrl) != null;
}
readFile(fileName) {
const resolvedUrl = this._failSafeResolveUrl(fileName);
if (isLibraryPath(resolvedUrl)) {
const libPath = require.resolve(`typescript/lib/${fileName}`);
return fs.readFileSync(libPath, { encoding: 'utf-8' });
}
const document = this.context._getScannedDocument(resolvedUrl);
// tslint:disable-next-line: no-any
return (document) ? document.document.contents : null;
}
resolveModuleNames(moduleNames, containingFile) {
return moduleNames.map((moduleName) => {
// We only support path resolution, not node resolution
if (!(moduleName.startsWith('./') || moduleName.startsWith('../') ||
moduleName.startsWith('/'))) {
// tslint:disable-next-line: no-any
return { resolvedFileName: null };
}
// since we have a path, we can simply resolve it
const fileName = path.resolve(path.dirname(containingFile), moduleName);
const resolvedFileName = this._failSafeResolveUrl(fileName);
return { resolvedFileName };
});
}
/**
* Resolves the given url.
*
* If the url is unresolvable, the given unresolved URL is returned as a
* resolved URL.
*/
_failSafeResolveUrl(url) {
const resolved = this.context.resolver.resolve(url);
// tslint:disable-next-line: no-any
return resolved === undefined ? url : resolved;
}
}
//# sourceMappingURL=typescript-analyzer.js.map
;