-
Notifications
You must be signed in to change notification settings - Fork 44
/
resolvePackage.js
81 lines (73 loc) · 2.85 KB
/
resolvePackage.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
const path = require('node:path');
const { default: memoize } = require('nano-memoize');
const debug = require('debug')('sku:resolvePackage');
const { cwd } = require('../../../lib/cwd');
function getProjectDependencies(readFileSync) {
let pkg;
try {
pkg = JSON.parse(readFileSync(`${cwd()}/package.json`).toString());
} catch (error) {
if (error.code === 'ENOENT') {
pkg = {};
} else {
throw error;
}
}
return {
...(pkg.dependencies || {}),
...(pkg.devDependencies || {}),
};
}
/**
* Create a `resolvePackage` function.
*
* This wrapper let's us inject fs and require dependencies for testing.
*
* @param {object} fs - Node's fs module
* @param {Function} resolve - Node's require.resolve
*/
const createPackageResolver = (fs, resolve) => {
/**
* Resolve a package name to an absolute path.
* e.g. my-package -> /Users/me/code/my-project/node_modules/my-package
*
* Throws if a package is listed in the project's dependencies and cannot be resolved.
*/
/** @param {string} packageName */
function resolvePackage(packageName) {
try {
// First, try to use require.resolve to find the package.
// We add /package.json and then dirname the result because require.resolve follows the `main`
// property in package.json to produce a path to a file deep inside the package, and we want
// the path to the top-level directory.
// This branch handles packages being symlinked into node_modules, for example with
// `npm link` or in sku's test cases.
const result = path.dirname(
resolve(`${packageName}/package.json`, { paths: [cwd()] }),
);
debug(`Resolved ${packageName} to ${result}`);
return result;
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
const dependencies = getProjectDependencies(fs.readFileSync);
if (!dependencies[packageName]) {
// If it's not we can safely return a naive local path, which prevents webpack from
// throwing config schema errors when this function is used to configure a loader.
// This branch handles the scenario where we're trying to resolve a design system module because
// it's in the default list of compilePackages, but it's not actually being used in the project.
const localPackage = path.join(cwd(), 'node_modules', packageName);
debug(`Resolved unused package ${packageName} to ${localPackage}`);
return localPackage;
}
}
// We've gotten this far because the package is listed as a project dependency and can't be
// resolved, or because the error is not ENOENT. In either case we want to throw.
throw error;
}
}
return memoize(resolvePackage);
};
module.exports = {
resolvePackage: createPackageResolver(require('node:fs'), require.resolve),
createPackageResolver,
};