import { core , SfdxCommand } from '@salesforce/command' ; import assert = require ( 'assert' ) ; import { join } from 'path' ; import { ClusterPackager } from '../../../lib/clusterPackager' ; import { ComponentGraph } from '../../../lib/componentGraph' ; import { FileWriter } from '../../../lib/fileWriter' ; import { Node , NodeGroup , ScalarNode } from '../../../lib/NodeDefs' ; import { Connection } from '@salesforce/core' ; core . Messages . importMessagesDirectory ( join ( __dirname ) ) ; const messages = core . Messages . loadMessages ( 'dependencies-cli' , 'components' ) ; export default class Analyze extends SfdxCommand { public static outputFolder = 'lib/' ; public static description = messages . getMessage ( 'commandDescription' ) ; public static examples = [ `sfdx org:dependencies:componentizer -u DevHub` ] ; public static args = [ { name : 'file' } ] ; protected static requiresUsername = true ; public static buildGraph ( results , connectAuras = false , forwards = true , backwards = false ) { const graph = new ComponentGraph ( ) ; for ( const edge of results ) { if ( edge . RefMetadataComponentName . startsWith ( '0' ) ) { continue ; } const srcId = edge . MetadataComponentId ; const srcName = Analyze . makeName ( edge . MetadataComponentNamespace , edge . MetadataComponentName ) ; const srcType = edge . MetadataComponentType ; const dstId = edge . RefMetadataComponentId ; const dstName = Analyze . makeName ( edge . RefMetadataComponentNamespace , edge . RefMetadataComponentName ) ; const dstType = edge . RefMetadataComponentType ; if ( edge . RefMetadataComponentNamespace ) { continue ; } const srcDetails = new Map < string , object > ( ) ; srcDetails . set ( 'name' , ( srcName as String ) ) ; srcDetails . set ( 'type' , ( srcType as String ) ) ; const srcNode = graph . getOrAddNode ( srcId , srcDetails ) ; const dstDetails = new Map < string , object > ( ) ; dstDetails . set ( 'name' , ( dstName as String ) ) ; dstDetails . set ( 'type' , ( dstType as String ) ) ; const dstNode = graph . getOrAddNode ( dstId , dstDetails ) ; if ( forwards ) { graph . addEdge ( srcNode , dstNode ) ; if ( connectAuras && srcType === 'AuraDefinition' && dstType === 'AuraDefinitionBundle' || srcType === 'FlexiPage' ) { graph . addEdge ( dstNode , srcNode ) ; } } if ( backwards ) { graph . addEdge ( dstNode , srcNode ) ; if ( connectAuras && srcType === 'AuraDefinition' && dstType === 'AuraDefinitionBundle' || srcType === 'FlexiPage' ) { graph . addEdge ( srcNode , dstNode ) ; } } } return graph ; } private static makeName ( ns , name ) { if ( ns ) { return ns + '.' + name ; } return name ; } private static nodeToDisplayName ( node ) { if ( node instanceof ScalarNode ) { return node . details . get ( 'name' ) + '(' + node . name + ')' ; } else { const ng = node as NodeGroup ; return Array . from ( ng . nodes ) . map ( n => Analyze . nodeToDisplayName ( n ) ) . join ( ', ' ) ; } } public async run ( ) { const anyConn = < Connection > this . org . getConnection ( ) ; anyConn . version = '45.0' ; const conn = anyConn . tooling ; const queryResults = await conn . query ( 'SELECT MetadataComponentId, MetadataComponentNamespace, ' + 'MetadataComponentName, MetadataComponentType, ' + 'RefMetadataComponentId, RefMetadataComponentNamespace, ' + 'RefMetadataComponentName, RefMetadataComponentType ' + 'FROM MetadataComponentDependency' ) ; const g = Analyze . buildGraph ( queryResults . records ) ; g . removeCycles ( ) ; const leafNodes = new Map < string , Node [ ] > ( ) ; for ( const node of g . nodes ) { const edges = new Set < Node > ( g . getEdges ( node ) ) ; if ( edges . size === 0 || ( edges . has ( node ) && edges . size === 1 ) ) { let type ; if ( node instanceof NodeGroup ) { type = 'Cluster' ; const xmlString = ClusterPackager . writeXMLNodeGroup ( ( node as NodeGroup ) ) ; const folder = Analyze . outputFolder + ( node as NodeGroup ) . name + '/' ; FileWriter . writeFile ( folder , 'package.xml' , xmlString ) ; } else { type = ( ( node . details . get ( 'type' ) ) as String ) . valueOf ( ) ; } let list = leafNodes . get ( type ) ; if ( ! list ) { assert . ok ( ! leafNodes . has ( type ) ) ; list = new Array < Node > ( ) ; leafNodes . set ( type , list ) ; } list . push ( node ) ; } } if ( leafNodes . size > 0 ) { this . ux . log ( 'Leaf components by type' ) ; Array . from ( leafNodes . entries ( ) ) . forEach ( pair => { const type = pair [ 0 ] ; const components = pair [ 1 ] ; this . ux . log ( type + ':' ) ; components . forEach ( component => { const name = Analyze . nodeToDisplayName ( component ) ; this . ux . log ( '\t' + name ) ; } ) ; this . ux . log ( ) ; } ) ; } return leafNodes ; } }