@@ -7,54 +7,217 @@ Object.defineProperty(exports, "__esModule", { value: true });
77 * Use of this source code is governed by an MIT-style license that can be
88 * found in the LICENSE file at https://angular.io/license
99 */
10- const core_1 = require ( "@angular-devkit/core" ) ;
10+ const path = require ( "path" ) ;
11+ const semver = require ( "semver" ) ;
1112const schematic_command_1 = require ( "../models/schematic-command" ) ;
1213const find_up_1 = require ( "../utilities/find-up" ) ;
1314const package_manager_1 = require ( "../utilities/package-manager" ) ;
15+ const package_metadata_1 = require ( "../utilities/package-metadata" ) ;
16+ const package_tree_1 = require ( "../utilities/package-tree" ) ;
17+ const npa = require ( 'npm-package-arg' ) ;
18+ const oldConfigFileNames = [
19+ '.angular-cli.json' ,
20+ 'angular-cli.json' ,
21+ ] ;
1422class UpdateCommand extends schematic_command_1 . SchematicCommand {
1523 constructor ( ) {
1624 super ( ...arguments ) ;
1725 this . allowMissingWorkspace = true ;
18- this . collectionName = '@schematics/update' ;
19- this . schematicName = 'update' ;
2026 }
21- async parseArguments ( schematicOptions , schema ) {
22- const args = await super . parseArguments ( schematicOptions , schema ) ;
23- const maybeArgsLeftovers = args [ '--' ] ;
24- if ( maybeArgsLeftovers
25- && maybeArgsLeftovers . length == 1
26- && maybeArgsLeftovers [ 0 ] == '@angular/cli'
27- && args . migrateOnly === undefined
28- && args . from === undefined ) {
29- // Check for a 1.7 angular-cli.json file.
30- const oldConfigFileNames = [
31- core_1 . normalize ( '.angular-cli.json' ) ,
32- core_1 . normalize ( 'angular-cli.json' ) ,
33- ] ;
34- const oldConfigFilePath = find_up_1 . findUp ( oldConfigFileNames , process . cwd ( ) )
35- || find_up_1 . findUp ( oldConfigFileNames , __dirname ) ;
36- if ( oldConfigFilePath ) {
37- args . migrateOnly = true ;
38- args . from = '1.0.0' ;
39- }
40- }
41- // Move `--` to packages.
42- if ( args . packages == undefined && args [ '--' ] ) {
43- args . packages = args [ '--' ] ;
44- delete args [ '--' ] ;
45- }
46- return args ;
27+ async parseArguments ( _schematicOptions , _schema ) {
28+ return { } ;
4729 }
30+ // tslint:disable-next-line:no-big-function
4831 async run ( options ) {
32+ const packages = [ ] ;
33+ for ( const request of options [ '--' ] || [ ] ) {
34+ try {
35+ const packageIdentifier = npa ( request ) ;
36+ // only registry identifiers are supported
37+ if ( ! packageIdentifier . registry ) {
38+ this . logger . error ( `Package '${ request } ' is not a registry package identifer.` ) ;
39+ return 1 ;
40+ }
41+ if ( packages . some ( v => v . name === packageIdentifier . name ) ) {
42+ this . logger . error ( `Duplicate package '${ packageIdentifier . name } ' specified.` ) ;
43+ return 1 ;
44+ }
45+ // If next option is used and no specifier supplied, use next tag
46+ if ( options . next && ! packageIdentifier . rawSpec ) {
47+ packageIdentifier . fetchSpec = 'next' ;
48+ }
49+ packages . push ( packageIdentifier ) ;
50+ }
51+ catch ( e ) {
52+ this . logger . error ( e . message ) ;
53+ return 1 ;
54+ }
55+ }
56+ if ( options . all && packages . length > 0 ) {
57+ this . logger . error ( 'Cannot specify packages when using the "all" option.' ) ;
58+ return 1 ;
59+ }
60+ else if ( options . all && options . migrateOnly ) {
61+ this . logger . error ( 'Cannot use "all" option with "migrate-only" option.' ) ;
62+ return 1 ;
63+ }
64+ else if ( ! options . migrateOnly && ( options . from || options . to ) ) {
65+ this . logger . error ( 'Can only use "from" or "to" options with "migrate-only" option.' ) ;
66+ return 1 ;
67+ }
4968 const packageManager = package_manager_1 . getPackageManager ( this . workspace . root ) ;
69+ this . logger . info ( `Using package manager: '${ packageManager } '` ) ;
70+ // Special handling for Angular CLI 1.x migrations
71+ if ( options . migrateOnly === undefined && options . from === undefined ) {
72+ if ( ! options . all && packages . length === 1 && packages [ 0 ] . name === '@angular/cli' ) {
73+ const oldConfigFilePath = find_up_1 . findUp ( oldConfigFileNames , process . cwd ( ) ) ;
74+ if ( oldConfigFilePath ) {
75+ options . migrateOnly = true ;
76+ options . from = '1.0.0' ;
77+ }
78+ }
79+ }
80+ this . logger . info ( 'Collecting installed dependencies...' ) ;
81+ const packageTree = await package_tree_1 . readPackageTree ( this . workspace . root ) ;
82+ const rootDependencies = package_tree_1 . findNodeDependencies ( packageTree ) ;
83+ this . logger . info ( `Found ${ Object . keys ( rootDependencies ) . length } dependencies.` ) ;
84+ if ( options . all || packages . length === 0 ) {
85+ // Either update all packages or show status
86+ return this . runSchematic ( {
87+ collectionName : '@schematics/update' ,
88+ schematicName : 'update' ,
89+ dryRun : ! ! options . dryRun ,
90+ showNothingDone : false ,
91+ additionalOptions : {
92+ force : options . force || false ,
93+ next : options . next || false ,
94+ packageManager,
95+ packages : options . all ? Object . keys ( rootDependencies ) : [ ] ,
96+ } ,
97+ } ) ;
98+ }
99+ if ( options . migrateOnly ) {
100+ if ( ! options . from ) {
101+ this . logger . error ( '"from" option is required when using the "migrate-only" option.' ) ;
102+ return 1 ;
103+ }
104+ else if ( packages . length !== 1 ) {
105+ this . logger . error ( 'A single package must be specified when using the "migrate-only" option.' ) ;
106+ return 1 ;
107+ }
108+ if ( options . next ) {
109+ this . logger . warn ( '"next" option has no effect when using "migrate-only" option.' ) ;
110+ }
111+ const packageName = packages [ 0 ] . name ;
112+ let packageNode = rootDependencies [ packageName ] ;
113+ if ( typeof packageNode === 'string' ) {
114+ this . logger . error ( 'Package found in package.json but is not installed.' ) ;
115+ return 1 ;
116+ }
117+ else if ( ! packageNode ) {
118+ // Allow running migrations on transitively installed dependencies
119+ // There can technically be nested multiple versions
120+ // TODO: If multiple, this should find all versions and ask which one to use
121+ const child = packageTree . children . find ( c => c . name === packageName ) ;
122+ if ( child ) {
123+ // A link represents a symlinked package so use the actual in this case
124+ packageNode = child . isLink ? child . target : child ;
125+ }
126+ if ( ! packageNode ) {
127+ this . logger . error ( 'Package is not installed.' ) ;
128+ return 1 ;
129+ }
130+ }
131+ const updateMetadata = packageNode . package [ 'ng-update' ] ;
132+ let migrations = updateMetadata && updateMetadata . migrations ;
133+ if ( migrations === undefined ) {
134+ this . logger . error ( 'Package does not provide migrations.' ) ;
135+ return 1 ;
136+ }
137+ else if ( typeof migrations !== 'string' ) {
138+ this . logger . error ( 'Package contains a malformed migrations field.' ) ;
139+ return 1 ;
140+ }
141+ // if not non-relative, add package name
142+ if ( migrations . startsWith ( '.' ) || migrations . startsWith ( '/' ) ) {
143+ migrations = path . join ( packageName , migrations ) ;
144+ }
145+ return this . runSchematic ( {
146+ collectionName : '@schematics/update' ,
147+ schematicName : 'migrate' ,
148+ dryRun : ! ! options . dryRun ,
149+ force : false ,
150+ showNothingDone : false ,
151+ additionalOptions : {
152+ package : packageName ,
153+ collection : migrations ,
154+ from : options . from ,
155+ to : options . to || packageNode . package . version ,
156+ } ,
157+ } ) ;
158+ }
159+ const requests = [ ] ;
160+ // Validate packages actually are part of the workspace
161+ for ( const pkg of packages ) {
162+ const node = rootDependencies [ pkg . name ] ;
163+ if ( ! node ) {
164+ this . logger . error ( `Package '${ pkg . name } ' is not a dependency.` ) ;
165+ return 1 ;
166+ }
167+ // If a specific version is requested and matches the installed version, skip.
168+ if ( pkg . type === 'version' &&
169+ typeof node === 'object' &&
170+ node . package . version === pkg . fetchSpec ) {
171+ this . logger . info ( `Package '${ pkg . name } ' is already at '${ pkg . fetchSpec } '.` ) ;
172+ continue ;
173+ }
174+ requests . push ( pkg ) ;
175+ }
176+ if ( requests . length === 0 ) {
177+ return 0 ;
178+ }
179+ this . logger . info ( 'Fetching dependency metadata from registry...' ) ;
180+ for ( const requestIdentifier of requests ) {
181+ let metadata ;
182+ try {
183+ // Metadata requests are internally cached; multiple requests for same name
184+ // does not result in additional network traffic
185+ metadata = await package_metadata_1 . fetchPackageMetadata ( requestIdentifier . name , this . logger ) ;
186+ }
187+ catch ( e ) {
188+ this . logger . error ( `Error fetching metadata for '${ requestIdentifier . name } ': ` + e . message ) ;
189+ return 1 ;
190+ }
191+ // Try to find a package version based on the user requested package specifier
192+ // registry specifier types are either version, range, or tag
193+ let manifest ;
194+ if ( requestIdentifier . type === 'version' ) {
195+ manifest = metadata . versions . get ( requestIdentifier . fetchSpec ) ;
196+ }
197+ else if ( requestIdentifier . type === 'range' ) {
198+ const maxVersion = semver . maxSatisfying ( Array . from ( metadata . versions . keys ( ) ) , requestIdentifier . fetchSpec ) ;
199+ if ( maxVersion ) {
200+ manifest = metadata . versions . get ( maxVersion ) ;
201+ }
202+ }
203+ else if ( requestIdentifier . type === 'tag' ) {
204+ manifest = metadata . tags [ requestIdentifier . fetchSpec ] ;
205+ }
206+ if ( ! manifest ) {
207+ this . logger . error ( `Package specified by '${ requestIdentifier . raw } ' does not exist within the registry.` ) ;
208+ return 1 ;
209+ }
210+ }
50211 return this . runSchematic ( {
51- collectionName : this . collectionName ,
52- schematicName : this . schematicName ,
53- schematicOptions : options [ '--' ] ,
212+ collectionName : '@schematics/update' ,
213+ schematicName : 'update' ,
54214 dryRun : ! ! options . dryRun ,
55- force : false ,
56215 showNothingDone : false ,
57- additionalOptions : { packageManager } ,
216+ additionalOptions : {
217+ force : options . force || false ,
218+ packageManager,
219+ packages : requests . map ( p => p . toString ( ) ) ,
220+ } ,
58221 } ) ;
59222 }
60223}
0 commit comments