11import {
22 promises as fs ,
3+ readFileSync ,
34} from 'node:fs' ;
45import {
56 createRequire ,
@@ -11,6 +12,9 @@ import {
1112 join ,
1213 resolve ,
1314} from 'node:path' ;
15+ import {
16+ globbySync ,
17+ } from 'globby' ;
1418import type {
1519 PackageJson ,
1620} from 'type-fest' ;
@@ -27,10 +31,15 @@ import {
2731 isJsonFile ,
2832 parseFile ,
2933} from '../file-system/parse-file.mjs' ;
30- import type {
31- Manifest ,
32- Model ,
33- Transform ,
34+ import {
35+ deserialize ,
36+ } from '../serialization.mjs' ;
37+ import {
38+ isPatternsModel ,
39+ type Manifest ,
40+ type Model ,
41+ type PatternsModel ,
42+ type Transform ,
3443} from './manifest.mjs' ;
3544
3645const INNER_PATH_SEPARATOR = '#[\\/]' ;
@@ -75,7 +84,7 @@ export const sanitizePackagePath = (artifactName: string) => {
7584 * Split model path into file path and inner path
7685 * @param modelPath
7786 */
78- const splitModelPath = ( modelPath : string ) => {
87+ export const splitModelPath = ( modelPath : string ) => {
7988 const [ filePath , innerPath ] = modelPath . split ( new RegExp ( INNER_PATH_SEPARATOR ) ) ;
8089 return { filePath, innerPath } ;
8190} ;
@@ -118,6 +127,19 @@ const getArtifactInfo = async (require: NodeJS.Require, artifactName: string) =>
118127 return { artifactBasePath, version } ;
119128} ;
120129
130+ /**
131+ * Get information for artifactory manifest file
132+ * @param require
133+ * @param artifactName
134+ */
135+ const getArtifactInfoSync = ( require : NodeJS . Require , artifactName : string ) => {
136+ const artifactPackageJson = require . resolve ( `${ artifactName } /package.json` ) ;
137+ const artifactBasePath = dirname ( artifactPackageJson ) ;
138+ const packageJsonContent = readFileSync ( artifactPackageJson , { encoding : 'utf8' } ) ;
139+ const version : string = ( JSON . parse ( packageJsonContent ) as PackageJson ) . version || 'latest' ;
140+ return { artifactBasePath, version } ;
141+ } ;
142+
121143/**
122144 * Extract dependency model given simple model definition (as string or boolean)
123145 * @param cwd
@@ -206,6 +228,63 @@ export const extractDependencyModelsObject = async (
206228 } ;
207229} ;
208230
231+ /**
232+ * Extract dependency models from patterns model definition
233+ * @param artifactName
234+ * @param models
235+ * @param context
236+ * @param outputDirectory
237+ */
238+ const extractDependencyPatternsModelsObject = (
239+ artifactName : string ,
240+ models : PatternsModel ,
241+ context : Context ,
242+ outputDirectory = OUTPUT_DIRECTORY ) => {
243+ const { cwd, logger } = context ;
244+ const paths = Array . isArray ( models . patterns ) ? models . patterns : [ models . patterns ] ;
245+ const require = createRequire ( resolve ( cwd , 'package.json' ) ) ;
246+ const { artifactBasePath } = getArtifactInfoSync ( require , artifactName ) ;
247+
248+ return paths . flatMap ( ( modelPath ) => {
249+ const { filePath, innerPath } = splitModelPath ( modelPath ) ;
250+ return globbySync ( filePath , { cwd : artifactBasePath } )
251+ . flatMap ( ( matchedFilePath ) => {
252+ if ( ! innerPath . includes ( '*' ) ) {
253+ return [ extractDependencyModelsSimple ( cwd , artifactName , matchedFilePath + ( innerPath ? `#/${ innerPath } ` : '' ) , logger , outputDirectory ) ] ;
254+ }
255+
256+ const content = readFileSync ( matchedFilePath , { encoding : 'utf8' } ) ;
257+ const deserialized = deserialize ( content , { isInputJson : isJsonFile ( matchedFilePath ) } ) ;
258+ const innerPathParts = innerPath . split ( '/' ) ;
259+ return innerPathParts
260+ . reduce ( ( nodes , part ) => {
261+ if ( part . includes ( '*' ) ) {
262+ const regex = new RegExp ( `^${ part . replaceAll ( '*' , '.*' ) } $` ) ;
263+ return nodes
264+ . flatMap ( ( { node, path } ) => {
265+ const keys = Object . keys ( node ) . filter ( ( k ) => regex . test ( k ) ) ;
266+ return keys
267+ . map ( ( k ) => ( {
268+ node : node [ k ] ,
269+ path : `${ path } /${ k } `
270+ } ) )
271+ . filter ( ( { node : n } ) => ! ! n ) ;
272+ } ) ;
273+ } else {
274+ return nodes
275+ . map ( ( { node, path } ) => ( {
276+ node : node ?. [ part ] ,
277+ path : `${ path } /${ part } `
278+ } ) )
279+ . filter ( ( { node } ) => ! ! node ) ;
280+ }
281+ } , [ { node : deserialized , path : '' } ] )
282+ . map ( ( { path } ) => path )
283+ . map ( ( path ) => extractDependencyModelsSimple ( cwd , artifactName , matchedFilePath + ( path ? `#${ path } ` : '' ) , logger , outputDirectory ) ) ;
284+ } ) ;
285+ } ) ;
286+ } ;
287+
209288/**
210289 * Extract dependency models from the manifest
211290 * @param manifest
@@ -217,8 +296,14 @@ export const extractDependencyModels = (manifest: Manifest, context: Context) =>
217296 logger ?. debug ?.( 'Extracting information from the manifest configuration: ' , manifest ) ;
218297 return Object . entries ( manifest . models ) . flatMap ( ( [ dependencyName , modelDefinition ] ) =>
219298 ( Array . isArray ( modelDefinition ) ? modelDefinition : [ modelDefinition ] )
220- . map ( ( model ) => ( typeof model === 'string' || typeof model === 'boolean' )
221- ? extractDependencyModelsSimple ( cwd , dependencyName , model , logger )
222- : extractDependencyModelsObject ( dependencyName , model , retrieveTransform ( cwd , model . transform ) , context ) )
299+ . flatMap ( ( model ) => {
300+ if ( isPatternsModel ( model ) ) {
301+ return extractDependencyPatternsModelsObject ( dependencyName , model , context ) ;
302+ }
303+ const singleModel = ( typeof model === 'string' || typeof model === 'boolean' )
304+ ? extractDependencyModelsSimple ( cwd , dependencyName , model , logger )
305+ : extractDependencyModelsObject ( dependencyName , model , retrieveTransform ( cwd , model . transform ) , context ) ;
306+ return [ singleModel ] ;
307+ } )
223308 ) ;
224309} ;
0 commit comments