Thanks for a great plugin! I'd suggest adding dynamic import syntax to the readme! :)
Opened this issue · 2 comments
Not sure if this is working as intended or just a happy byproduct of Babel improvements, but I was able to use rewire APIs not just with top-level ES6 imports but also with dynamic imports using ES6 import() syntax. And with webpack, I can remove cached modules to force their re-evaluation. An example (assuming '../src/someFunc.js'
contains a top-level const Q
that it doesn't export, which in turn has a set named queue
):
it('supports dynamic imports -- sweet', async () => {
const exportedFunc = await import('../src/someFunc.js');
const Q = exportedFunc.__get__('Q');
expect(Q.queue.size).to.equal(0);
Q.queue.add('test');
expect(Q.queue.size).to.equal(1);
// NOTE: The following line is required or webpack will return the same module instance in the next import...
delete require.cache[require.resolve('../src/someFunc.js')];
const exportedFunc = await import('../src/someFunc.js');
const Q = exportedFunc.__get__('Q');
expect(Q.queue.size).to.equal(0);
});
Very happy with how this is working out! :)
If it helps anybody, I'm using open-wc's Karma config with webpack config modified as follows (I've included the original open-wc Karma webpack config inline...):
{
webpack: {
watch: true,
mode: 'development',
devtool: 'inline-cheap-module-source-map',
resolve: {
mainFields: [
// current leading de-facto standard - see https://github.com/rollup/rollup/wiki/pkg.module
'module',
// previous de-facto standard, superceded by `module`, but still in use by some packages
'jsnext:main',
// standard package.json fields
'browser',
'main',
],
},
module: {
rules: [
coverage && {
test: /\.js$/,
loader: require.resolve('istanbul-instrumenter-loader'),
enforce: 'post',
include: path.resolve('./'),
exclude: /node_modules|bower_components|\.(spec|test)\.js$/,
options: {
esModules: true,
},
},
legacy && {
test: /\.js$|\.ts$/,
use: {
loader: 'babel-loader',
options: {
plugins: [
require.resolve('@babel/plugin-syntax-dynamic-import'),
require.resolve('@babel/plugin-syntax-import-meta'),
// webpack does not support import.meta.url yet, so we rewrite them in babel
[require.resolve('babel-plugin-bundled-import-meta'), { importStyle: 'baseURI' }],
].filter(_ => !!_),
presets: [[require.resolve('@babel/preset-env'), { targets: 'IE 11' }]],
},
},
},
!legacy && {
test: /\.js$/,
loader: require.resolve('@open-wc/webpack-import-meta-loader'),
},
{
test: /\.js$|\.ts$/,
exclude: /node_modules|bower_components|\.(spec|test)\.js$/,
use: {
loader: 'babel-loader',
options: {
plugins: [require.resolve('babel-plugin-rewire')].filter(_ => !!_),
sourceMaps: 'both',
},
},
},
].filter(_ => !!_),
},
}
To help mask the above webpack delete nastiness, I made a very simple wrapper using promises:
const importAndForget = filename => {
return import(filename).finally(() => {
delete require.cache[require.resolve(filename)];
});
};
which simplifies the above example to:
it('supports dynamic imports -- sweet', async () => {
importAndForget('../src/someFunc.js').then(module => {
const Q = module.__get__('Q');
expect(Q.queue.size).to.equal(0);
Q.queue.add('test');
expect(Q.queue.size).to.equal(1);
});
importAndForget('../src/someFunc.js').then(module => {
const Q = module.__get__('Q');
expect(Q.queue.size).to.equal(0);
});
});
Had a hard time making the filenames dynamic while still keeping things await-compatible, so I ended up with the following function hard-coded:
function loadSomeFunc() {
delete require.cache[require.resolve('../src/someFunc.js')];
return import('../src/someFunc.js');
}
which can be used as follows:
it('supports dynamic imports -- sweet', async () => {
let module = await loadSomeFunc();
let Q = module.__get__('Q');
expect(Q.queue.size).to.equal(0);
Q.queue.add('test');
expect(Q.queue.size).to.equal(1);
module = await loadSomeFunc();
Q = module.__get__('Q');
expect(Q.queue.size).to.equal(0);
});