Entire library is included when transpiling from TypeScript
Problem
When including individual components from the @mui/material library, the final bundle size compiled from a .tsx file is much bigger than its .jsx counterpart. Simply changing .jsx to .tsx increased main.js from 72.6 KiB to 482 KiB. I discovered that this is due to typescript including the entire Material UI library in the emitted code prior to GAS compilation.
Steps to Replicate
- Create a new project
- Install
@mui/materialand associated libraries https://mui.com/material-ui/getting-started/installation/ - Import a component from
@mui/materialand use it in the returned jsx - Run
npm run buildand check the bundled file size - Change the file extension to
.tsc - Build again and notice the much larger filesize
Solution
After some tinkering I discovered that setting "target": true and "moduleResolution": "node" inside tsconfig.json fixed the issue. TypeScript now only includes the imported components instead of the entire library.
Was wondering if there were any drawbacks to this fix or if it should be added as the default configuration so others might avoid the same issue.
Update:
I neglected to fully test my solution. The project compiles successfully but public functions are not outputted tocode.gs anymore. It seems that gas-webpack-plugin is unable to pick up named exports and add them to the global scope when using node's resolution method. However, renaming index.ts to index.js and reverting to the global.myFunc = publicFunctions.myFunc pattern found in previous versions of this repo seems to work.
Hi @BarryMolina, thanks for pointing this out. As you've seen, there are limitations to how gas-webpack-plugin's powers, which I don't fully understand. One option may be to revert back to the .js version with assignment to the global object as you report.
My approach has been to try to include a sample version that works reasonably well with both javascript and typescript, so that users may decide which to use, but it probably has its limitations.
I'd love to include a sample app using material-ui since it's very popular, but have not gotten around to it, and I think when I tried I had similar issues with avoiding large bundle sizes. Let me know if you figure something out!
Wondering if you found a workaround @BarryMolina that works with index.ts server code.
Hello,
I am having the same issue as well: I am using MUI (+MUI icons) and Formik (which uses Lodash), and the final HTML file for a dialog is above the recommended 244kb size. This is mainly because of MUI.
I have implemented the right pattern to import MUI components. I have tried using .tsx and .jsx files, but even in the latter case, I see no difference.
From your last message @enuchi, I must admit I am a bit confused. Is the server code impacting the bundle size for the client side? I thought that they were separated. I might be missing something here, which would explain why .jsx and .tsx extension does not matter on the client side for me.
I will keep on digging on this subject. I will post my findings (if there are any) here.
Note: I updated slightly my tsconfig, which might have an impact:
{
"compilerOptions": {
"types": ["gas-types-detailed", "jest", "node"],
"jsx": "react",
"esModuleInterop": true,
"moduleResolution": "node",
"lib": ["esnext", "DOM"],
"strict": true,
"importHelpers": true,
"target": "ES2019"
},
"include": ["**/*.ts"],
"exclude": ["node_modules"]
}
Hi @h-contributeur , server code is not impacting bundle size or anything. @BarryMolina seems to have found a way to correctly import MUI files using "moduleResolution": "node", but also points out that while it fixes things on the client, it breaks the behavior of gas-webpack-plugin and how it reads the global assignment in the server file https://github.com/enuchi/React-Google-Apps-Script/blob/main/src/server/index.ts.
If that is the only issue you could try reverting to using global assignment like in https://github.com/enuchi/React-Google-Apps-Script/blob/89c12fa161989257cb30e29d97d97d805deda923/src/server/index.js but this may have other unintended consequences.
Well I might have the solution then.
First of all, thank you very much @enuchi for your explanations, I am quite new to Typescript and Webpack.
I have had "moduleResolution": "node" since the beginning and it works for me. My final size is 300kB for using MUI, Lodash, Formik & Yup. I thought it was too much as it is above the recommended 244kb by Webpack.
Looking in detail at MUI (picture below), I see only what I am using + the "stat" size of @mui (using webpack-bundle-analyzer) is 500kb just above the minified version of @mui/material. I am still surprised by the 300kb though.
I have a global.d.ts file:
export {}
declare global {
function onOpen(): void
}
My index.ts file:
import {
onOpen,
} from './functions'
export {
onOpen,
}
global.onOpen = onOpen
My tsconfig.json:
{
"compilerOptions": {
"types": ["gas-types-detailed", "jest", "node"],
"jsx": "react",
"esModuleInterop": true,
"moduleResolution": "node",
"lib": ["esnext", "DOM"],
"strict": true,
"importHelpers": true,
"target": "ES2019"
},
"include": ["**/*.ts"],
"exclude": ["node_modules"]
}
I am also using ecma: 6 in my webpack.config.js.
Here is the analyzed size with webpack bundle analyzer:

@mui stat size: 500kb. Index.js parsed size: 224kb, node modules parsed size: 89kb.
Note: I must admit being confused about "target": true in the original question. It won't compile for me if I put it in the compilerOptions, and it doesn't change much if I put it outside.
I'm sorry if this is not the solution. If this is, I can do a proper PR.
Here is the Pull Request.
Tree-shaking is working properly.
As we cannot include MUI with a CDN, the bundle size automatically gets bigger. I included Formik too as I find it useful (for Formik, we should be able to include the library via CDN, but I haven't found a way to make it work, even by update webpack config properly, as for react-bootstrap for instance.)