I’m trying to reduce my package size and have noticed a 30 meg increase when I use the googleapis NPM module, which contains support for a lot of APIs that I don’t need. The following (via webpack-bundle-analyzer
) shows a directory per Google API under node_modules/googleapis/build/src/apis/
for each of their APIs, when all I use is the YouTube API:
Is there any way I can exclude some of these deeply nested subdirectories under a specific node module to reduce my bundle size?
I’m using serverles-webpack and tried the following, but I don’t think these options are applicable if I use that plugin:
package:
individually: true
excludeDevDependencies: true
browser: false
exclude:
- node_modules/googleapis/build/src/apis/**
include:
- node_modules/googleapis/build/src/apis/youtube/**
The 30 meg increase is a local increase only (presumably in uncompressed filesize). It’s what I see in the serverless-webpack
output when I run serverless offline
:
Asset Size Chunks Chunk Names
server/api/channels.js 39.9 MiB server/api/channels server/api/channels
server/api/status.js 7.95 MiB server/api/status [emitted] server/api/status
server/api/videos.js 37.5 MiB server/api/videos server/api/videos
server/update.js 39.9 MiB server/update server/update
When I deploy I’m telling webpack to minimize for production, so the size
Asset Size Chunks Chunk Names
server/api/videos.js 4.9 MiB 0 [emitted] server/api/videos
server/api/channels.js 5.17 MiB 0 [emitted] server/api/channels
server/api/status.js 1000 KiB 0 [emitted] server/api/status
server/update.js 5.16 MiB 0 [emitted] server/update
server/update.js 5.16 MiB 0 [emitted] server/update
Still, the individual package size goes up 5x for any of the entrypoints that load googleapis, so I’d love to cut that down.
Here’s my webpack.config.js:
const slsw = require('serverless-webpack');
var ContextReplacementPlugin = require("webpack/lib/ContextReplacementPlugin");
module.exports = {
target: 'node',
entry: slsw.lib.entries,
mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
node: false,
optimization: {
minimize: slsw.lib.webpack.isLocal ? false : true,
},
devtool: 'inline-cheap-module-source-map',
stats: {
warningsFilter: [
'mongodb-client-encryption',
'saslprep'
],
modules: false,
entrypoints: false
},
plugins: [
new ContextReplacementPlugin(/.*/)
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{ targets: { node: '12' }, useBuiltIns: 'usage', corejs: 3 }
]
]
}
}
]
}
]
}
};
Hey Joost.
How are you importing/requiring the googleapis
package into your js module files?
I haven’t used this particular package before but if you’re using serverless-webpack and you only import the particular functions you need (using the import { funcName} from 'modulename'
or const {funcName} = require('modulename')
syntax), then the treeshaking should only bundle the functions that your code references.
Some prototype-oriented packages that have all functions hang off a single class (e.g. momentjs) don’t play nicely with treeshaking, but most do in my experience.
I typically configure my serverless-webpack config to set includeModules: false
, meaning no node_modules folder get packaged at all and I trust webpack to only bundle what it needs.
@paulswail It’s a multistep process, but my handler imports a function from another module to run the actual route specific logic, which uses a YouTube wrapper function in a utility module, which then does:
import { google } from 'googleapis';
…which then does
const youtube = google.youtube({
version: 'v3',
auth: process.env.YOUTUBE_API_KEY,
});
There’s no way to do something like import { youtube } from 'googleapis'
unfortunately.
After some more digging, there is a thread and ongoing work to break the module up into smaller pieces that can be imported individually, but there seem to be some logistical complexities in finishing this work.
I did try includeModules: false
(thanks for the tip), but that didn’t change the bundle size. I did verify that it was including all the APIs by looking at the generated package and found unique references that only existed in other APIs source files in my resulting bundle.
Then I looked at the module source and just went and straight up commented out all the APIs I didn’t need in this file and suddenly my bundle was a third of the original size.
Until Google update the module, this seems a hacky, but workable solution. There was also a reference to browser bundles of specific APIs, but I’m not really sure how to integrate this into my workflow.