1import path from 'path'; 2import resolveFrom from 'resolve-from'; 3 4import { DestinationResolutionProps, selectTemplatesAsync, TEMPLATES } from './templates'; 5import { installAsync } from '../install/installAsync'; 6import { Log } from '../log'; 7import { copyAsync } from '../utils/dir'; 8import { CommandError } from '../utils/errors'; 9 10export async function queryAndGenerateAsync( 11 projectRoot: string, 12 { 13 files, 14 props, 15 extras, 16 }: { 17 files: string[]; 18 props: DestinationResolutionProps; 19 /** Any extra props to pass to the install command. */ 20 extras: any[]; 21 } 22) { 23 const valid = files.filter( 24 (file) => !!TEMPLATES.find((template) => template.destination(props) === file) 25 ); 26 27 if (valid.length !== files.length) { 28 const diff = files.filter( 29 (file) => !TEMPLATES.find((template) => template.destination(props) === file) 30 ); 31 throw new CommandError( 32 `Invalid files: ${diff.join(', ')}. Allowed: ${TEMPLATES.map((template) => 33 template.destination(props) 34 ).join(', ')}` 35 ); 36 } 37 38 if (!valid.length) { 39 return; 40 } 41 Log.log(`Generating: ${valid.join(', ')}`); 42 return generateAsync(projectRoot, { 43 answer: files.map((file) => 44 TEMPLATES.findIndex((template) => template.destination(props) === file) 45 ), 46 props, 47 extras, 48 }); 49} 50 51/** Select templates to generate then generate and install. */ 52export async function selectAndGenerateAsync( 53 projectRoot: string, 54 { 55 props, 56 extras, 57 }: { 58 props: DestinationResolutionProps; 59 /** Any extra props to pass to the install command. */ 60 extras: any[]; 61 } 62) { 63 const answer = await selectTemplatesAsync(projectRoot, props); 64 65 if (!answer?.length) { 66 Log.exit('\n\u203A Exiting with no change...', 0); 67 } 68 69 await generateAsync(projectRoot, { 70 answer, 71 props, 72 extras, 73 }); 74} 75 76async function generateAsync( 77 projectRoot: string, 78 { 79 answer, 80 props, 81 extras, 82 }: { 83 answer: number[]; 84 props: DestinationResolutionProps; 85 /** Any extra props to pass to the install command. */ 86 extras: any[]; 87 } 88) { 89 // Copy files 90 await Promise.all( 91 answer.map(async (file) => { 92 const template = TEMPLATES[file]; 93 94 if (template.id === 'tsconfig.json') { 95 const { typescript } = await import('./typescript.js'); 96 return typescript(projectRoot); 97 } 98 99 const projectFilePath = path.resolve(projectRoot, template.destination(props)); 100 // copy the file from template 101 return copyAsync(template.file(projectRoot), projectFilePath, { 102 overwrite: true, 103 recursive: true, 104 }); 105 }) 106 ); 107 108 // Install dependencies 109 const packages = answer 110 .map((file) => TEMPLATES[file].dependencies) 111 .flat() 112 .filter((pkg) => !resolveFrom.silent(projectRoot, pkg)); 113 if (packages.length) { 114 Log.debug('Installing ' + packages.join(', ')); 115 await installAsync(packages, {}, ['--dev', ...extras]); 116 } 117} 118