I've been recently frustrated when developing using Webpack, because whenever I introuduce a new dependency there are no printed errors if I forget to include the depending JavaScript file(s).
I have created an example project: https://github.com/manstie/test-django-webpack
In the example you can see I have purposely set up the home module to depend on the utils module.
When you run the server and load localhost:8000 it is expected that you get a console log of Here and You have successfully depended on utils.js if you import all the necessary script files.
When utils.js is not included in index.html, base.js does not run at all - it silently fails, with no errors.
I am hoping there is a way to have errors show in the javascript console in these cases? I just can't find any resources or related questions on this issue.
Here is the webpack.common.js config:
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const dist = path.resolve(__dirname, 'static/bundles');
module.exports = {
entry: {
utils: './home/js/utils.js',
home: {
import: './home/js/index.js',
dependOn: ['utils']
}
},
resolve: {
modules: ['node_modules']
},
output: {
filename: '[name].[chunkhash].js',
path: dist,
publicPath: 'bundles/',
},
plugins: [
new CleanWebpackPlugin(), // deletes files inside of output path folder
new WebpackManifestPlugin({ fileName: "../../manifest.json" }),
],
optimization: {
moduleIds: 'deterministic', // so that file hashes don't change unexpectedly?
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `npm.${packageName.replace('#', '')}`;
},
},
},
},
},
};
Here is the html with a missing dependency - I am using django-manifest-loader in order to prevent JavaScript caching issues
...
{% load manifest %}
<script src="{% manifest 'runtime.js' %}"></script>
<!-- Is there a way to have webpack / js tell you about the missing dependency? -->
<!-- <script src="{% manifest 'utils.js' %}"></script> -->
<script src="{% manifest 'home.js' %}"></script>
...
And here are the js files:
index.js
import { testFunc } from "./utils.js";
console.log("Here");
testFunc();
utils.js
export function testFunc()
{
console.log("You have successfully depended on utils.js");
}
Thanks in advance.
Related
Imagine having below structure of html files:
./home.html
./settings.html
./contact.html
Also having below js files
./nav.js <-- common - used in all html files
./home.js <-- used only in home.html, below follow the same rule
./settings.js
./contact.js
And some modules from node_modules:
"jquery"
"moment"
that are being imported as if when required:
./home.js
import $ from "jquery";
(...)
I have setup webpack to have each entry point as each file name. Now what would be the way to include common js files such as `./nav.js" into each file?
entry: {
home: "./resources/js/sections/home.js",
settings: "./resources/js/sections/settings.js",
(...)
}
(...)
output: {
filename: '[name].js',
}
// Option A
Import raw nav.js like another module in every subpage (home.js, contact.js, settings.js)
import nav from ./nav.js
nav();
// Option B
create another entry for ./nav.js and manually add bundled nav.js to each html alongside other bundled files.
entry: {
(...)
nav: "./resources/js/sections/nav.js"
}
You may use HtmlWebPackPlugin in order to append scripts dynamically to your HTML pages.
First of all install the plugin:
npm install html-loader html-webpack-plugin --save-dev
Then use the config:
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
nav: './resources/js/sections/nav.js',
home: './resources/js/sections/home.js',
settings: './resources/js/sections/settings.js',
contact: './resources/js/sections/contact.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'), // folder where all tranformed files will be placed
},
rules: [
{
test: /\.html$/,
use: [{
loader: "html-loader",
options: { minimize: true }
}]
}
],
plugins: [
// create an instance of HtmlWebPackPlugin for every page of a multipage website
new HtmlWebPackPlugin({
template: "./resources/html/home.html", // take html from this path
filename: "./home.html", // name it 'home.html' and insert to the root of output folder
chunks: ['nav', 'home'] // insert dymamically nav.js and home.js to home.html
}),
new HtmlWebPackPlugin({
template: "./resources/html/settings.html",
filename: "./settings.html",
chunks: ['nav', 'settings']
}),
new HtmlWebPackPlugin({
template: "./resources/html/contact.html",
filename: "./contact.html",
chunks: ['nav', 'contact']
}),
]
}
I am trying to create and reuse Polymer 3 components in ASP.NET MVC application. Right now I am not sure if I am approaching the issue correctly.
So first thing, I want to run everything from IIS express.
Right now I have this issue:
Uncaught TypeError: Failed to resolve module specifier "#polymer/polymer/polymer-element.js".
Relative references must start with "/", "./", or "../".
Here is my code:
Index.cshtml:
<head>
<script src="~/Scripts/PolymerApp/node_modules/#("#webcomponents")/webcomponentsjs/webco
mponents-loader.js"></script>
<script type="module" src="~/Scripts/PolymerApp/first-element.js">
</script>
</head>
<h2>Index</h2>
<div>
<first-element></first-element>
</div>
This is my first-element.js:
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
class FirstElement extends PolymerElement {
static get template() {
return html`
<style>
:host {
display: block;
}
</style>
<h2>Hello [[prop1]]!</h2>
`;
}
static get properties() {
return {
prop1: {
type: String,
value: 'first-element',
},
};
}
}
window.customElements.define('first-element', FirstElement);
I created this through cmd: polymer init and then chose element template.
When I run this through polymer serve on polymer`s localhost it works, so i guess there is some build process going on.
Thanks in advance. I hope that i described everything.
I've attempted to do a string replacement in the polymer generated html file using webpack and a plug-in, but it doesn't seem to find the file. Maybe someone more knowledgeable in Webpack-fu can figure out the rest.
// webpack.config.js
var webpack = require('webpack');
const ReplaceInFileWebpackPlugin = require('replace-in-file-webpack-plugin');
"use strict";
module.exports = {
entry: {
main: '/'
},
output: {
filename: "./wwwroot/dist/[name].bundle.js",
publicPath: "/temp/"
},
devServer: {
contentBase: ".",
host: "localhost",
port: 9000
}, mode: "development",
plugins: [
new ReplaceInFileWebpackPlugin([{
dir: './path/to/polymer-built-app/build/default/',
test: /\.html$/,
rules: [{
search: '/#webcomponents/',
replace: '/#{\'#webcomponents\'}/'
}]
}])
]
};
**EDIT: 08/04/2018 **
I've figured this much out:
/// <binding BeforeBuild='Run - Development' />
// webpack.config.js
var webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackStringReplacePlugin = require('html-webpack-string-replace-plugin');
"use strict";
module.exports = {
entry: {
main: './'
},
output: {
publicPath: '/',
path: path.resolve(__dirname, 'wwwroot'),
filename: "./dist/[name].bundle.js"
},
devServer: {
contentBase: ".",
host: "localhost",
port: 9000
},
mode: "development",
plugins: [
new HtmlWebpackPlugin({
"template": "./path/to/index.html",
"filename": "../path/to/Views/Shared/_PolymerLayout.cshtml"
}),
new HtmlWebpackStringReplacePlugin({
'#webcomponents':
'#Html.Raw("#webcomponents")',
'%40webcomponents':
'#Html.Raw("#webcomponents")',
'%40polymer':
'#Html.Raw("#polymer")',
'/node_modules/':
'/path/to/node_modules/',
'./src/path/to/polymer-app',
'<!-- See google short url -->':
'<!-- See google short url -->\r\n<base href="/custom/base/ref">'
})
]
};
If the .js import path starts like this:
from '#polymer/...'
Polymer 3 has a command "polymer build" that automatically translates the path to a real location:
Before:
from '#polymer/polymer/polymer-element.js';
After:
from "./node_modules/#polymer/polymer/polymer-element.js";
You can type ./node_modules/ in front to skip using the polymer build command line tool.
I'm using webpack to generate hashed bundle filenames.
Assuming I'm using static HTML, CSS & JS, what is the best way to automatically update index.html to point to the latest bundles?
For example,
update:
<script src="e8e839c3a189c25de178.app.js"></script>
<script src="c353591725524074032b.vendor.js"></script>
to:
<script src="d6cba2f2e2fb3f9d98aa.app.js"></script>
<script src="c353591725524074032b.vendor.js"></script> // no change
automatically everytime a new bundle version is available.
Amazingly, this is what the html-webpack-plugin is for.
var webpack = require('webpack');
var path = require('path');
var HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = function(env) {
return {
entry: {
main: './src/index.js',
vendor: 'moment'
},
output: {
filename: '[chunkhash].[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor', 'manifest']
}),
new HTMLWebpackPlugin({
tempate: path.join(__dirname, './src/index.html')
})
]
}
};
This generates an index.html in the dist directory that includes the script's in the correct order.
youtube example
I'm new to Django and ReactJS, was trying to compile a simple JSX code to JS using this tutorial : http://geezhawk.github.io/2016/02/02/using-react-with-django-rest-framework.html
Didn't work, so I used npm run dev to compile, now it worked but giving error in browser console : Uncaught ReferenceError: react is not defined
Here is my webpack.config.js
var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
var nodeExternals = require('webpack-node-externals');
module.exports = {
//the base directory (absolute path) for resolving the entry option
context: __dirname,
//the entry point we created earlier. Note that './' means
//your current directory. You don't have to specify the extension now,
//because you will specify extensions later in the `resolve` section
entry: './assets/js/index',
output: {
//where you want your compiled bundle to be stored
path: path.resolve('./assets/bundles/'),
//naming convention webpack should use for your files
filename: '[name]-[hash].js',
},
target: 'node', // in order to ignore built-in modules like path, fs, etc.
externals: {
react: 'react'
}, // in order to ignore all modules in node_modules folder
plugins: [
//tells webpack where to store data about your bundles.
new BundleTracker({filename: './webpack-stats.json'}),
//makes jQuery available in every module
new webpack.ProvidePlugin({
//React: "react",
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
})
],
module: {
loaders: [
//a regexp that tells webpack use the following loaders on all
//.js and .jsx files
{test: /\.jsx?$/,
//we definitely don't want babel to transpile all the files in
//node_modules. That would take a long time.
/*exclude: /node_modules/,*/
//use the babel loader
loader: 'babel-loader',
query: {
//specify that we will be dealing with React code
presets: ['react']
}
}
]
},
resolve: {
//tells webpack where to look for modules
modulesDirectories: ['node_modules'],
//extensions that should be used to resolve modules
extensions: ['', '.js', '.jsx']
}
}
And assets/bundles/index.js
var React = require('react')
var ReactDOM = require('react-dom')
//snaha//
var BooksList = React.createClass({
loadBooksFromServer: function(){
console.log(123454657);
$.ajax({
url: this.props.url,
datatype: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this)
})
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadBooksFromServer();
setInterval(this.loadBooksFromServer,
this.props.pollInterval)
},
render: function() {
if (this.state.data) {
console.log('DATA!')
var bookNodes = this.state.data.map(function(book){
return <li> {book.title} </li>
})
}
return (
<div>
<h1>Hello React!</h1>
<ul>
{bookNodes}
</ul>
</div>
)
}
})
ReactDOM.render(<BooksList url='/api/' pollInterval={1000} />,
document.getElementById('container'))
And templates/body.html
{% load render_bundle from webpack_loader %}
<!doctype html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-with-addons.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script>
<meta charset="UTF-8">
<title>Hello React
{% block content %}
{{ id }}
{% endblock %}
</title>
</head>
<body>
<div id="container"></div>
{% render_bundle 'main' %}
</body>
</html>
Anything I'm missing? here is my Django project structure
Finally I've solved it!
Problem was : it was trying to get variable react where as React.js on browser was providing variable React!
So I simple change of externals of webpack.config.js to
externals: {
React: 'react'
},
solved the issue!
Next Problem Faced :
"process was not defined"
Solution : added
var env = process.env.WEBPACK_ENV;
to top of webpack.config.js
and
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"production"'
}
})
into the plugins part of model.export
So Final webpack.config.js would be :
var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
var nodeExternals = require('webpack-node-externals');
var env = process.env.WEBPACK_ENV;
module.exports = {
//the base directory (absolute path) for resolving the entry option
context: __dirname,
//the entry point we created earlier. Note that './' means
//your current directory. You don't have to specify the extension now,
//because you will specify extensions later in the `resolve` section
entry: './assets/js/index',
output: {
//where you want your compiled bundle to be stored
path: path.resolve('./assets/bundles/'),
//naming convention webpack should use for your files
filename: '[name]-[hash].js',
},
target: 'node', // in order to ignore built-in modules like path, fs, etc.
externals: {
React: 'react'
}, // in order to ignore all modules in node_modules folder
plugins: [
//tells webpack where to store data about your bundles.
new BundleTracker({filename: './webpack-stats.json'}),
//makes jQuery available in every module
new webpack.ProvidePlugin({
//React: "react",
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"production"'
}
})
],
module: {
loaders: [
//a regexp that tells webpack use the following loaders on all
//.js and .jsx files
{test: /\.jsx?$/,
//we definitely don't want babel to transpile all the files in
//node_modules. That would take a long time.
/*exclude: /node_modules/,*/
//use the babel loader
loader: 'babel-loader',
query: {
//specify that we will be dealing with React code
presets: ['react']
}
}
]
},
resolve: {
//tells webpack where to look for modules
modulesDirectories: ['node_modules'],
//extensions that should be used to resolve modules
extensions: ['', '.js', '.jsx']
}
}
Now Enjoy React! Happy Coding :-)
Can you look if you have all the requirements installed.
Look inside package.json. You should have react noted in requirements if you do run.
npm install
If you don't, then run
npm install react --save
ps: in my option if you are running Webpack try to add babel to Webpack presets and write javascript in ES2015 specification.
I have a web app that I compile with webpack. One of the modules that my code uses is named table.js. Until recently, it's just been another module and has been compiled into my bundle.js file with everything else.
Now I need to run table.js in a Web Worker, so I need to pull it and its dependencies into a separate file that can be loaded both standalone and by my other modules.
At first I thought to include table.js in my webpack.config.js's entry.
var config = {
...
entry: {
app: [ './src/main.js', './src/classes/table.js' ],
vendors: [],
},
...
}
That didn't work. Then I thought to separate it out like my vendors bundle.
var config = {
/* for vendors (and other modules) we have a CDN for */
addExternal: function (name, globalVar) {
this.externals[name] = globalVar;
this.entry.vendors.push(name);
},
/* for vendors we don't have a CDN for */
addVendor: function (name, path) {
this.resolve.alias[name] = path;
this.entry.vendors.push(name);
},
addPlugin: function (plugin) {
this.plugins.push(plugin);
},
entry: {
app: [ './src/main.js' ],
vendors: [],
table: [ __dirname + '/src/classes/table.js' ]
},
plugins: [],
externals: { },
output: {
path: __dirname + '/public/dist/',
filename: 'bundle.js',
publicPath: '/dist/',
sourceMapFile: '[file].map'
},
resolve: {
alias: { 'table': './src/classes/table.js' },
extensions: [ '', '.js', '.jsx' ]
},
...
}
/* add vendors and externals */
...
config.addPlugin(new CommonsChunkPlugin('vendors', 'vendors.js'));
config.addPlugin(new CommonsChunkPlugin('table', 'table.js'));
This seems to pull Table and its dependencies into a chunk of bundle.js, 1.bundle.js. Unfortunately, then calling import Table from 'table' causes this error:
ERROR in CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (table)
I also have a circular dependency between TableStore and Table. TableStore needs to stay in bundle.js because it shouldn't be loaded into the Web Worker. Previously, when I've needed to throw things into a separate chunk, I've done:
if (someThingNeedsRequiring) {
require.ensure([], () => {
require('something');
}
}
With the circular dependency, this doesn't seem to work.
/* table.js */
let _inWebWorker = self instanceof Window,
TableStore = null;
if (!_inWebWorker) {
require.ensure([], function() { TableStore = require('../stores/table-store'); } );
}
/* table-store.js */
import Table from 'table';
Could someone set me straight on the correct way to have my webpack.config.js and how to use my imports in my module files?
(It's been quite a while since I figured this out, and I haven't touched the project in nearly six months, so I may have missed some of the details. Comment if it's not working, and I'll try to figure out what I'm missing.)
webpack.config
It turns out there are two handy-dandy JavaScript packages for doing what I want: worker-loader and workerjs.
npm install --save workerjs worker-loader
I added this in my webpack.config.js:
var config = {
// ...
worker: {
output: {
filename: '[name].worker.js',
chunkFilename: '[name].worker.js'
}
},
// ...
}
require()
In order to specify that I want my class to be run in a WebWorker file, my require looks like:
// ecmaScript 6
import TableWorker from 'worker?name=tableRoller!path/to/table';
// ecmaScript 5
var TableWorker = require('worker?name=tableRoller!path/to/table');
TableWorker is just a variable name I used for table.js's export default class Table {...}. The name=tableRoller specifies the generated outputted [name].worker.js filename. For example, I have another WebWorker named distCalc.worker.js, so my import looks like:
import DistWorker from 'worker?name=distCalc!path/to/distWorker';
Note that in this case, distWorker only ever runs in a WebWorker, while Table is used in both my main.js entry point and my tableRoller.worker.js WebWorker file.
workerjs and worker-loader generate a new entry point file and pull in all of the dependencies of those classes. Tobias Koppers (worker-loader) and Eugene Ware (workerjs) are geniuses.
Detecting WebWorker
My _inWebWorker detection is:
let _inWebWorker = typeof Window === 'undefined';
Change output filename in your webpack.config.js file
output: {
path: __dirname + '/public/dist/',
filename: '[name].js',
publicPath: '/dist/',
sourceMapFile: '[file].map'
},
then Webpack can separate your entries with its name in dist directory.