@@ -6,10 +6,12 @@ import { createCommandRunner } from "@microsoft/powerplatform-cli-wrapper";
66import find from "find" ;
77import path from "path" ;
88import { rm } from "fs/promises" ;
9+ import { info } from "fancy-log" ;
910
1011import { createRequire } from "node:module" ;
1112const require = createRequire ( import . meta. url ) ;
1213const tar = require ( "tar" ) ;
14+ const AdmZip = require ( "adm-zip" ) ;
1315
1416const outDir = 'out' ;
1517const stagingDir = `${ outDir } /staging` ;
@@ -266,4 +268,73 @@ async function generateAllStages(manifest, taskVersion, manifestVersion) {
266268 outputPath : packagesDir ,
267269 } ) ;
268270 }
271+
272+ // Fix [Content_Types].xml in all generated VSIX files to add Override entries for
273+ // extension-less files inside the pac CLI directories.
274+ //
275+ // Background: VSIX files are OPC (Open Packaging Convention) containers. Every part
276+ // (file) inside must be covered by [Content_Types].xml — either via a <Default> entry
277+ // keyed on file extension, or via an <Override> entry keyed on the full part path.
278+ //
279+ // 'tfx extension create' only generates <Default> entries keyed on extensions, so any
280+ // extension-less file gets no entry. When the VSIX is processed by OPC-aware tools
281+ // (e.g. the AzDO Marketplace ingestion pipeline), any part not registered in
282+ // [Content_Types].xml is silently dropped from the output package.
283+ //
284+ // Known extension-less files in bin/pac* that must be preserved:
285+ // bin/pac_linux/tools/pac — Linux PAC CLI executable
286+ // bin/pac*/tools/.playwright/.../xdg-open — used by Playwright for browser auth flows
287+ // bin/pac*/tools/.playwright/.../LICENSE|NOTICE — license text (harmless but included for completeness)
288+ //
289+ // Note: _rels/.rels files are already excluded during copyDependencies() so they never
290+ // reach the VSIX and do not need to be handled here.
291+ async function fixVsixContentTypes ( ) {
292+ // Match any extension-less file inside the pac or pac_linux tool directories.
293+ const PAC_DIR_PATTERN = / ^ t a s k s \/ t o o l - i n s t a l l e r \/ t o o l - i n s t a l l e r - v 2 \/ b i n \/ p a c [ ^ / ] * \/ / ;
294+
295+ const vsixFiles = await findFiles ( / \. v s i x $ / , packagesDir ) ;
296+ for ( const vsixPath of vsixFiles ) {
297+ const zip = new AdmZip ( vsixPath ) ;
298+ const ctEntry = zip . getEntry ( '[Content_Types].xml' ) ;
299+ if ( ! ctEntry ) {
300+ throw new Error ( `[Content_Types].xml not found in ${ vsixPath } ` ) ;
301+ }
302+
303+ const ctXml = ctEntry . getData ( ) . toString ( 'utf8' ) ;
304+
305+ // Collect part paths already covered by <Override> entries (strip leading '/').
306+ const overridePaths = new Set (
307+ [ ...ctXml . matchAll ( / P a r t N a m e = " ( [ ^ " ] + ) " / g) ] . map ( m => m [ 1 ] . replace ( / ^ \/ / , '' ) )
308+ ) ;
309+
310+ // Find all extension-less files inside the pac directories that are missing an Override.
311+ const newOverrides = [ ] ;
312+ for ( const entry of zip . getEntries ( ) ) {
313+ if ( entry . isDirectory ) continue ;
314+ const entryName = entry . entryName . replace ( / \\ / g, '/' ) ;
315+ const hasNoExtension = path . extname ( entryName ) === '' ;
316+ if ( PAC_DIR_PATTERN . test ( entryName ) && hasNoExtension && ! overridePaths . has ( entryName ) ) {
317+ newOverrides . push (
318+ ` <Override ContentType="application/octet-stream" PartName="/${ entryName } "/>`
319+ ) ;
320+ }
321+ }
322+
323+ if ( newOverrides . length > 0 ) {
324+ const modified = ctXml . replace (
325+ '</Types>' ,
326+ newOverrides . join ( '\n' ) + '\n</Types>'
327+ ) ;
328+ zip . updateFile ( '[Content_Types].xml' , Buffer . from ( modified , 'utf8' ) ) ;
329+ zip . writeZip ( vsixPath ) ;
330+ info (
331+ `Fixed [Content_Types].xml in ${ path . basename ( vsixPath ) } : ` +
332+ `added ${ newOverrides . length } Override entr${ newOverrides . length === 1 ? 'y' : 'ies' } ` +
333+ `(${ newOverrides . map ( o => / P a r t N a m e = " \/ ( [ ^ " ] + ) " / . exec ( o ) ?. [ 1 ] ) . join ( ', ' ) } )`
334+ ) ;
335+ }
336+ }
337+ }
338+
339+ await fixVsixContentTypes ( ) ;
269340}
0 commit comments