Я думаю, вы могли бы использовать опции resolve.modules
, так как он делает то, что вы описываете. Однако, поскольку речь идет о плагине для разрешения проблем, я постараюсь написать и его.
Resolve.modules
Вот пример с lodash
в качестве целевого модуля. Мы можем настроить структуру следующим образом:
node_modules
`--lodash
src
|--lodash
| `-- zip.js
`-index.js
zip.js
может быть чем-то вроде export default () => 'local zip'
В нашем webpack.config.js
делаем
module.exports = {
...
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
...
}
В нашем index.js давайте импортируем zip
и isObject
.
// src/index.js
import zip from 'lodash/zip';
import isObject from 'lodash/isObject';
console.log(zip()); // 'local zip'
console.log(isObject({ a: 100 })); // 'true'
Это, по сути, то, что вы хотите, но вместо записи относительного пути к вашим пользовательским компонентам, вместо этого вы пишете путь к модулю.
Разрешить плагин
Но поскольку вопрос касается плагина, давайте попробуем! Я прокомментировал ваши вопросы раньше, но затем обнаружил, что система плагинов изменилась в веб-пакете 4. Я нахожусь на узле v10, поэтому некоторые синтаксисы могут не работать в более старых версиях.
Структура каталогов:
node_modules
`--lodash
src
|--components
| `-- zip.js
`-index.js
Сначала краткий обзор плагина разрешения. Webpack позволяет нам использовать несколько хуков в конвейере разрешения ( полный список вы можете увидеть здесь ). Мы особенно заинтересованы в resolve
, parsedResolve
и module
. Наш план:
1. Tap into the `resolve` hook
2. Is the resolve request points to our 'components' folder?
- If not, go to **step 3**.
- If yes, is there something there it can use?
- If not, point it to `lodash` module instead.
- If yes, go to **step 3**.
3. Continue to the next hook in the pipeline (`parsedResolve`).
Когда мы подключаемся к крючку, мы получим очень полезный request
объект с этими опорами:
context
: содержит эмитента (абсолютный путь к index.js
)
path
: каталог эмитента (абсолютный путь к src
)
request
: строка запроса ('./components/zip')
С этим мы можем написать наш плагин:
const path = require('path');
const fs = require('fs');
class CustomResolverPlugin {
constructor ({ dir, moduleName }) {
this.dir = dir; // absolute path to our 'components' folder
this.moduleName = moduleName; // name of the module, 'lodash' in this case
}
apply(resolver) {
resolver.getHook('resolve').tapAsync('CustomResolverPlugin', (request, resolveContext, callback) => {
// 1. check if the request is point to our component folder
// resolver.join is same as path.join, but memoized
const { dir } = path.parse(resolver.join(request.path, request.request));
const match = dir === this.dir;
if (match) {
// 2. get the name of the file being requested & check if it exists.
// in import zip from `./src/components/zip`, 'zip' is the name.
const { name } = path.parse(request.request);
const pathExist = fs.existsSync(path.join(this.dir, `${name}`));
if (!pathExist) {
// create a new request object.
// we'll swap the request to something like 'lodash/zip'
const _request = {
...request,
request: `${this.moduleName}/${name}`
}
// swap the target hook to 'module' to resolve it as a module.
const _target = resolver.ensureHook('module');
return resolver.doResolve(_target, _request, null, resolveContext, callback);
}
}
// 3. otherwise continue to the next hook
const target = resolver.ensureHook('parsedResolve');
return resolver.doResolve(target, request, null, resolveContext, callback);
});
}
}
Использование в webpack.config.js
module.exports = {
...
resolve: {
plugins: [
new CustomResolverPlugin({
dir: path.resolve(__dirname, './src/components'),
moduleName: 'lodash',
}),
],
},
...
}
В вашем index.js:
import zip from './components/zip';
import isObject from './components/isObject';
console.log(zip(['a', 'b'], [1, 2])); // 'local zip'
console.log(isObject({ a: 100 })); // true
Надеюсь, это поможет!