build: add lint rule for global flags in rollup config (#20028)
We now verify that every imports is part of the globals defined in the files rollup.config.js. PR Close #20028
This commit is contained in:
parent
54480f7dfc
commit
f1a9e1e361
|
@ -0,0 +1,147 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import {RuleFailure} from 'tslint/lib';
|
||||||
|
import {AbstractRule} from 'tslint/lib/rules';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
|
||||||
|
function _isRollupPath(path: string) {
|
||||||
|
return /rollup\.config\.js$/.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regexes to blacklist.
|
||||||
|
const sourceFilePathBlacklist = [
|
||||||
|
/\.spec\.ts$/,
|
||||||
|
/_spec\.ts$/,
|
||||||
|
/_perf\.ts$/,
|
||||||
|
/_example\.ts$/,
|
||||||
|
/[/\\]test[/\\]/,
|
||||||
|
/[/\\]testing_internal\.ts$/,
|
||||||
|
/[/\\]integrationtest[/\\]/,
|
||||||
|
/[/\\]packages[/\\]bazel[/\\]/,
|
||||||
|
/[/\\]packages[/\\]benchpress[/\\]/,
|
||||||
|
/[/\\]packages[/\\]examples[/\\]/,
|
||||||
|
|
||||||
|
// language-service bundles everything in its UMD, so we don't need a globals. There are
|
||||||
|
// exceptions but we simply ignore those files from this rule.
|
||||||
|
/[/\\]packages[/\\]language-service[/\\]/,
|
||||||
|
|
||||||
|
// service-worker is a special package that has more than one rollup config. It confuses
|
||||||
|
// this lint rule and we simply ignore those files.
|
||||||
|
/[/\\]packages[/\\]service-worker[/\\]/,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Import package name whitelist. These will be ignored.
|
||||||
|
const importsWhitelist = [
|
||||||
|
'@angular/compiler-cli', // Not used in a browser.
|
||||||
|
'@angular/compiler-cli/src/language_services', // Deep import from language-service.
|
||||||
|
'chokidar', // Not part of compiler-cli/browser, but still imported.
|
||||||
|
'reflect-metadata',
|
||||||
|
'tsickle',
|
||||||
|
'url', // Part of node, no need to alias in rollup.
|
||||||
|
'zone.js',
|
||||||
|
];
|
||||||
|
|
||||||
|
const packageScopedImportWhitelist: [RegExp, string[]][] = [
|
||||||
|
[/service-worker[/\\]cli/, ['@angular/service-worker']],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// Return true if the file should be linted.
|
||||||
|
function _pathShouldBeLinted(path: string) {
|
||||||
|
return /[/\\]packages[/\\]/.test(path) && sourceFilePathBlacklist.every(re => !re.test(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface RollupMatchInfo {
|
||||||
|
filePath: string;
|
||||||
|
globals: {[packageName: string]: string};
|
||||||
|
}
|
||||||
|
const rollupConfigMap = new Map<string, RollupMatchInfo>();
|
||||||
|
|
||||||
|
|
||||||
|
export class Rule extends AbstractRule {
|
||||||
|
public apply(sourceFile: ts.SourceFile): RuleFailure[] {
|
||||||
|
const allImports = <ts.ImportDeclaration[]>sourceFile.statements.filter(
|
||||||
|
x => x.kind === ts.SyntaxKind.ImportDeclaration);
|
||||||
|
|
||||||
|
// Ignore specs, non-package files, and examples.
|
||||||
|
if (!_pathShouldBeLinted(sourceFile.fileName)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the rollup.config.js from this location, if it exists.
|
||||||
|
// If rollup cannot be found, this is an error.
|
||||||
|
let p = path.dirname(sourceFile.fileName);
|
||||||
|
let checkedPaths = [];
|
||||||
|
let match: RollupMatchInfo;
|
||||||
|
|
||||||
|
while (p.startsWith(process.cwd())) {
|
||||||
|
if (rollupConfigMap.has(p)) {
|
||||||
|
// We already resolved for this directory, just return it.
|
||||||
|
match = rollupConfigMap.get(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allFiles = fs.readdirSync(p);
|
||||||
|
const maybeRollupPath = allFiles.find(x => _isRollupPath(path.join(p, x)));
|
||||||
|
if (maybeRollupPath) {
|
||||||
|
const rollupFilePath = path.join(p, maybeRollupPath);
|
||||||
|
const rollupConfig = require(rollupFilePath).default;
|
||||||
|
match = {filePath: rollupFilePath, globals: rollupConfig && rollupConfig.globals};
|
||||||
|
|
||||||
|
// Update all paths that we checked along the way.
|
||||||
|
checkedPaths.forEach(path => rollupConfigMap.set(path, match));
|
||||||
|
rollupConfigMap.set(rollupFilePath, match);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkedPaths.push(p);
|
||||||
|
p = path.dirname(p);
|
||||||
|
}
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find rollup.config.js for ${JSON.stringify(sourceFile.fileName)}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rollupFilePath = match.filePath;
|
||||||
|
const globalConfig = match.globals || Object.create(null);
|
||||||
|
|
||||||
|
return allImports
|
||||||
|
.map(importStatement => {
|
||||||
|
const modulePath = (importStatement.moduleSpecifier as ts.StringLiteral).text;
|
||||||
|
if (modulePath.startsWith('.')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importsWhitelist.indexOf(modulePath) != -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [re, arr] of packageScopedImportWhitelist) {
|
||||||
|
if (re.test(sourceFile.fileName) && arr.indexOf(modulePath) != -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(modulePath in globalConfig)) {
|
||||||
|
return new RuleFailure(
|
||||||
|
sourceFile, importStatement.getStart(), importStatement.getWidth(),
|
||||||
|
`Import ${JSON.stringify(modulePath)} could not be found in the rollup config ` +
|
||||||
|
`at path ${JSON.stringify(rollupFilePath)}.`,
|
||||||
|
this.ruleName, );
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(x => !!x);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@
|
||||||
"no-jasmine-focus": true,
|
"no-jasmine-focus": true,
|
||||||
"no-var-keyword": true,
|
"no-var-keyword": true,
|
||||||
"require-internal-with-underscore": true,
|
"require-internal-with-underscore": true,
|
||||||
|
"rollup-config": true,
|
||||||
"semicolon": [true],
|
"semicolon": [true],
|
||||||
"variable-name": [true, "ban-keywords"],
|
"variable-name": [true, "ban-keywords"],
|
||||||
"no-inner-declarations": [true, "function"]
|
"no-inner-declarations": [true, "function"]
|
||||||
|
|
Loading…
Reference in New Issue