@@ -98,17 +98,45 @@ export class Merger {
9898 }
9999 }
100100
101+ private loadManifestJs ( ) : any {
102+ trace . debug ( "merger.manifestJs" ) ;
103+
104+ // build environment object from --env parameter
105+ const env = { } ;
106+ ( this . settings . env || [ ] ) . forEach ( kvp => {
107+ const [ key , ...value ] = kvp . split ( '=' ) ;
108+ env [ key ] = value . join ( '=' ) ;
109+ } ) ;
110+
111+ const fullJsFile = path . resolve ( this . settings . manifestJs ) ;
112+ const manifestModuleFn = require ( fullJsFile ) ;
113+ if ( ! manifestModuleFn || typeof manifestModuleFn != "function" ) {
114+ throw new Error ( `Missing export function from manifest-js file ${ fullJsFile } ` )
115+ }
116+ const manifestData = manifestModuleFn ( env ) ;
117+ if ( ! manifestData ) {
118+ throw new Error ( `The export function from manifest-js file ${ fullJsFile } must return the manifest object` )
119+ }
120+ return manifestData ;
121+ }
122+
101123 /**
102124 * Finds all manifests and merges them into two JS Objects: vsoManifest and vsixManifest
103125 * @return Q.Promise<SplitManifest> An object containing the two manifests
104126 */
105- public merge ( ) : Promise < VsixComponents > {
127+ public async merge ( ) : Promise < VsixComponents > {
106128 trace . debug ( "merger.merge" ) ;
107129
108- return this . gatherManifests ( ) . then ( files => {
109- let overridesProvided = false ;
110- const manifestPromises : Promise < any > [ ] = [ ] ;
111- files . forEach ( file => {
130+ let overridesProvided = false ;
131+ const manifestPromises : Promise < any > [ ] = [ ] ;
132+
133+ if ( this . settings . manifestJs ) {
134+ const result = this . loadManifestJs ( ) ;
135+ result . __origin = this . settings . manifestJs ; // save the origin in order to resolve relative paths later.
136+ manifestPromises . push ( Promise . resolve ( result ) ) ;
137+ } else {
138+ let manifestFiles = await this . gatherManifests ( ) ;
139+ manifestFiles . forEach ( file => {
112140 manifestPromises . push (
113141 promisify ( readFile ) ( file , "utf8" ) . then ( data => {
114142 const jsonData = data . replace ( / ^ \uFEFF / , "" ) ;
@@ -129,141 +157,141 @@ export class Merger {
129157 if ( this . settings . overrides ) {
130158 overridesProvided = true ;
131159 manifestPromises . push ( Promise . resolve ( this . settings . overrides ) ) ;
132- }
160+ }
161+ }
133162
134- return Promise . all ( manifestPromises ) . then ( partials => {
135- // Determine the targets so we can construct the builders
136- let targets : TargetDeclaration [ ] = [ ] ;
137- partials . forEach ( partial => {
138- if ( _ . isArray ( partial [ "targets" ] ) ) {
139- targets = targets . concat ( partial [ "targets" ] ) ;
140- }
141- } ) ;
142- this . extensionComposer = ComposerFactory . GetComposer ( this . settings , targets ) ;
143- this . manifestBuilders = this . extensionComposer . getBuilders ( ) ;
144- let updateVersionPromise = Promise . resolve < void > ( null ) ;
145- partials . forEach ( ( partial , partialIndex ) => {
146- // Rev the version if necessary
147- if ( this . settings . revVersion ) {
148- if ( partial [ "version" ] && partial . __origin ) {
149- try {
150- const parsedVersion = version . DynamicVersion . parse ( partial [ "version" ] ) ;
151- const newVersion = version . DynamicVersion . increase ( parsedVersion ) ;
152- const newVersionString = newVersion . toString ( ) ;
153- partial [ "version" ] = newVersionString ;
154-
155- updateVersionPromise = promisify ( readFile ) ( partial . __origin , "utf8" ) . then ( versionPartial => {
156- try {
157- let newPartial : any ;
158- if ( this . settings . json5 ) {
159- const parsed = jju . parse ( versionPartial ) ;
160- parsed [ "version" ] = newVersionString ;
161- newPartial = jju . update ( versionPartial , parsed ) ;
162- } else {
163+ return Promise . all ( manifestPromises ) . then ( partials => {
164+ // Determine the targets so we can construct the builders
165+ let targets : TargetDeclaration [ ] = [ ] ;
166+ partials . forEach ( partial => {
167+ if ( _ . isArray ( partial [ "targets" ] ) ) {
168+ targets = targets . concat ( partial [ "targets" ] ) ;
169+ }
170+ } ) ;
171+ this . extensionComposer = ComposerFactory . GetComposer ( this . settings , targets ) ;
172+ this . manifestBuilders = this . extensionComposer . getBuilders ( ) ;
173+ let updateVersionPromise = Promise . resolve < void > ( null ) ;
174+ partials . forEach ( ( partial , partialIndex ) => {
175+ // Rev the version if necessary
176+ if ( this . settings . revVersion ) {
177+ if ( partial [ "version" ] && partial . __origin ) {
178+ try {
179+ const parsedVersion = version . DynamicVersion . parse ( partial [ "version" ] ) ;
180+ const newVersion = version . DynamicVersion . increase ( parsedVersion ) ;
181+ const newVersionString = newVersion . toString ( ) ;
182+ partial [ "version" ] = newVersionString ;
183+
184+ updateVersionPromise = promisify ( readFile ) ( partial . __origin , "utf8" ) . then ( versionPartial => {
185+ try {
186+ let newPartial : any ;
187+ if ( this . settings . json5 ) {
188+ const parsed = jju . parse ( versionPartial ) ;
189+ parsed [ "version" ] = newVersionString ;
190+ newPartial = jju . update ( versionPartial , parsed ) ;
191+ } else {
163192 newPartial = jsonInPlace ( versionPartial ) . set ( "version" , newVersionString ) . toString ( ) ;
164- }
165- return promisify ( writeFile ) ( partial . __origin , newPartial ) ;
166- } catch ( e ) {
167- trace . warn (
168- "Failed to lex partial as JSON to update the version. Skipping version rev..." ,
169- ) ;
170193 }
171- } ) ;
172- } catch ( e ) {
173- trace . warn (
174- "Could not parse %s as a version (e.g. major.minor.patch). Skipping version rev..." ,
175- partial [ "version" ] ,
176- ) ;
177- }
194+ return promisify ( writeFile ) ( partial . __origin , newPartial ) ;
195+ } catch ( e ) {
196+ trace . warn (
197+ "Failed to lex partial as JSON to update the version. Skipping version rev..." ,
198+ ) ;
199+ }
200+ } ) ;
201+ } catch ( e ) {
202+ trace . warn (
203+ "Could not parse %s as a version (e.g. major.minor.patch). Skipping version rev..." ,
204+ partial [ "version" ] ,
205+ ) ;
178206 }
179207 }
208+ }
180209
181- // Transform asset paths to be relative to the root of all manifests, verify assets
182- if ( _ . isArray ( partial [ "files" ] ) ) {
183- ( < Array < FileDeclaration > > partial [ "files" ] ) . forEach ( asset => {
184- const keys = Object . keys ( asset ) ;
185- if ( keys . indexOf ( "path" ) < 0 ) {
186- throw new Error ( "Files must have an absolute or relative (to the manifest) path." ) ;
187- }
188- let absolutePath ;
189- if ( path . isAbsolute ( asset . path ) ) {
190- absolutePath = asset . path ;
191- } else {
192- absolutePath = path . join ( path . dirname ( partial . __origin ) , asset . path ) ;
193- }
194- asset . path = path . relative ( this . settings . root , absolutePath ) ;
195- } ) ;
196- }
197- // Transform icon paths as above
198- if ( _ . isObject ( partial [ "icons" ] ) ) {
199- const icons = partial [ "icons" ] ;
200- Object . keys ( icons ) . forEach ( ( iconKind : string ) => {
201- const absolutePath = path . join ( path . dirname ( partial . __origin ) , icons [ iconKind ] ) ;
202- icons [ iconKind ] = path . relative ( this . settings . root , absolutePath ) ;
203- } ) ;
204- }
210+ // Transform asset paths to be relative to the root of all manifests, verify assets
211+ if ( _ . isArray ( partial [ "files" ] ) ) {
212+ ( < Array < FileDeclaration > > partial [ "files" ] ) . forEach ( asset => {
213+ const keys = Object . keys ( asset ) ;
214+ if ( keys . indexOf ( "path" ) < 0 ) {
215+ throw new Error ( "Files must have an absolute or relative (to the manifest) path." ) ;
216+ }
217+ let absolutePath ;
218+ if ( path . isAbsolute ( asset . path ) ) {
219+ absolutePath = asset . path ;
220+ } else {
221+ absolutePath = path . join ( path . dirname ( partial . __origin ) , asset . path ) ;
222+ }
223+ asset . path = path . relative ( this . settings . root , absolutePath ) ;
224+ } ) ;
225+ }
226+ // Transform icon paths as above
227+ if ( _ . isObject ( partial [ "icons" ] ) ) {
228+ const icons = partial [ "icons" ] ;
229+ Object . keys ( icons ) . forEach ( ( iconKind : string ) => {
230+ const absolutePath = path . join ( path . dirname ( partial . __origin ) , icons [ iconKind ] ) ;
231+ icons [ iconKind ] = path . relative ( this . settings . root , absolutePath ) ;
232+ } ) ;
233+ }
205234
206- // Expand any directories listed in the files array
207- if ( _ . isArray ( partial [ "files" ] ) ) {
208- for ( let i = partial [ "files" ] . length - 1 ; i >= 0 ; -- i ) {
209- const fileDecl : FileDeclaration = partial [ "files" ] [ i ] ;
210- const fsPath = path . join ( this . settings . root , fileDecl . path ) ;
211- if ( fs . lstatSync ( fsPath ) . isDirectory ( ) ) {
212- Array . prototype . splice . apply (
213- partial [ "files" ] ,
214- ( < any [ ] > [ i , 1 ] ) . concat ( this . pathToFileDeclarations ( fsPath , this . settings . root , fileDecl ) ) ,
215- ) ;
216- }
235+ // Expand any directories listed in the files array
236+ if ( _ . isArray ( partial [ "files" ] ) ) {
237+ for ( let i = partial [ "files" ] . length - 1 ; i >= 0 ; -- i ) {
238+ const fileDecl : FileDeclaration = partial [ "files" ] [ i ] ;
239+ const fsPath = path . join ( this . settings . root , fileDecl . path ) ;
240+ if ( fs . lstatSync ( fsPath ) . isDirectory ( ) ) {
241+ Array . prototype . splice . apply (
242+ partial [ "files" ] ,
243+ ( < any [ ] > [ i , 1 ] ) . concat ( this . pathToFileDeclarations ( fsPath , this . settings . root , fileDecl ) ) ,
244+ ) ;
217245 }
218246 }
247+ }
219248
220- // Process each key by each manifest builder.
221- Object . keys ( partial ) . forEach ( key => {
222- const isOverridePartial = partials . length - 1 === partialIndex && overridesProvided ;
223- if ( partial [ key ] !== undefined && ( partial [ key ] !== null || isOverridePartial ) ) {
224- // Notify each manifest builder of the key/value pair
225- this . manifestBuilders . forEach ( builder => {
226- builder . processKey ( key , partial [ key ] , isOverridePartial ) ;
227- } ) ;
228- }
229- } ) ;
249+ // Process each key by each manifest builder.
250+ Object . keys ( partial ) . forEach ( key => {
251+ const isOverridePartial = partials . length - 1 === partialIndex && overridesProvided ;
252+ if ( partial [ key ] !== undefined && ( partial [ key ] !== null || isOverridePartial ) ) {
253+ // Notify each manifest builder of the key/value pair
254+ this . manifestBuilders . forEach ( builder => {
255+ builder . processKey ( key , partial [ key ] , isOverridePartial ) ;
256+ } ) ;
257+ }
230258 } ) ;
259+ } ) ;
231260
232- // Generate localization resources
233- const locPrepper = new loc . LocPrep . LocKeyGenerator ( this . manifestBuilders ) ;
234- const resources = locPrepper . generateLocalizationKeys ( ) ;
261+ // Generate localization resources
262+ const locPrepper = new loc . LocPrep . LocKeyGenerator ( this . manifestBuilders ) ;
263+ const resources = locPrepper . generateLocalizationKeys ( ) ;
235264
236- // Build up resource data by reading the translations from disk
237- return this . buildResourcesData ( ) . then ( resourceData => {
238- if ( resourceData ) {
239- resourceData [ "defaults" ] = resources . combined ;
240- }
265+ // Build up resource data by reading the translations from disk
266+ return this . buildResourcesData ( ) . then ( resourceData => {
267+ if ( resourceData ) {
268+ resourceData [ "defaults" ] = resources . combined ;
269+ }
241270
242- // Build up a master file list
243- const packageFiles : PackageFiles = { } ;
244- this . manifestBuilders . forEach ( builder => {
245- _ . assign ( packageFiles , builder . files ) ;
246- } ) ;
271+ // Build up a master file list
272+ const packageFiles : PackageFiles = { } ;
273+ this . manifestBuilders . forEach ( builder => {
274+ _ . assign ( packageFiles , builder . files ) ;
275+ } ) ;
247276
248- const components : VsixComponents = { builders : this . manifestBuilders , resources : resources } ;
249-
250- // Finalize each builder
251- return Promise . all (
252- [ updateVersionPromise ] . concat (
253- this . manifestBuilders . map ( b => b . finalize ( packageFiles , resourceData , this . manifestBuilders ) ) ,
254- ) ,
255- ) . then ( ( ) => {
256- // const the composer do validation
257- return this . extensionComposer . validate ( components ) . then ( validationResult => {
258- if ( validationResult . length === 0 || this . settings . bypassValidation ) {
259- return components ;
260- } else {
261- throw new Error (
262- "There were errors with your extension. Address the following and re-run the tool.\n" +
263- validationResult ,
264- ) ;
265- }
266- } ) ;
277+ const components : VsixComponents = { builders : this . manifestBuilders , resources : resources } ;
278+
279+ // Finalize each builder
280+ return Promise . all (
281+ [ updateVersionPromise ] . concat (
282+ this . manifestBuilders . map ( b => b . finalize ( packageFiles , resourceData , this . manifestBuilders ) ) ,
283+ ) ,
284+ ) . then ( ( ) => {
285+ // const the composer do validation
286+ return this . extensionComposer . validate ( components ) . then ( validationResult => {
287+ if ( validationResult . length === 0 || this . settings . bypassValidation ) {
288+ return components ;
289+ } else {
290+ throw new Error (
291+ "There were errors with your extension. Address the following and re-run the tool.\n" +
292+ validationResult ,
293+ ) ;
294+ }
267295 } ) ;
268296 } ) ;
269297 } ) ;
0 commit comments