1 | const path = require('path')
|
2 | const fs = require('fs')
|
3 | const packList = require('npm-packlist')
|
4 | const precinct = require('precinct')
|
5 | const resolve = require('resolve')
|
6 | const readPkgUp = require('read-pkg-up')
|
7 | const requirePackageName = require('require-package-name')
|
8 | const alwaysIgnored = new Set(['aws-sdk'])
|
9 | const debug = require('debug')('netlify-dev-plugin:src/utils/finders')
|
10 |
|
11 | const ignoredExtensions = new Set([
|
12 | '.log',
|
13 | '.lock',
|
14 | '.html',
|
15 | '.md',
|
16 | '.map',
|
17 | '.ts',
|
18 | '.png',
|
19 | '.jpeg',
|
20 | '.jpg',
|
21 | '.gif',
|
22 | '.css',
|
23 | '.patch'
|
24 | ])
|
25 |
|
26 | function ignoreMissing(dependency, optional) {
|
27 | return alwaysIgnored.has(dependency) || (optional && dependency in optional)
|
28 | }
|
29 |
|
30 | function includeModuleFile(packageJson, moduleFilePath) {
|
31 | if (packageJson.files) {
|
32 | return true
|
33 | }
|
34 |
|
35 | return !ignoredExtensions.has(path.extname(moduleFilePath))
|
36 | }
|
37 |
|
38 | function getDependencies(filename, basedir) {
|
39 | const servicePath = basedir
|
40 |
|
41 | const filePaths = new Set()
|
42 | const modulePaths = new Set()
|
43 | const pkgs = {}
|
44 |
|
45 | const modulesToProcess = []
|
46 | const localFilesToProcess = [filename]
|
47 |
|
48 | function handle(name, basedir, optionalDependencies) {
|
49 | const moduleName = requirePackageName(name.replace(/\\/g, '/'))
|
50 |
|
51 | if (alwaysIgnored.has(moduleName)) {
|
52 | return
|
53 | }
|
54 |
|
55 | try {
|
56 | const pathToModule = resolve.sync(path.join(moduleName, 'package.json'), {
|
57 | basedir
|
58 | })
|
59 | const pkg = readPkgUp.sync({ cwd: pathToModule })
|
60 |
|
61 | if (pkg) {
|
62 | modulesToProcess.push(pkg)
|
63 | }
|
64 | } catch (e) {
|
65 | if (e.code === 'MODULE_NOT_FOUND') {
|
66 | if (ignoreMissing(moduleName, optionalDependencies)) {
|
67 | debug(`WARNING missing optional dependency: ${moduleName}`)
|
68 | return null
|
69 | }
|
70 | try {
|
71 |
|
72 | const resolved = require.resolve(name)
|
73 | localFilesToProcess.push(resolved)
|
74 | return
|
75 | } catch (e) {
|
76 | throw new Error(`Could not find "${moduleName}" module in file: ${filename.replace(
|
77 | path.dirname(basedir),
|
78 | ''
|
79 | )}.
|
80 |
|
81 | Please ensure "${moduleName}" is installed in the project.`)
|
82 | }
|
83 | }
|
84 | throw e
|
85 | }
|
86 | }
|
87 |
|
88 | while (localFilesToProcess.length) {
|
89 | const currentLocalFile = localFilesToProcess.pop()
|
90 |
|
91 | if (filePaths.has(currentLocalFile)) {
|
92 | continue
|
93 | }
|
94 |
|
95 | filePaths.add(currentLocalFile)
|
96 | precinct.paperwork(currentLocalFile, { includeCore: false }).forEach(dependency => {
|
97 | if (dependency.indexOf('.') === 0) {
|
98 | const abs = resolve.sync(dependency, {
|
99 | basedir: path.dirname(currentLocalFile)
|
100 | })
|
101 | localFilesToProcess.push(abs)
|
102 | } else {
|
103 | handle(dependency, servicePath)
|
104 | }
|
105 | })
|
106 | }
|
107 |
|
108 | while (modulesToProcess.length) {
|
109 | const currentModule = modulesToProcess.pop()
|
110 | const currentModulePath = path.join(currentModule.path, '..')
|
111 | const packageJson = currentModule.pkg
|
112 |
|
113 | if (modulePaths.has(currentModulePath)) {
|
114 | continue
|
115 | }
|
116 | modulePaths.add(currentModulePath)
|
117 | pkgs[currentModulePath] = packageJson
|
118 | ;['dependencies', 'peerDependencies', 'optionalDependencies'].forEach(key => {
|
119 | const dependencies = packageJson[key]
|
120 |
|
121 | if (dependencies) {
|
122 | Object.keys(dependencies).forEach(dependency => {
|
123 | handle(dependency, currentModulePath, packageJson.optionalDependencies)
|
124 | })
|
125 | }
|
126 | })
|
127 | }
|
128 |
|
129 | modulePaths.forEach(modulePath => {
|
130 | const packageJson = pkgs[modulePath]
|
131 | let moduleFilePaths
|
132 |
|
133 | moduleFilePaths = packList.sync({ path: modulePath })
|
134 |
|
135 | moduleFilePaths.forEach(moduleFilePath => {
|
136 | if (includeModuleFile(packageJson, moduleFilePath)) {
|
137 | filePaths.add(path.join(modulePath, moduleFilePath))
|
138 | }
|
139 | })
|
140 | })
|
141 |
|
142 |
|
143 | const sizes = {}
|
144 | filePaths.forEach(filepath => {
|
145 | const stat = fs.lstatSync(filepath)
|
146 | const ext = path.extname(filepath)
|
147 | sizes[ext] = (sizes[ext] || 0) + stat.size
|
148 | })
|
149 | debug('Sizes per extension: ', sizes)
|
150 |
|
151 | return [...filePaths]
|
152 | }
|
153 |
|
154 | function findModuleDir(dir) {
|
155 | let basedir = dir
|
156 | while (!fs.existsSync(path.join(basedir, 'package.json'))) {
|
157 | const newBasedir = path.dirname(basedir)
|
158 | if (newBasedir === basedir) {
|
159 | return null
|
160 | }
|
161 | basedir = newBasedir
|
162 | }
|
163 | return basedir
|
164 | }
|
165 |
|
166 | function findHandler(functionPath) {
|
167 | if (fs.lstatSync(functionPath).isFile()) {
|
168 | return functionPath
|
169 | }
|
170 |
|
171 | const handlerPath = path.join(functionPath, `${path.basename(functionPath)}.js`)
|
172 | if (!fs.existsSync(handlerPath)) {
|
173 | return
|
174 | }
|
175 | return handlerPath
|
176 | }
|
177 |
|
178 | module.exports = { getDependencies, findModuleDir, findHandler }
|