Skip to content

Commit

Permalink
feat(node-resolve): add support for self-package imports
Browse files Browse the repository at this point in the history
  • Loading branch information
LarsDenBakker committed Dec 12, 2020
1 parent 816786d commit 6e7c827
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 37 deletions.
27 changes: 1 addition & 26 deletions packages/node-resolve/src/package/resolvePackageImports.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
/* eslint-disable no-await-in-loop */
import path from 'path';
import fs from 'fs';
import { pathToFileURL } from 'url';
import { promisify } from 'util';

import { createBaseErrorMsg, InvalidModuleSpecifierError } from './utils';
import { createBaseErrorMsg, findPackageJson, InvalidModuleSpecifierError } from './utils';
import resolvePackageImportsExports from './resolvePackageImportsExports';

const fileExists = promisify(fs.exists);

function isModuleDir(current, moduleDirs) {
return moduleDirs.some((dir) => current.endsWith(dir));
}

async function findPackageJson(base, moduleDirs) {
const { root } = path.parse(base);
let current = base;

while (current !== root && !isModuleDir(current, moduleDirs)) {
const pkgJsonPath = path.join(current, 'package.json');
if (await fileExists(pkgJsonPath)) {
const pkgJsonString = fs.readFileSync(pkgJsonPath, 'utf-8');
return { pkgJson: JSON.parse(pkgJsonString), pkgPath: current, pkgJsonPath };
}
current = path.resolve(current, '..');
}
return null;
}

async function resolvePackageImports({
importSpecifier,
importer,
Expand Down
26 changes: 26 additions & 0 deletions packages/node-resolve/src/package/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
/* eslint-disable no-await-in-loop */
import path from 'path';
import fs from 'fs';
import { promisify } from 'util';

const fileExists = promisify(fs.exists);

function isModuleDir(current, moduleDirs) {
return moduleDirs.some((dir) => current.endsWith(dir));
}

export async function findPackageJson(base, moduleDirs) {
const { root } = path.parse(base);
let current = base;

while (current !== root && !isModuleDir(current, moduleDirs)) {
const pkgJsonPath = path.join(current, 'package.json');
if (await fileExists(pkgJsonPath)) {
const pkgJsonString = fs.readFileSync(pkgJsonPath, 'utf-8');
return { pkgJson: JSON.parse(pkgJsonString), pkgPath: current, pkgJsonPath };
}
current = path.resolve(current, '..');
}
return null;
}

export function isUrl(str) {
try {
return !!new URL(str);
Expand Down
35 changes: 24 additions & 11 deletions packages/node-resolve/src/resolveImportSpecifiers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,29 @@ import { exists, realpath } from './fs';
import { isDirCached, isFileCached, readCachedFile } from './cache';
import resolvePackageExports from './package/resolvePackageExports';
import resolvePackageImports from './package/resolvePackageImports';
import { ResolveError } from './package/utils';
import { findPackageJson, ResolveError } from './package/utils';

const resolveImportPath = promisify(resolve);
const readFile = promisify(fs.readFile);

async function getPackageJson(importer, pkgName, resolveOptions, moduleDirectories) {
if (importer) {
const selfPackageJsonResult = await findPackageJson(importer, moduleDirectories);
if (selfPackageJsonResult && selfPackageJsonResult.pkgJson.name === pkgName) {
// the referenced package name is the current package
return selfPackageJsonResult;
}
}

try {
const pkgJsonPath = await resolveImportPath(`${pkgName}/package.json`, resolveOptions);
const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
return { pkgJsonPath, pkgJson };
} catch (_) {
return null;
}
}

async function resolveId({
importer,
importSpecifier,
Expand Down Expand Up @@ -88,22 +106,17 @@ async function resolveId({
});
location = fileURLToPath(resolveResult);
} else if (pkgName) {
// this is a bare import, resolve using package exports if possible
let pkgJsonPath;
let pkgJson;
try {
pkgJsonPath = await resolveImportPath(`${pkgName}/package.json`, resolveOptions);
pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
} catch (_) {
// if there is no package.json we defer to regular resolve behavior
}
// it's a bare import, find the package.json and resolve using package exports if available
const result = await getPackageJson(importer, pkgName, resolveOptions, moduleDirectories);

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

const context = {
importer,
importSpecifier,
Expand Down

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.

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

3 changes: 3 additions & 0 deletions packages/node-resolve/test/fixtures/self-package-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import selfPkgImport from 'self-package-import/a';

export default selfPkgImport;
16 changes: 16 additions & 0 deletions packages/node-resolve/test/package-entry-points.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,19 @@ test('can override a star pattern using null', async (t) => {

t.true(errors[0].message.includes('Could not resolve import "exports-null-override/foo/a" in '));
});

test('can self-import a package when using exports field', async (t) => {
const bundle = await rollup({
input: 'self-package-import',
onwarn: () => {
t.fail('No warnings were expected');
},
plugins: [nodeResolve()]
});
const { module } = await testBundle(t, bundle);

t.deepEqual(module.exports, {
a: 'a',
b: 'b'
});
});

0 comments on commit 6e7c827

Please sign in to comment.