blob: a4dc132c6304e3720466bebbbb17f5442763e5fd [file] [log] [blame]
/**
* @fileoverview Bans 'export' of mutable variables.
* It is illegal to mutate them, so you might as well use 'const'.
*/
import * as ts from 'typescript';
import {Checker} from '../checker';
import {ErrorCode} from '../error_code';
import {AbstractRule} from '../rule';
export class Rule extends AbstractRule {
readonly ruleName = 'ban-mutable-exports';
readonly code = ErrorCode.BAN_MUTABLE_EXPORTS;
register(checker: Checker) {
// Strategy: take all the exports of the file, then look at whether
// they're const or not. This is simpler than the alternative of
// trying to match all the various kinds of 'export' syntax, such
// as 'export var ...', 'export {...}', 'export default ...'.
checker.on(ts.SyntaxKind.SourceFile, checkFile, this.code);
}
}
function checkFile(checker: Checker, file: ts.SourceFile) {
// Allow in d.ts files, which are modelling external JS that doesn't
// follow our rules.
if (file.fileName.endsWith('.d.ts')) {
return;
}
const sym = checker.typeChecker.getSymbolAtLocation(file);
if (!sym) return;
const exports = checker.typeChecker.getExportsOfModule(sym);
for (const exp of exports) {
// In the case of
// let x = 3; export {x};
// The exported symbol is the latter x, but we must dealias to
// the former to judge whether it's const or not.
let sym = exp;
if (sym.flags & ts.SymbolFlags.Alias) {
sym = checker.typeChecker.getAliasedSymbol(exp);
}
const decl = sym.valueDeclaration;
if (!decl) continue; // Skip e.g. type declarations.
if (decl.getSourceFile() !== file) {
// Reexports are best warned about in the original file
continue;
}
if (!ts.isVariableDeclaration(decl) && !ts.isBindingElement(decl)) {
// Skip e.g. class declarations.
continue;
}
const isConst = (ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0;
if (!isConst) {
// Note: show the failure at the exported symbol's declaration site,
// not the dealiased 'sym', so that the error message shows at the
// 'export' statement and not the variable declaration.
checker.addFailureAtNode(
exp.declarations[0],
`Exports must be const.`);
}
}
}