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

Update webpack.config for better performance and support hot reloading #158

Open
suuunly opened this issue May 14, 2024 · 9 comments
Open
Assignees

Comments

@suuunly
Copy link

suuunly commented May 14, 2024

Expected behavior

I was expecting when I run npm run start in an office addin project, (which is the equivalent to:
office-addin-debugging start manifest.xml), that the project starts up fairly quickly, and uses a reasonable amount of memory given that I have done nothing to the project yet.

Current behavior

  • The project runs extremely slowly.
  • Hot reload does not seem to trigger, unless I am just getting too impatient
  • And webpack is shown to use 8.08 GB after the project has started up.

If I make changes in my project, and reload the addin from within Excel, then the memory usage jumps up to 10-11 gig, and then drops back down to 8.

Steps to Reproduce

Please provide detailed steps for reproducing the issue.

  1. create a brand new excel addin react template, using the microsoft guide:

  2. run the project using the npm run start command

  3. make a change in your project, and notice how it does not update (or at least not relatively quickly). Also notice the excessive memory usage, in your Activity Monitor

Is this expected behaviour? or am I missing something?

Context

Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.

  • Operating System: Mac OS 14.4 (23E214)
  • Node version: v20.11.0
  • Office version: Excel Version 16.84 (24041420)
  • Tool version: "office-addin-debugging": "^5.1.2",
@suuunly
Copy link
Author

suuunly commented May 15, 2024

First I tried running the project on a windows machine (Windows 10), to see if it somehow ran better on windows for some reason (given that Office is a Microsoft product).
Turns out it was also running insanely slowly and was just eating up the device's RAM.

My second solution was to start from scratch again, on the windows machine:

1. installing the office addin project generator

2. generate a brand new Excel Add-in Creact Typescript Template

3. Compare the difference between the projects.

When comparing, I noticed that:

  • all the office packages had updated versions since I generated the original project
  • it now has a .hintrc file
  • it added a .eslintrc.json file
  • generated a bunch of .vscode files related to the edge extension debugger in vscode
  • added forceConsistentCasingInFileNames to my tsconfig.json file
  • and other minor details

After doing this, I noticed that the project no longer swallowed up the device's memory, and remained at a acceptable level. So there must have been a bug in the version I was using, as this new one works substantially better 🥳

But the reload time was still abysmally slow, which lead me to step 4.

4. Update the hot reloading configuration within the generated webpack file.

I did this through a culmination of these three links:

After making the needed changes to the webpack.config.js, the project also runs exponentially faster, and actually reacts to changes made within the code almost instantaneously. Granted I sometimes have to do a manual taskpane reload for some changes (such as style changes) - but it is still a huge improvement.

5. Running the project on the mac again

Afterwards, I tried running the project on my mac, and it runs just as smoothly as on the windows machine, only taking up about 1-2 gig of memory (as oppose to the 8-10)

Final thoughts

I was thinking of writing a medium article on things to consider when working with a project like mine (incl. setup hot-reloading, seeing console logs, how to communicate with external api, setup a non azure based login approach) - I can post it here if people are interested :)

@Rick-Kirkham
Copy link
Contributor

I'm going to move this to the React project template repo and reopen it.

@Rick-Kirkham Rick-Kirkham reopened this May 15, 2024
@Rick-Kirkham Rick-Kirkham transferred this issue from OfficeDev/Office-Addin-Scripts May 15, 2024
@Rick-Kirkham Rick-Kirkham changed the title Excel Addins (React Template) use an excessive amount of memory and are slow Update webpack.config for better performance and support hot reloading May 15, 2024
@suuunly
Copy link
Author

suuunly commented May 15, 2024

I'm going to move this to the React project template repo and reopen it.

By all means 😁
I'd be happy to post the final hot-reloading code, if that is of interest 😊

@Rick-Kirkham
Copy link
Contributor

I'm going to move this to the React project template repo and reopen it.

By all means 😁 I'd be happy to post the final hot-reloading code, if that is of interest 😊

It is definitely of interest. Thanks for your help.

@suuunly
Copy link
Author

suuunly commented May 16, 2024

Absolutely no worries! Happy to contribute in any way I can 😁
Here's the code. Lemme know if something doesn't make sense 😁

Package.json

// (...)
"devDependencies": {
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.13",
  "react-refresh": "^0.14.2",
  "react-refresh-typescript": "^2.0.9",
  "fork-ts-checker-webpack-plugin": "^9.0.2",
}
// (...)

Granted, I don't think I need the react-refresh package, as I utilise the typescript variant (as stated here - under ts-loader), instead of the babel-loader approach (which does use the react-refresh package)

Webpack.config.js

/* eslint-disable no-undef */

const devCerts = require("office-addin-dev-certs");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");

// Setup Hot-Reloading
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const ReactRefreshTypeScript = require("react-refresh-typescript");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");

const urlDev = "https://localhost:3000/";
const urlProd = "https://www.contoso.com/"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION

async function getHttpsOptions() {
  const httpsOptions = await devCerts.getHttpsServerOptions();
  return { ca: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert };
}

module.exports = async (env, options) => {
  const dev = options.mode === "development";
  const config = {
    devtool: "source-map",
    entry: {
      polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
      vendor: ["react", "react-dom", "core-js", "@fluentui/react-components", "@fluentui/react-icons"],
      taskpane: ["./src/taskpane/index.tsx", "./src/taskpane/taskpane.html"],
      commands: "./src/commands/commands.ts",
    },
    output: {
      clean: true,
    },
    resolve: {
      extensions: [".ts", ".tsx", ".html", ".js"],
    },
    module: {
      rules: [
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            options: {
              presets: ["@babel/preset-typescript"],
            },
          },
        },

        // Setup Hot-Reloading
        {
          test: /\.tsx?$/,
          exclude: /node_modules/,
          use: [
            {
              loader: "ts-loader",
              options: {
                configFile: "tsconfig.json",
                transpileOnly: dev,
                ...(dev && {
                  getCustomTransformers: () => ({
                    before: [ReactRefreshTypeScript()],
                  }),
                }),
              },
            },
          ],
        },

        {
          test: /\.html$/,
          exclude: /node_modules/,
          use: "html-loader",
        },
        {
          test: /\.(png|jpg|jpeg|ttf|woff|woff2|gif|ico)$/,
          type: "asset/resource",
          generator: {
            filename: "assets/[name][ext][query]",
          },
        },
      ],
    },
    plugins: [
      // Setup Hot-Reloading
      dev && new ReactRefreshWebpackPlugin(),
      new ForkTsCheckerWebpackPlugin(),

      new CopyWebpackPlugin({
        patterns: [
          {
            from: "assets/*",
            to: "assets/[name][ext][query]",
          },
          {
            from: "manifest*.xml",
            to: "[name]" + "[ext]",
            transform(content) {
              if (dev) {
                return content;
              } else {
                return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
              }
            },
          },
        ],
      }),
      new HtmlWebpackPlugin({
        filename: "taskpane.html",
        template: "./src/taskpane/taskpane.html",
        chunks: ["polyfill", "vendor", "taskpane"],
      }),
      new HtmlWebpackPlugin({
        filename: "commands.html",
        template: "./src/commands/commands.html",
        chunks: ["commands"],
      }),
      new webpack.ProvidePlugin({
        Promise: ["es6-promise", "Promise"],
      }),
    ],
    devServer: {
      hot: true,
      headers: {
        "Access-Control-Allow-Origin": "*",
      },
      server: {
        type: "https",
        options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(),
      },
      port: process.env.npm_package_config_dev_server_port || 3000,
    },
  };

  return config;
};

@DanielYKPan
Copy link

Hi,
I am also having the same issue.
I followed this to start a word-addin with React.
Everytime, I made changes into the project, the add-in could not do "hot-reload" and you could see a "warning" message in the console:
image

We had to refresh the addin manually so that the addin got the latest updates.

Please give some advice.

@suuunly
Copy link
Author

suuunly commented Jun 6, 2024

Hey @DanielYKPan 😁
Just to clarify, did you try to update the your webpack.config.js file with my suggested changes? Or are you getting those warnings from running it as-is, with no modifications made? :)

@TanguyFir
Copy link

Thanks @suuunly ! 🙏 It was a good first step for me 👍 But I needed to add :

optimization: dev && {
   runtimeChunk: 'single',
}

to my webpack config to make it works 🤷

@suuunly
Copy link
Author

suuunly commented Aug 2, 2024

@TanguyFir, awesome. Happy it helped! 😄
And, also thanks for the additional insight 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants