import fs from 'fs' ; import path from 'path' ; import axios , { AxiosRequestConfig } from 'flex-dev-utils/dist/axios' ; import { mkdirpSync } from 'flex-dev-utils/dist/fs' ; export interface GitHubInfo { owner : string ; repo : string ; ref : string ; } export interface GitHubBranches { name : string ; commit : string ; protected : boolean ; } export enum GitHubContentType { File = 'file' , Dir = 'dir' } export interface GitHubContent { name : string ; path : string ; sha : string ; size : number ; url : string ; html_url : string ; git_url : string ; download_url : string ; type : GitHubContentType ; _links : { self : string ; git : string ; html : string ; } ; } export const ERROR_GITHUB_URL_PARSE = 'Could not get owner and repo name from GitHub URL' ; export const ERROR_BRANCH_MASTER_MAIN = 'Could not find branch main or master on GitHub' ; param info { GitHubInfo } param hasTemplateDir { boolean } private export const _getBaseRegex = ( info , hasTemplateDir ) : string => { const baseRegex = `\/${ info . owner }\/${ info . repo }\/${ info . ref }\/` ; if ( hasTemplateDir ) { return `${ baseRegex }template\/` ; } return baseRegex ; } ; param info { GitHubInfo } the  GitHubInfo information
 *  private export const _hasTemplateDir = async ( info ) : Promise < boolean > => { const url = `https://api.github.com/repos/${ info . owner }/${ info . repo }/contents?ref=${ info . ref }` ; return axios . get < GitHubContent [ ] > ( url ) . then ( ( resp ) => resp . data ) . then ( ( contents ) => contents . some ( ( content ) => content . name === 'template' && content . type === 'dir' ) ) ; } ; param url { string } param output { string } private export const _downloadFile = async ( url , output ) : Promise < void > => { const config = { url , responseType : 'arraybuffer' , method : 'GET' , } ; const dir = path . dirname ( output ) ; await mkdirpSync ( dir ) ; return axios . request ( config ) . then ( ( result ) => fs . writeFileSync ( output , result . data ) ) ; } ; param url { string } param dir { string } param baseRegex { String } private export const _downloadDir = async ( url , dir , baseRegex ) : Promise < void > => { return axios . get < GitHubContent [ ] > ( url ) . then ( ( resp ) => resp . data ) . then ( async ( contents ) => { const promises = contents . map ( async ( content ) => { if ( content . type === GitHubContentType . Dir ) { return _downloadDir ( content . url , dir , baseRegex ) ; } if ( content . type !== GitHubContentType . File ) { throw new Error ( `Unexpected content type ${ content . type }` ) ; } const regex = new RegExp ( `${ baseRegex }(.+)\\??` ) ; const relativePath = content . download_url . match ( regex ) ; if ( ! relativePath || relativePath . length !== 2 ) { throw new Error ( 'Received invalid URL template' ) ; } const output = path . resolve ( dir , relativePath [ 1 ] ) ; return _downloadFile ( content . download_url , output ) ; } ) ; await Promise . all ( promises ) ; } ) ; } ; param url { string } return returns the  GitHubInfo export const parseGitHubUrl = async ( url ) : Promise < GitHubInfo > => { const matches = url . match ( /github\.com\/([0-9a-zA-Z-_]+)\/([0-9a-zA-Z-_]+)(\/tree\/([0-9a-zA-Z._-]+))?/ ) ; if ( ! matches || matches . length < 3 ) { throw new Error ( ERROR_GITHUB_URL_PARSE ) ; } const info = { owner : matches [ 1 ] , repo : matches [ 2 ] , ref : matches [ 4 ] || 'master' , } ; if ( info . ref === 'master' || info . ref === 'main' ) { const branches = await axios . get < GitHubBranches [ ] > ( `https://api.github.com/repos/${ info . owner }/${ info . repo }/branches` ) . then ( ( resp ) => resp . data ) ; const hasMaster = branches . find ( ( branch ) => branch . name === 'master' ) ; const hasMain = branches . find ( ( branch ) => branch . name === 'main' ) ; if ( hasMain ) { info . ref = 'main' ; } else if ( hasMaster ) { info . ref = 'master' ; } else { throw new Error ( ERROR_BRANCH_MASTER_MAIN ) ; } } return info ; } ; param info { GitHubInfo } param dir { string } return export const downloadRepo = async ( info , dir ) : Promise < void > => { const hasTemplateDir = await _hasTemplateDir ( info ) ; const url = hasTemplateDir ? `https://api.github.com/repos/${ info . owner }/${ info . repo }/contents/template?ref=${ info . ref }` : `https://api.github.com/repos/${ info . owner }/${ info . repo }/contents?ref=${ info . ref }` ; return _downloadDir ( url , dir , _getBaseRegex ( info , hasTemplateDir ) ) ; } ;