@@ -5,57 +5,55 @@ import { globby } from 'globby'
5
5
import ignore from 'ignore'
6
6
// @ts -ignore This package provides no types
7
7
import { directories } from 'ignore-by-default'
8
+ import micromatch from 'micromatch'
8
9
import { ErrorWithCause } from 'pony-cause'
9
10
10
11
import { InputError } from './errors.js'
11
12
import { isErrnoException } from './type-helpers.js'
12
13
13
- /** @type {readonly string[] } */
14
- const SUPPORTED_LOCKFILES = [
15
- 'package-lock.json' ,
16
- 'yarn.lock' ,
17
- ]
18
-
19
14
/**
20
15
* There are a lot of possible folders that we should not be looking in and "ignore-by-default" helps us with defining those
21
16
*
22
17
* @type {readonly string[] }
23
18
*/
24
19
const ignoreByDefault = directories ( )
25
20
26
- /** @type {readonly string[] } */
27
- const GLOB_IGNORE = [
28
- ...ignoreByDefault . map ( item => '**/' + item )
29
- ]
21
+ /** @type {import('globby').Options } */
22
+ const BASE_GLOBBY_OPTS = {
23
+ absolute : true ,
24
+ expandDirectories : false ,
25
+ gitignore : true ,
26
+ ignore : [
27
+ ...ignoreByDefault . map ( item => '**/' + item )
28
+ ] ,
29
+ markDirectories : true ,
30
+ unique : true ,
31
+ }
30
32
31
33
/**
32
34
* Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
33
35
*
34
36
* @param {string } cwd The working directory to use when resolving paths
35
37
* @param {string[] } inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
36
38
* @param {import('@socketsecurity/config').SocketYml|undefined } config
39
+ * @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data'] } supportedFiles
37
40
* @param {typeof console.error } debugLog
38
41
* @returns {Promise<string[]> }
39
42
* @throws {InputError }
40
43
*/
41
- export async function getPackageFiles ( cwd , inputPaths , config , debugLog ) {
44
+ export async function getPackageFiles ( cwd , inputPaths , config , supportedFiles , debugLog ) {
42
45
debugLog ( `Globbed resolving ${ inputPaths . length } paths:` , inputPaths )
43
46
44
47
// TODO: Does not support `~/` paths
45
48
const entries = await globby ( inputPaths , {
46
- absolute : true ,
49
+ ... BASE_GLOBBY_OPTS ,
47
50
cwd,
48
- expandDirectories : false ,
49
- gitignore : true ,
50
- ignore : [ ...GLOB_IGNORE ] ,
51
- markDirectories : true ,
52
- onlyFiles : false ,
53
- unique : true ,
51
+ onlyFiles : false
54
52
} )
55
53
56
54
debugLog ( `Globbed resolved ${ inputPaths . length } paths to ${ entries . length } paths:` , entries )
57
55
58
- const packageFiles = await mapGlobResultToFiles ( entries )
56
+ const packageFiles = await mapGlobResultToFiles ( entries , supportedFiles )
59
57
60
58
debugLog ( `Mapped ${ entries . length } entries to ${ packageFiles . length } files:` , packageFiles )
61
59
@@ -73,11 +71,14 @@ export async function getPackageFiles (cwd, inputPaths, config, debugLog) {
73
71
* Takes paths to folders, package.json and/or recognized lock files and resolves them to package.json + lockfile pairs (where possible)
74
72
*
75
73
* @param {string[] } entries
74
+ * @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data'] } supportedFiles
76
75
* @returns {Promise<string[]> }
77
76
* @throws {InputError }
78
77
*/
79
- export async function mapGlobResultToFiles ( entries ) {
80
- const packageFiles = await Promise . all ( entries . map ( mapGlobEntryToFiles ) )
78
+ export async function mapGlobResultToFiles ( entries , supportedFiles ) {
79
+ const packageFiles = await Promise . all (
80
+ entries . map ( entry => mapGlobEntryToFiles ( entry , supportedFiles ) )
81
+ )
81
82
82
83
const uniquePackageFiles = [ ...new Set ( packageFiles . flat ( ) ) ]
83
84
@@ -88,46 +89,58 @@ export async function mapGlobResultToFiles (entries) {
88
89
* Takes a single path to a folder, package.json or a recognized lock file and resolves to a package.json + lockfile pair (where possible)
89
90
*
90
91
* @param {string } entry
92
+ * @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getReportSupportedFiles'>['data'] } supportedFiles
91
93
* @returns {Promise<string[]> }
92
94
* @throws {InputError }
93
95
*/
94
- export async function mapGlobEntryToFiles ( entry ) {
96
+ export async function mapGlobEntryToFiles ( entry , supportedFiles ) {
95
97
/** @type {string|undefined } */
96
- let pkgFile
97
- /** @type {string|undefined } */
98
- let lockFile
99
-
98
+ let pkgJSFile
99
+ /** @type {string[] } */
100
+ let jsLockFiles = [ ]
101
+ /** @type {string[] } */
102
+ let pyFiles = [ ]
103
+
104
+ const jsSupported = supportedFiles [ 'npm' ] || { }
105
+ const jsLockFilePatterns = Object . keys ( jsSupported )
106
+ . filter ( key => key !== 'packagejson' )
107
+ . map ( key => /** @type {{ pattern: string } } */ ( jsSupported [ key ] ) . pattern )
108
+
109
+ const pyFilePatterns = Object . values ( supportedFiles [ 'pypi' ] || { } ) . map ( p => p . pattern )
100
110
if ( entry . endsWith ( '/' ) ) {
101
111
// If the match is a folder and that folder contains a package.json file, then include it
102
112
const filePath = path . resolve ( entry , 'package.json' )
103
- pkgFile = await fileExists ( filePath ) ? filePath : undefined
104
- } else if ( path . basename ( entry ) === 'package.json' ) {
105
- // If the match is a package.json file, then include it
106
- pkgFile = entry
107
- } else if ( SUPPORTED_LOCKFILES . includes ( path . basename ( entry ) ) ) {
108
- // If the match is a lock file, include both it and the corresponding package.json file
109
- lockFile = entry
110
- pkgFile = path . resolve ( path . dirname ( entry ) , 'package.json' )
113
+ if ( await fileExists ( filePath ) ) pkgJSFile = filePath
114
+ pyFiles = await globby ( pyFilePatterns , {
115
+ ...BASE_GLOBBY_OPTS ,
116
+ cwd : entry
117
+ } )
118
+ } else {
119
+ const entryFile = path . basename ( entry )
120
+
121
+ if ( entryFile === 'package.json' ) {
122
+ // If the match is a package.json file, then include it
123
+ pkgJSFile = entry
124
+ } else if ( micromatch . isMatch ( entryFile , jsLockFilePatterns ) ) {
125
+ jsLockFiles = [ entry ]
126
+ pkgJSFile = path . resolve ( path . dirname ( entry ) , 'package.json' )
127
+ if ( ! ( await fileExists ( pkgJSFile ) ) ) return [ ]
128
+ } else if ( micromatch . isMatch ( entryFile , pyFilePatterns ) ) {
129
+ pyFiles = [ entry ]
130
+ }
111
131
}
112
132
113
133
// If we will include a package.json file but don't already have a corresponding lockfile, then look for one
114
- if ( ! lockFile && pkgFile ) {
115
- const pkgDir = path . dirname ( pkgFile )
116
-
117
- for ( const name of SUPPORTED_LOCKFILES ) {
118
- const lockFileAlternative = path . resolve ( pkgDir , name )
119
- if ( await fileExists ( lockFileAlternative ) ) {
120
- lockFile = lockFileAlternative
121
- break
122
- }
123
- }
124
- }
134
+ if ( ! jsLockFiles . length && pkgJSFile ) {
135
+ const pkgDir = path . dirname ( pkgJSFile )
125
136
126
- if ( pkgFile && lockFile ) {
127
- return [ pkgFile , lockFile ]
137
+ jsLockFiles = await globby ( jsLockFilePatterns , {
138
+ ...BASE_GLOBBY_OPTS ,
139
+ cwd : pkgDir
140
+ } )
128
141
}
129
142
130
- return pkgFile ? [ pkgFile ] : [ ]
143
+ return [ ... jsLockFiles , ... pyFiles ] . concat ( pkgJSFile ? [ pkgJSFile ] : [ ] )
131
144
}
132
145
133
146
/**
0 commit comments