Webpack renaming imported modules in transpiled test files - javascript

My test files are written in javascript but external modules are imported using es6 import syntax. However, when I transpile my test files using awesome-typescript-loader, the test files contains the imported module assigned to a different variable.
Hence, when I try to check any spy calls on the imported module's method in my tests, they fail. My guess is that the imported modules and the actual module which is called inside my .ts files are different. Any ideas what's happening?
My webpac.karma.config file used to transpile the test files is:
module.exports = {
module: {
loaders: [{
test: /\.ts$/,
loader: 'awesome-typescript-loader'
}]
},
output: {
path: __dirname + "/unittests/"
},
resolve: {
modules: ["node_modules"],
extensions: [".ts", ".js"]
}
};
My test file (example)...
import { HELLO } from './hello';
describe('check', function(){
it('should call friend method', function() {
spyOn(HELLO, 'friend');
$ctrl.methodThatCallsFriendMethod();
expect(HELLO.friend).toHaveBeenCalled(); // This is not called
});
})
In the transpiled test file inside unittests folder, the module is imported like this:
Object.defineProperty(exports, "__esModule", { value: true });
var hello_1 = __webpack_require__(5); // I fear this hello_1 does not reference to the same module imported in my main.ts file
...
...
describe('should call friend method', function() {
it('should call friend method', function() {
spyOn(hello_1.HELLO, 'friend');
$ctrl.methodThatCallsFriendMethod();
expect(hello_1.HELLO.friend).toHaveBeenCalled(); // This is not called
});
});
My HELLO module is very simple:
export class Hello {
static friend() {
return 'dneirf;
}
}
In my main ts file, I import the HELLO module normally (import {HELLO} from ...) and all works fine. It just doesn't work in test files. Thanks for any suggestions.

Related

Get class functions from webpack module

I need to call class function, what writed in webpack module from outside script.
There is webpack config:
entry: {
main: ["./src/js/main.js"],
},
performance: {
hints: false,
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/js"),
},
optimization: optimization(),
module: {
rules: ifBabel(),
},
In main.js i have
import { finishing } from "./modules/forms/finishing";
var fns = new finishing();
fns.init();
I need to call some function of fns from outside script. But webpack compiling script inside his function and i cant access to it..
You have 2 primary options:
Use Webpack's library to define how your Webpack bundle exposes itself to the page when loaded, and have your main.js export functions to be used by your page via the library, like
import { foo } from "./foo";
export { foo };
with the page then able to use LibraryName.foo()
Have main.js explicitly do something like
import { foo } from "./foo";
window.foo = foo;
to manually expose specific functions on the window or some other location.
I would recommend the first approach, but either is fine.

bundle web workers as integral part of npm package with single file webpack output

I am writing an npm package which is a plugin for the popular library leafletjs. I am using webpack to bundle the package. I want this package to be able to spawn and destroy some web workers on command. The web worker code is part of my source files. But I want to be able to distribute my package both as an npm module, or through a cdn, meaning it must be compiled down to a singular file that can be included through an HTML header. I am using webpack to do it. So lets say I have a worker file:
// sample.worker.js
import { doSomeStuff } from './algorithm.js';
onmessage = function (e) {
const results = doSomeStuff(e.data)
postMessage({
id: e.data.id,
message: results',
});
};
Fairly simple, but an important point here is that my worker is actually importing some code from an algorithm file, which is in turn importing some node modules. My worker is used in the main module somewhere, like this:
// plugin.js
import SampleWorker from 'worker-loader!./workers/dem.worker.js';
export function plugin(){
// do some stuff
const worker = new SampleWorker()
worker.postMessage(someData);
worker.onmessage = (event) => {};
}
My webpack config looks like this:
module.exports = {
entry: './src/index',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'my-plugin.js',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
},
module: {
rules: [
{
test: /\.worker\.js$/,
loader: 'worker-loader',
options: {
inline: 'fallback',
},
},
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
],
},
externals: { ... },
plugins: { ... }
};
This doesn't work as is - webpack tries to bundle the main bundle and each worker script file under the same name. Changing to filename: '[name].js' under output fixes this issue, but gives me many files - one for the main bundle, and another file for each worker file in my source code.
Reading the webpack options, I thought that using the inline: 'fallback' option would actually create a Blob for each worker and bundle that into the main output file. That is not happening.
So far my solution is to write my workers as blobs, like this:
// workers.js
const workerblob = new Blob([`
// cannot import in a blob!
// have to put algorithm.js code here, copy in any external module code here
onmessage = function (e) {
const results = doSomeStuff(e.data)
postMessage({
id: e.data.id,
message: results,
});
};
`])
export const sampleWorker = URL.createObjectURL(workerblob);
// my-plugin.js
import { sampleWorker } from 'workers.js'
const worker = new Worker(sampleWorker)
This does in fact work - webpack now outputs 1 single file which includes the worker code. Using a modified version of this from this answer, I can at least place my code inside a function( ...code... ){}.toString() format, so I can at least get intellisense, syntax highlighting, etc. But I cannot use imports.
How can I use webpack to bundle my workers so that the entire bundle ends up in 1 file, worker code and all?

Keep a ES module outside the bundle

My application has a configuration file
external-config.js
import {constants} from "./constants.js";
export const ExternalConfig = {
title: "My application",
version: "2.0",
constants: constants
list: ["uno", "due", "tre"]
}
I don't want it to be bundled with the application, I want to keep it outside. I tried the IgnorePlugin, but apparently this simply breaks the dependency graph and I get the error ReferenceError: Config is not defined even if the reference to the path of the config file in the budle is correct.
plugins: [
new webpack.IgnorePlugin({
checkResource (resource) {
if (resource === "./conf/external-config.js") return true;
return false;
}
})
]
I cannot even import it in the main html page like
<script type="module" src="./conf/config.js"></script>
because in this way I couldn't access the object outside its own script.
Is there a way to do that?
EDIT: following #raz-nonen advice, I tried null-loader, it seems it could be a solution. The problem with that is the physical position of the configuration file.
rules: [
{
test: path.resolve(__dirname, "src/conf/external-config.js"),
use: "null-loader"
}
...]
And this is the result in the built script
// EXTERNAL MODULE: ./src/conf/external-config.js
var external_config = __webpack_require__(13);
But the actual position of the configuration in the dist folder is ./conf/external-config.js, not ./src/conf/external-config.js
This is the chunk of my app that consumes the external file
import {ExternalConfig} from "./conf/external-config.js";
class MyApp extends LitElement {
constructor() {
super();
console.log(ExternalConfig.list)
}
}
You'll need to make this file available in the dist folder. You can do that with copy-webpack-plugin
Tell webpack that ExternalConfig will be imported from external. It means that you'll have to take care that it'll be available at runtime. You can do it simply by importing your conf/config.js that you copied from a <script> tag in your index.html.
Add:
externals: {
'conf/external-config': 'conf/external-config'
}
In your webpack configuration.

Export object from typescript through webpack and use it in JS

What I'm trying to do is to bundle a library that I've wrote in Typescript with Webpack 2 and to be able to use this library even with vanilla JS. The building goes fine and without errors, but when it comes to use the exported object it appears like an empty object {}.
So what I've tried to do is the following:
Bundle the library in a bundle.js file
Create a my-js-script.js file that logs to the console the object exported by the .ts file
Create a index.html file
Import both the bundle.js and my-js-script.js files
Serve the index.html file and see the object in the console.
Typescript file
import {SomeStuff} from 'some-stuff';
export class ClassA {
constructor(){
// Does other stuff with SomeStuff
}
}
// What I want to achieve is something like this
export const myModule = new ClassA();
I thought that creating an html file that imports the bundle and my-js-script file were enough in order to have access to the myModule constant. But unfortunately it wasn't enough.
index.html
<html>
<head>
<script src="./bundle.js"></script>
<script src="./my-js-script.js"></script>
</head>
<body>
</body>
</html>
Javascript file
myModule.doSomething();
What am I missing? Or there's simply non chances to do that? The webpack configuration is dead simple
var path = require("path");
module.exports = {
entry: "./src/web-onion.ts",
output: {
path: path.resolve(__dirname, 'dist/'),
filename: "bundle.js"
},
resolve: {
extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
module: {
loaders: [
{ test: /\.tsx?$/, loader: "ts-loader" }
]
}
}
You can't do that this way. myModule won't be exported as a global. If you want to export something globally, use in the TypeScript file:
window.something = myModule
Note: you will have to either extend Window interface or use (window as any).

Using expose-loader with webpack 2 to load a module

I have jwplayer in my lib/ folder because no node_module exists. I tried to use expose-loader in order to be able to import it. In my webpack, I have the following basic setup in order to get this to work:
const path = require('path');
module.exports = {
// ...
module: {
rules: [{
test: /\.jwplayer\.js$/,
use: {
loader: 'expose-loader',
options: 'jwplayer', // have also tried `options: { name: 'jwplayer' }`
},
}]
},
resolve: {
alias: {
jwplayer: path.join(__dirname, './lib/jwplayer-7.7.4/jwplayer.js'),
}
},
externals: {
window: 'Window',
}
};
The strange thing is, jwplayer is exposed on the window object, but it is not available as a module.
import jwplayer from 'jwplayer';
console.log(jwplayer); // Object {} (not jwplayer)
console.log(window.jwplayer); // function jwplayer() {}
Am I loading it incorrectly? How should I load in jwplayer with webpack 2?
That's not how you use the expose loader. The expose loader tells to webpack to expose something to the global context when the bundle is loaded. My understanding is that you want to use jwplayer inside the bundle itself.
You can use the script-loader, that's how I usually import scripts (analytics, for instance)
Actually you can use
externals: ['jwplayer'],
Because externals is for passing global variables inside the bundle to be able to use them as a dependency and then you can import your library as any other
import jwplayer from 'jwplayer';
webpack documentation

Categories

Resources