Copy in folder and package with publish

Is there a way to copy the files includes in package.json (in the File field) to a folder to package them together with npm publish?

My repo:

.
├── package.json
└── folder0
    ├── file0
    └── folder1
       └── file1

My package.json:

{
  "name": "example",
  "version": "0.2.0",
  "files": [
    "folder0/file0",
    "folder0/folder1/file1"
  ],
  "author": "cruiz"
}

Desired output:

I want in my package:

.
├── package.json
└── build
    ├── file0
    └── file1

EDIT:

Thank you very much for the reply. I'm trying to use npm to package VHDL code, so I'm really noob :(

Is it possible to put the files to be packaged in a field of package.json and read them from the script?

{ 
  "name": "example",
  "version": "0.2.0",
  "customField": [
    "folder0/file0",
    "folder0/folder1/file1"
  ],
  "scripts": {
    "prepublishOnly": "mkdirp build && copyfiles -f [..read CustomField...] build"
  },
  "author": "cruiz"
}

Upvotes: 0

Views: 4339

Answers (2)

RobC
RobC

Reputation: 25002

Revised Answer:

Edit based on the OP's edit.

Is it possible to put the files to be packaged in a field of package.json and read them from the script.

Yes that's possible, however you will need to utilize a custom node script to do that. There's no existing packages available to achieve this that I am aware of.

Solution:

  1. Firstly, cd to your project directory and uninstall the packages I mentioned in by original answer as we wont be using them. You can do this by running the following command:

    npm un -D mkdirp copyfile
    
  2. Install shelljs using the following command:

    npm i -D shelljs
    

    Note: shelljs will now be utlized instead of mkdirp and copyfile to create the directory then copy the files listed in package.json.

  3. Next create a node script with the following contents. Lets name the file copy-files.js and save it to a hidden directory named .scripts at the top level of your project directory. I.e.

    Directory structure

    project
    ├── .scripts
    │   └── copy-files.js
    ├── node_modules
    │   └── ...
    ├── package.json
    ├── folder0
    │   ├── file0.js
    │   └── folder1
    │      └── file1.js
    └── ...
    

    Contents of copy-files.js

    const shell = require('shelljs');
    const pkg = require('../package.json');
    
    // The path to the directory to copy the file(s) to.
    const DEST_DIR = './build';
    
    // NOTE: `customField` below should be replaced with the
    // actual key name in the final `package.json`.
    const srcFilePaths = pkg.customField;
    
    // Create a new destination directory and intermediate
    // directories if necessary.
    if (!shell.test('-d', DEST_DIR)) {
      shell.mkdir('-p', DEST_DIR);
    }
    
    // Recursively copy each file listed in the `customField` Array.
    // Logs and error if the file listed cannot be found.
    srcFilePaths.forEach(function (srcPath) {
      if (!shell.test('-e', srcPath)) {
        shell.echo('Error: Cannot find file listed in package.json: %s', srcPath);
        process.exit(1);
      }
      shell.cp(srcPath, DEST_DIR);
    });
    

    Note: The customField part in const srcFilePaths = pkg.customField;, (on line 9 of copy-files.js above), will need to be replaced with the actual real key name you decide to use in the final package.json.

  4. Finally, update your prepublishOnly script to invoke the custom node script (i.e. copy-file.js) as shown in the following excerpt of package.json:

    {
      ...
      "customField": [
        "folder0/file0.js",
        "folder0/folder1/file1.js"
      ],
      "scripts": {
        "prepublishOnly": "node .scripts/copy-files",
      },
      ...
    }
    

Original Answer

This can be achieved by utilizing a custom npm-script in your package.json file. Using a pre script will also suit your requriement as you mentioned:

package them together with npm publish


1. Using pre and post hooks with npm scripts:

All npm scripts support what are referred to as pre and post hooks. Any pre script will run before its respective script (or command), and any post script will run after its respective script (or command).

To better understand this feature let's say our package.json contains the contrived scripts shown in the following excerpt:

...
"scripts": {
  "prefoo": "...",
  "foo": "...",
  "postfoo": "..."
},
...

As you can see:

  1. There is a script named foo which has a corresponding script with the pre prefix, i.e. prefoo.
  2. Similarly, the same foo script also has another corresponding script, however this one is prefixed with post, i.e. postfoo.

Specifying scripts in this way will result in the following whenever you invoke the foo script via your CLI by running npm run foo:

  1. The prefoo script will automatically run before the foo script is executed.
  2. The postfoo will automatically run after foo completes.

Further information regarding pre and post hooks can be found here


2. So which pre script should I use?

There are a couple of built-in pre scripts that are likely to suit your requirement; those being prepublish or prepublishOnly. The npm docs provides the following description for these hooks:

  • prepublish: Run BEFORE the package is packed and published, as well as on local npm install without any arguments. (See below)

  • prepublishOnly: Run BEFORE the package is prepared and packed, ONLY on npm publish...

Note: The following solution uses the prepublishOnly as I'm quite sure you don't want this script to run when a user runs npm install. However, you'll need to decide whether to use this or swap it for prepublish. Refer to the docs for the finer details of each pre hook.


3. Solution

  1. For a solution that works cross platforms you'll need to firstly cd to your project directory and install a couple of additional npm packages.

    • Install mkdirp by running: npm i -D mkdirp

    • Install copyfile by running: npm i -D copyfiles

  2. In the scripts section of your package.json file add the following:

    ...
    "scripts": {
      "prepublishOnly": "mkdirp build && copyfiles -f folder0/file0.js folder0/folder1/file1.js build"
    },
    ...
    

Now when you run; npm run publish, the prepublishOnly script will automatically run, (i.e. copy the files to meet your desired directory structure), before your package is prepared and packed for publishing.

You can of course run; npm run prepublishOnly, if you'd like to test the results of the script before running: npm run publish.

Upvotes: 2

ralphtheninja
ralphtheninja

Reputation: 133018

What you basically want is a build step. You could do all those things in a prepublish script, e.g.:

package.json

{
  "scripts": {
    "prepublish": "mkdir -p build && cp folder0/file0 folder0/folder1/file1 build"
  }
}

But you should change your files as well, unless you really want both structures, in build and folder0/*.

Upvotes: 0

Related Questions