Create a zip file for nodejs distribution without dev dependencies (bundling)

Viewed 2038

I wrote a set of functions and I want to create a zip file containing all stuff necessary for production. So I want to exclude all stuff used for dev (Nothing from devDependencies).

Why do I need to create the ZIP? I know that It's possible to install dependencies in the server, but serverless platforms are expecting a zip with all dependencies included to avoid long time start-ups (cold start). Also, I want to integrate the deployment with Terraform.

The problem is that all solutions that I found googling don't exclude dev dependencies and transitive dev dependencies automatically. Basically, they take all from node_modules and the root and make a zip, including stuff that is not necessary for production, like unit test and tools used for developing.

I'm looking something like:

  • sbt-assembly or sbt-native-packager in Scala.
  • Maven Shade Plugin or Maven Assembly Plugin in Java.
  • python3 setup.py sdist bdist_wheel in Python.

Important: I don't want a solution where I need to manually specify the list of dependencies to exclude. I have this information in the package.json and the method should use this information to know what to put in the zip file.

2 Answers

usually, you don't want to pack any package dependencies (node_modules directory), since some packages are being compiled during installation (npm install).

if you still want to do it, i will advise you to consider packing it in a docker images.

you can keep files out of the package using .npmignore and leverage npm pack to create a tar.gz of your module, which you can deliver (for instance, publishing it to npm registry).

depending on the propose of your npm module, you might like to leverage npx and execute your module from github\npm\etc.

I was not able to find a standard way to do it. All posts that I found are workarounds and homemade solutions, so it looks like there is not a standard way to assembly or package customs applications like in other languages. I think that this is a big gap in Nodejs world. In my opinion, packaging or assembly or whatever should be a common configurable module in the core, shared by npm, webpack, etc...

Next, the script that I wrote to do the following steps:

  1. Execute npm pack to package only production dependencies.
  2. Search the zipped tar generated.
  3. Decompress the file to be able to manipulate the content.
  4. Generate a new zip without the package folder.
const fs = require('fs');
const rimraf = require('rimraf');
const child_process = require('child_process');
const decompress = require('decompress');
const decompressTargz = require('decompress-targz');
const glob = require('glob');
const zip = require('zip-a-folder');

const zipFileName = 'functionapp.zip';
const targetFolder = './build';

// Create temporal folder to work in.
if (fs.existsSync(targetFolder)) {
    rimraf.sync(targetFolder);
}
fs.mkdirSync(targetFolder);

// Execute npm pack to package only production dependencies.
child_process.exec(
    'npm pack ../',
    {cwd: targetFolder},
    (error, stdout, stderr) => {
        if(error != null) {
            console.error(`Error executing npm pack: ${stderr}`);
        } else {
            console.log(stdout);

            // Search the tar generated
            glob(`${targetFolder}/*.tgz`, {}, (err, files) => {
                if(err != null) {
                    console.error(`Error finding packaged file: ${err}`);
                } else {

                    // Decompress the file to remove the package root folder and generate zip.
                    decompress(files[0], targetFolder, {
                        plugins: [
                            decompressTargz()
                        ]
                    }).then(() => {
                        console.log('Files decompressed');

                        // Zip content to create the deployable file.
                        zip.zipFolder(
                            `${targetFolder}/package`,
                            `${targetFolder}/${zipFileName}`,
                            error=> console.error(error)
                        );
                    });
                }
            });
        }
    }
);

Related