-
Notifications
You must be signed in to change notification settings - Fork 38
feat: Compatibility with specifier imports #211
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
c9b2a33
a153042
6e3fe6a
2022e5e
f9c7f54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,10 +2,10 @@ | |||||
|
||||||
const getEsmExports = require('./get-esm-exports.js') | ||||||
const { parse: parseCjs } = require('cjs-module-lexer') | ||||||
const { readFileSync } = require('fs') | ||||||
const { readFileSync, existsSync } = require('fs') | ||||||
const { builtinModules } = require('module') | ||||||
const { fileURLToPath, pathToFileURL } = require('url') | ||||||
const { dirname } = require('path') | ||||||
const { dirname, join } = require('path') | ||||||
|
||||||
function addDefault (arr) { | ||||||
return new Set(['default', ...arr]) | ||||||
|
@@ -27,6 +27,64 @@ | |||||
|
||||||
const urlsBeingProcessed = new Set() // Guard against circular imports. | ||||||
|
||||||
/** | ||||||
* This function looks for the package.json which contains the specifier trying to resolve. | ||||||
* Once the package.json file has been found, we extract the file path from the specifier | ||||||
* @param {*} specifier The specifier that is being search for inside the imports object | ||||||
* @param {*} fromUrl The url from which the search starts from | ||||||
* @returns file to url to file export | ||||||
*/ | ||||||
function resolvePackageImports (specifier, fromUrl) { | ||||||
if (!specifier.startsWith('#')) { | ||||||
return null | ||||||
} | ||||||
|
||||||
BridgeAR marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
try { | ||||||
const fromPath = fileURLToPath(fromUrl) | ||||||
let currentDir = dirname(fromPath) | ||||||
|
||||||
// search for package.json file which has the real url to export | ||||||
while (currentDir !== dirname(currentDir)) { | ||||||
const packageJsonPath = join(currentDir, 'package.json') | ||||||
|
||||||
if (existsSync(packageJsonPath)) { | ||||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) | ||||||
timfish marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
if (packageJson.imports && packageJson.imports[specifier]) { | ||||||
const imports = packageJson.imports[specifier] | ||||||
|
||||||
// Look for path inside packageJson | ||||||
let resolvedPath | ||||||
if (typeof imports == 'object') { | ||||||
|
||||||
const requireSpecifier = imports.require | ||||||
const importSpecifier = imports.import | ||||||
// look for the possibility of require and import which is standard for CJS/ESM | ||||||
if (requireSpecifier || importSpecifier) { | ||||||
// trying to resolve based on order of importance | ||||||
resolvedPath = requireSpecifier.node || requireSpecifier.default || importSpecifier.node || importSpecifier.default | ||||||
BridgeAR marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
} else if (imports.node || imports.default) { | ||||||
resolvedPath = imports.node || imports.default | ||||||
} | ||||||
} else if (typeof imports == 'string') { | ||||||
resolvedPath = imports | ||||||
} | ||||||
|
||||||
if (existsSync(resolvedPath)) { | ||||||
return resolvedPath | ||||||
} | ||||||
} | ||||||
// return if we find a package.json but did not find an import | ||||||
return null | ||||||
} | ||||||
currentDir = dirname(currentDir) | ||||||
} | ||||||
} catch (error) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Alternatively, we could also just add the |
||||||
throw new Error(`Failed to find sub path export: ${specifier}`) | ||||||
} | ||||||
|
||||||
return null | ||||||
} | ||||||
|
||||||
async function getCjsExports (url, context, parentLoad, source) { | ||||||
if (urlsBeingProcessed.has(url)) { | ||||||
return [] | ||||||
|
@@ -46,8 +104,19 @@ | |||||
if (re === '.') { | ||||||
re = './' | ||||||
} | ||||||
|
||||||
let newUrl | ||||||
// Entries in the import field should always start with # | ||||||
if (re.startsWith('#')) { | ||||||
re = resolvePackageImports(re, url) | ||||||
if (!re) { | ||||||
// Unable to resolve specifier import | ||||||
return | ||||||
} | ||||||
} | ||||||
|
||||||
// Resolve the re-exported module relative to the current module. | ||||||
const newUrl = pathToFileURL(require.resolve(re, { paths: [dirname(fileURLToPath(url))] })).href | ||||||
newUrl = pathToFileURL(require.resolve(re, { paths: [dirname(fileURLToPath(url))] })).href | ||||||
|
||||||
if (newUrl.endsWith('.node') || newUrl.endsWith('.json')) { | ||||||
return | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"name": "test-fixtures", | ||
"imports": { | ||
"#main-entry-point": { | ||
"require": { | ||
"node": "./something.js", | ||
"default": "./something.js" | ||
}, | ||
"import": { | ||
"node":"./something.mjs", | ||
"default": "./something.js" | ||
} | ||
}, | ||
"#main-entry-point-string" : "./something.js", | ||
"#main-entry-point-external" : "some-external-cjs-module" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = { ...require('#main-entry-point-external') } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = { ...require('#main-entry-point-string') } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = { ...require('#main-entry-point') } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { foo } from '../fixtures/specifier-external.js' | ||
import Hook from '../../index.js' | ||
import { strictEqual } from 'assert' | ||
|
||
Hook((exports, name) => { | ||
if (name.endsWith('fixtures/specifier-external.js')) { | ||
exports.foo = 'bar2' | ||
} | ||
}) | ||
|
||
strictEqual(foo, 'bar2') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { foo } from '../fixtures/specifier.js' | ||
import Hook from '../../index.js' | ||
import { strictEqual } from 'assert' | ||
|
||
Hook((exports, name) => { | ||
if (name.endsWith('fixtures/specifier.js')) { | ||
exports.foo = 1 | ||
} | ||
}) | ||
|
||
strictEqual(foo, 1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { foo } from '../fixtures/specifier-string.js' | ||
import Hook from '../../index.js' | ||
import { strictEqual } from 'assert' | ||
|
||
Hook((exports, name) => { | ||
if (name.endsWith('fixtures/specifier-string.js')) { | ||
exports.foo = 1 | ||
} | ||
}) | ||
|
||
strictEqual(foo, 1) |
Uh oh!
There was an error while loading. Please reload this page.