ipsos-components
Version:
Material Design components for Angular
122 lines (100 loc) • 4.6 kB
text/typescript
import {red} from 'chalk';
import {readdirSync, readFileSync, statSync} from 'fs';
import {task} from 'gulp';
import {buildConfig} from 'material2-build-tools';
import {IMinimatch, Minimatch} from 'minimatch';
import {join} from 'path';
import {execNodeTask} from '../util/task_helpers';
// These types lack of type definitions
const madge = require('madge');
/** Glob that matches all SCSS or CSS files that should be linted. */
const stylesGlob = '+(tools|src)/**/!(*.bundle).+(css|scss)';
/** List of flags that will passed to the different TSLint tasks. */
const tsLintBaseFlags = ['-c', 'tslint.json', '--project', './tsconfig.json'];
/** Path to the output of the Material package. */
const materialOutPath = join(buildConfig.outputDir, 'packages', 'material');
/** Path to the output of the CDK package. */
const cdkOutPath = join(buildConfig.outputDir, 'packages', 'cdk');
/** Path for the Github owners file. */
const ownersFilePath = '.github/CODEOWNERS';
/** Path for the .gitignore file. */
const gitIgnorePath = '.gitignore';
task('lint', ['tslint', 'stylelint', 'madge', 'ownerslint']);
/** Task to lint Angular Material's scss stylesheets. */
task('stylelint', execNodeTask(
'stylelint', [stylesGlob, '--config', 'stylelint-config.json', '--syntax', 'scss']
));
/** Task to run TSLint against the e2e/ and src/ directories. */
task('tslint', execNodeTask('tslint', tsLintBaseFlags));
/** Task that automatically fixes TSLint warnings. */
task('tslint:fix', execNodeTask('tslint', [...tsLintBaseFlags, '--fix']));
/** Task that runs madge to detect circular dependencies. */
task('madge', ['material:clean-build'], () => {
madge([materialOutPath, cdkOutPath]).then((res: any) => {
const circularModules = res.circular();
if (circularModules.length) {
console.error();
console.error(red(`Madge found modules with circular dependencies.`));
console.error(formatMadgeCircularModules(circularModules));
console.error();
}
});
});
task('ownerslint', () => {
let errors = 0;
let ownedPaths = readFileSync(ownersFilePath, 'utf8').split('\n')
// Trim lines.
.map(line => line.trim())
// Remove empty lines and comments.
.filter(line => line && !line.startsWith('#'))
// Split off just the path glob.
.map(line => line.split(/\s+/)[0])
// Turn paths into Minimatch objects.
.map(path => new Minimatch(path, {dot: true, matchBase: true}));
let ignoredPaths = readFileSync(gitIgnorePath, 'utf8').split('\n')
// Trim lines.
.map(line => line.trim())
// Remove empty lines and comments.
.filter(line => line && !line.startsWith('#'))
// Turn paths into Minimatch objects.
.map(path => new Minimatch(path, {dot: true, matchBase: true}));
for (let paths = getChildPaths('.'); paths.length;) {
paths = Array.prototype.concat(...paths
// Remove ignored paths
.filter(path => !ignoredPaths.reduce(
(isIgnored, ignoredPath) => isIgnored || ignoredPath.match('/' + path), false))
// Remove paths that match an owned path.
.filter(path => !ownedPaths.reduce(
(isOwned, ownedPath) => isOwned || isOwnedBy(path, ownedPath), false))
// Report an error for any files that didn't match any owned paths.
.filter(path => {
if (statSync(path).isFile()) {
console.log(red(`No code owner found for "${path}".`));
errors++;
return false;
}
return true;
})
// Get the next level of children for any directories.
.map(path => getChildPaths(path)));
}
if (errors) {
throw Error(`Found ${errors} files with no owner.`);
}
});
/** Check if the given path is owned by the given owned path matcher. */
function isOwnedBy(path: string, ownedPath: IMinimatch) {
// If the owned path ends with `**` its safe to eliminate whole directories.
if (ownedPath.pattern.endsWith('**') || statSync(path).isFile()) {
return ownedPath.match('/' + path);
}
return false;
}
/** Get the immediate child paths of the given path. */
function getChildPaths(path: string) {
return readdirSync(path).map(child => join(path, child));
}
/** Returns a string that formats the graph of circular modules. */
function formatMadgeCircularModules(circularModules: string[][]): string {
return circularModules.map((modulePaths: string[]) => `\n - ${modulePaths.join(' > ')}`).join('');
}