@@ -19,12 +19,21 @@ import deepmerge from 'deepmerge';
1919import minimist from 'minimist' ;
2020import { color , logger } from 'rslog' ;
2121import { x } from 'tinyexec' ;
22+ import { isNpmTemplate , resolveCustomTemplate } from './template-manager.js' ;
2223
2324const __filename = fileURLToPath ( import . meta. url ) ;
2425const __dirname = dirname ( __filename ) ;
2526
2627export { autocomplete , groupMultiselect , multiselect , select , text } ;
2728
29+ // Export npm template utilities
30+ export {
31+ isNpmTemplate ,
32+ resolveCustomTemplate ,
33+ resolveNpmTemplate ,
34+ sanitizeCacheKey ,
35+ } from './template-manager.js' ;
36+
2837function cancelAndExit ( ) {
2938 cancel ( 'Operation cancelled.' ) ;
3039 process . exit ( 0 ) ;
@@ -113,6 +122,8 @@ export type Argv = {
113122 skill ?: string | string [ ] ;
114123 packageName ?: string ;
115124 'package-name' ?: string ;
125+ templateVersion ?: string ;
126+ 'template-version' ?: string ;
116127} ;
117128
118129export const BUILTIN_TOOLS = [ 'eslint' , 'rslint' , 'biome' , 'prettier' ] ;
@@ -163,6 +174,7 @@ function logHelpMessage(
163174 --tools <tool> add additional tools, comma separated
164175${ skillsOptionLine } --override override files in target directory
165176 --packageName <name> specify the package name
177+ --template-version <ver> specify the npm template version
166178
167179 Available templates:
168180 ${ templates . join ( ', ' ) }
@@ -341,6 +353,11 @@ const parseArgv = (processArgv: string[]) => {
341353 argv . packageName = argv [ 'package-name' ] ;
342354 }
343355
356+ // Handle template-version alias
357+ if ( argv [ 'template-version' ] ) {
358+ argv . templateVersion = argv [ 'template-version' ] ;
359+ }
360+
344361 return argv ;
345362} ;
346363
@@ -488,6 +505,27 @@ async function runSkillCommand(skills: ExtraSkill[], cwd: string) {
488505 installationTaskLog . success ( `Installed ${ skillNoun } ${ skillLabel } ` ) ;
489506}
490507
508+ function logNextStepsAndOutro (
509+ noteInformation : string [ ] | undefined ,
510+ targetDir : string ,
511+ packageManager : string ,
512+ ) {
513+ const nextSteps = noteInformation
514+ ? noteInformation
515+ : [
516+ `1. ${ color . cyan ( `cd ${ targetDir } ` ) } ` ,
517+ `2. ${ color . cyan ( 'git init' ) } ${ color . dim ( '(optional)' ) } ` ,
518+ `3. ${ color . cyan ( `${ packageManager } install` ) } ` ,
519+ `4. ${ color . cyan ( `${ packageManager } run dev` ) } ` ,
520+ ] ;
521+
522+ if ( nextSteps . length ) {
523+ note ( nextSteps . map ( ( step ) => color . reset ( step ) ) . join ( '\n' ) , 'Next steps' ) ;
524+ }
525+
526+ outro ( 'All set, happy coding!' ) ;
527+ }
528+
491529export async function create ( {
492530 name,
493531 root,
@@ -592,6 +630,35 @@ export async function create({
592630 }
593631
594632 const templateName = await getTemplateName ( argv ) ;
633+
634+ const srcFolder = path . join ( root , `template-${ templateName } ` ) ;
635+
636+ // Handle npm template: only when the local template doesn't exist
637+ // and the template input looks like an npm package
638+ if (
639+ typeof argv . template === 'string' &&
640+ isNpmTemplate ( argv . template ) &&
641+ ! fs . existsSync ( srcFolder )
642+ ) {
643+ const templateVersion = argv . templateVersion ?? argv [ 'template-version' ] ;
644+ const templatePath = resolveCustomTemplate ( argv . template , templateVersion , {
645+ cacheDir : root ,
646+ } ) ;
647+
648+ // Copy npm template directly to distFolder
649+ copyFolder ( {
650+ from : templatePath ,
651+ to : distFolder ,
652+ version,
653+ packageName,
654+ templateParameters,
655+ skipFiles,
656+ } ) ;
657+
658+ logNextStepsAndOutro ( noteInformation , targetDir , packageManager ) ;
659+ return ;
660+ }
661+
595662 const tools = await getTools ( argv , extraTools , templateName ) ;
596663 const skills = await getSkills (
597664 argv ,
@@ -601,7 +668,6 @@ export async function create({
601668 multiselect ,
602669 ) ;
603670
604- const srcFolder = path . join ( root , `template-${ templateName } ` ) ;
605671 const commonFolder = path . join ( root , 'template-common' ) ;
606672
607673 if ( ! fs . existsSync ( srcFolder ) ) {
@@ -734,20 +800,7 @@ export async function create({
734800 ) ;
735801 }
736802
737- const nextSteps = noteInformation
738- ? noteInformation
739- : [
740- `1. ${ color . cyan ( `cd ${ targetDir } ` ) } ` ,
741- `2. ${ color . cyan ( 'git init' ) } ${ color . dim ( '(optional)' ) } ` ,
742- `3. ${ color . cyan ( `${ packageManager } install` ) } ` ,
743- `4. ${ color . cyan ( `${ packageManager } run dev` ) } ` ,
744- ] ;
745-
746- if ( nextSteps . length ) {
747- note ( nextSteps . map ( ( step ) => color . reset ( step ) ) . join ( '\n' ) , 'Next steps' ) ;
748- }
749-
750- outro ( 'All set, happy coding!' ) ;
803+ logNextStepsAndOutro ( noteInformation , targetDir , packageManager ) ;
751804}
752805
753806function sortObjectKeys ( obj : Record < string , unknown > ) {
0 commit comments