2018-03-25 15:43:17 -04:00

212 lines
5.4 KiB
TypeScript

import { execSync } from 'child_process';
import * as path from 'path';
import {
affectedApps,
ProjectNode,
ProjectType,
touchedProjects
} from './affected-apps';
import * as fs from 'fs';
import {
dependencies,
Dependency
} from '@nrwl/schematics/src/command-line/affected-apps';
import { readFileSync, statSync } from 'fs';
import * as appRoot from 'app-root-path';
export function parseFiles(
args: string[]
): { files: string[]; rest: string[] } {
let unnamed = [];
let named = [];
args.forEach(a => {
if (a.startsWith('--') || a.startsWith('-')) {
named.push(a);
} else {
unnamed.push(a);
}
});
const dashDashFiles = named.filter(a => a.startsWith('--files='))[0];
if (dashDashFiles) {
named.splice(named.indexOf(dashDashFiles), 1);
return {
files: parseDashDashFiles(dashDashFiles),
rest: [...unnamed, ...named]
};
} else if (unnamed.length >= 2) {
return {
files: getFilesFromShash(unnamed[0], unnamed[1]),
rest: [...unnamed.slice(2), ...named]
};
} else {
throw new Error('Invalid options provided');
}
}
function parseDashDashFiles(dashDashFiles: string): string[] {
let f = dashDashFiles.substring(8); // remove --files=
if (f.startsWith('"') || f.startsWith("'")) {
f = f.substring(1, f.length - 1);
}
return f.split(',').map(f => f.trim());
}
function getFilesFromShash(sha1: string, sha2: string): string[] {
return execSync(`git diff --name-only ${sha1} ${sha2}`)
.toString('utf-8')
.split('\n')
.map(a => a.trim())
.filter(a => a.length > 0);
}
export function getProjectNodes(config) {
return (config.apps ? config.apps : [])
.filter(p => p.name !== '$workspaceRoot')
.map(p => {
return {
name: p.name,
root: p.root,
type: p.root.startsWith('apps/') ? ProjectType.app : ProjectType.lib,
tags: p.tags,
files: allFilesInDir(`${appRoot.path}/${path.dirname(p.root)}`)
};
});
}
export function readCliConfig(): any {
const config = JSON.parse(
fs.readFileSync(`${appRoot.path}/.angular-cli.json`, 'utf-8')
);
if (!config.project.npmScope) {
throw new Error(`.angular-cli.json must define the npmScope property.`);
}
return config;
}
export function getAffectedApps(touchedFiles: string[]): string[] {
const config = readCliConfig();
const projects = getProjectNodes(config);
return affectedApps(
config.project.npmScope,
projects,
f => fs.readFileSync(`${appRoot.path}/${f}`, 'utf-8'),
touchedFiles
);
}
export function getTouchedProjects(touchedFiles: string[]): string[] {
return touchedProjects(getProjectNodes(readCliConfig()), touchedFiles).filter(
p => !!p
);
}
export function getProjectRoots(projectNames: string[]): string[] {
const projects = getProjectNodes(readCliConfig());
return projectNames.map(name =>
path.dirname(projects.filter(p => p.name === name)[0].root)
);
}
function allFilesInDir(dirName: string): string[] {
let res = [];
fs.readdirSync(dirName).forEach(c => {
const child = path.join(dirName, c);
try {
if (!fs.statSync(child).isDirectory()) {
// add starting with "apps/myapp/..." or "libs/mylib/..."
res.push(normalizePath(child.substring(appRoot.path.length + 1)));
} else if (fs.statSync(child).isDirectory()) {
res = [...res, ...allFilesInDir(child)];
}
} catch (e) {}
});
return res;
}
export function readDependencies(
npmScope: string,
projectNodes: ProjectNode[]
): { [projectName: string]: Dependency[] } {
const m = lastModifiedAmongProjectFiles();
if (!directoryExists(`${appRoot.path}/dist`)) {
fs.mkdirSync(`${appRoot.path}/dist`);
}
if (
!fileExists(`${appRoot.path}/dist/nxdeps.json`) ||
m > mtime(`${appRoot.path}/dist/nxdeps.json`)
) {
const deps = dependencies(npmScope, projectNodes, f =>
fs.readFileSync(`${appRoot.path}/${f}`, 'UTF-8')
);
fs.writeFileSync(
`${appRoot.path}/dist/nxdeps.json`,
JSON.stringify(deps, null, 2),
'UTF-8'
);
return deps;
} else {
return JSON.parse(
fs.readFileSync(`${appRoot.path}/dist/nxdeps.json`, 'UTF-8')
);
}
}
export function lastModifiedAmongProjectFiles() {
return [
recursiveMtime(`${appRoot.path}/libs`),
recursiveMtime(`${appRoot.path}/apps`),
mtime(`${appRoot.path}/.angular-cli.json`),
mtime(`${appRoot.path}/tslint.json`),
mtime(`${appRoot.path}/package.json`)
].reduce((a, b) => (a > b ? a : b), 0);
}
function recursiveMtime(dirName: string) {
let res = mtime(dirName);
fs.readdirSync(dirName).forEach(c => {
const child = path.join(dirName, c);
try {
if (!fs.statSync(child).isDirectory()) {
const c = mtime(child);
if (c > res) {
res = c;
}
} else if (fs.statSync(child).isDirectory()) {
const c = recursiveMtime(child);
if (c > res) {
res = c;
}
}
} catch (e) {}
});
return res;
}
function mtime(f: string): number {
return fs.fstatSync(fs.openSync(f, 'r')).mtime.getTime();
}
function normalizePath(file: string): string {
return file.split(path.sep).join('/');
}
function directoryExists(filePath: string): boolean {
try {
return statSync(filePath).isDirectory();
} catch (err) {
return false;
}
}
function fileExists(filePath: string): boolean {
try {
return statSync(filePath).isFile();
} catch (err) {
return false;
}
}