4
4
* Licensed under the BSD 3-Clause license.
5
5
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
6
*/
7
+ import * as fs from 'fs' ;
8
+ import * as path from 'path' ;
7
9
import { Messages } from '@salesforce/core' ;
8
10
import { SfCommand , Flags } from '@salesforce/sf-plugins-core' ;
9
11
import { SourceTracking } from '@salesforce/source-tracking' ;
10
- import { ForceIgnore } from '@salesforce/source-deploy-retrieve' ;
12
+ import { ForceIgnore , MetadataResolver , NodeFSTreeContainer , RegistryAccess } from '@salesforce/source-deploy-retrieve' ;
11
13
import { buildComponentSet } from '../../../utils/deploy' ;
12
- import { PreviewResult , printTables , compileResults , getConflictFiles } from '../../../utils/previewOutput' ;
14
+ import {
15
+ PreviewResult ,
16
+ printTables ,
17
+ compileResults ,
18
+ getConflictFiles ,
19
+ printIgnoredTable ,
20
+ PreviewFile ,
21
+ } from '../../../utils/previewOutput' ;
13
22
14
23
Messages . importMessagesDirectory ( __dirname ) ;
15
24
const messages = Messages . loadMessages ( '@salesforce/plugin-deploy-retrieve' , 'deploy.metadata.preview' ) ;
@@ -32,6 +41,11 @@ export default class DeployMetadataPreview extends SfCommand<PreviewResult> {
32
41
description : messages . getMessage ( 'flags.ignore-conflicts.description' ) ,
33
42
default : false ,
34
43
} ) ,
44
+ 'only-ignored' : Flags . boolean ( {
45
+ char : 'i' ,
46
+ summary : messages . getMessage ( 'flags.only-ignored.summary' ) ,
47
+ exclusive : [ 'manifest' , 'metadata' , 'ignore-conflicts' ] ,
48
+ } ) ,
35
49
manifest : Flags . file ( {
36
50
char : 'x' ,
37
51
description : messages . getMessage ( 'flags.manifest.description' ) ,
@@ -60,9 +74,17 @@ export default class DeployMetadataPreview extends SfCommand<PreviewResult> {
60
74
} ) ,
61
75
} ;
62
76
77
+ public forceIgnore ! : ForceIgnore ;
78
+
63
79
public async run ( ) : Promise < PreviewResult > {
64
80
const { flags } = await this . parse ( DeployMetadataPreview ) ;
65
81
const deploySpecified = [ flags . manifest , flags . metadata , flags [ 'source-dir' ] ] . some ( ( f ) => f !== undefined ) ;
82
+ const defaultPackagePath = this . project . getDefaultPackage ( ) . path ;
83
+ this . forceIgnore = ForceIgnore . findAndCreate ( defaultPackagePath ) ;
84
+
85
+ if ( flags [ 'only-ignored' ] ) {
86
+ return this . calculateAndPrintForceIgnoredFiles ( { sourceDir : flags [ 'source-dir' ] , defaultPackagePath } ) ;
87
+ }
66
88
67
89
// we'll need STL both to check conflicts and to get the list of local changes if no flags are provided
68
90
const stl =
@@ -73,8 +95,6 @@ export default class DeployMetadataPreview extends SfCommand<PreviewResult> {
73
95
project : this . project ,
74
96
} ) ;
75
97
76
- const forceIgnore = ForceIgnore . findAndCreate ( this . project . getDefaultPackage ( ) . path ) ;
77
-
78
98
const [ componentSet , filesWithConflicts ] = await Promise . all ( [
79
99
buildComponentSet ( { ...flags , 'target-org' : flags [ 'target-org' ] . getUsername ( ) } , stl ) ,
80
100
getConflictFiles ( stl , flags [ 'ignore-conflicts' ] ) ,
@@ -84,7 +104,7 @@ export default class DeployMetadataPreview extends SfCommand<PreviewResult> {
84
104
componentSet,
85
105
projectPath : this . project . getPath ( ) ,
86
106
filesWithConflicts,
87
- forceIgnore,
107
+ forceIgnore : this . forceIgnore ,
88
108
baseOperation : 'deploy' ,
89
109
} ) ;
90
110
@@ -93,4 +113,49 @@ export default class DeployMetadataPreview extends SfCommand<PreviewResult> {
93
113
}
94
114
return output ;
95
115
}
116
+
117
+ private async calculateAndPrintForceIgnoredFiles ( options : {
118
+ sourceDir ?: string [ ] ;
119
+ defaultPackagePath : string ;
120
+ } ) : Promise < PreviewResult > {
121
+ // the third parameter makes the resolver use the default ForceIgnore entries, which will allow us to .getComponentsFromPath of a .forceignored path
122
+ const mdr = new MetadataResolver ( new RegistryAccess ( ) , new NodeFSTreeContainer ( ) , false ) ;
123
+
124
+ const ignoredFiles : PreviewFile [ ] = (
125
+ await Promise . all ( ( options . sourceDir ?? [ options . defaultPackagePath ] ) . map ( ( sp ) => this . statIgnored ( sp . trim ( ) ) ) )
126
+ )
127
+ . flat ( )
128
+ . map ( ( entry ) => {
129
+ try {
130
+ const component = mdr . getComponentsFromPath ( path . resolve ( entry ) ) [ 0 ] ;
131
+ return {
132
+ projectRelativePath : entry ,
133
+ fullName : component ?. fullName ,
134
+ type : component ?. type . name ,
135
+ ignored : true ,
136
+ conflict : false ,
137
+ } ;
138
+ } catch ( e ) {
139
+ // some file paths, such as aura/.eslintrc.json will cause issues when getComponentsFromPath(), so catch the error and continue without type information
140
+ return { projectRelativePath : entry , ignored : true , conflict : false } as PreviewFile ;
141
+ }
142
+ } ) ;
143
+ if ( ! this . jsonEnabled ( ) ) printIgnoredTable ( ignoredFiles , 'deploy' ) ;
144
+ return { ignored : ignoredFiles , conflicts : [ ] , toDeploy : [ ] , toDelete : [ ] , toRetrieve : [ ] } ;
145
+ }
146
+
147
+ // Stat the filepath. Test if a file, recurse if a directory.
148
+ private async statIgnored ( filepath : string ) : Promise < string [ ] > {
149
+ const stats = await fs . promises . stat ( filepath ) ;
150
+ if ( stats . isDirectory ( ) ) {
151
+ return ( await Promise . all ( await this . findIgnored ( filepath ) ) ) . flat ( ) ;
152
+ } else {
153
+ return this . forceIgnore . denies ( filepath ) ? [ filepath ] : [ ] ;
154
+ }
155
+ }
156
+
157
+ // Recursively search a directory for source files to test.
158
+ private async findIgnored ( dir : string ) : Promise < Array < Promise < string [ ] > > > {
159
+ return ( await fs . promises . readdir ( dir ) ) . map ( ( filename ) => this . statIgnored ( path . join ( dir , filename ) ) ) ;
160
+ }
96
161
}
0 commit comments