AngularJS + webpack hot module replace ignores changes in app.controllers - javascript

I have implemented the Webpack HMR to my AngularJS 1.7.2 project.
If I change something in the .js files and save I have the update hot without refresh.
However, everything resides under app.controller or anything else made by AngularJS, is doesn't working.
For example:
module.exports = app => {
console.log(1); // will be hot updated
app.controller("myCtrl", () => console.log(2)); // will not update in case I change the 2 to 3, or something else.
}
This is the webpack.config.js file:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = (env, argv) => {
return {
entry: './src/webpack.entry.js',
output: {
filename: 'bundle.js',
chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, './www')
},
plugins: [
// new HtmlWebpackPlugin({
// template: "./src/index.html"
// }),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: path.resolve(__dirname, './www'),
hot: true,
inline: true
},
// devtool: argv.mode == "development" ? 'inline-source-map' : false,
module: {
rules: [
{
test: /\.(html)$/,
exclude: /node_modules/,
use: {
loader: 'html-loader',
options: {
attrs: [':data-src']
}
}
},
{
test: /\.(woff(2)?|ttf|eot|svg|jpg|jpeg|png|jpg|gif)$/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {}
}
]
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
'style-loader',
'css-loader',
]
},
// {
// test: /\.scss$/,
// exclude: /node_modules/,
// use: [
// 'sass-loader'
// ]
// }
]
},
optimization: {
minimize: false
}
};
}
And I would like to provide with the webpack.entry.js file too:
// angularjs app first initialization
var app = require('./app/app');
// ionic core css
require('./assets/css/ionic.bundle.css');
// app scripts
require('./app/index')(app);
// screens scripts
require('./screens/index')(app);
// assets
require('./assets/css/custom-both.css');
// NOTE: without this, it fully reloads the webpage instead of making it hot reload - even when the config sets with hot:true
if (module.hot) {
module.hot.accept();
}

Related

Issue with integrating cortex-js/compute machine in react application

I am trying to use compute machine provided by the cortexjs. https://cortexjs.io/compute-engine
I saved this dependency using
npm i #cortex-js/compute-engine
After I ran npm start, I am getting this error:
ERROR in ./node_modules/#cortex-js/compute-engine/dist/compute-engine.min.js 2:1360
Module parse failed: Unexpected token (2:1360)
You may need an appropriate loader to handle this file type.
Following is my webpack file:
var webpack = require('webpack');
const HtmlWebPackPlugin = require("html-webpack-plugin");
var path = require('path');
module.exports = {
// output: {
// path: path.resolve(__dirname, 'dist'),
// filename: 'main.js',
// publicPath: '/'
// },
mode : 'production',
devServer: {
historyApiFallback: true,
},
devtool: "source-map",
module: {
rules: [
{
test: /\.(png|jpg|woff|woff2|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
},
{
test: /\.css$/,
loader: 'style-loader'
},
{
test: /\.css$/,
loader: 'css-loader',
options: {
import: true,
},
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
})
]
};
Please can someone suggest which loader should I add to webpack.config file to make this work.

How do I access process.env.VAR in Webpack 5?

I've never used Webpack before and I'm working on a project that's just vanilla JS and HTML. I'm having an issue accessing the values I set in .env. Here's my config.
const path = require("path");
const dotenv = require('dotenv');
var webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = () => {
env = dotenv.config().parsed;
const envKeys = Object.keys(env).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(env[next]);
return prev;
}, {});
return {
entry: {
main: './src/index.js'
},
output: {
path: path.join(__dirname, '../build'),
filename: '[name].bundle.js'
},
mode: 'development',
devServer: {
contentBase: "./src/",
publicPath: "./src/",
compress: true,
port: 9000,
overlay: true,
disableHostCheck: true
},
devtool: 'inline-source-map',
resolve: {
alias: {
process: "process/browser"
}},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/'
}
}
]
},
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
//attributes: ['img:src', ':data-src'],
minimize: true
}
}
}
]
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
new Dotenv(),
new webpack.DefinePlugin(envKeys),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
]
}
};
As you can see, I'm using dotEnv, definePlugin, and even the dotEnv-webpack plugin. Unfortunately, none of these solutions seem to allow me to access process.env.originURL.
originURL=https://localhost:3000
I'm not importing or requiring anything in my javascript file, index.js. I'm assuming this should work, but the console tells me that process is undefined.
console.log(process.env.originURL);
How can I access process.env.originURL in my index.js?
It should work. Maybe you could try this version:
new Dotenv({ systemvars: true })
If it still doesn't work, please show your package.json, as well as you .env file (randomize the values of course). Is .env at the root of you app?

AngularJS + webpack how to hash template htmls

I have migrated our AngularJS application to use webpack - before it used gulp. in the gulp version I have used rev plugin to rev all the files (css,js and html) however in the webpack mode , I cannot find a way to add hash to the html templates - which cause issues as the browser serve old html files. How can it be fixed? below is by webpack conf file
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const OpenBrowserPlugin = require('open-browser-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const RemoteServer = process.env.REMOTE_SERVER;
const appEnv = process.env.NODE_ENV || 'development';
const isProduction = appEnv === 'production';
const patterns = require('../server/src/main/resources/regex.json');
const appPath = path.join(__dirname, 'app');
const buildPath = path.join(__dirname, 'artifacts');
const config = {
entry: [path.join(appPath, 'index.js')],
output: {
path: buildPath,
filename: '[name].[hash].js',
chunkFilename: '[name].[hash].js'
},
resolve: {
modules: ['node_modules', appPath],
alias: {
'ui-select-css': path.resolve('./node_modules/ui-select/dist/select.css'),
fonts: path.resolve(__dirname, 'assets/fonts')
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
emitWarning: true,
quiet: true
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'resolve-url-loader'
]
},
{
test: /\.less$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader', options: {
url: false,
sourceMap: true
}
}, {
loader: 'less-loader', options: {
relativeUrls: false,
sourceMap: true
}
}]
},
{
test: /\.(jpe?g|png|gif)(\?.*)?$/i,
use: [{
loader: 'file-loader',
options: {name: '[path][name].[hash].[ext]'}
}]
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {limit: 10000, mimetype: 'application/octet-stream'}
}
]
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader'
}
]
},
{
test: /\.svg$/i,
loader: 'raw-loader'
},
{
test: require.resolve('angular'),
use: [
{loader: 'expose-loader', options: 'angular'},
]
},
{
test: require.resolve('jquery'),
use: [
{loader: 'expose-loader', options: '$'},
{loader: 'expose-loader', options: 'jQuery'},
]
},
{
test: require.resolve('lodash'),
use: [
{loader: 'expose-loader', options: '_'},
]
},
{
test: require.resolve('moment'),
use: [
{loader: 'expose-loader', options: 'moment'},
]
},
{
test: /\.html$/,
use: [{
loader: 'raw-loader',
options: {name: '[path][name].[hash].[ext]'}
}]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(appPath, 'index.html')
}),
new webpack.DefinePlugin({
INJECT_REGEX_HERE: JSON.stringify(patterns)
}),
new CopyWebpackPlugin([
{from: 'app/images', to: 'assets/images'},
{from: 'app/fonts', to: 'assets/fonts'},
{from: 'app/templates', to: 'assets/templates'},
{from: 'app/silent-callback.html', to: 'silent-callback.html'},
{from: 'node_modules/font-awesome/css', to: 'assets/font-awesome/css'},
{from: 'node_modules/font-awesome/fonts', to: 'assets/font-awesome/fonts'},
{from: 'node_modules/angular-ui-grid/fonts', to: 'assets/fonts'},
{from: 'node_modules/d3/d3.min.js', to: 'assets/d3'}
]),
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css'
}),
new OpenBrowserPlugin({url: 'http://localhost:1337'})
],
devtool: isProduction ? 'source-map' : 'inline-source-map',
devServer: {
port: 1337
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
if (RemoteServer) {
console.log('running with remote server', RemoteServer);
config.devServer.proxy = {
'/occm/*': 'http://' + RemoteServer
};
}
if (isProduction) {
config.plugins.push(
new CleanWebpackPlugin(buildPath)
);
}
module.exports = config;
One of the main benefits of using Webpack is reducing the number of requests a browser has to perform for rendering your application and making your app start faster.
To achieve this, it groups related resources together in "chunks", that get loaded together in one request. Separate loading of required single files like HTML-templates (without a specific reason) could be considered an anti-pattern here.
Best practice is grouping all related JS, HTML and CSS code together in one big bundle that gets loaded once, sometimes (for bigger apps) having a second 'vendor' bundle for the code from node_modules, speeding up development, because this chunk won't change as often.
Alternative solution:
So in your case, if there is no specific reason for keeping things separate (which you didn't write about), I would rather recommend serving HTML together with the controlling code in one chunk instead of loading the HTML files separately.
A good and simple starting point would be to build just two chunks. Replace your whole optimization block by the following code:
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
},
That will build two chunks: One main chunk for all of your JS and HTML files and another one, explicitly for everything from the node_modules folder.
That way you won't have to worry about browser caches for HTML files anymore, because they are built in your main chunk, and, as a benefit, your app will start faster.
I did a similar migration a couple of year and I didn't need that type of solution. My goal was encapsulate each component as a module and then lazy load as much as possible.
// webpack config
{
test: /\.html$/,
use: ['html-loader'],
},
Then in each component I just require styles and template like:
require('./_proposal-page.scss');
(function() {
'use strict';
angular.module('component.myComponent', []).component('myComponent', {
template: require('./proposal-page.html'),
controller: MyController,
});
/** #ngInject */
function MyController($log) {
const $ctrl = this;
$ctrl.$onInit = function() {
$log.log('$onInit myComponent');
}
}
})();
if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.exports === exports) {
module.exports = 'component.myComponent';
}
Webpack detect the requires and export each .html file as a module, it works smoothly.

Webpack HMR not reloading HTML file

I have a simple HMR setup for reloading typescript files and postcss files. And they work perfectly well and modules reload without a page refresh. But when I change my HTML files, the website doesn't reload on it's own and the HTML content is not being hot-reloaded in.
This is my webpack config file:
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: resolve(__dirname, 'src/main.ts'),
output: {
path: resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
}
]
}
]
},
devtool: 'source-map',
mode: 'development',
plugins: [
new HtmlWebpackPlugin({
template: resolve(__dirname, './src/index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(resolve(__dirname, 'dist'))
],
devServer: {
contentBase: resolve(__dirname, 'dist'),
port: 9000,
hot: true,
open: true,
progress: true,
}
}
With webpack 5,this setting is work for me:
devServer: {
hot:true,
open:true,
watchFiles: ['src/**/*']
},
webpack-dev-server github issue #3881
Problem is that html-webpack-plugin doesn't react to change and doesn't trigger the hmr.
In order to achieve that you could try something like this:
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let devServer; // set below in devserver part
function reloadHtml() {
this.plugin('compilation',
thing => thing.plugin('html-webpack-plugin-after-emit', trigger));
const cache = {};
function trigger(data, callback) {
const orig = cache[data.outputName];
const html = data.html.source();
if (orig && orig !== html)
devServer.sockWrite(devServer.sockets, 'content-changed');
cache[data.outputName] = html;
callback();
}
}
module.exports = {
entry: resolve(__dirname, 'src/main.ts'),
output: {
path: resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
}
]
}
]
},
devtool: 'source-map',
mode: 'development',
plugins: [
reloadHtml,
new HtmlWebpackPlugin({
template: resolve(__dirname, './src/index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(resolve(__dirname, 'dist'))
],
devServer: {
before(app, server) {
devServer = server;
},
contentBase: resolve(__dirname, 'dist'),
port: 9000,
hot: true,
open: true,
progress: true,
}
}
If you get :
DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
You can change reloadHtml to this:
function reloadHtml() {
const cache = {}
const plugin = {name: 'CustomHtmlReloadPlugin'}
this.hooks.compilation.tap(plugin, compilation => {
compilation.hooks.htmlWebpackPluginAfterEmit.tap(plugin, data => {
const orig = cache[data.outputName]
const html = data.html.source()
if (orig && orig !== html) {
devServer.sockWrite(devServer.sockets, 'content-changed')
}
cache[data.outputName] = html
})
})
}
Not sure if this is the case, but if you only want to extend your current HMR config to do a browser reload when your outer html/view files changed, then you can do it with a few extra lines using chokidar.
Maybe you already have it, because webpack-dev-server is using chokidar internally, but if not found then install it first with npm or yarn:
npm install chokidar --save
yarn add -D chokidar
Then require it in your webpack config:
const chokidar = require('chokidar');
Then in your devServer config:
devServer: {
before(app, server) {
chokidar.watch([
'./src/views/**/*.html'
]).on('all', function() {
server.sockWrite(server.sockets, 'content-changed');
})
},
Also check the API for more options.
I'm using it with Webpack4. Don't know if it works with earlier versions...
Hope it helps you or others looking for this situation.

SCSS sourcemaps not working with Webpack 3 + ExtractTextPlugin

I'm trying to enable sourcemaps for my SCSS in my app and have a really puzzling situation on my hands. Here's my config file:
const webpack = require('webpack');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssets = require('optimize-css-assets-webpack-plugin');
let config = {
entry: './src/index.ts',
devtool: 'eval-source-map', // for choosing a style of source map
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist')
},
devServer: {
contentBase: path.resolve(__dirname, './dist'), // A directory or URL to serve HTML content from
historyApiFallback: true, // fallback to /index.html for SPA
inline: true,
open: false // open default browser on launch
},
module: {
rules: [
{
test: /\.ts$/, // files ending with .ts
exclude: /node_modules/, // exclude the node modules directory
use: 'ts-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["env"]
}
}
},
{
test: /\.scss$/, // files ending with .scss
use: ['css-hot-loader'].concat(
ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
// { loader: 'style-loader', options: { sourceMap: true } },
{ loader: 'css-loader', options: { sourceMap: true } },
{ loader: 'postcss-loader', options: { sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
],
})
)
},
{
test: /\.(png|svg|jpg|gif)$/, // for other file types
use: ['file-loader?name=[name].[ext]&outputPath=img/']
}
]
},
resolve: {
extensions: ['.ts', '.js']
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
new ExtractTextPlugin('styles.css') // call the ExtractTextPlugin and name our CSS file
]
};
module.exports = config;
if (process.env.NODE_ENV === 'production') {
module.exports.plugins.push(
new webpack.optimize.UglifyJsPlugin(), // call the uglify plugin
new OptimizeCSSAssets() // call the CSS optimizer (for minification)
);
}
What's really strange is that here, everything in the terminal compiles fine but when I inspect an element, there's no sourcemap:
HOWEVER, if uncomment this line in my webpack.config file:
// { loader: 'style-loader', options: { sourceMap: true } },
and run npm start again, the sourcemaps work correctly!...
...but now the terminal throws an error, which apparently is because you aren't supposed to use style-loader inside of the ExtractTextPlugin, but rather only as a fallback. My app still works in this case, but I don't want that error! How can I enable the SCSS sourcemaps without including style-loader in my SCSS rules?

Categories