Giter Club home page Giter Club logo

node-hot-loader's Introduction

Node Hot Loader npm package

Node Hot Loader is a small tool for Hot Module Replacement support for Node.js application development with webpack. Under the hood it uses webpack and babel, so you can use all you need configurations in config files for babel and webpack.

Node Hot Loader, by default, runs all webpack entries in the same single process or in forked process, if you set corresponding option.

The most suitable use case for Node Hot Loader is hot-reloaded express application. Express application can contains API and frontend together, moreover frontend can use own HMR, e.g. React with React Hot Loader. See how to setup React HMR with Express in React Hot Loader docs. Thus, both the frontend and the server will be hot-reloadable.

Node Hot Loader also supports webpack config files written on ES2015+/TypeScript (via babel). For using ES2015+/TypeScript in webpack configuration you have to provide babel configuration file in project root directory.

If you have suggestions or you find a bug, please, open an issue or make a PR.

Installation

npm install --save-dev node-hot-loader webpack
# or
yarn add --dev node-hot-loader webpack

Command line usage

Node Hot Loader uses yargs for parsing command line arguments.

Usage: node-hot {options}
Name Description Note
--config Path to the webpack config file. If not set then search webpack.config.js in root directory.
--fork Launch compiled assets in forked process with optional node exec arguments.
--args List of arguments for forked process.
--autoRestart Auto restart forked process if unaccepted modules discovered.
--inMemory Launch compiled assets in memory fs. Not worked with forked process.
--logLevel Log level related to webpack stats configuration presets names. If not set then uses webpack stats configuration.

Usage example

node-hot --config webpack.config.server.js
# or
node-hot --logLevel minimal
# or
node-hot --fork
# or
node-hot --fork --autoRestart
# or
node-hot --fork=--arg1,--arg2 --
# or
node-hot --fork --args=--arg1,--arg2
# or just
node-hot
# Use the --help option to get the list of available options

Of course, you can add script into you package.json:

"scripts": {
  "start": "node-hot --config webpack.config.server.js"
}

and then run with your favorite package manager:

npm run start
# or
yarn run start

Webpack plugin

import NodeHotLoaderWebpackPlugin from 'node-hot-loader/NodeHotLoaderWebpackPlugin';

// Webpack configuration
export default {
  plugins: [
    // All options are optional
    new NodeHotLoaderWebpackPlugin({
      force, // boolean. true - always launch entries, false (by default) - launch entries only in watch mode.
      fork, // boolean | string[]. For example ['--key', 'key value'].
      args, // string[]. For example ['--arg1', 'arg2'].
      autoRestart, // boolean
      logLevel, // string
    }),
  ],
};

and run webpack in watch mode:

webpack --watch

The minimum required configuration:

Node Hot Loader adds all necessaries to webpack config if not present already (e.g. HotModuleReplacementPlugin), but it's require the minimum configuration in your webpack config file:

export default {
  output: {
    // Webpack can't find hot-update if output file is not directly in output.path.
    // For example, filename: 'js/[name].js' will not work.
    // However, I have no many tests for that.
    filename: '[name].js',
  },
};

Express Hot Reload Example

import app from './app'; // configuring express app, e.g. routes and logic

function startServer() {
  return new Promise((resolve, reject) => {
    const httpServer = app.listen(app.get('port'));

    httpServer.once('error', (err: any) => {
      if (err.code === 'EADDRINUSE') {
        reject(err);
      }
    });

    httpServer.once('listening', () => resolve(httpServer));
  }).then((httpServer) => {
    const { port } = httpServer.address();
    console.info(`==> 🌎 Listening on ${port}. Open up http://localhost:${port}/ in your browser.`);

    // Hot Module Replacement API
    if (module.hot) {
      let currentApp = app;
      module.hot.accept('./app', () => {
        httpServer.removeListener('request', currentApp);
        import('./app')
          .then(({ default: nextApp }) => {
            currentApp = nextApp;
            httpServer.on('request', currentApp);
            console.log('HttpServer reloaded!');
          })
          .catch((err) => console.error(err));
      });

      // For reload main module (self). It will be restart http-server.
      module.hot.accept((err) => console.error(err));
      module.hot.dispose(() => {
        console.log('Disposing entry module...');
        httpServer.close();
      });
    }
  });
}

console.log('Starting http server...');
startServer().catch((err) => {
  console.error('Error in server start script.', err);
});

Troubleshooting

Running Node Hot Loader inside a Docker container

If you attempt to run the Node Hot Loader inside a Docker container, it will start and serve as expected, but will not Hot Module Reload without some additional configuration. Add the following to your webpack config:

module.exports = {
  //...
  watchOptions: {
    poll: 1000, // Check for changes every second
  },
};

This instructs webpack to poll for changes (every second) instead of watching. This is necessary because watching does not work with NFS and machines in VirtualBox. See Webpack Configuration docs for more information.

Debugging in an IDE

If you have problems with sourcemaps or breakpoints, try to launch node-hot-loader with fork option. Also you may have to use one of the eval sourcemap styles for the devtool option in the webpack config.

License

MIT

node-hot-loader's People

Contributors

daniel-ac-martin avatar matt-d-rat avatar psychobolt avatar rodeyseijkens avatar vlazh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

node-hot-loader's Issues

Question: How to HMR React Front End and Express Backend?

Hello, I just came across your repo and I am interested in using this to HMR my back-end API. What is not clear from the docs is how to set this up for both the front-end and back-end simulatenously.

My current setup is very similar to the react-hot-boilerplate example, in that I have a server.js file which sets up the express server for HMR, this however is purely used for development.

This works well for the React app, but now I wish to work on a express server to create a REST API for the application.

My question: Do I have two different servers running HMR, one for the front-end React app, and one for the back-end express API? Or can I set this up in such a way as to have one HMR which will handle changes both to the front-end and the back-end?

A simple example demonstrating this setup would be most appreciated.

Full reload

In some cases it is not possible to do hot update, how to execute script reload like close previous process and create clearly new one?

autoRestart not work

Server has started:
- RestAPI: http://127.0.0.1:3333/api
- RestDocs:
    [DEFAULT]:
      <app>: http://127.0.0.1:3333/api-docs/app

    [V1]:
      <app>: http://127.0.0.1:3333/api-docs/v1/app
ReferenceError: kkk is not defined
    at Object.<anonymous> (/Users/nangongmo/Code/one/apps/server/dist/0.aebffefb798ce7a83fcc.hot-update.js:35:1)
    at __webpack_require__ (/Users/nangongmo/Code/one/apps/server/dist/server.js:9803:32)
    at Object._requireSelf [as require] (/Users/nangongmo/Code/one/apps/server/dist/server.js:9943:15)
    at Object.apply (/Users/nangongmo/Code/one/apps/server/dist/server.js:10558:22)
    at /Users/nangongmo/Code/one/apps/server/dist/server.js:10159:36
    at Array.forEach (<anonymous>)
    at internalApply (/Users/nangongmo/Code/one/apps/server/dist/server.js:10157:21)
    at /Users/nangongmo/Code/one/apps/server/dist/server.js:10098:24
    at waitForBlockingPromises (/Users/nangongmo/Code/one/apps/server/dist/server.js:10054:55)
    at /Users/nangongmo/Code/one/apps/server/dist/server.js:10096:22
[HMR] Updated modules:
[HMR]  - 0
[HMR] Update applied.

✔ build successfully!
[HMR] App is up to date.
[HMR] Updated modules:
[HMR]  - 0
[HMR] Update applied.

✔ build successfully!
[HMR] App is up to date.
new NodeHotLoaderWebpackPlugin({
                        force: true, // boolean. true - always launch entries, false (by default) - launch entries only in watch mode.
                        fork: true, // boolean | string[]. For example ['--key', 'key value'].
                        args: ['start'], // string[]. For example ['--arg1', 'arg2'].
                        autoRestart: true, // boolean
                    }),
const creator = createApp({
    configs,
    register: async ({ BootModule }) => {
        const app = await NestFactory.create<NestFastifyApplication>(
            BootModule,
            new FastifyAdapter(),
            {
                cors: true,
                logger: ['error', 'warn'],
            },
        );
        return app;
    },
    hooks: {
        inited: (configure, util) => buildApi(configure, util, api),
        started: (app, util) => util.get(ApiUtil).registerDocs(app),
        hmr: (instance) => {
            if (module.hot) {
                module.hot.accept((err: any) => console.error(err));
                module.hot.dispose(async () => instance.close());
            }
        },
        echo: (configure) => echoApi(configure),
    },
});
run(creator);

after catch error, it not restart

Can't install in development mode

Cloning the repo and trying to install the packages results in the following error:

npm ERR! code E401
npm ERR! Incorrect or missing password.

coming from

 GET https://npm.pkg.github.com/@js-toolkit%2Fconfigs: Unauthorized - 401

Looks like there's an .npmrc set up that uses a custom npm registry that other developers can not access by default?

babel node

hi how i can run this lib alongside babel node ?

Support for multiple configurations

Webpack allows multiple configurations to be defined within a single configuration file. (See: https://webpack.js.org/configuration/configuration-types/#exporting-multiple-configurations )

It seems to me that this would be useful for isomorphic web applications to avoid the separate webpack.config.server.js file. However, one would need to specify to node-hot-loader which config to pick from the file.

This doesn't appear to be supported at the moment. (Am I wrong?)

Is this something that you would consider supporting?

Auto-restart stops working when file is changed twice

Hi!

Thank you for amazing module, didn't even thought that is possible.

It mostly works very well, but I've encountered one annoying issue. If i change the same file twice fast, something breaks in auto-reload functionality.

Normal sequence (file edited once):

  1. file edited
  2. [Webpack] Compiling...
  3. [Webpack] Compiled successfully.
  4. [HMR] Ignored an update to unaccepted module
  5. [HMR] AutoRestart is on. Restart process...
  6. Process restarted

Bad sequence (file edited twice quickly enough (when first compile is still in progress) )

  1. file edited
  2. [Webpack] Compiling...
  3. file edited once again
  4. [Webpack] Compiled successfully.
  5. [Webpack] Compiling...
  6. [Webpack] Compiled successfully
  7. [HMR] Cannot find update. You need to restart the application!
  8. [HMR] Cannot find update. You need to restart the application!

And no restart happens. All subsequent updates trigger same message and no restart.

Is it possible to improve this usecase (restart the app)? I've tried to fix it in fork, but have trouble understanding inner workings of HMR.

Invalid Configuration Object

I downloaded node-hot-loader and tried to launch it, but I get this error:

WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
 - configuration should be an object.
    at webpack (C:\Users\myron.uecker\workspace\eagle-zion\eagle\documentsearchclientweb\client\node_modules\webpack\lib\webpack.js:19:9)
    at Promise.resolve.then.then.then.then.webpackConfig (C:\Users\myron.uecker\workspace\eagle-zion\eagle\documentsearchclientweb\client\node_modules\node-hot-loader\lib\loader.js:85:297)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
    at Function.Module.runMain (module.js:695:11)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:612:3

our webpack config is set up with

module.exports = (env) => { ...

I did a quick search and found this link, which suggests that instead of "webpackConfig" the code should use "webpackConfig()"
webpack/webpack#6960

HELP ME - how to use HMR view engine pug

let router = require('./router.js');
app.set('views', path.resolve(__dirname, entryPath));
app.locals.basedir = path.resolve(__dirname, appConfig.folders.path);
app.set('view engine', 'pug');
app.use('/', router);

Not working with Webpack 5

Issue: App is not rendering on the latest Webpack after upgrading to v5.

yarn run v1.22.10
$ npm run clean && node-hot

> [email protected] clean /Users/psychobolt/Sources/node-hot-loader/examples/simple-example
> rimraf out

[HMR] Waiting webpack...
Webpack asset bundle.js 47.9 KiB [emitted] (name: main)
runtime modules 20.9 KiB 6 modules
cacheable modules 9.67 KiB
  modules by path ./node_modules/node-hot-loader/*.js 8.75 KiB
    ./node_modules/node-hot-loader/HmrClient.js 5.98 KiB [built] [code generated]
    ./node_modules/node-hot-loader/LogColors.js 855 bytes [built] [code generated]
    ./node_modules/node-hot-loader/Logger.js 1 KiB [built] [code generated]
    ./node_modules/node-hot-loader/LogLevel.js 654 bytes [built] [code generated]
    ./node_modules/node-hot-loader/messageActionType.js 301 bytes [built] [code generated]
  modules by path ./*.js 943 bytes
    ./index.js 314 bytes [built] [code generated]
    ./renderApp.js 436 bytes [built] [code generated]
    ./welcome.js 193 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 126 ms
Webpack Compiled successfully.
Webpack Compiling...
Webpack assets by status 47.9 KiB [cached] 1 asset
cached modules 9.67 KiB (javascript) 20.9 KiB (runtime) [cached] 14 modules
webpack 5.4.0 compiled successfully in 13 ms
Webpack Compiled successfully.

Expectation: Should print log in console after successful compilation e.g.

Hello World Module loaded at 9:09:31 AM Rendered at 9:10:26 AM

Unable to use .ts extension for webpack config

webpack allows using webpack.config.ts. The issue seems to be that babel requires you to add an argument of --extensions ".ts" for it to work. There doesn't seem to be a way to add accepted extensions to .babelrc.

Sourcemap/debug breakpoint support

Hi

Is it possible to get sourcemaps/debug breakpoints in VS Code/WebStorm working?

I have tried to get interactive debugging working by adding the devtool: 'cheap-module-source-map' setting, but neither VSCode or WebStorm is able to resolve the files back to the original source code.

It may be complicated by the fact that I am running node-hot-loader via TypeScript, like so:

ts-node node_modules/.bin/node-hot --config webpack.server.ts

Have you been ever able to get interactive debugging working with node-hot-loader, and is it possible to post an example configuration if so?

Error when using with socket.io

I tried to use this library in the application with the express - everything is fine! I also tried to connect the socket.io library, but I got an error when I changed express router and refreshed the page in the browser.
The error is as follows:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at validateHeader (_http_outgoing.js:503:11)
    at ServerResponse.setHeader (_http_outgoing.js:510:3)
    at ServerResponse.header (/myproject/node_modules/express/lib/response.js:767:10)
    at ServerResponse.contentType (/myproject/node_modules/express/lib/response.js:595:15)
    at ServerResponse.send (/myproject/node_modules/express/lib/response.js:145:14)
    at app.get (/myproject/build/back/0.e82fa0b64b13baf96886.hot-update.js:22:31)
    at Layer.handle [as handle_request] (/myproject/node_modules/express/lib/router/layer.js:95:5)
    at next (/myproject/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/myproject/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/myproject/node_modules/express/lib/router/layer.js:95:5)
_http_outgoing.js:503
    throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'set');

Dependencies:

  • "node-hot-loader": "^1.6.0"
  • "express": "^4.16.2"
  • "socket.io": "^2.0.4"
  • "socket.io-client": "^2.0.4"

Engines:

  • "node": "^9.7.1"
  • "yarn": "^1.5.1"

Code

    server.js

/* @flow */

import withSocket from 'socket.io'
import { app } from './express'

const PORT = process.env.PORT || 3000
const server = app.listen(PORT, () =>
  console.log(`Server is running at ${PORT} port`),
)
const io = withSocket(server, { path: '/ws' })

io.on('connection', socket => {
  console.log('connection')
  socket.emit('message', 'hello')
})

if (module.hot && typeof module.hot.accept == 'function') {
  let currentApp = app

  module.hot.accept('./express', async () => {
    server.removeListener('request', currentApp)

    currentApp = require('./express').app
    server.on('request', currentApp)

    console.log('Server reloaded!')
  })

  module.hot.accept()
  module.hot.dispose(() => {
    console.log('Disposing entry module...')
    server.close()
  })
}

    express.js

/* @flow */

import express from 'express'
import { resolve } from 'path'

const staticPath = resolve(__dirname, '../front')
export const app = express()

app.use(express.static(staticPath))
app.get('/test', (_, res) => res.send('Hello world!'))

    client.js

/* @flow */

import io from 'socket.io-client'

const socket = io(undefined, { path: '/ws' })
socket.on('connect', () => console.log('connect'))
socket.on('disconnect', () => console.log('disconnect'))
socket.on('message', msg => console.log(msg))

Any ideas?

DeprecationWarning: Tapable.plugin

Hi, I seem to be getting a deprecation warning when using node-hot-loader with webpack v4.4.1

(node:46156) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead

After a quick glance at the code a migration might look something like (HmrServer.js#L221-L222):

this.context.compiler.hooks.done.tap('CompilerDone', this.compilerDone);
this.context.compiler.hooks.compile.tap('ComplierInvalid', this.compilerInvalid);

Thanks

Silent mode

A cli option to select between:

  • verbose logging (as now)
  • "Updated" and errors
  • only on errors (when an update failed)

would be great.

[HMR] Waiting webpack...
Webpack    9 modules
Webpack Compiled successfully.
[HMR] Launch assets in forked process.
[HMR] Waiting for update signal from webpack...
Webpack Compiling...
Webpack    9 modules
Webpack Compiled successfully.
[HMR] Checking for updates...
[HMR] Updated modules:
[HMR]  - ./src/app.js
[HMR] App is up to date.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.