66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import assert from 'assert' ;
10- import * as child_process from 'child_process' ;
11- import * as fs from 'fs ' ;
12- import * as path from 'path' ;
13- import { fileURLToPath , pathToFileURL } from 'url' ;
9+ import assert from 'node: assert' ;
10+ import * as child_process from 'node: child_process' ;
11+ import { copyFile , readFile , rm , writeFile } from 'node:fs/promises ' ;
12+ import * as path from 'node: path' ;
13+ import { fileURLToPath , pathToFileURL } from 'node: url' ;
1414import build from './build.mjs' ;
1515import { packages } from './packages.mjs' ;
1616
17- export interface CreateOptions {
17+ export interface CreateOptions extends Record < string , unknown > {
1818 _ : string [ ] ;
1919}
2020
@@ -37,34 +37,52 @@ async function _exec(command: string, args: string[], opts: { cwd?: string }) {
3737}
3838
3939export default async function ( args : CreateOptions , cwd : string ) : Promise < number > {
40- const projectName = args . _ [ 0 ] ;
40+ const { _, ...otherArgOptions } = args ;
41+ const projectName = _ [ 0 ] ;
42+ assert ( projectName , 'Project name must be provided.' ) ;
43+
44+ const ngNewAdditionalOptions = Object . entries ( otherArgOptions ) . map (
45+ ( [ key , value ] ) => `--${ key } =${ value } ` ,
46+ ) ;
4147
4248 const oldCwd = process . cwd ( ) ;
4349 console . info ( 'Building...' ) ;
44- await build ( { local : true } ) ;
50+
51+ const buildResult = await build ( { local : true } ) ;
52+ const cliBuild = buildResult . find ( ( { name } ) => name === 'angular/cli' ) ;
53+
54+ assert ( cliBuild ) ;
4555
4656 process . chdir ( cwd ) ;
47- console . info ( 'Creating project...' ) ;
4857
49- assert ( projectName , 'Project name must be provided.' ) ;
58+ // The below is needed as NPX does not guarantee that the updated version is used unless the file name changes.
59+ const newTarballName = cliBuild . tarPath . replace ( '.tgz' , '-' + Date . now ( ) + '.tgz' ) ;
60+ await copyFile ( cliBuild . tarPath , newTarballName ) ;
5061
51- await _exec (
52- 'npx' ,
53- [
54- '--yes' ,
55- pathToFileURL ( path . join ( __dirname , '../dist/_angular_cli.tgz' ) ) . toString ( ) ,
56- 'new' ,
57- projectName ,
58- '--skip-install' ,
59- '--skip-git' ,
60- '--no-interactive' ,
61- ] ,
62- { cwd } ,
63- ) ;
62+ console . info ( 'Creating project...' ) ;
63+
64+ try {
65+ await _exec (
66+ 'npx' ,
67+ [
68+ '--yes' ,
69+ pathToFileURL ( newTarballName ) . toString ( ) ,
70+ 'new' ,
71+ projectName ,
72+ '--skip-install' ,
73+ '--skip-git' ,
74+ '--no-interactive' ,
75+ ...ngNewAdditionalOptions ,
76+ ] ,
77+ { cwd } ,
78+ ) ;
79+ } finally {
80+ await rm ( newTarballName , { maxRetries : 3 } ) ;
81+ }
6482
6583 console . info ( 'Updating package.json...' ) ;
6684 const packageJsonPath = path . join ( projectName , 'package.json' ) ;
67- const packageJson = JSON . parse ( fs . readFileSync ( packageJsonPath , 'utf-8' ) ) ;
85+ const packageJson = JSON . parse ( await readFile ( packageJsonPath , 'utf-8' ) ) ;
6886
6987 if ( ! packageJson [ 'dependencies' ] ) {
7088 packageJson [ 'dependencies' ] = { } ;
@@ -84,7 +102,7 @@ export default async function (args: CreateOptions, cwd: string): Promise<number
84102 }
85103 }
86104
87- fs . writeFileSync ( packageJsonPath , JSON . stringify ( packageJson , null , 2 ) , 'utf-8' ) ;
105+ await writeFile ( packageJsonPath , JSON . stringify ( packageJson , null , 2 ) , 'utf-8' ) ;
88106
89107 console . info ( 'Installing npm packages...' ) ;
90108 await _exec ( 'npm' , [ 'install' ] , { cwd : path . join ( cwd , projectName ) } ) ;
0 commit comments