Javascript es6 + Webpack referencing imports - javascript

I am trying to work out why i can reference an import from one file, but not the other
(I think it is to do with the way javascript works and that I cannot reference a variable before it is defined... If that is the case then i'm hoping one of you can give me some reference to a workaround for that.)
I am building an electron app with webpack and babel.
my webpack.config.js looks like this:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: {
app: ['./app/src/app.js', './app/src/test.js']
},
output: {
path: __dirname,
filename: './app/lib/bundle.js'
},
module: {
loaders: [
{
test: /.js?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['env']
}
}
]
},
};
And my app.js
import { test, foo } from './app';
class app {
constructor() {
console.log("hello from app.js");
}
}
// let a = new test(); // gives error (shown below)
// foo(); // same exact error but referencing function instead of constructor
export { app };
And test.js
import { app } from './app';
class test {
constructor() {
console.log("hello from test.js");
}
}
let foo = () => console.log("foo");
let b = new test(); // works as expected
export { test, foo };
the error is as follows:
Uncaught TypeError: _app.test is not a constructor
at Object.<anonymous> (bundle.js:88)
at __webpack_require__ (bundle.js:20)
at Object.defineProperty.value (bundle.js:97)
at __webpack_require__ (bundle.js:20)
at Object.defineProperty.value (bundle.js:63)
at bundle.js:66
So I can call classes from app inside test, but cannot do the same the other way around? why?
Thanks for responses in advance

This is a circular dependency issue. Your app imports test, which in turn imports app.

Thank you commenters Luke and Li357.
I was breaking my brain over this and confused myself with folder names vs file names.
The problem was i was importing files from the same places and causing circular dependency error.
Solved.

Related

How to access DOM from webpack loader

I'm writing a webpack loader for a specific file extension, and I would like to append a custom import to the DOM
module.exports = function(source) {
...
document.appendChild(myImport)
return `export default 'hello'`;
}
but the DOM is not accessible
ReferenceError: document is not defined
Is there a way to access the DOM from my loader?
My webpack configuration is:
const path = require('path')
module.exports = {
...
module: {
rules: [{
test: /\.myextension$/,
use: {
loader: 'my-loader'
}
}]
}
};
No, the code from loader is going to be ran on a node.js env, which has no power over the dom (browser env). To be able to manipulate the dom, you would have to output a code though loader which would be inserted on that type of file and then when executed on the browser it would do the modifications.
Something like:
module.exports = function(source) {
return `export default function(){
document.appendChild(myImport)
}`;
}

How to properly export an ES6 module function as a library for use in a node app?

Let's say that I have a node.js application, which does NOT go through my webpack bundling:
Node App
const Html = require('./build/ssr-bundle.js');
let result = Html.ssrbundle.render();
console.log(result);
Here is my ES6/JSX file, which is getting processed by webpack and I want to be able to access that render function in my node app (you guessed right, I am trying to SSR react stuff ;) )
src/Html.js -(webpack)-> build/ssr-bundle.js
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import CustomComponent from './custom-component.js';
module.exports = {
render : function () {
return ReactDOMServer.renderToString(<CustomComponent />);
} };
And here is my Webpack config
webpack.config.js
var path = require('path');
module.exports = {
entry: {
ssr: './src/Html.js',
//frontend: './src/frontend-Html.js'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: 'ssr-bundle.js',
library: 'ssrbundle'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ['env','react'],
plugins: ["transform-es2015-destructuring", "transform-object-rest-spread"]
}
},
{
test:/\.css$/,
use:['style-loader','css-loader']
}
]
},
stats: {
colors: true
},
devtool: 'source-map'
};
Whatever I do, I cannot figure out how to properly use that exported variable "ssrbundle" and subsequently the render function. If I had my node app included in the bundle, everything would be all right, but this is not what I want to do.
As apokryfos suggested, I played around with the libraryTarget Webpack setting. You can find more info on using Webpack to author a library (what I was really trying to achieve) here:
https://webpack.js.org/guides/author-libraries/
and here are code examples:
https://github.com/kalcifer/webpack-library-example/blob/master/webpack.config.babel.js.
What did the trick for me, was to set the libraryTarget to "umd" , which is different than the "var" setting which is set by default and is suitable i.e. for including the script in an HTML file

How do I reference or access my classes from a bundle created by webpack?

I am following this tutorial to learn webpack. It's out-dated, since the tools referenced are upgraded in my case. My versions are as follows.
ts-loader ^1.3.3
tsd ^0.6.5
typescript ^2.1.5
webpack ^1.14.0
After successfully creating a bundle.js file, I seem to be having problems accessing/referencing my TypeScript to JavaScript classes. My tsconfig.json looks like the following.
{
"compilerOptions": {
"module": "commonjs",
"sourceMap": true,
"declaration": true
},
"include": [
"src/**/*"
]
}
My wepack.config.js looks like the following.
var webpack = require('webpack');
module.exports = {
entry: './src/app.ts',
output: {
filename: 'bundle.js'
},
devtool: 'source-map',
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' }
]
}
}
To transpile the TypeScript code, I simply type in tsc. To bundle all the code into bundle.js, I simply type in webpack. (tsc and webpack are installed globally).
In my src directory, I only have 2 simple classes. The car.ts looks like the following.
export class Car {
public sayHi(): void {
console.log('car says hi');
}
}
The app.ts looks like the following.
import { Car } from './car'; //alternatively, import Car = require('./car');
let car = new Car();
car.sayHi();
I then load this bundle.js into a browser in a HTML page as follows.
<html>
<head>
<title>Test Webpack</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
I can see in the (Chrome) JavaScript console the log message "car says hi". But how do I create a new instance of Car? The following attempts at the JavaScript console doesn't work.
var c1 = new Car(); //Uncaught reference error
var c2 = new car.Car(); //Uncaught reference error
Inside bundle.js, I do see this code/modules being generated (snippet).
/* 0 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
var car_1 = __webpack_require__(1);
var car = new car_1.Car();
car.sayHi();
/***/ },
/* 1 */
/***/ function(module, exports) {
"use strict";
var Car = (function () {
function Car() {
}
Car.prototype.sayHi = function () {
console.log('car says hi');
};
return Car;
}());
exports.Car = Car;
/***/ }
My end goal is to get all TypeScript files in the src directory transpiled with (TypeScript) mappings into a single bundle.js as a module so I can use/reference the code (as JavaScript in a browser) as follows.
var car = new mylib.Car();
var plane = new mylib.Plane();
car.sayHi();
plane.sayHi();
Any ideas on what I'm doing wrong?
You need to set the output.library setting:
output: {
...
library: 'MyLibrary'
}
Then you can use it this way in the console:
var x = new MyLibrary.Car();
Webpack documentation (output.libray)
Similar answer on SO
What you should do is let webPack handle the transpilation for you as well.
Using a TypeScript loader.
What webpack does is making modules (this last file you put, everything is inside a function with context and without the Name you gave on a global space).Any global names are bound to the scope (or changed if you ugligy) so You can't access them from the console. Make webpack use the typescript loader and you can reference the car simply requiring or importing the file.

How to expose an es6 module globally

I need to write a module that will be available on the window global.
I'm using es6 to create the module and every single class I define has it's own file.
I'm using webpack to babelify and bundle these classes.
The entry point of my module is also the file containing the global to be exposed.
I've tried every method to make this possibe, icluding:
expose-loader
import-loader
expoert-loader
output: library
black-magic :(
Example of code I've tried:
I want to get: window.MyMod
// mymod.js
export class MyMod {
constructor(aaa) {
this.aaa = aaa;
}
toString() {
return this.aaa;
}
}
// webpack.config
var entries = [
'./src/mymod.js'
];
module.exports = {
...,
module: {
loaders: [
{
test: require.resolve('./src/mymod.js'),
loader: 'expose?MyMod'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}
]
}
This only gets me an object MyMod on the window that contains MyMod as a constructor.
Any help will be appreciated.
You should combine export default class Foo with the library and libraryTarget settings in Webpack's config. Something like:
// src/Foo.js
export default class Foo { ... }
// webpack.config.json
{
"output": {
"library": "Foo",
"libraryTarget": "var"
}
}
You should be able to use the library as window.Foo once the bundle has been loaded.
This is basically the same issue as Exporting a class with Webpack and Babel not working , except that you have a named export instead of a default export. Your entry file should be
import {MyMod} from './mymod';
module.exports = MyMod;
or
module.exports = require('./mymod').MyMod;
If you don't want to do any of these and keep './src/mymod.js' as entry file, use a CommonJS export instead of an ES6 export in that file:
// mymod.js
exports.MyMod = class MyMod {
constructor(aaa) {
this.aaa = aaa;
}
toString() {
return this.aaa;
}
}

Writing embeddable Javascript plugin with React & Webpack

I want to be able to bundle my React app with Webpack such that distributed copies put onto a CDN can be sourced, called and initialised with a bunch of config relevant to a client.
After reading this and this, I'm setting up my webpack entry file as follows:
// ... React requires etc.
(() => {
this.MyApp = (config) => {
// some constructor code here
}
MyApp.prototype.init = () => {
ReactDOM.render(<MyReactApp config={MyApp.config} />, someSelector);
}
})();
The idea being that in my client, I can do something like the following:
<script src="./bundle.js" type="text/javascript"></script>
<script type="text/javascript">
MyApp.init({
some: "config"
});
</script>
And my MyApp#init function will render my React app inside some container on the client.
Am I thinking about this in the right way? Is there a simpler or more efficient way to go about this?
My error is Uncaught TypeError: Cannot set property 'MyApp' of undefined, since this inside the IIFE is undefined. I'd really like to understand both why this is happening and advice on how to fix it.
Thanks in advance!
So I kind of found a solution to this, as described here
If I change my webpack.config.js file to add the following attributes to the output object, i.e.
var config = {
// ...
output: {
// ...
library: 'MyApp',
libraryTarget: 'umd',
umdNamedDefine: true,
}
}
This specifies the file I'm bundling with webpack as a UMD module, so if I have a function in that file and export it...
export const init = (config) => {
ReactDOM.render(<MyReactApp config={config} />, someSelector);
}
I can then, in my client, do the following.
<script src="./bundle.js" type="text/javascript"></script>
<script type="text/javascript">
MyApp.init({
some: "config"
});
</script>
And my React app renders.
If anyone thinks this is a daft way of doing it, I'd love to hear it!
MORE INFORMATION ON WEBPACK CONFIG
Please bear in mind I haven't touched this code in a while. Given it's Javascript, the world has likely moved on and some practises may be outdated.
This is my React entrypoint file (src/index.js)
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import Root from './components/Root';
import configureStore from './lib/configureStore';
const store = configureStore();
export const init = (config) => {
render(
<Root store={store} config={config} />,
document.querySelector(config.selector || "")
);
}
This is my Webpack config (webpack.config.js)
var webpack = require('webpack');
var path = require('path');
var loaders = require('./webpack.loaders');
module.exports = {
entry: [
'webpack-dev-server/client?http://0.0.0.0:8080', // WebpackDevServer host and port
'webpack/hot/only-dev-server',
'./src/index.js' // Your appʼs entry point
],
devtool: process.env.WEBPACK_DEVTOOL || 'source-map',
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js',
library: 'Foo',
libraryTarget: 'umd',
umdNamedDefine: true,
},
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: loaders
},
devServer: {
contentBase: "./public",
noInfo: true, // --no-info option
hot: true,
inline: true
},
plugins: [
new webpack.NoErrorsPlugin()
]
};
As you can see, my Webpack config outputs my bundle.js which is what my front-end will ingest.
You have enclosure around your class.
MyApp needs to be exported or attached to the global window object before you can call it like that.
In your situation you are actually not calling MyApp.init() but you are calling window.MyApp.init(), but global object window does not have object MyApp attached to it.
// ... Simple attaching MyApp to the window (as a function)
window.MyApp = (config) => {
...
}
// ... Using class and export
class MyApp {
constructor(config){...}
}
export default MyApp
// or simply attach to the window
window.MyApp = MyApp
I would prefer to create class and export module using export. Then create another file just for attaching to the window. Since it is not considered best practice to attach classes to the window like that.
// Import module and attach it to the window
import MyApp from '.my-app'
window.MyApp = MyApp
You can look for advanced options of exporting modules as UMD, AMD...

Categories