Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(node-resolve): handle "package.json" being in path #927

Merged
merged 5 commits into from Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintignore
Expand Up @@ -4,3 +4,6 @@
**/test/**/output
packages/commonjs/test/fixtures
packages/typescript/test/fixtures/syntax-error

# temporary workaround for eslint bug where package.json is a directory
packages/node-resolve/test/fixtures/package-json-in-path
7 changes: 4 additions & 3 deletions packages/node-resolve/src/fs.js
Expand Up @@ -7,10 +7,11 @@ export const readFile = promisify(fs.readFile);
export const realpath = promisify(fs.realpath);
export { realpathSync } from 'fs';
export const stat = promisify(fs.stat);
export async function exists(filePath) {

export async function fileExists(filePath) {
try {
await access(filePath);
return true;
const res = await stat(filePath);
return res.isFile();
} catch {
return false;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/node-resolve/src/index.js
Expand Up @@ -6,7 +6,7 @@ import deepMerge from 'deepmerge';
import isModule from 'is-module';

import { isDirCached, isFileCached, readCachedFile } from './cache';
import { exists, readFile, realpath } from './fs';
import { fileExists, readFile, realpath } from './fs';
import resolveImportSpecifiers from './resolveImportSpecifiers';
import { getMainFields, getPackageName, normalizeInput } from './util';
import handleDeprecatedOptions from './deprecated-options';
Expand Down Expand Up @@ -207,8 +207,8 @@ export function nodeResolve(opts = {}) {
}

if (hasPackageEntry && !preserveSymlinks) {
const fileExists = await exists(location);
if (fileExists) {
const exists = await fileExists(location);
if (exists) {
location = await realpath(location);
}
}
Expand All @@ -228,7 +228,7 @@ export function nodeResolve(opts = {}) {
}
}

if (options.modulesOnly && (await exists(location))) {
if (options.modulesOnly && (await fileExists(location))) {
const code = await readFile(location, 'utf-8');
if (isModule(code)) {
return {
Expand Down
3 changes: 1 addition & 2 deletions packages/node-resolve/src/package/utils.js
@@ -1,9 +1,8 @@
/* eslint-disable no-await-in-loop */
import path from 'path';
import fs from 'fs';
import { promisify } from 'util';

const fileExists = promisify(fs.exists);
import { fileExists } from '../fs';

function isModuleDir(current, moduleDirs) {
return moduleDirs.some((dir) => current.endsWith(dir));
Expand Down
13 changes: 7 additions & 6 deletions packages/node-resolve/src/resolveImportSpecifiers.js
Expand Up @@ -2,10 +2,12 @@ import fs from 'fs';
import { promisify } from 'util';
import { fileURLToPath, pathToFileURL } from 'url';

import { dirname } from 'path';

import resolve from 'resolve';

import { getPackageInfo, getPackageName } from './util';
import { exists, realpath } from './fs';
import { fileExists, realpath } from './fs';
import { isDirCached, isFileCached, readCachedFile } from './cache';
import resolvePackageExports from './package/resolvePackageExports';
import resolvePackageImports from './package/resolvePackageImports';
Expand All @@ -26,7 +28,7 @@ async function getPackageJson(importer, pkgName, resolveOptions, moduleDirectori
try {
const pkgJsonPath = await resolveImportPath(`${pkgName}/package.json`, resolveOptions);
const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
return { pkgJsonPath, pkgJson };
return { pkgJsonPath, pkgJson, pkgPath: dirname(pkgJsonPath) };
} catch (_) {
return null;
}
Expand Down Expand Up @@ -114,12 +116,11 @@ async function resolveId({
const result = await getPackageJson(importer, pkgName, resolveOptions, moduleDirectories);

if (result && result.pkgJson.exports) {
const { pkgJson, pkgJsonPath } = result;
const { pkgJson, pkgJsonPath, pkgPath } = result;
try {
const subpath =
pkgName === importSpecifier ? '.' : `.${importSpecifier.substring(pkgName.length)}`;
const pkgDr = pkgJsonPath.replace('package.json', '');
const pkgURL = pathToFileURL(pkgDr);
const pkgURL = pathToFileURL(`${pkgPath}/`);

const context = {
importer,
Expand Down Expand Up @@ -157,7 +158,7 @@ async function resolveId({
}

if (!preserveSymlinks) {
if (await exists(location)) {
if (await fileExists(location)) {
location = await realpath(location);
}
}
Expand Down
@@ -0,0 +1,3 @@
import { main } from "dependency/dist/something.js";

export default main();

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions packages/node-resolve/test/test.js
Expand Up @@ -124,6 +124,20 @@ test('supports JS extensions in TS when referring to TS imports', async (t) => {
t.is(module.exports, 'It works!');
});

test("handles package.json being a directory earlier in the path", async (t) => {
const bundle = await rollup({
input: "package-json-in-path/package.json/main.js",
onwarn: () => t.fail("No warnings were expected"),
plugins: [
nodeResolve({
extensions: [".js"],
}),
],
});
const { module } = await testBundle(t, bundle);
t.is(module.exports, "It works!");
});

test('ignores IDs with null character', async (t) => {
const result = await nodeResolve().resolveId('\0someid', 'test.js');
t.is(result, null);
Expand Down