webpack 4 + babel 6 + ES Module and Script consumption - javascript

I'm trying to create a library that could be used using one of following method:
import MyLibrary from 'mylibrary';
const instance = new Mylibrary();
and
<script src="mylibrary.js"></script>
<script>
var instance = new MyLibrary();
</script>
To do this I have a very simple index.js which contains:
class MyLibrary {
hello() {
console.log('hello');
}
}
export default MyLibrary;
And I installed webpack / webpack-cli / babel-core / babel-cli / babel-preset-env and babel-loader via npm.
Then I created a webpack.config.js file with the following content:
const path = require('path')
module.exports = {
entry: {
MyLibrary: './src/index.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].js',
library: '[name]',
libraryExport: 'default',
libraryTarget: 'umd',
umdNamedDefine: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
}
};
When I test it :
<script src="MyLibrary.js"></script>
<script>
var instance = new MyLibrary();
console.log(instance.hello());
</script>
Is working
import MyLibrary from './MyLibrary.js';
console.log(MyLibrary);
I get the following error: Uncaught SyntaxError: The requested module './MyLibrary.js' does not provide an export named 'default'
If I remove the rule libraryExport: 'default'from my webpack config then:
I have to call my library like this: var instance = new MyLibrary.default();
I still having the same issue Uncaught SyntaxError: The requested module './MyLibrary.js' does not provide an export named 'default'
How can I do to get this work ?

Related

Webpack bundles jQuery but I can't use it

I'm a beginner with Webpack and simply stuck.
I'm trying to bundle jQuery plugins and have them in a plugins-bundled.js file.
Then I have my local scripts file where all jQuery code goes. These 2 files are loaded in HTML in order 1. plugins-bundled.js 2. scripts.js But then I got error in a console Uncaught ReferenceError: jQuery is not defined. Is there something I'm missing here?
Webpack config
const path = require('path'),
settings = require('./settings');
module.exports = {
entry: {
App: settings.themeLocation + "js/plugins.js"
},
output: {
path: path.resolve(__dirname, settings.themeLocation + "js"),
filename: "plugins-bundled.js"
},
module: {
rules: [
{
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
},
test: /\.js$/,
exclude: /node_modules/
}
]
}
}
Webpack App file
import $ from 'jquery';
window.jQuery = $;
window.$ = $;
import slick from 'slick-carousel';
import localScroll from 'jquery.localscroll';
import popper from 'popper.js';
import bootstrap from './bootstrap.min';
Local scripts file
(function($){
"use strict";
// code goes here...
})(jQuery);

How to import jquery in webpack

I have a problem with jquery when i using it on webpack.
My code:
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
entry: {
vendor: [
'./src/main/webapp/js/vendor/jquery-3.3.1.min.js',
// './src/main/webapp/js/vendor/fs.js',
'./src/main/webapp/js/vendor/google-adsense.js',
'./src/main/webapp/js/vendor/jquery.menu-aim.min.js',
'./src/main/webapp/js/vendor/jquery.touchSwipe.min.js',
],
app: './src/main/assets/js/desktop/app.js',
mobile: './src/main/assets/js/mobile/app.js',
touch: './src/main/assets/js/touch/app.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: require.resolve('jquery'),
loader: 'expose-loader?jQuery!expose-loader?$'
}
],
},
plugins: [
// new CleanWebpackPlugin(['src/main/webapp/assets']),
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
}),
new UglifyJsPlugin({
test: /\.js$/,
sourceMap: process.env.NODE_ENV === "development"
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, './src/main/webapp/js')
}
};
When above code compiles , console throws this error
vendor.js:1 Uncaught ReferenceError: webpackJsonp is not defined
at vendor.js:1
And when i try to use this
externals: {
jquery: 'jQuery'
}
It throws
vendor.js:1 Uncaught ReferenceError: jQuery is not defined
at Object.231 (vendor.js:1)
at o (common.js:1)
at Object.228 (vendor.js:1)
at o (common.js:1)
at window.webpackJsonp (common.js:1)
at vendor.js:1
And i using jquery in my core js file import $ from 'jquery'.
What did i do wrong ? any help ? Thank you.
So there are few themes in your webpack.config.js, some of which conflict. Just going to list them so I can better understand what I think you are trying to achieve.
Theme 1
You have an entry called vendor that is clearly referencing a minified jQuery library you have presumably downloaded and placed in the directory specified.
Theme 2
You also have an expose-loader that is essential exposing the jquery library from its node_modules probably listed in the dependencies of your package.json.
This makes the jquery in the node_modules available as $ and jQuery in the global scope of the page where your bundle is included.
Theme 3
You also have included the ProvidePlugin with configuration for jQuery.
The ProvidePlugin is supposed to inject dependencies into the scope of your module code, meaning you do not need to have import $ from 'jquery' instead $ and jQuery will already be available in all of your modules.
Conclusion
From what I have gathered I think you're trying to bundle jQuery from the static file at ./src/main/webapp/js/vendor/jquery-3.3.1.min.js in a vendor bundle.
You are then trying to expose the libraries in the vendor bundle to the global scope (jQuery).
Then also have your application code able to import jQuery from what is made available by the vendor bundle in the global scope.
Answer
So if that is what you are doing you need to do the following things.
Firstly, check in your package.json files dependencies for jquery. If its there you want to remove it, there's no need for it if you're going to use your jquery-3.3.1.min.js file instead to provide jQuery to your application.
Secondly, change your test of the expose-loader to trigger when it sees your jquery-3.3.1.min.js file in your entries, not resolve it from the jquery dependency from your node_modules.
This regex pattern should do the trick.
{
test: /jquery.+\.js$/,
use: [{
loader: 'expose-loader',
options: 'jQuery'
},{
loader: 'expose-loader',
options: '$'
}]
}
Thirdly, remove the ProvidePlugin if you're going to import the library explicitly with import $ from 'jquery' you do not need it.
Lastly, you need to tell webpack when it sees an import for jquery it can resolve this from window.jQuery in the global scope. You can do this with the externals configuration you already referenced.
externals: {
jquery: 'jQuery'
}
With all these changes you should end up with a webpack.config.js file that looks like this.
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
entry: {
vendor: [
'./src/main/webapp/js/vendor/jquery-3.3.1.min.js',
// './src/main/webapp/js/vendor/fs.js',
'./src/main/webapp/js/vendor/google-adsense.js',
'./src/main/webapp/js/vendor/jquery.menu-aim.min.js',
'./src/main/webapp/js/vendor/jquery.touchSwipe.min.js',
],
app: './src/main/assets/js/desktop/app.js',
mobile: './src/main/assets/js/mobile/app.js',
touch: './src/main/assets/js/touch/app.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /jquery.+\.js$/,
use: [{
loader: 'expose-loader',
options: 'jQuery'
},{
loader: 'expose-loader',
options: '$'
}]
}
],
},
plugins: [
// new CleanWebpackPlugin(['src/main/webapp/assets']),
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
}),
new UglifyJsPlugin({
test: /\.js$/,
sourceMap: process.env.NODE_ENV === "development"
})
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, './src/main/webapp/js')
},
externals: {
jquery: 'jQuery'
}
};
I hope that does not just give you the answer but enough explanation as to where you were going wrong.

Webpack and AWS Lambda issue - handler missing on module

I'm using ES6, babel and Webpack 2 to bundle an AWS Lambda. I am then running/testing it using AWS SAM local. I get the following error when I hit the api -
Handler 'handler' missing on module 'dist/main'
Here is my webpack.config.js -
const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
libraryTarget: 'commonjs'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: [require('babel-plugin-transform-flow-strip-types')],
presets: [
[
'env',
{
target: { node: 6.10 }, // Node version on AWS Lambda
useBuiltIns: false,
loose: false,
exclude: [],
debug: false
},
],
],
},
}
],
}
};
And here is a snippet of the compiled main.js -
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.handler = handler;
var _amazonCognitoIdentityJs = __webpack_require__(60);
var _aws_profile = __webpack_require__(290);
// A signin Lambda function
function handler(event, context, callback) {
switch (event.httpMethod) {
case "GET":
A little background.... this is a Lambda I initially wrote NOT in ES6 and not bundling using Webpack and it was working. I now need for it to be in ES6 and work with Webpack. N.B. this is Webpack 2
Much thanks...
To fix this issue I had to specify a library property and change the libraryTarget to commonjs2. The webpack.config.js file output now looks like this -
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
library: 'main',
libraryTarget: 'commonjs2'
},
I ran into this issue as well. However, I believe my situation was the inverse of what SamBrick shares. I was moving from transpiling ES6 with babel to run on lambda/node 6.10 to no transpiling and targeting lambda/node 8.10. Removing the library field and changing to the libraryTarget: 'commonjs' solved the problem for me.
Props to this guy: https://gist.github.com/nirnanaaa/d7f40deb38f1cf7f931dc7ef0c582bf0
I ended up here because I was being an idiot and was exporting the handler function as default instead of the named function.
export default handler;
... instead of ...
export { handler };

Making a node_module global using Webpack 2

I am using the below package on an angular project:
https://github.com/pablojim/highcharts-ng
A requirement is that it needs highcharts as a global dependency, in which it tells you to add a script tag into your html:
<script src="http://code.highcharts.com/highcharts.src.js"></script>
Rather than add the above script tag into my HTML I would like to make it global via Webpack.
I have installed highcharts via npm and have tried using the ProvidePlugin and the noParse methods described here (to no avail): https://webpack.js.org/guides/shimming/#scripts-loader
For the ProvidePlugin option I used:
new webpack.ProvidePlugin({
Highcharts: "highcharts",
})
for noParse:
noParse: [
/[\/\\]node_modules[\/\\]highcharts[\/\\]highcharts\.js$/,
],
Neither worked, meaning when highcharts-ng tried to work, it gets an error because it cannot create a new Highcharts:
TypeError: Cannot read property 'Chart' of undefined
// from highcharts-ng which throws above error
chart = new Highcharts[chartType](mergedOptions, func);
Here is my angular module
import angular from 'angular'
import highchartsNg from 'highcharts-ng'
import { ReportsData } from './reports.data'
import { reportsWidget } from './reports-widget/component'
export const ReportsModule = angular
.module('reports', [
highchartsNg,
])
.factory('ReportsData', ReportsData)
.component('reportsWidget', reportsWidget)
.name
My webpack config:
var webpack = require('webpack')
module.exports = {
context: __dirname + '/app/modules',
entry: {
vendor: ['angular', 'highcharts-ng'],
modules: './modules.js',
},
output: {
path: __dirname + '/.tmp/modules',
filename: '[name].bundle.js',
},
plugins: [
// info: https://webpack.js.org/guides/code-splitting-libraries/
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor', 'manifest'],
}),
],
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: { presets: ['es2015'] },
},
],
},
],
},
}
Use expose-loader and write require('expose-loader?Highcharts!highcharts'); somewhere before the first usage of Highcharts global variable. This will require highcharts and save expose it as window.Highcharts in the browser.
Under the hood webpack makes it available for you as:
/* 0 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Highcharts"] = __webpack_require__(1);
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)))
/***/ }

Webpack fails to build ES6 in external packages

I'm trying to use an ES6 npm package (in this case one called gamesystem) in a project and build with webpack (and babel) it fails to build any ES6 code inside the external dependency, why is that? If I have the same code as a dependency with a relative path it works fine.
App code:
'use strict';
import World from 'gamesystem'; // Breaks
//import World from '../node_modules/gamesystem'; // Also breaks
//import World from '../gamesystem'; // Works (copied the same directory gamesystem outside the node_modules directory)
let world = new World();
Error:
ERROR in ./~/gamesystem/World.js
Module parse failed: /home/user/project/node_modules/gamesystem/World.js Line 3: Unexpected token
You may need an appropriate loader to handle this file type.
| 'use strict';
|
| import System from './System';
|
| export default class World {
# ./src/app.js 3:18-39
Webpack config:
'use strict';
// Modules
let WebpackNotifierPlugin = require('webpack-notifier');
let HtmlWebpackPlugin = require('html-webpack-plugin');
let config = {
entry: {
app: __dirname + '/../src/app.js'
},
devtool: 'source-map',
devServer: {
stats: {
modules: false,
cached: false,
colors: true,
chunk: false
},
proxy: require('./devProxy')
},
module: {
loaders: [{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
query: {
presets: ['es2015', 'stage-0']
}
},
{
test: /\.css$/,
loader: 'style!css'
},
{
test: /\.html$/,
loader: 'raw'
}]
},
plugins: [
new WebpackNotifierPlugin(),
new HtmlWebpackPlugin({
template: __dirname + '/../src/index.html',
inject: 'body',
minify: false
})
]
};
module.exports = config;
You're explicitly excluding node_modules from babel-loader:
exclude: /node_modules/,
You'll need to tweak that if you want to pass modules from node_modules through babel. You might consider explicit includes like this:
// be sure to req path
// var path = require('path')
include: [
// Include everything from your app path
path.resolve(__dirname, 'my-app-js-path'),
// Include gamesystem modules
/\bgamesystem\b/,
],

Categories

Resources