mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-15 10:11:50 +08:00
181 lines
5.4 KiB
JavaScript
181 lines
5.4 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
/* eslint-disable no-console */
|
|
const chalk = require('chalk');
|
|
const fs = require('fs');
|
|
const glob = require('glob');
|
|
const packages = require('../../lib/packages').packages;
|
|
const path = require('path');
|
|
|
|
|
|
const IMPORT_RE = /(^|\n)\s*import\b(?:.|\n)*?\'[^\']*\'/g;
|
|
const REQUIRE_RE = /\brequire\('[^)]+?'\)/g;
|
|
const IGNORE_RE = /\s+@ignoreDep\s+\S+/g;
|
|
const NODE_PACKAGES = [
|
|
'child_process',
|
|
'crypto',
|
|
'fs',
|
|
'https',
|
|
'os',
|
|
'path',
|
|
'process',
|
|
'url',
|
|
'vm',
|
|
'zlib'
|
|
];
|
|
const ANGULAR_PACKAGES = [
|
|
'@angular/compiler',
|
|
'@angular/compiler-cli',
|
|
'@angular/core',
|
|
'@schematics/package-update'
|
|
];
|
|
const OPTIONAL_PACKAGES = [
|
|
'@angular/service-worker',
|
|
];
|
|
const PEERDEP_HACK_PACKAGES = [
|
|
'typescript',
|
|
];
|
|
|
|
|
|
function listImportedModules(source) {
|
|
const imports = source.match(IMPORT_RE);
|
|
|
|
return (imports || [])
|
|
.map(match => {
|
|
const m = match.match(/'(.*)'/);
|
|
return m && m[1];
|
|
})
|
|
.filter(x => !!x)
|
|
.filter(modulePath => modulePath[0] != '.')
|
|
.map(fullImportPath => {
|
|
if (fullImportPath[0] == '@') {
|
|
// Need to get the scope as well.
|
|
return fullImportPath.split('/').slice(0, 2).join('/');
|
|
} else {
|
|
return fullImportPath.split('/')[0];
|
|
}
|
|
});
|
|
}
|
|
|
|
function listRequiredModules(source) {
|
|
const requires = source.match(REQUIRE_RE);
|
|
|
|
return (requires || [])
|
|
.map(match => {
|
|
const m = match.match(/'([^']*)'/);
|
|
return m && m[1];
|
|
})
|
|
.filter(x => !!x)
|
|
.filter(modulePath => modulePath[0] != '.')
|
|
.map(fullImportPath => {
|
|
if (fullImportPath[0] == '@') {
|
|
// Need to get the scope as well.
|
|
return fullImportPath.split('/').slice(0, 2).join('/');
|
|
} else {
|
|
return fullImportPath.split('/')[0];
|
|
}
|
|
});
|
|
}
|
|
|
|
function listIgnoredModules(source) {
|
|
const ignored = source.match(IGNORE_RE);
|
|
return (ignored || [])
|
|
.map(match => {
|
|
const m = match.match(/@ignoreDep\s+(\S+)/);
|
|
return m && m[1];
|
|
})
|
|
.filter(x => !!x);
|
|
}
|
|
|
|
function reportMissingDependencies(missingDeps) {
|
|
if (missingDeps.length == 0) {
|
|
console.log(chalk.green(' no dependency missing from package.json.'));
|
|
} else {
|
|
console.log(chalk.yellow(` ${missingDeps.length} missing from package.json:`));
|
|
missingDeps.forEach(md => console.log(` ${md}`));
|
|
exitCode = 1;
|
|
}
|
|
}
|
|
|
|
function reportExcessiveDependencies(overDeps) {
|
|
if (overDeps.length == 0) {
|
|
console.log(chalk.green(' no excessive dependencies in package.json.'));
|
|
} else {
|
|
console.log(chalk.yellow(` ${overDeps.length} excessive dependencies in package.json:`));
|
|
overDeps.forEach(md => console.log(` ${md}`));
|
|
exitCode = 1;
|
|
}
|
|
}
|
|
|
|
let exitCode = 0;
|
|
const overallDeps = [];
|
|
for (const packageName of Object.keys(packages)) {
|
|
console.log(chalk.green(`Reading dependencies of "${packageName}".`));
|
|
|
|
const allSources = glob.sync(path.join(__dirname, '../../packages/', packageName, '**/*'))
|
|
.filter(p => p.match(/\.(js|ts)$/))
|
|
.filter(p => !p.match(/\.spec\./))
|
|
.filter(p => !p.match(/\/blueprints\//));
|
|
|
|
const importMap = {};
|
|
allSources.forEach(function(filePath) {
|
|
const source = fs.readFileSync(filePath, 'utf8');
|
|
|
|
listImportedModules(source)
|
|
.forEach(modulePath => importMap[modulePath] = true);
|
|
listRequiredModules(source)
|
|
.forEach(modulePath => importMap[modulePath] = true);
|
|
listIgnoredModules(source)
|
|
.forEach(modulePath => {
|
|
delete importMap[modulePath];
|
|
});
|
|
});
|
|
|
|
const dependencies = Object.keys(importMap)
|
|
// Filter out the node packages that should not be depended on.
|
|
.filter(x => NODE_PACKAGES.indexOf(x) == -1);
|
|
overallDeps.push(...dependencies);
|
|
|
|
console.log(chalk.green(` found ${dependencies.length} dependencies...`));
|
|
const packageJson = JSON.parse(fs.readFileSync(packages[packageName].packageJson, 'utf8'));
|
|
const allDeps = []
|
|
.concat(Object.keys(packageJson['dependencies'] || {}))
|
|
.concat(Object.keys(packageJson['optionalDependencies'] || {}))
|
|
.concat(Object.keys(packageJson['devDependencies'] || {}))
|
|
.concat(Object.keys(packageJson['peerDependencies'] || {}));
|
|
|
|
const missingDeps = dependencies
|
|
.filter(d => allDeps.indexOf(d) == -1)
|
|
.filter(d => OPTIONAL_PACKAGES.indexOf(d) == -1);
|
|
reportMissingDependencies(missingDeps);
|
|
|
|
const overDeps = allDeps.filter(d => dependencies.indexOf(d) == -1)
|
|
.filter(x => ANGULAR_PACKAGES.indexOf(x) == -1);
|
|
reportExcessiveDependencies(overDeps);
|
|
|
|
console.log('');
|
|
}
|
|
|
|
console.log(chalk.green('Validating root package. [devDependencies ignored]'));
|
|
const rootPackagePath = path.join(__dirname, '../../package.json');
|
|
const rootPackageJson = JSON.parse(fs.readFileSync(rootPackagePath, 'utf8'));
|
|
// devDependencies are ignored
|
|
const allRootDeps = []
|
|
.concat(Object.keys(rootPackageJson['dependencies'] || {}))
|
|
.concat(Object.keys(rootPackageJson['optionalDependencies'] || {}))
|
|
.concat(Object.keys(rootPackageJson['peerDependencies'] || {}));
|
|
|
|
const internalPackages = Object.keys(packages);
|
|
const missingRootDeps = overallDeps.filter(d => allRootDeps.indexOf(d) == -1)
|
|
.filter(d => internalPackages.indexOf(d) == -1)
|
|
.filter(x => OPTIONAL_PACKAGES.indexOf(x) == -1);
|
|
reportMissingDependencies(missingRootDeps);
|
|
|
|
const overRootDeps = allRootDeps.filter(d => overallDeps.indexOf(d) == -1)
|
|
.filter(x => ANGULAR_PACKAGES.indexOf(x) == -1)
|
|
.filter(x => PEERDEP_HACK_PACKAGES.indexOf(x) == -1);
|
|
reportExcessiveDependencies(overRootDeps);
|
|
|
|
process.exit(exitCode);
|