Skip to content

Commit

Permalink
[resolvers/webpack] [New] add support for webpack5 'externals function'
Browse files Browse the repository at this point in the history
  • Loading branch information
jet2jet authored and ljharb committed Apr 3, 2021
1 parent 7974acc commit da647d7
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 9 deletions.
3 changes: 3 additions & 0 deletions resolvers/webpack/.eslintrc
Expand Up @@ -3,4 +3,7 @@
"import/no-extraneous-dependencies": 1,
"no-console": 1,
},
"env": {
"es6": true,
},
}
9 changes: 7 additions & 2 deletions resolvers/webpack/CHANGELOG.md
Expand Up @@ -5,8 +5,11 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel

## Unreleased

### Added
- add support for webpack5 'externals function' ([#2023], thanks [@jet2jet])

### Changed
- Add warning about async Webpack configs ([#1962], thanks [@ogonkov])
- Add warning about async Webpack configs ([#1962], thanks [@ogonkov])
- Replace node-libs-browser with is-core-module ([#1967], thanks [@andersk])

## 0.13.0 - 2020-09-27
Expand Down Expand Up @@ -141,6 +144,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
- `interpret` configs (such as `.babel.js`).
Thanks to [@gausie] for the initial PR ([#164], ages ago! 😅) and [@jquense] for tests ([#278]).

[#2023]: https://github.com/benmosher/eslint-plugin-import/pull/2023
[#1967]: https://github.com/benmosher/eslint-plugin-import/pull/1967
[#1962]: https://github.com/benmosher/eslint-plugin-import/pull/1962
[#1705]: https://github.com/benmosher/eslint-plugin-import/pull/1705
Expand Down Expand Up @@ -200,4 +204,5 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
[@migueloller]: https://github.com/migueloller
[@opichals]: https://github.com/opichals
[@andersk]: https://github.com/andersk
[@ogonkov]: https://github.com/ogonkov
[@ogonkov]: https://github.com/ogonkov
[@jet2jet]: https://github.com/jet2jet
47 changes: 40 additions & 7 deletions resolvers/webpack/index.js
Expand Up @@ -140,13 +140,14 @@ exports.resolve = function (source, file, settings) {

log('Using config: ', webpackConfig);

const resolveSync = getResolveSync(configPath, webpackConfig, cwd);

// externals
if (findExternal(source, webpackConfig.externals, path.dirname(file))) {
if (findExternal(source, webpackConfig.externals, path.dirname(file), resolveSync)) {
return { found: true, path: null };
}

// otherwise, resolve "normally"
const resolveSync = getResolveSync(configPath, webpackConfig, cwd);

try {
return { found: true, path: resolveSync(path.dirname(file), source) };
Expand Down Expand Up @@ -323,15 +324,15 @@ function makeRootPlugin(ModulesInRootPlugin, name, root) {
}
/* eslint-enable */

function findExternal(source, externals, context) {
function findExternal(source, externals, context, resolveSync) {
if (!externals) return false;

// string match
if (typeof externals === 'string') return (source === externals);

// array: recurse
if (Array.isArray(externals)) {
return externals.some(function (e) { return findExternal(source, e, context); });
return externals.some(function (e) { return findExternal(source, e, context, resolveSync); });
}

if (isRegex(externals)) {
Expand All @@ -340,13 +341,45 @@ function findExternal(source, externals, context) {

if (typeof externals === 'function') {
let functionExternalFound = false;
externals.call(null, context, source, function(err, value) {
const callback = function (err, value) {
if (err) {
functionExternalFound = false;
} else {
functionExternalFound = findExternal(source, value, context);
functionExternalFound = findExternal(source, value, context, resolveSync);
}
});
};
// - for prior webpack 5, 'externals function' uses 3 arguments
// - for webpack 5, the count of arguments is less than 3
if (externals.length === 3) {
externals.call(null, context, source, callback);
} else {
const ctx = {
context,
request: source,
contextInfo: {
issuer: '',
issuerLayer: null,
compiler: '',
},
getResolve: () => (resolveContext, requestToResolve, cb) => {
if (cb) {
try {
cb(null, resolveSync(resolveContext, requestToResolve));
} catch (e) {
cb(e);
}
} else {
log('getResolve without callback not supported');
return Promise.reject(new Error('Not supported'));
}
},
};
const result = externals.call(null, ctx, callback);
// todo handling Promise object (using synchronous-promise package?)
if (result && typeof result.then === 'function') {
log('Asynchronous functions for externals not supported');
}
}
return functionExternalFound;
}

Expand Down
29 changes: 29 additions & 0 deletions resolvers/webpack/test/externals.js
Expand Up @@ -9,6 +9,13 @@ const webpack = require('../index');
const file = path.join(__dirname, 'files', 'dummy.js');

describe('externals', function () {
const settingsWebpack5 = {
config: require(path.join(__dirname, './files/webpack.config.webpack5.js')),
};
const settingsWebpack5Async = {
config: require(path.join(__dirname, './files/webpack.config.webpack5.async-externals.js')),
};

it('works on just a string', function () {
const resolved = webpack.resolve('bootstrap', file);
expect(resolved).to.have.property('found', true);
Expand All @@ -32,4 +39,26 @@ describe('externals', function () {
expect(resolved).to.have.property('found', true);
expect(resolved).to.have.property('path', null);
});

it('works on a function (synchronous) for webpack 5', function () {
const resolved = webpack.resolve('underscore', file, settingsWebpack5);
expect(resolved).to.have.property('found', true);
expect(resolved).to.have.property('path', null);
});

it('works on a function (synchronous) which uses getResolve for webpack 5', function () {
const resolved = webpack.resolve('graphql', file, settingsWebpack5);
expect(resolved).to.have.property('found', true);
expect(resolved).to.have.property('path', null);
});

it('prevents using an asynchronous function for webpack 5', function () {
const resolved = webpack.resolve('underscore', file, settingsWebpack5Async);
expect(resolved).to.have.property('found', false);
});

it('prevents using a function which uses Promise returned by getResolve for webpack 5', function () {
const resolved = webpack.resolve('graphql', file, settingsWebpack5Async);
expect(resolved).to.have.property('found', false);
});
});
@@ -0,0 +1,21 @@
module.exports = {
externals: [
{ 'jquery': 'jQuery' },
'bootstrap',
async function ({ request },) {
if (request === 'underscore') {
return 'underscore'
}
},
function ({ request, getResolve }, callback) {
if (request === 'graphql') {
const resolve = getResolve()
// dummy call (some-module should be resolved on __dirname)
resolve(__dirname, 'some-module').then(
function () { callback(null, 'graphql') },
function (e) { callback(e) }
)
}
},
],
}
27 changes: 27 additions & 0 deletions resolvers/webpack/test/files/webpack.config.webpack5.js
@@ -0,0 +1,27 @@
module.exports = {
externals: [
{ 'jquery': 'jQuery' },
'bootstrap',
function ({ request }, callback) {
if (request === 'underscore') {
return callback(null, 'underscore')
}
callback()
},
function ({ request, getResolve }, callback) {
if (request === 'graphql') {
const resolve = getResolve()
// dummy call (some-module should be resolved on __dirname)
resolve(__dirname, 'some-module', function (err, value) {
if (err) {
callback(err)
} else {
callback(null, 'graphql')
}
})
} else {
callback()
}
},
],
}

0 comments on commit da647d7

Please sign in to comment.