import * as checksum from 'checksum'; import * as fsExtra from 'fs-extra-promise'; import co_assert from '../../utils/co-assert' import * as Promise from 'bluebird' import * as path from 'path' import * as upath from 'upath' import PresentationFileChecksums from './PresentationFileChecksums' import glob = require('glob-promise') import * as utils from '@cobalt-engine/utils' import { ignoredPatterns, ignoredFiles } from '../../ignored-patterns' const ignorePatterns = [ '**/demo', 'node_modules/**/*.map', '**/*.md', '**/.DS_Store', '**/*changelog*'] .concat(ignoredPatterns.map((pattern: string) => `${pattern}/**`)) .concat(ignoredFiles); export default class ChecksumCalculator { public constructor(private ignoredFiles: string[]) { this.ignoredFiles = ignoredFiles; } public calculate(directoryPath: string): Promise { return this.directoryChecksum(directoryPath); } private directoryChecksum(directoryPath: string): Promise { const result = new PresentationFileChecksums(); return this.getIgnoredFiles(directoryPath) .then((ignoredFiles) => this.iterateFiles(directoryPath, ignoredFiles, (filePath): Promise => { return this.calculateFileChecksum(filePath) .then((fileChecksum: string) => { const relativePath = upath.toUnix(path.relative(directoryPath, filePath)); result.add(relativePath, fileChecksum); }); }) .then(() => { return result; })) } private iterateFiles(directoryPath: string, ignoredFiles: string[], handleFunc: (fullFilePath: string) => Promise): Promise { co_assert(handleFunc !== undefined, '"handleFunc" parameter is required.'); return Promise.all([this.getAllFilesInDirectory(directoryPath)]) .then(([files]) => { const filteredFiles = files.filter((file) => !this.isIgnoredFile(file, ignoredFiles, directoryPath)); return Promise.all(filteredFiles.map((file) => { const fullPath = path.join(directoryPath, file); return this.isDirectory(fullPath) .then((isDirectory: boolean) => { if (isDirectory) { return this.iterateFiles(fullPath, ignoredFiles, handleFunc); } else { return handleFunc(fullPath); } }); })); }) .then(() => {}); } private getIgnoredFiles(directoryPath: string): Promise { return Promise.all(ignorePatterns.map((ignorePattern: string) => glob(ignorePattern, {cwd: directoryPath}))) .then(utils.flattenArray) .then(patternMatch => { return this.ignoredFiles .concat(patternMatch) .map((ignoredFile: string) => path.join(directoryPath, ignoredFile)) }); } public getAllFilesInDirectory(directoryPath: string): Promise { return fsExtra.readdirAsync(directoryPath); } public isDirectory(fileFullPath: string): Promise { return fsExtra.statAsync(fileFullPath) .then((stat: fsExtra.Stats) => { return stat && stat.isDirectory(); }); } public calculateFileChecksum(filePath: string): Promise { const fileChecksum = Promise.promisify(checksum.file); return fileChecksum(filePath); } private isIgnoredFile(file: string, ignoredFiles: string[], directoryPath: string) { const fullPath = path.join(directoryPath, file); return ignoredFiles.some(f => { return f === fullPath }); } }