How include node_modules in output directory with TypeScript

21,919

Solution 1

I like Peopleware's solution a lot more than the accepted solution, but you don't even need gulp for that. You can simply do this in your package.json:

{
  "scripts": {
    "build": "tsc <your command line options>",
    "postbuild": "cp package.json dist/package.json && cd dist && npm install --only=production"
  }
}

The benefit of doing it this way is that you're not copying the entirety of your node_modules folder, because it might have a ton of dependencies only used during development.

You can do the same with static assets such as images or what not with:

"copy-statics": "cp -r static dist/static"

Update: instead of using npm install --only-production it is safer to copy both package.json and package-lock.json and then run npm ci --production. This ensures that you only install the dependency snapshot that you have in your package-lock.json. So the command would look like this:

"postbuild": "cp package.json dist/package.json && cp package-lock.json dist/package-lock.json && cd dist && npm ci --production"

Solution 2

This solution is an improvement of the naive "copy the whole folder" solution. It copy-paste only needed dependencies and/or devDependencies from the source node_modules to the target folder. Only works on POSIX environments. If cross-platform is mandatory then I advice you to stick to the cp strategy or a node-js script.

{
    "scripts": {
        "build": "tsc <your command line options>",
        "postbuild": "out='lib/node_modules' && root=$(npm root) && mkdir -p $out && npm ls --production --parseable | tail --lines=+2 | sed \"s%$root%%\" | cut --delimiter='/' --fields=2 | sort | uniq | xargs -I % cp -r \"$root/%\" $out"
    }
}

You can list only dependencies or devDependencies respectively with --production and --development flags of the npm ls command

How it works

out='lib/node_modules' \               # output dir relative to package.json
&& root=$(npm root) \                  # get path to node_modules dir
&& mkdir -p $out \
&& npm ls --production --parseable \   # (1)
| tail --lines=+2 \                    # (2)
| sed "s%$root%%" \                    # (3)
| cut --delimiter='/' --fields=2 \     # (4)
| sort | uniq \                        # (5) optional
| xargs -I % cp -r "$root/%" $out      # (6)

(1) List packages. We use npm ls which lists installed packages in tree form. Note that the default format output contains superfluous information and packages can be listed multiple times. Thus we use the --parseable flag.

$ npm ls --parseable                                           
/home/code
/home/code/node_modules/mongoose
/home/code/node_modules/is-typed-array
/home/code/node_modules/is-typed-array/node_modules/es-abstract
...

(2) remove the first output line because we won't use it. Here package is-typed-array has its own copy of package es-abstract.

/home/code/node_modules/mongoose
/home/code/node_modules/is-typed-array
/home/code/node_modules/is-typed-array/node_modules/es-abstract
...

(3) remove node_modules path from each package path.

/mongoose
/is-typed-array
/is-typed-array/node_modules/es-abstract
...

(4) keep the first path member

/mongoose
/is-typed-array
/is-typed-array
...

(5) remove the duplicate lines. This step is optional but recommended as we won't copy the same package multiple times. However it can slow down the whole process.

mongoose
is-typed-array
...

(6) final step: copy. You can log the executed actions with xargs -t

cp -r /home/code/node_modules/is-typed-array lib/node_modules
cp -r /home/code/node_modules/mongoosee lib/node_modules
...

Solution 3

For this purpose I created a simple gulp task.

gulpfile.js:

var gulp = require('gulp');
var install = require('gulp-install');

const PROD_DEST = '../dist';

gulp.task('default', function () {
    return gulp.src(['./package.json'])
        .pipe(gulp.dest(PROD_DEST))
        .pipe(install({
            args: ['only=production']
        }));
});


package.json:

...
"scripts": {
  "build:prod": "tsc && gulp"
}
"devDependencies": {
  "gulp": "^3.9.1",
  "gulp-install": "^0.6.0",
},
...

This way I can run npm run build:prod to transpile the TypeScript sources into PROD_DEST.
gulp copies the package.json into that folder and runs npm install --only=production in it which installs only the runtime dependencies.
This approach is cleaner if you have a lot of devDependencies and platform independent.

Share:
21,919
Vladimir Venegas
Author by

Vladimir Venegas

Updated on July 09, 2022

Comments

  • Vladimir Venegas
    Vladimir Venegas almost 2 years

    I want to know if it possible copy node_modules folder into output directory after run tsc command.

    My situation it that I have a project with TypeScript and use some npm packages. And i need that my output directory has all npm dependencies, because i need to compress it and send by http (to AWS Lambda).

    My project structure is like this:

    |-.vscode --> visual studio code
    |-lib --> output dir
    |-node_modules --> npm dependencies
    |-src --> .ts files
    |-jsconfig.json
    |-tsconfig.json
    

    How can achieve it?

    Thanks a lot!