Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Feb 20, 2022
1 parent 0460fe4 commit 42b8710
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 205 deletions.
27 changes: 17 additions & 10 deletions src/config/plugin/lib/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { pathToFileURL } from 'url'
import { wrapError } from '../../../error/wrap.js'

import { PluginError } from './error.js'
import { isBuiltinLocation, isInlineLocation } from './location.js'

// Builtin modules are lazy loaded for performance reasons.
// The return value is shallow cloned to make it a plain object instead of
Expand All @@ -13,10 +12,11 @@ import { isBuiltinLocation, isInlineLocation } from './location.js'
// properties by reference.
export const importPlugin = async function (
location,
locationType,
{ name, builtins, pluginProp },
) {
try {
return await importPluginByLocation(location, builtins)
return await IMPORTERS[locationType](location, builtins)
} catch (error) {
throw wrapError(
error,
Expand All @@ -26,16 +26,23 @@ export const importPlugin = async function (
}
}

const importPluginByLocation = async function (location, builtins) {
if (isInlineLocation(location)) {
return { plugin: location }
}
const importInline = function (location) {
return { plugin: location }
}

if (isBuiltinLocation(location, builtins)) {
const builtinPlugin = await builtins[location]()
return { plugin: { ...builtinPlugin } }
}
const importBuiltin = async function (location, builtins) {
const builtinPlugin = await builtins[location]()
return { plugin: { ...builtinPlugin } }
}

const importPath = async function (location) {
const plugin = await import(pathToFileURL(location))
return { plugin: { ...plugin }, path: location }
}

const IMPORTERS = {
inline: importInline,
builtin: importBuiltin,
path: importPath,
module: importPath,
}
35 changes: 21 additions & 14 deletions src/config/plugin/lib/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,42 @@ import { wrapError } from '../../../error/wrap.js'
import { normalizePluginConfig } from './config.js'
import { PluginError } from './error.js'
import { importPlugin } from './import.js'
import { normalizeItem } from './item.js'
import { getLocationInfo } from './location_info.js'
import { normalizeLocation } from './location_normalize.js'
import { normalizeShape } from './shape.js'
import { normalizePluginConfigTop } from './top.js'

// Get each `pluginInfo`, i.e. normalized `plugin` + `pluginConfig`
export const getPluginInfo = async function (pluginConfig, opts) {
const {
[opts.pluginProp]: location,
moduleId,
...pluginConfigA
} = await normalizeItem(pluginConfig, opts)
const pluginConfigA = await normalizePluginConfigTop(pluginConfig, opts)
const { originalLocation, locationType } = getLocationInfo(
pluginConfigA,
opts,
)

try {
const { plugin, path } = await importPlugin(location, opts)
const { pluginConfig: pluginConfigB, location } = await normalizeLocation(
pluginConfigA,
locationType,
opts,
)
const { plugin, path } = await importPlugin(location, locationType, opts)
const { config: pluginConfigDefinitions, ...pluginA } =
await normalizeShape(plugin, moduleId, opts)
const pluginConfigB = await normalizePluginConfig({
pluginConfig: pluginConfigA,
await normalizeShape({ plugin, locationType, originalLocation, opts })
const pluginConfigC = await normalizePluginConfig({
pluginConfig: pluginConfigB,
plugin: pluginA,
pluginConfigDefinitions,
opts,
})
return { plugin: pluginA, path, config: pluginConfigB }
return { plugin: pluginA, path, config: pluginConfigC }
} catch (error) {
throw handlePluginError(error, location)
throw handlePluginError(error, originalLocation)
}
}

const handlePluginError = function (error, location) {
const handlePluginError = function (error, originalLocation) {
return error instanceof PluginError
? wrapError(error, `Invalid plugin "${location}":`)
? wrapError(error, `Invalid plugin "${originalLocation}":`)
: error
}
99 changes: 0 additions & 99 deletions src/config/plugin/lib/item.js

This file was deleted.

65 changes: 0 additions & 65 deletions src/config/plugin/lib/location.js

This file was deleted.

39 changes: 39 additions & 0 deletions src/config/plugin/lib/location_info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { isAbsolute } from 'path'

// `pluginConfig[pluginProp]` can be:
// - The direct value
// - This is useful for programmatic usage,
// - For example, by exposing to plugin consumers a function like:
// (pluginConfig) => ({ plugin, ...pluginConfig })
// which is passed as argument to this library
// - A builtin identifier among `opts.builtins`
// - A file path starting with . or /
// - A Node module prefixed with `modulePrefix` (which is optional)
export const getLocationInfo = function (
pluginConfig,
{ pluginProp, builtins },
) {
const originalLocation = pluginConfig[pluginProp]
const locationType = getLocationType(originalLocation, builtins)
return { originalLocation, locationType }
}

const getLocationType = function (originalLocation, builtins) {
if (typeof originalLocation !== 'string') {
return 'inline'
}

if (builtins[originalLocation] !== undefined) {
return 'builtin'
}

if (isPathLocation(originalLocation)) {
return 'path'
}

return 'module'
}

const isPathLocation = function (originalLocation) {
return originalLocation.startsWith('.') || isAbsolute(originalLocation)
}
82 changes: 82 additions & 0 deletions src/config/plugin/lib/location_normalize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { validateObjectOrString } from '../../normalize/validate/complex.js'
import {
validateFileExists,
validateRegularFile,
} from '../../normalize/validate/fs.js'
import { validateDefinedString } from '../../normalize/validate/simple.js'

import { CoreError, ConsumerError } from './error.js'
import { resolveModuleLocation } from './module.js'
import { safeNormalizeConfig } from './normalize.js'

// Normalize a single `pluginConfig`
export const normalizeLocation = async function (
pluginConfig,
locationType,
{ name, cwd, pluginProp, builtins, modulePrefix },
) {
const locationDefinitions = getLocationDefinitions(locationType, pluginProp)
const { [pluginProp]: location, ...pluginConfigA } =
await safeNormalizeConfig(pluginConfig, locationDefinitions, {
context: { locationType, builtins, modulePrefix },
cwd,
prefix: `${name}.`,
UserErrorType: ConsumerError,
SystemErrorType: CoreError,
})
return { pluginConfig: pluginConfigA, location }
}

const getLocationDefinitions = function (locationType, pluginProp) {
return [
{ name: pluginProp, ...normalizeLocationProp },
{ name: pluginProp, ...NORMALIZE_LOCATIONS[locationType] },
]
}

export const getExampleLocation = function (value, { context: { builtins } }) {
const builtinNames = Object.keys(builtins)
return builtinNames.length !== 0 && builtinNames[0].trim() !== ''
? builtinNames[0]
: undefined
}

const normalizeLocationProp = {
required: true,
example: getExampleLocation,
}

const normalizeInlineLocation = {
validate: validateObjectOrString,
}

const normalizeBuiltinLocation = {}

const normalizePathLocation = {
path: true,
async validate(location) {
await validateFileExists(location)
await validateRegularFile(location)
},
}

const normalizeModuleLocation = {
validate(value, { context: { modulePrefix } }) {
validateDefinedString(value)
validateHasModulePrefix(modulePrefix)
},
transform: resolveModuleLocation,
}

const validateHasModulePrefix = function (modulePrefix) {
if (modulePrefix === undefined) {
throw new Error('must start with . or / when it is a file path.')
}
}

const NORMALIZE_LOCATIONS = {
inline: normalizeInlineLocation,
builtin: normalizeBuiltinLocation,
path: normalizePathLocation,
module: normalizeModuleLocation,
}
Loading

0 comments on commit 42b8710

Please sign in to comment.