Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot module reloading with Webpack #289

Closed
DevNebulae opened this issue Mar 26, 2019 · 13 comments
Closed

Hot module reloading with Webpack #289

DevNebulae opened this issue Mar 26, 2019 · 13 comments
Labels
Question ❔ Not future request, proposal or bug issue Solved ✔️ The issue has been solved

Comments

@DevNebulae
Copy link

DevNebulae commented Mar 26, 2019

Describe the issue
Currently, the library provides no documentation on how to implement hot module reloading in combination with Webpack. Is it even possible? If so, how can one achieve this?

Are you able to make a PR that fix this?
At the moment, just looking for a hotfix.

index.ts

import "reflect-metadata";
import { ApolloServer } from "apollo-server";
import { environment } from "./Environment";
import { buildSchema } from "type-graphql";
import BoardResolver from "./types/boards/BoardResolver";

async function bootstrap() {
  const schema = await buildSchema({
    resolvers: [BoardResolver]
  });

  const server = new ApolloServer({
    schema
  });

  server.listen(environment.port).then(({ url }) => {
    console.log(`Server is ready at ${url}`);
  });

  if (module.hot) {
    module.hot.accept();
    module.hot.dispose(() => server.stop());
  }
}

bootstrap();

Result
Every time I reload a type, the module throws this error:

UnhandledPromiseRejectionWarning: Error: Schema must contain unique named types but contains multiple types named "Board".
@MichalLytek
Copy link
Owner

Every time I reload a type, the module throws this error:

I think it's not possible for now - see more in #234.

how to implement hot module reloading in combination with Webpack. Is it even possible? If so, how can one achieve this?

I would like to know too 😄
How is it better than nodemon or ts-node-dev?

@MichalLytek MichalLytek added Enhancement 🆕 New feature or request Question ❔ Not future request, proposal or bug issue Blocked 🚫 Resolving this issue is blocked by other issue or 3rd party stuffs labels Mar 26, 2019
@DevNebulae
Copy link
Author

That hint was good enough to push me in the right direction. I currently ditched the all-Webpack approach to reload the compiled main.js file with Nodemon. Webpack is a really good build tool which confines to the way JavaScript currently works: modules.

I can import any file and it will be compiled to a JavaScript-compatible module. This goes for CSS, YAML configuration, Handlebar templates, et cetera. Not saying the tool needs to compatible to support those, but a better compatibility with Webpack (hot reloading) would be nice.

@demouserforsale
Copy link

@DevNebulae hi there. Can I take a look at your webpack config? I have troubles with type-graphql. I created the most basic example with webpack and after building it I have my type destroyed:
image

But when I build with tsc -b everything is fine:
image

So when I create additional resolvers I get buildSchema errors and my guess is it's because of the omitted types.

I don't need HMR, just bundling, that's why I am asking.

Latest versions of all packages.
My webpack config:

const nodeModule = require("webpack-node-externals");
const resolve = pth => path.resolve(__dirname, pth);
const nodeExternals = nodeModule();

module.exports = {
	entry: "./src/index.ts",
	target: "node",
	module: {
		rules: [
			{
				test: /\.ts?$/,
				use: "ts-loader",
				exclude: /node_modules/
			}
		]
	},
	resolve: {
		extensions: [".ts"],
		modules: [resolve("src"), "node_modules"]
	},
	output: {
		filename: "app.js",
		path: resolve("dist")
	},
	externals: [nodeExternals]
};

@DevNebulae
Copy link
Author

I had the same issue as you were having (never bothered to run the production build yet). What happens is that Webpack in production mode mangles the class and functions names. Meaning that they are shortened to a single letter. In my case, this lead to naming conflicts. I would recommend reading this: https://webpack.js.org/plugins/terser-webpack-plugin/.

@demouserforsale
Copy link

I had the same issue as you were having (never bothered to run the production build yet). What happens is that Webpack in production mode mangles the class and functions names. Meaning that they are shortened to a single letter. In my case, this lead to naming conflicts. I would recommend reading this: https://webpack.js.org/plugins/terser-webpack-plugin/.

You were right, thanks for your help!

@MichalLytek MichalLytek added Help Wanted 🆘 Extra attention is needed and removed Question ❔ Not future request, proposal or bug issue Help Wanted 🆘 Extra attention is needed labels May 30, 2019
@ddialar
Copy link

ddialar commented Jul 3, 2019

Hi @DevNebulae, I had the same issue with the hot reload and maybe the solution that I found is suitable for your case. Take a look to this repo Typegraphql Hot Reload.

Any constructive feedback is welcome.

Regards.

@MichalLytek
Copy link
Owner

@ddialar In which part it is hot reload? It just restart the whole node server on bundle.js change, not update only the schema part on the fly.

@ddialar
Copy link

ddialar commented Jul 4, 2019

Hi @19majkel94, maybe we don't understand the same by hot-reload but in my repo's code, with webpack running, if I open the recipe-type.ts file and I comment the description field, after saving the file, the server is reloaded, that's true, but the schema.graphql file is also updated. The same way, if I uncomment the field and save again, after rebooting the field appears in the schema again.

That is what I understand as hot-reloading and at least for me, that is enought in order to make easier the development process.

Regards.

@MichalLytek
Copy link
Owner

MichalLytek commented Jul 4, 2019

It's full reload, with creating DB connection again, etc.
Hot reload is that our app react on changes, e.g. when we change the GraphQL field name, it reloads only the schema but the rest of the code (containers, connections, etc.) are not touched at all.

In frontend world:

  • hot reload: change in CSS (e.g. font color) -> quick update of the color without loosing e.g. form state or input values
  • full reload: we see loading circle on the tab, the page disappears and loads again with lost state (we have to click the button to open the modal again, etc.)

@ddialar
Copy link

ddialar commented Jul 4, 2019

OK. I understand. Thank you so much for share your point of view with me.

I match with your point of view if we talk about frontend, but I have a new question: if the schema is generated before the server start-up and someone wants to change the types definition, it's imperative to stop the server, compile the new structure and restart the server with the new schema definition, doesn't it? If this hypotesis is right, any change in the types definition couldn't be compiled and accepted by the server on the fly.

What do you think?

@wonbyte
Copy link

wonbyte commented Feb 12, 2020

I have webpack working with hot reloading:

webpack.common.js

const path = require('path');

module.exports = {
  module: {
    rules: [
      {
        exclude: [path.resolve(__dirname, 'node_modules')],
        test: /\.ts$/,
        use: 'ts-loader'
      },
      {
        exclude: /node_modules/,
        loader: 'graphql-tag/loader',
        test: /\.(graphql|gql)$/
      }
    ]
  },
  output: {
    filename: 'server.js',
    path: path.resolve(__dirname, 'dist')
  },
  resolve: {
    extensions: ['.ts', '.js', '.graphql']
  },
  target: 'node'
};

webpack.development.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const merge = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');
const path = require('path');
const webpack = require('webpack');

const common = require('./webpack.common.js');

module.exports = merge.smart(common, {
  devtool: 'inline-source-map',
  entry: ['webpack/hot/poll?1000', path.join(__dirname, 'src/index.ts')],
  externals: [
    nodeExternals({
      whitelist: ['webpack/hot/poll?1000']
    })
  ],
  mode: 'development',
  plugins: [new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin()],
  watch: true
});

webpack.production.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const merge = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');
const path = require('path');

const common = require('./webpack.common.js');

module.exports = merge(common, {
  devtool: 'source-map',
  entry: [path.join(__dirname, 'src/index.ts')],
  externals: [nodeExternals({})],
  mode: 'production',
  plugins: [new CleanWebpackPlugin()]
});
"dev": "NODE_ENV=development npm run build",
"build": "webpack --config webpack.$NODE_ENV.js",
"start": "node dist/server",

index.js

import 'reflect-metadata';
import { ApolloServer, Config } from 'apollo-server';
import { buildSchema } from 'type-graphql';
import { Container } from 'typedi';

import { environment } from './environment';

async function main(): Promise<void> {
  const schema = await buildSchema({
    resolvers: [],
    container: Container
  });

  // configure the server here
  const serverConfig: Config = {
    schema,
    introspection: true,
    playground: true
  };

  const server = new ApolloServer(serverConfig);

  server
    .listen(environment.port)
    .then(({ url }) => console.log(`🚀 Server ready at ${url}.`));

  if (module.hot) {
    module.hot.accept();
    module.hot.dispose(() => server.stop());
  }
}

main();

Hope this helps!

@MichalLytek
Copy link
Owner

As #234 is now solved, this issue should be fixed too 🎉

Please install [email protected] and let me now if the issue still happens 😉

@MichalLytek MichalLytek added Solved ✔️ The issue has been solved Question ❔ Not future request, proposal or bug issue and removed Blocked 🚫 Resolving this issue is blocked by other issue or 3rd party stuffs Enhancement 🆕 New feature or request labels May 3, 2020
@SyamalaPu
Copy link

I have webpack working with hot reloading:

webpack.common.js

const path = require('path');

module.exports = {
  module: {
    rules: [
      {
        exclude: [path.resolve(__dirname, 'node_modules')],
        test: /\.ts$/,
        use: 'ts-loader'
      },
      {
        exclude: /node_modules/,
        loader: 'graphql-tag/loader',
        test: /\.(graphql|gql)$/
      }
    ]
  },
  output: {
    filename: 'server.js',
    path: path.resolve(__dirname, 'dist')
  },
  resolve: {
    extensions: ['.ts', '.js', '.graphql']
  },
  target: 'node'
};

webpack.development.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const merge = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');
const path = require('path');
const webpack = require('webpack');

const common = require('./webpack.common.js');

module.exports = merge.smart(common, {
  devtool: 'inline-source-map',
  entry: ['webpack/hot/poll?1000', path.join(__dirname, 'src/index.ts')],
  externals: [
    nodeExternals({
      whitelist: ['webpack/hot/poll?1000']
    })
  ],
  mode: 'development',
  plugins: [new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin()],
  watch: true
});

webpack.production.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const merge = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');
const path = require('path');

const common = require('./webpack.common.js');

module.exports = merge(common, {
  devtool: 'source-map',
  entry: [path.join(__dirname, 'src/index.ts')],
  externals: [nodeExternals({})],
  mode: 'production',
  plugins: [new CleanWebpackPlugin()]
});
"dev": "NODE_ENV=development npm run build",
"build": "webpack --config webpack.$NODE_ENV.js",
"start": "node dist/server",

index.js

import 'reflect-metadata';
import { ApolloServer, Config } from 'apollo-server';
import { buildSchema } from 'type-graphql';
import { Container } from 'typedi';

import { environment } from './environment';

async function main(): Promise<void> {
  const schema = await buildSchema({
    resolvers: [],
    container: Container
  });

  // configure the server here
  const serverConfig: Config = {
    schema,
    introspection: true,
    playground: true
  };

  const server = new ApolloServer(serverConfig);

  server
    .listen(environment.port)
    .then(({ url }) => console.log(`🚀 Server ready at ${url}.`));

  if (module.hot) {
    module.hot.accept();
    module.hot.dispose(() => server.stop());
  }
}

main();

Hope this helps!

The development mode webpack with hot reloading is working, but is failing with production mode. Can you tell me what are the versions of webpack used to test this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question ❔ Not future request, proposal or bug issue Solved ✔️ The issue has been solved
Projects
None yet
Development

No branches or pull requests

6 participants