Skip to content

Commit b531497

Browse files
fixup: throw on relative statLocation with remediation instructions
1 parent da29815 commit b531497

File tree

7 files changed

+69
-3
lines changed

7 files changed

+69
-3
lines changed

lib/internal/modules/package_json_reader.js

+26-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@
22

33
const {
44
ArrayIsArray,
5+
ArrayPrototypeJoin,
56
JSONParse,
67
ObjectDefineProperty,
8+
StringPrototypeEndsWith,
79
} = primordials;
10+
const {
11+
codes: {
12+
ERR_INVALID_ARG_VALUE,
13+
},
14+
} = require('internal/errors');
815
const modulesBinding = internalBinding('modules');
9-
const { resolve } = require('path');
16+
const path = require('path');
1017
const { kEmptyObject } = require('internal/util');
1118
const { fileURLToPath, URL } = require('internal/url');
1219
const {
@@ -111,7 +118,7 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) {
111118
*/
112119
function readPackage(requestPath) {
113120
// TODO(@anonrig): Remove this function.
114-
return read(resolve(requestPath, 'package.json'));
121+
return read(path.resolve(requestPath, 'package.json'));
115122
}
116123

117124
/**
@@ -122,10 +129,26 @@ function readPackage(requestPath) {
122129
* @returns {undefined | DeserializedPackageConfig<everything>}
123130
*/
124131
function getNearestParentPackageJSON(startLocation, everything = false) {
125-
const startPath = URL.canParse(startLocation) ? fileURLToPath(startLocation) : startLocation;
132+
let startPath = URL.canParse(startLocation) ? fileURLToPath(startLocation) : startLocation;
133+
126134
validateString(startPath, 'startPath');
127135
validateBoolean(everything, 'everything');
128136

137+
if (!path.isAbsolute(startPath)) {
138+
throw new ERR_INVALID_ARG_VALUE(
139+
'startLocation',
140+
startLocation,
141+
ArrayPrototypeJoin([
142+
'must be a fully resolved location. To use a relative location, first wrap with',
143+
'`import.meta.resolve(startLocation)` in ESM',
144+
'or',
145+
'`path.resolve(__dirname, startLocation) in CJS',
146+
], ' '),
147+
);
148+
}
149+
150+
if (!StringPrototypeEndsWith(startPath, path.sep)) { startPath += path.sep; }
151+
129152
if (everything) {
130153
const result = modulesBinding.getNearestRawParentPackageJSON(startPath);
131154

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name": "package-with-sub-package"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const { getPackageJSON } = require('node:module');
2+
const { resolve } = require('node:path');
3+
4+
module.exports = getPackageJSON(resolve(__dirname, '..'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name": "sub-package", "type": "commonjs"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { getPackageJSON } from 'node:module';
2+
3+
export default getPackageJSON(import.meta.resolve('..'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name": "sub-package", "type": "module"}

test/parallel/test-get-package-json.js

+33
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,36 @@ const pkgType = 'module'; // a non-default value
104104
},
105105
});
106106
}
107+
108+
{ // Throws on unresolved location
109+
let err;
110+
try {
111+
getPackageJSON('..');
112+
} catch (e) {
113+
err = e;
114+
}
115+
116+
assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE');
117+
assert.match(err.message, /fully resolved/);
118+
assert.match(err.message, /relative/);
119+
assert.match(err.message, /import\.meta\.resolve/);
120+
assert.match(err.message, /path\.resolve\(__dirname/);
121+
}
122+
123+
{ // Can crawl up (CJS)
124+
const pathToMod = fixtures.path('packages/nested/sub-pkg-cjs/index.js');
125+
const parentPkg = require(pathToMod);
126+
127+
assert.strictEqual(parentPkg.data.name, 'package-with-sub-package');
128+
const pathToParent = fixtures.path('packages/nested/package.json');
129+
assert.strictEqual(parentPkg.path, pathToParent);
130+
}
131+
132+
{ // Can crawl up (ESM)
133+
const pathToMod = fixtures.path('packages/nested/sub-pkg-mjs/index.js');
134+
const parentPkg = require(pathToMod).default;
135+
136+
assert.strictEqual(parentPkg.data.name, 'package-with-sub-package');
137+
const pathToParent = fixtures.path('packages/nested/package.json');
138+
assert.strictEqual(parentPkg.path, pathToParent);
139+
}

0 commit comments

Comments
 (0)