npm publish - removing scripts from package.json?
Short answer.
"Would this be possible to kind of remove scripts before publishing the package ?"
npm does not include a built-in feature to remove scripts from package.json
.
Long answer with solution.
"Is this feasible? Or should I be thinking about another way ?"
There are a couple of built-in features known as Pre and Post hooks which can be utilized to meet your requirement, albeit in a rather custom way. The pertinent hooks are prepublish
and postpublish
and are described in the documentation as follows;
prepublish: Run BEFORE the package is packed and published, as well as on local
npm install
without any arguments...postpublish: Run AFTER the package is published.
A synopsis of the solution is:
-
Utilize a
prepublish
script in your projectspackage.json
to invoke a custom nodejs script. This nodejs script performs the following:- Reads the original
package.json
data and caches it. - Removes specific scripts/keys from the
scripts
section ofpackage.json
. - Writes the revised data back to the original
package.json
.
- Reads the original
-
Utilize a
postpublish
script in your projectspackage.json
to invoke another custom nodejs script. This secondary nodejs script performs the following:- Reverts the content of
package.json
back to it's original state.
- Reverts the content of
Code examples/gists.
-
The following nodejs script will carry out the tasks mentioned in point one above. Let's name it
cleanse-pkg.js
.cleanse-pkg.js
const fs = require('fs'); const path = require('path'); // Define absolute paths for original pkg and temporary pkg. const ORIG_PKG_PATH = path.resolve(__dirname, '../package.json'); const CACHED_PKG_PATH = path.resolve(__dirname, '../../cached-package.json'); // Obtain original `package.json` contents. const pkgData = require(ORIG_PKG_PATH); if (process.argv.length <= 2) { throw new Error('Missing npm scripts key/name argument(s)'); } // Get list of arguments passed to script. const scriptsToRemove = process.argv[2].split(','); const devDepsToRemove = process.argv[3] ? process.argv[3].split(',') : []; // Write/cache the original `package.json` data to `cached-package.json` file. fs.writeFile(CACHED_PKG_PATH, JSON.stringify(pkgData), function (err) { if (err) throw err; }); // Remove the specified named scripts from the scripts section. scriptsToRemove.forEach(function (scriptName) { delete pkgData.scripts[scriptName]; }); // Remove the specified named pkgs from the devDependencies section. devDepsToRemove.forEach(function (pkgName) { delete pkgData.devDependencies[pkgName]; }); // Overwrite original `package.json` with new data (i.e. minus the specific data). fs.writeFile(ORIG_PKG_PATH, JSON.stringify(pkgData, null, 2), function (err) { if (err) throw err; });
-
The following secondary nodejs script will carry out the task mentioned in point two above. Let's name this one
restore-pkg.js
.restore-pkg.js
const fs = require('fs'); const path = require('path'); // Define absolute paths for original pkg and temporary pkg. const ORIG_PKG_PATH = path.resolve(__dirname, '../package.json'); const CACHED_PKG_PATH = path.resolve(__dirname, '../../cached-package.json'); // Obtain original/cached contents from `cached-package.json`. const pkgData = JSON.stringify(require(CACHED_PKG_PATH), null, 2) + '\n'; // Write data from `cached-package.json` back to original `package.json`. fs.writeFile(ORIG_PKG_PATH, pkgData, function (err) { if (err) throw err; }); // Delete the temporary `cached-package.json` file. fs.unlink(CACHED_PKG_PATH, function (err) { if (err) throw err; });
Implementation and Usage.
-
The
prepublish
andpostpublish
scripts are defined in the projectspackage.json
as follows:Contrived original package.json
{ ... "scripts": { "keep": ... , "a": ... , "b": ... , "prepublish": "node .scripts/cleanse-pkg \"a,b,prepublish,postpublish\"", "postpublish": "node .scripts/restore-pkg" }, ... }
Note the
\"a,b,prepublish,postpublish\"
part in theprepublish
script. This defines the argument to pass tocleanse-pkg.js
(i.e. it lists the names of each script to be removed before publishing). Each named script to be removed must be; provided as a single string, be separated with commas, and must not include spaces.Both
cleanse-pkg.js
andrestore-pkg.js
reside in a hidden folder named.scripts
, which itself resides at the top level of the projects directory, (i.e. at the same level as the projectspackage.json
). Both nodejs scripts can be relocated as preferred, and the paths to them redefined as necessary in the respective npm-script .
-
Given the contrived
package.json
above, the actualpackage.json
contents in the resultant published tarball will be as follows:Resultant/published package.json
{ ... "scripts": { "keep": ... }, ... }
Packages in the devDependencies section.
Maybe there are packages listed in the devDependencies
section of your projects package.json
that you also want removed in the published package.json
.
(Note: Any packages listed in the devDependencies section are not downloaded when the user installs via the npm-registry though).
However, perhaps you'd like to remove them anyway. If that's a requirement, then cleanse-pkg.js
also accepts an optional second argument. This argument is analogous to the first argument, whereby each named package to be removed from the devDependencies
section must be; provided as a single string, be separated with commas, and must not include spaces.
-
Let's assume the original
package.json
is as follows this time:Contrived original package.json
{ ... "scripts": { "keep": ... , "a": ... , "b": ... , "prepublish": "node .scripts/cleanse-pkg \"a,b,prepublish,postpublish\" \"x,z\"", "postpublish": "node .scripts/restore-pkg" }, "devDependencies": { "x": "^1.0.2", "y": "^0.8.1", "z": "^0.8.1" }, ... }
- Note the additional second
\"x,z\"
argument added to theprepublish
script to specify which packages to omit from thedevDependecies
section.
- Note the additional second
-
This time, given the contrived
package.json
above, the actualpackage.json
contents in the resultant published tarball will be as follows:Resultant/published package.json
{ ... "scripts": { "keep": ... }, "devDependencies": { "y": "^0.8.1" }, ... }
Running
This solution assumes npm publish will be run utlizing one of the following methods:
- From within the project directory without any argument(s). E.g.
npm publish
. - Specifying a the path to your projects
package.json
. E.g.npm publish path/to/package.json
.
This will not work by providing a url or file path to a gzipped tar archive containing a single folder with a package.json
file inside.
Additional Notes
To prevent both utility nodejs scripts, (
cleanse-pkg.js
andrestore-pkg.js
), from being published they should be added to your projects .npmignore file. Given the location of both these files explained above you can add a.scripts
entry in.npmignore
.-
Both utility nodejs scripts,
cleanse-pkg.js
andrestore-pkg.js
, define:- An absolute path to the original
package.json
file. - An absolute path to where the temporary
cached-package.json
file should be written. Currently this is saved one-level-up/outside from the project directory to avoid it being published).
If you choose to store
cleanse-pkg.js
andrestore-pkg.js
to a different location than the one described above, the paths in following code snippet will need to redefined as necessary in both files.const ORIG_PKG_PATH = path.resolve(__dirname, '../package.json'); const CACHED_PKG_PATH = path.resolve(__dirname, '../../cached-package.json');
- An absolute path to the original
You might consider this package as well. https://www.npmjs.com/package/clean-publish
Specifically, they provide a tool for working only with the packakge.json
.
$ npm run clear-package-json package.json -o package/package.json --fields scripts name # or $ npm run clear-package-json package.json > package/package.json # or $ cat package.json | npm run clear-package-json # `fields` also will be getted from config file
It seems to do exactly what you're requesting, but also a lot more. It does seem highly configurable.
Update
Here's a package I've created which does what you're requesting, but in a significantly different manner.
https://www.npmjs.com/package/clean-package
$ clean-package --remove scripts