-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
76 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { createRequire } from 'module' | ||
|
||
import { PluginError, UserError } from '../../error/main.js' | ||
import { wrapError } from '../../error/wrap.js' | ||
import { PLUGINS_IMPORT_BASE } from '../normalize/cwd.js' | ||
|
||
import { getModuleId } from './id.js' | ||
|
||
// Builtin modules are lazy loaded for performance reasons. | ||
// The return value is shallow merged to make it a plain object instead of | ||
// a dynamic `Module` instance. | ||
export const importPlugin = async function ({ | ||
id, | ||
type, | ||
selectProp, | ||
modulePrefix, | ||
builtins, | ||
isCombinationDimension, | ||
}) { | ||
const moduleId = getModuleId(id, type, isCombinationDimension) | ||
const builtin = builtins[moduleId] | ||
|
||
if (builtin !== undefined) { | ||
return await builtin() | ||
} | ||
|
||
const moduleName = `${modulePrefix}${moduleId}` | ||
const pluginPath = safeGetPluginPath({ | ||
moduleName, | ||
type, | ||
selectProp, | ||
base: PLUGINS_IMPORT_BASE, | ||
}) | ||
|
||
try { | ||
return await import(pluginPath) | ||
} catch (error) { | ||
throw wrapError( | ||
error, | ||
`Could not load "${type}" module "${moduleId}"\n\n`, | ||
PluginError, | ||
) | ||
} | ||
} | ||
|
||
// Find the local file path of a plugin. | ||
// We enforce a naming convention for all plugins. | ||
// All plugins are Node modules. | ||
// We do not allow npm @scope because: | ||
// - This is simpler for users | ||
// - This prevent the confusion (which could be malicious) created by the | ||
// ambiguity | ||
// TODO: use import.meta.resolve() when available | ||
const safeGetPluginPath = function ({ moduleName, type, selectProp, base }) { | ||
try { | ||
return getPluginPath(moduleName, type, base) | ||
} catch (error) { | ||
throw wrapError(error, `Configuration property "${selectProp}"`, UserError) | ||
} | ||
} | ||
|
||
export const getPluginPath = function (moduleName, type, base) { | ||
const { resolve } = createRequire(new URL(base, import.meta.url)) | ||
|
||
try { | ||
return resolve(moduleName) | ||
} catch (error) { | ||
throw wrapError( | ||
error, | ||
`must be a valid package name: "${moduleName}". | ||
This Node module was not found, please ensure it is installed.\n\n`, | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters