import * as fs from 'fs' ; import * as https from 'https' ; import * as path from 'path' ; import { compile } from 'handlebars' ; import { CopyDescriptor } from './copy-list' ; import { rm , mkdir , readFile , copyFile , writeFile } from './fs' ; const templatesDir = path . resolve ( __dirname , '../templates' ) ; function getTemplatePathsForDir ( inputDir , excludes ) { const directoryEntries = fs . readdirSync ( path . resolve ( templatesDir , ... inputDir ) , { withFileTypes : true } ) ; return directoryEntries . reduce ( ( templates , directoryEntry ) => { if ( directoryEntry . isDirectory ( ) && ! excludes . includes ( directoryEntry . name ) ) { return [ ... templates , ... getTemplatePathsForDir ( [ ... inputDir , directoryEntry . name ] , excludes ) ] ; } if ( directoryEntry . isFile ( ) && ! excludes . includes ( directoryEntry . name ) ) { return [ ... templates , path . resolve ( templatesDir , ... inputDir , directoryEntry . name ) ] ; } return templates ; } , [ ] ) ; } export function getTemplatePaths ( inputDirs , excludes = [ ] ) { return inputDirs . reduce ( ( templatePaths , inputDir ) => ( { ... templatePaths , [ inputDir ] : getTemplatePathsForDir ( [ inputDir ] , excludes ) } ) , { } ) ; } function getCopyDescriptorForPath ( targetDir , templateSubDir , templatePath ) { const relativeSourcePath = path . relative ( path . resolve ( templatesDir , templateSubDir ) , path . dirname ( templatePath ) ) ; const targetTemplateDir = path . resolve ( targetDir , relativeSourcePath ) ; return { sourcePath : templatePath , fileName : path . resolve ( targetTemplateDir , path . basename ( templatePath , '.mu' ) ) } ; } export function getCopyDescriptors ( targetDir , templatePaths ) { return Object . entries ( templatePaths ) . reduce ( ( allCopyDescriptors , [ templateSubDir , paths ] ) => [ ... allCopyDescriptors , ... paths . map ( templatePath => getCopyDescriptorForPath ( targetDir , templateSubDir , templatePath ) ) ] , [ ] ) ; } export async function findConflicts ( copyDescriptors , force = false ) { const conflicts = copyDescriptors . filter ( copyDescriptor => fs . existsSync ( copyDescriptor . fileName ) ) ; if ( conflicts . length > 0 ) { if ( force ) { await Promise . all ( conflicts . map ( copyDescriptor => rm ( copyDescriptor . fileName ) ) ) ; } else { const listOfFiles = conflicts . map ( f => path . basename ( f . fileName ) ) . join ( '", "' ) ; throw new Error ( conflicts . length > 1 ? `Files with the names "${ listOfFiles }" already exist. If you want to overwrite them, rerun the command with \`--force\`.` : `A file with the name "${ listOfFiles }" already exists. If you want to overwrite it, rerun the command with \`--force\`.` ) ; } } } export async function copyFiles ( copyDescriptors , options ) { return Promise . all ( copyDescriptors . map ( ( { sourcePath , fileName } ) => sourcePath instanceof URL ? copyRemote ( sourcePath , fileName ) : copyLocal ( sourcePath , fileName , options ) ) ) ; } async function copyRemote ( sourcePath , fileName ) { return new Promise < void > ( ( resolve , reject ) => { https . get ( sourcePath , response => { if ( response . statusCode && ( response . statusCode < 200 || response . statusCode > 299 ) ) { reject ( new Error ( 'Failed to load page, status code: ' + response . statusCode ) ) ; } let content = '' ; response . on ( 'data' , ( chunk ) => { content += chunk ; } ) ; response . on ( 'end' , async ( ) => { await mkdir ( path . dirname ( fileName ) , { recursive : true } ) ; await writeFile ( fileName , content ) ; resolve ( undefined ) ; } ) ; } ) . on ( 'error' , e => { reject ( e ) ; } ) ; } ) ; } async function copyLocal ( sourcePath , fileName , options ) { await mkdir ( path . dirname ( fileName ) , { recursive : true } ) ; if ( path . extname ( sourcePath ) === '.mu' ) { const template = compile ( await readFile ( sourcePath , { encoding : 'utf8' } ) ) ; await writeFile ( fileName , template ( options ) ) ; } else { await copyFile ( sourcePath , fileName ) ; } }