node/test/parallel/test-find-package-json.js
Jacob Smith 61a57f7761 test: add case for unrecognised fields within pjson "exports"
PR-URL: https://github.com/nodejs/node/pull/57026
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
2025-02-15 11:45:13 -08:00

208 lines
7.4 KiB
JavaScript

'use strict';
const common = require('../common');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const assert = require('node:assert');
const fs = require('node:fs');
const { findPackageJSON } = require('node:module');
const path = require('node:path');
const { describe, it } = require('node:test');
describe('findPackageJSON', () => { // Throws when no arguments are provided
it('should throw when no arguments are provided', () => {
assert.throws(
() => findPackageJSON(),
{ code: 'ERR_MISSING_ARGS' }
);
});
it('should throw when parentLocation is invalid', () => {
for (const invalid of [null, {}, [], Symbol(), () => {}, true, false, 1, 0]) {
assert.throws(
() => findPackageJSON('', invalid),
{ code: 'ERR_INVALID_ARG_TYPE' },
);
}
});
it('should accept a file URL (string), like from `import.meta.resolve()`', () => {
const importMetaUrl = `${fixtures.fileURL('whatever.ext')}`;
const specifierBase = './packages/root-types-field';
assert.strictEqual(
findPackageJSON(`${specifierBase}/index.js`, importMetaUrl),
path.toNamespacedPath(fixtures.path(specifierBase, 'package.json')),
);
});
it('should accept a file URL instance', () => {
const importMetaUrl = fixtures.fileURL('whatever.ext');
const specifierBase = './packages/root-types-field';
assert.strictEqual(
findPackageJSON(
new URL(`${specifierBase}/index.js`, importMetaUrl),
importMetaUrl,
),
path.toNamespacedPath(fixtures.path(specifierBase, 'package.json')),
);
});
it('should be able to crawl up (CJS)', () => {
const pathToMod = fixtures.path('packages/nested/sub-pkg-cjs/index.js');
const parentPkg = require(pathToMod);
const pathToParent = path.toNamespacedPath(fixtures.path('packages/nested/package.json'));
assert.strictEqual(parentPkg, pathToParent);
});
it('should be able to crawl up (ESM)', () => {
const pathToMod = fixtures.path('packages/nested/sub-pkg-esm/index.js');
const parentPkg = require(pathToMod).default; // This test is a CJS file
const pathToParent = path.toNamespacedPath(fixtures.path('packages/nested/package.json'));
assert.strictEqual(parentPkg, pathToParent);
});
it('can require via package.json', () => {
const pathToMod = fixtures.path('packages/cjs-main-no-index/other.js');
// `require()` falls back to package.json values like "main" to resolve when there is no index
const answer = require(pathToMod);
assert.strictEqual(answer, 43);
});
it('should be able to resolve both root and closest package.json', () => {
tmpdir.refresh();
fs.writeFileSync(tmpdir.resolve('entry.mjs'), `
import { findPackageJSON } from 'node:module';
import fs from 'node:fs';
const readPJSON = (locus) => JSON.parse(fs.readFileSync(locus, 'utf8'));
const { secretNumberPkgRoot } = readPJSON(findPackageJSON('pkg', import.meta.url));
const { secretNumberSubfolder } = readPJSON(findPackageJSON(import.meta.resolve('pkg')));
const { secretNumberSubfolder2 } = readPJSON(findPackageJSON(import.meta.resolve('pkg2')));
const { secretNumberPkg2 } = readPJSON(findPackageJSON('pkg2', import.meta.url));
console.log(secretNumberPkgRoot, secretNumberSubfolder, secretNumberSubfolder2, secretNumberPkg2);
`);
const secretNumberPkgRoot = Math.ceil(Math.random() * 999);
const secretNumberSubfolder = Math.ceil(Math.random() * 999);
const secretNumberSubfolder2 = Math.ceil(Math.random() * 999);
const secretNumberPkg2 = Math.ceil(Math.random() * 999);
fs.mkdirSync(tmpdir.resolve('node_modules/pkg/subfolder'), { recursive: true });
fs.writeFileSync(
tmpdir.resolve('node_modules/pkg/subfolder/index.js'),
'',
);
fs.writeFileSync(
tmpdir.resolve('node_modules/pkg/subfolder/package.json'),
JSON.stringify({
type: 'module',
secretNumberSubfolder,
}),
);
fs.writeFileSync(
tmpdir.resolve('node_modules/pkg/package.json'),
JSON.stringify({
name: 'pkg',
exports: './subfolder/index.js',
secretNumberPkgRoot,
}),
);
fs.mkdirSync(tmpdir.resolve('node_modules/pkg/subfolder2'));
fs.writeFileSync(
tmpdir.resolve('node_modules/pkg/subfolder2/package.json'),
JSON.stringify({
type: 'module',
secretNumberSubfolder2,
}),
);
fs.writeFileSync(
tmpdir.resolve('node_modules/pkg/subfolder2/index.js'),
'',
);
fs.mkdirSync(tmpdir.resolve('node_modules/pkg2'));
fs.writeFileSync(
tmpdir.resolve('node_modules/pkg2/package.json'),
JSON.stringify({
name: 'pkg',
main: tmpdir.resolve('node_modules/pkg/subfolder2/index.js'),
secretNumberPkg2,
}),
);
common.spawnPromisified(process.execPath, [tmpdir.resolve('entry.mjs')]).then(common.mustCall((result) => {
console.error(result.stderr);
console.log(result.stdout);
assert.deepStrictEqual(result, {
stdout: `${secretNumberPkgRoot} ${secretNumberSubfolder} ${secretNumberSubfolder2} ${secretNumberPkg2}\n`,
stderr: '',
code: 0,
signal: null,
});
}));
});
it('should work within a loader', async () => {
const specifierBase = './packages/root-types-field';
const target = fixtures.fileURL(specifierBase, 'index.js');
const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json'));
const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [
'--no-warnings',
'--loader',
[
'data:text/javascript,',
'import fs from "node:fs";',
'import module from "node:module";',
encodeURIComponent(`fs.writeSync(1, module.findPackageJSON(${JSON.stringify(target)}));`),
'export const resolve = async (s, c, n) => n(s);',
].join(''),
'--eval',
'import "node:os";', // Can be anything that triggers the resolve hook chain
]);
assert.strictEqual(stderr, '');
assert.ok(stdout.includes(foundPjsonPath), stdout);
assert.strictEqual(code, 0);
});
it('should work with an async resolve hook registered', async () => {
const specifierBase = './packages/root-types-field';
const target = fixtures.fileURL(specifierBase, 'index.js');
const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json'));
const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [
'--no-warnings',
'--loader',
'data:text/javascript,export const resolve = async (s, c, n) => n(s);',
'--print',
`require("node:module").findPackageJSON(${JSON.stringify(target)})`,
]);
assert.strictEqual(stderr, '');
assert.ok(stdout.includes(foundPjsonPath), stdout);
assert.strictEqual(code, 0);
});
it('should work when unrecognised keys are specified within the export', async () => {
const specifierBase = './packages/unrecognised-export-keys';
const target = fixtures.fileURL(specifierBase, 'index.js');
const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json'));
const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [
'--print',
`require("node:module").findPackageJSON(${JSON.stringify(target)})`,
]);
assert.strictEqual(stderr, '');
assert.ok(stdout.includes(foundPjsonPath), stdout);
assert.strictEqual(code, 0);
});
});