Hot reload with typescript and webpack - javascript

I'm facing problems while developing both frontend (Redux) and backend (Express) in Typescript. I could not make hot reload work. Here is configuration of webpack.config.js in root folder:
const webpack = require('webpack');
const path = require('path');
const { CheckerPlugin } = require('awesome-typescript-loader');
const config = {
cache: true,
mode: 'development',
entry: {
'user': ['./dist/client/User/index', 'webpack-hot-middleware/client'],
'guest': ['./dist/client/Guest/index', 'webpack-hot-middleware/client']
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CheckerPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/static'
},
devServer: {
contentBase: './dist',
hot: true
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx']
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
include: path.join(__dirname, 'client'),
options: { cacheDirectory: true }
}
]
},
node: { fs: 'empty' }
};
module.exports = config;
And tsconfig.json:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"jsx": "react",
"esModuleInterop": true,
"outDir": "dist",
"sourceMap": true,
"baseUrl": "."
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
In src I separate two folders client and server, my npm start script is tsc && node dist/server. In server/index I declare utilization of webpack compiler like this:
import config from '../../webpack.config';
const compiler = webpack(config);
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: config.output.publicPath
}));
app.use(require('webpack-hot-middleware')(compiler));
When I start the application, webpack builds normally, but it never build hot reload when I make changes in client files. I spent a whole day but still confused and don't know how to fix it yet.

are you currently using react 17?
if so, create the following in the .env file
FAST_REFRESH=false
directory

Related

Webpack source-map targets to *.min.js bundle and not to the source files in production mode

we have almost the same configuration for development and production mode in webpack. In both cases we want to have source maps to be able to debug in browser. For development mode all works fine. Our source files appear in the browser dev tools and all our files are listed in the app2.min.js.map file.
{
"version":3,
"sources":[
"webpack:///webpack/bootstrap",
"webpack:///./app2/app.dev.ts",
"webpack:///./app2/app.module.ts",
"webpack:///./app2/app.routing.ts",
"webpack:///./app2/components/absenceManagement/...
...
But in production mode the source map targets back to the minified bundle file
{
"version":3,
"sources":[
"webpack:///js/app2.min.js"
],
"names":[
"modules","installedModules",
"__webpack_require__",
...
Therefore the bundled js file targets to the source map (//# sourceMappingURL=app2.min.js.map) and the source map back to the bundle file.
Our configuration
webpack.dev.js
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
module.exports = function () {
return webpackMerge(commonConfig,
{
devtool: 'source-map',
entry: {
app: './app2/app.dev.ts'
},
mode: 'development'
});
}
webpack.prod.js
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
module.exports = function () {
return webpackMerge(commonConfig,
{
devtool: 'source-map',
entry: {
app: './app2/app.prod.ts'
},
mode: 'production'
});
}
webpack.common.js
const { CheckerPlugin } = require('awesome-typescript-loader');
module.exports = {
// Currently we need to add '.ts' to the resolve.extensions array.
resolve: {
extensions: ['.ts', '.tsx', '.js', '.html', '.css'],
modules: ['app2', 'node_modules', 'js']
},
// Add the loader for .ts files.
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /\.spec\.ts$/,
use: ['awesome-typescript-loader', 'angular2-template-loader']
},
{
test: /\.css$/,
loader: 'raw-loader'
},
{
test: /\.html$/,
loader: 'raw-loader'
}
]
},
plugins: [
new CheckerPlugin()
],
stats: {
colors: false
},
output: {
filename: './js/app2.min.js'
}
};
tsconfig.js
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"./#types/",
"./node_modules/#types/",
"./js/"
],
"baseUrl": ".",
"paths": {
"typemoq": [ "js/typemoq" ]
},
"types": [
"lodash",
"moment",
"jquery",
"typemoq"
]
},
"awesomeTypescriptLoaderOptions": {
"useCache": true
},
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
some of the package.json
"angular2-template-loader": "^0.6.0",
"awesome-typescript-loader": "^5.2.1",
...
"typescript": "^2.9.2",
"webpack": "^4.19.1",
"webpack-merge": "^4.1.4",
"webpack-stream": "^5.1.1"
You can try the following:
Setup terser-webpack-plugin in your webpack config. Therefore see: https://webpack.js.org/configuration/optimization/
And then use UglifyJS for the minification of your code. See: https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions and https://github.com/mishoo/UglifyJS#minify-options.
Also disable the optimiztion that is done by webpack itself which you can do with:
module.exports = {
//...
optimization: {
minimize: false,
},
};`
Your configuration for your productive environment should look like this:
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = function () {
return webpackMerge(commonConfig,
{
devtool: 'source-map',
entry: {
app: './app2/app.prod.ts'
},
mode: 'production',
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
minify: TerserPlugin.uglifyJsMinify,
terserOptions: {
sourceMap: {
filename: 'app2.min.js',
url: 'app2.min.js.map'
}
},
}),
],
},
});
}

"Parsing error: invalid character" when bundling images with Webpack's loaders

When I'm trying to bundle my React project, I get the following error regarding an image I'm about to load:
Oddly enough, when I hide the error overlay, I can see in a browser that my picture has been loaded correctly, despite Webpack's complaints. I've already been carefully following other working config examples, where that problem doesn't occur, but I can't easily identify the source of the issue, what leaves me here with no idea how to approach it or where to look for a hint. I've also tried different loaders: url-loader, file-loader and image-webpack-loader, but to no avail. Maybe that's Typescript what causes these troubles?
App.tsx where I'm importing my image:
import "./app.d";
import React from "react";
import SampleImage from "./assets/images/sample-image.jpg";
const App: React.FC = () => (
<div>
<img src={SampleImage} />
</div>
);
export default App;
app.d.ts with modules declarations to allow importing assets in typescripts files:
declare module "*.jpeg";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.png";
webpack.common.ts - my main Webpack config file, where I placed both url-loader and file-loader as it is in ejected create-react-app config, but it doesn't really change anything:
import path from "path";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { CleanWebpackPlugin } from "clean-webpack-plugin";
module.exports = {
entry: path.resolve(__dirname, "..", "./src/index.tsx"),
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript",
],
},
},
],
},
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.(ts|tsx)$/,
/\.css$/,
/\.json$/,
/\.bmp$/,
/\.gif$/,
/\.jpe?g$/,
/\.png$/,
],
loader: require.resolve("file-loader"),
options: {
name: "static/media/[name].[hash:8].[ext]",
},
include: path.resolve(__dirname, "..", "./src/assets"),
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
options: {
name: "static/media/[name].[hash:8].[ext]",
},
include: path.resolve(__dirname, "..", "./src/assets"),
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".jsx"],
},
output: {
path: path.resolve(__dirname, "..", "./dist"),
filename: "bundle.js",
},
devServer: {
contentBase: path.resolve(__dirname, "..", "./dist"),
hot: true,
compress: true,
open: true,
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "React App",
template: path.resolve(__dirname, "..", "./src/index.html"),
}),
new ForkTsCheckerWebpackPlugin({
async: false,
eslint: {
files: path.resolve(__dirname, "..", "./src/**/*"),
},
}),
],
};
webpack.dev.ts
import webpack from "webpack";
import ReactRefreshWebpackPlugin from "#pmmmwh/react-refresh-webpack-plugin";
module.exports = {
mode: "development",
devtool: "eval-source-map",
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript",
],
plugins: [require.resolve("react-refresh/babel")],
},
},
],
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new ReactRefreshWebpackPlugin({
overlay: {
sockIntegration: "wds",
},
}),
],
};
webpack.prod.ts
import webpack from "webpack";
module.exports = {
mode: "production",
devtool: "source-map",
};
webpack.config.ts
import merge from "webpack-merge";
import devConfig = require("./webpack.dev");
import prodConfig = require("./webpack.prod");
import commonConfig = require("./webpack.common");
module.exports = ({ env }: { env: "dev" | "prod" }) => {
const envConfig = env === "dev" ? devConfig : prodConfig;
return merge(commonConfig, envConfig);
};
Additionaly, my .babelrc file:
{
"presets": [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript"
],
"plugins": [
"react-refresh/babel",
[
"#babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
}
and tsconfig.json:
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": ["src"]
}
Scripts in package.json:
"scripts": {
"start": "webpack serve --config config/webpack.config.ts --env env=dev --hot",
"build": "webpack --config config/webpack.config.ts --env env=prod",
},
I'd be really grateful for any hint how to solve the issue.
Thanks to help received here: https://github.com/webpack/webpack/issues/12276#event-4152056913, I've managed to surpass the error overlay by changing the options passed to the ForkTsCheckerWebpackPlugin in my webpack.common.ts file.
Before:
new ForkTsCheckerWebpackPlugin({
async: false,
eslint: {
files: path.resolve(__dirname, "..", "./src/**/*"),
},
}),
Now:
new ForkTsCheckerWebpackPlugin({
async: false,
eslint: {
files: path.resolve(__dirname, "..", "./src/**/*.{ts,tsx,js,jsx}"),
},
}),

Cannot access prototype after compiling with Webpack in Typescritp

I Converted an old Javascript library for the company I work with, to a brand new Typescript Library. I use WebPack since I heard i need a module management system in order to make my library works in the browser. Problem is, whenever i'm trying to initiate my library ( in the JQuery function $(document).ready() , i cannot access my object prototype, I got a typeError :
Uncaught TypeError: Cannot read property 'loadPage' of undefined
Here is my code :
webpack.config.js
const path = require('path');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var webpack = require("webpack");
var fs = require("fs");
module.exports = {
entry: './src/libraryLoader.ts',
module: {
rules: [
{
test: /\.(tsx?)$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.json$/,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]",
publicPath: "config/"
}
},
exclude: /node_modules/
}
]
},
plugins: [
new CopyWebpackPlugin([
{ from: 'src/settings/config', to: "config" }
]),
new UglifyJsPlugin({
uglifyOptions: {
ie8: true,
//compress: true
}
}),
new webpack.BannerPlugin({
banner: fs.readFileSync('./copyright', 'utf8'),
raw: true
})
],
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
modules: [
"node_modules"
]
},
// Enable sourcemaps for debugging webpack's output.
devtool: "eval",
output: {
filename: 'library.min.js',
path: path.resolve(__dirname, 'dist'),
},
watch: false,
watchOptions: {
ignored: /node_modules/
},
externals: {
// require("jquery") is external and available
// on the global var jQuery
"jquery": "jQuery"
}
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"moduleResolution": "node",
"module": "commonjs",
"outDir": "build",
"strict": true,
"noImplicitAny": true,
"allowJs": true,
"sourceMap": true,
"typeRoots": [
"./node_modules/#types"
],
"project": "src",
"lib": [ "es2015", "dom"]
},
"include": [
"src/**/*",
"typings/**/*"
]
}
Where I load my library :
$(document).ready(function () {
let loader = new LibraryLoader();
loader.library().loadPage(); // <- Error here
/*...*/
});
My Loader class
export class LibraryLoader{
private _library: Library;
public LibraryLoader() {
this.setLibrary(new Library(/* ... unrelated options ... */));
}
public library(): Library{
return this._library;
}
private setLibrary(value: Library) : void {
this._library = value;
}
}
P.S. There is a loadPage function in the library, but the function library() return undefined anyways,

Webpack2 + Typescript2 fails to dynamic import json

I have a problem at import json dynamically using webpack.
(https://webpack.js.org/api/module-methods/#import-)
It keep shows this error - TS2307: Cannot find module
package versions are..
webpack version: 2.7.0
typescript version: 2.4.2
awesome-typescript-loader: 3.2.1
tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "esnext",
"moduleResolution": "node",
"target": "es6",
"jsx": "react",
"allowJs": true,
"allowSyntheticDefaultImports": true
},
"awesomeTypescriptLoaderOptions": {
"useBabel": true,
"useCache": true
},
"exclude": ["node_modules"],
"include": [
"./components/**/*",
"./core/*",
"./pages/*",
"./utils/*",
"./urls/*",
"./translations/**/*",
"./main.tsx"
]
}
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const AssetsPlugin = require('assets-webpack-plugin');
const pkg = require('./package.json');
const isDebug = global.DEBUG === false ? false : !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v');
const useHMR = !!global.HMR; // Hot Module Replacement (HMR)
const babelConfig = Object.assign({}, pkg.babel, {
babelrc: false,
cacheDirectory: useHMR,
});
// Webpack configuration (main.js => public/dist/main.{hash}.js)
// http://webpack.github.io/docs/configuration.html
const config = {
// The base directory for resolving the entry option
context: __dirname,
// The entry point for the bundle
entry: [
// require('babel-polyfill'),
'./main.tsx'
],
// Options affecting the output of the compilation
output: {
path: path.resolve(__dirname, './public/dist'),
publicPath: '/dist/',
filename: isDebug ? '[name].js?[hash]' : '[name].[hash].js',
chunkFilename: isDebug ? '[id].js?[chunkhash]' : '[id].[chunkhash].js',
sourcePrefix: ' ',
},
// Developer tool to enhance debugging, source maps
// http://webpack.github.io/docs/configuration.html#devtool
devtool: isDebug ? 'inline-source-map' : false,
// What information should be printed to the console
stats: {
colors: true,
reasons: isDebug,
hash: isVerbose,
version: isVerbose,
timings: true,
chunks: isVerbose,
chunkModules: isVerbose,
cached: isVerbose,
cachedAssets: isVerbose,
},
// The list of plugins for Webpack compiler
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
__DEV__: isDebug,
}),
// Emit a JSON file with assets paths
// https://github.com/sporto/assets-webpack-plugin#options
new AssetsPlugin({
path: path.resolve(__dirname, './public/dist'),
filename: 'assets.json',
prettyPrint: true,
}),
],
// Options affecting the normal modules
module: {
rules: [
{
test: /\.tsx?$/,
include: [
path.resolve(__dirname, './components'),
path.resolve(__dirname, './core'),
path.resolve(__dirname, './pages'),
path.resolve(__dirname, './apis'),
path.resolve(__dirname, './urls'),
path.resolve(__dirname, './translations'),
path.resolve(__dirname, './main')
],
loader: 'awesome-typescript-loader'
},
{
test: /\.css/,
use: [{loader: 'style-loader'}, {loader: 'css-loader', options:
{
sourceMap: isDebug,
// CSS Modules https://github.com/css-modules/css-modules
modules: true,
localIdentName: isDebug ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]',
// CSS Nano http://cssnano.co/options/
minimize: !isDebug,
}},
// {loader: 'postcss-loader'}
],
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
loader: 'url-loader?limit=10000',
},
{
test: /\.(eot|ttf|wav|mp3)$/,
loader: 'file-loader',
},
],
},
externals: {
react: 'React'
},
resolve: {
extensions: ['.js', '.ts', '.tsx']
}
};
// Optimize the bundle in release (production) mode
if (!isDebug) {
config.plugins.push(new webpack.optimize.UglifyJsPlugin({ compress: { warnings: isVerbose }, sourceMap: isDebug }));
config.plugins.push(new webpack.optimize.AggressiveMergingPlugin());
}
// Hot Module Replacement (HMR) + React Hot Reload
if (isDebug && useHMR) {
babelConfig.plugins.unshift('react-hot-loader/babel');
config.entry.unshift('react-hot-loader/patch', 'webpack-hot-middleware/client');
config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.plugins.push(new webpack.NoEmitOnErrorsPlugin());
}
module.exports = config;
custom.d.ts
declare module "*.json" {
const value: any;
export default value;
}
my error code
// TS2307: Cannot find module './test.json'.
import('./test.json').then(_ => {
console.log(_
})
// but these codes work
import './test.json'
import('./test.js').then(_ => { console.log(_)})
I have searched issues as much as possible I can. but no one seems to encountered this issue.
Here's related links what I found.
https://github.com/Microsoft/TypeScript/issues/16820
https://blog.josequinto.com/2017/06/29/dynamic-import-expressions-and-webpack-code-splitting-integration-with-typescript-2-4/
If anyone have a clue, please let me know.
Are you sure your custom.d.ts is in a folder included in the compilation? I believe only typings in packages under node_modules/#types are included if they are not in the "files" or "include" properties in tsconfig.json.

import node modules in electron angular webpack app

I have a electron application using Angular (2) and I pack it using Webpack. I want to use my node modules in my Angular components
import { Injectable } from '#angular/core';
import {Bonjour} from 'bonjour';
Update:
I set the target to electron-renderer in my webpack configuration. Now I get the error error TS2693: 'Bonjour' only refers to a type, but is being used as a value here
Bonjour remains undefined.
Here is my webpack.config.js:
var path = require('path');
var webpack = require('webpack');
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
module.exports = {
devtool: 'source-map',
target: 'electron-renderer',
entry: {
'angular': [
'rxjs',
'reflect-metadata',
'#angular/core',
'#angular/router',
'#angular/http'
],
'app': './app/main'
},
output: {
path: __dirname + '/build/',
publicPath: 'build/',
filename: '[name].js',
sourceMapFilename: '[name].js.map',
chunkFilename: '[id].chunk.js'
},
resolve: {
extensions: ['.ts', '.js', '.json', '.css', '.html']
},
module: {
loaders: [{
test: /\.ts$/,
loader: 'ts-loader',
exclude: [/node_modules/]
}]
},
plugins: [
new CommonsChunkPlugin({ name: 'angular', filename: 'angular.js', minChunks: Infinity }),
new CommonsChunkPlugin({ name: 'common', filename: 'common.js' })
]
};
How can I setup my Electron/Angular application in such a way that I can import node modules as mentioned above?
added tsconfig:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"moduleResolution": "node",
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"typeRoots": [
"../node_modules/#types"
]
},
"files": [
"app/main.ts"
]
}
This plugin in question exported a prototype function in its module.exports. Thus the plugin needed to be instantiated like this:
var Bonjour = require('bonjour')
var bonjour = new Bonjour();

Categories

Resources