Sorry in advance if this is covered in other issues. I looked through them and watched the video/blog post/README, but I'm still a little confused.
I came across esm because I saw it was used to allow mocking modules in sinon (#266). I'm basically having the same problem with my jasmine unit tests, and they started when I upgraded to webpack 4 (because they made their modules immutable). I'm hoping to be able to say
import * as foo from 'foo'
...
spyOn(foo, 'bar');
without having to change my babel configuration like described in this comment. I currently have @babel/preset-env with modules set to false.
The thing I'm confused about is if jasmine needs to use esm when it builds the library or if I use it when running my unit tests. I saw the example for jasmine that was linked in the readme, but I am running my unit tests using karma and karma-webpack so it didn't seem like it would work.
Also, if I create a fork (using child_process.fork), will esm work in the forked process or will I need to do something to get it working?
Also, if you'd like, I'm happy to help update the readme or whatever docs after I finally wrap my head around everything so that people don't have the same confusion I do.
Thanks again!
Hi @bdwain!
The good news is yes esm can help with your spyOn() case. I also found a way to support it using a jasmine config using the helpers field
"helpers": [
"../../../node_modules/esm"
]
The bad news is esm doesn't recognize its higher privileged mode when used in the helper field. So it won't work juuust yet with it. I need to update its detection and then that will be a sure fire way to support it. If you want you can prep a PR for the updated guidance to use the helpers field. I'll merge it when I've added support.
thanks for getting back so quick @jdalton! I'll make a docs PR soon, though I'll probably wait till I get it working since I'll have a better understanding of it then.
I also had one other question. I use a tool called sambac to run individual jasmine tests in the browser so that they are debuggable and I'm trying to figure out the best way to use esm with that too. The tool creates a forked process in node and then basically runs webpack-dev-server (via its node API) to create a runnable bundle for each spec file, but jasmine is only added once in the browser. It uses webpack-jasmine-html-runner-plugin to create an html page for each spec file that will run jasmine and load the generated bundle, so there's not really a place to use the jasmine helpers option.
Here's the code that actually runs webpack in sambac.
let compiler = webpack(webpackConfig);
let webpackServer = new webpackDevServer(compiler, {
stats: {
colors: true
},
contentBase: [process.cwd(), path.resolve(__dirname, '..', '..')]
});
webpackServer.listen(parseInt(process.argv[4]));
I figured if I just loaded esm in the node process running sambac, that would help, but it didn't seem to make a difference. I ran node -r esm node_modules/sambac/bin/sambac.js but things still failed (I also added the mutableNamespace option to my package.json). I figured maybe the forked process was causing esm to not be loaded, but since I'm running webpack via its node api and not the CLI, i didn't have access to a register option that I knew of. From what I can tell, the register option just requires whatever your pass it and not much more than that, so I tried adding require('esm') to the top of the forked process that runs webpack, but had no luck.
Any ideas what I can do to get it running with esm? Thanks!
Ah thanks for clarifying @bdwain!
The esm loader is for running code in Node. If the code you're running is not in Node and is running in the browser via some kind of bundle then you'll need to tackle it at the transpile/bundle level. For webpack you can create a custom plugin that removes the read-only namespace object additions. See our webpack.config.js for how we do it for our own bundle of esm. The bit you'll be interested in are the tweaks to the __webpack_require__.n helper.
Ah got it. Thanks for clarifying. I think that might apply to both of of my
cases then since karma runs your tests in a browser also.
On Thu, Dec 13, 2018 at 8:08 AM John-David Dalton notifications@github.com
wrote:
Ah thanks for clarifying @bdwain https://github.com/bdwain!
The esm loader is for running code in Node. If the code you're running is
not in Node and is running in the browser via some kind of bundle then
you'll need to tackle it at the transpile/bundle level. For webpack you can
create a custom plugin that removes the read-only namespace object
additions. Here's how we do it in our own built bundle of esm. The bit
you'll be interested in are the tweaks to the __webpack_require__.n
helper.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/standard-things/esm/issues/687#issuecomment-446980495,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ADzDDuRnexyB5JJ8RV5h_jZJ8Q4LAcn7ks5u4l9LgaJpZM4ZQb8K
.
fyi @jdalton I tried copying the webpack plugin you have in my webpack config but it seems to have an issue with this code
import enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
enzyme.configure({ adapter: new Adapter() });
//transpiled
enzyme__WEBPACK_IMPORTED_MODULE_0___default.a.configure({
adapter: new enzyme_adapter_react_16__WEBPACK_IMPORTED_MODULE_1___default.a()
});
enzyme__WEBPACK_IMPORTED_MODULE_0___default is a function
enzyme__WEBPACK_IMPORTED_MODULE_0___default
Æ’ () { return exported }
so it should read (note the parentheses)
enzyme__WEBPACK_IMPORTED_MODULE_0___default().a.configure({
adapter: new enzyme_adapter_react_16__WEBPACK_IMPORTED_MODULE_1___default.a()
});
I'm still messing around with this and it's possible that it's just that I'm not using all of esm and only this webpack plugin, but figured I'd let you know.
Thanks again!
Ya, you may need more of the helpers than we do for the esm bundle. Instead of replacing the entire "MainTemplate" result like we do you might do a string.replace() on it.
Look to the first argument of the tap'ed template:
hooks.requireExtensions.tap("MainTemplate", (somethingHere) => {
// ...
})
Between your advice and this comment, I think I've got something working.
function AllowMutateEsmExports() {}
AllowMutateEsmExports.prototype.apply = function(compiler) {
compiler.hooks.compilation.tap('AllowMutateEsmExports', function(compilation) {
compilation.mainTemplate.hooks.requireExtensions.tap('AllowMutateEsmExports', source =>
source.replace(
'Object.defineProperty(exports, name, { enumerable: true, get: getter });',
[
"var proxyName = '__mutateESM__' + name;",
" exports[proxyName] = getter;",
" var newGetter = function(){return exports[proxyName]()};",
" var setter = function(val){exports[proxyName] = function(){return val}};",
" Object.defineProperty(exports, name, { enumerable: true, get: newGetter, set: setter });"
].join('\n')
)
);
});
};
Thanks again! I really appreciate the help.