I have an open source project that is written in TypeScript, and is compiled to JavaScript and then published as a CLI tool via npm publish
.
I've been unable to exclude the TypeScript source code from the npm package, and there are other *.ts
files that seem to get included at random (but we'll focus on one file here).
I do not have a problem including the TypeScript source files, but that's just adding extra bytes to the package for something the user will never need.
I can reproduce the problem with a very small setup
package.json
{
"name": "example",
"version": "1.0.0",
"main": "readme.js"
}
I've tried a very aggressive ignore list:
.npmignore
**/*
*.ts
**
*
*.*
readme.ts
Create some source code files
touch readme.ts
touch readme.js
Now if I try to generate a npm package it will include the readme.ts
file.
$ npm pack
npm notice
npm notice package: example@1.0.0
npm notice === Tarball Contents ===
npm notice 0 readme.js
npm notice 76B package.json
npm notice 0 readme.ts
npm notice === Tarball Details ===
npm notice name: example
npm notice version: 1.0.0
npm notice filename: example-1.0.0.tgz
npm notice package size: 195 B
npm notice unpacked size: 76 B
npm notice shasum: a1ee7cc6b7b0d1e2eccf870e95d7df38c5dcb609
npm notice integrity: sha512-ppNEfKEvT9DEP[...]+d16IWbrj7OdA==
npm notice total files: 3
npm notice
example-1.0.0.tgz
So why is it including readme.ts
?
I have all files excluded, and only the readme.js
is referenced in the package.json
file.
$ npm --version
6.13.1
Short Answer: Files named readme.*
(e.g. readme.foo
, readme.quux
, etc) will be included by npm regardless of how you try to negate it in your .npmignore file.
In one of npm's tests for npm-cli titled "certain files included unconditionally" starting at line #511
they include a test for a file named readme.randomext
.
From observing this test we can conclude that any file named readme.*
is going to be included in the tarball (.tgz
) file, even though it may be specified in the .npmignore
file.
Below is an excerpt from the aforementioned NPM test file named pack-files-and-ignores.js
test('certain files included unconditionally', function (t) { var fixture = new Tacks( Dir({ 'package.json': File({ name: 'npm-test-files', version: '1.2.5' }), '.npmignore': File( 'package.json', 'README', 'Readme', 'readme.md', 'readme.randomext', // <---- 'changelog', 'CHAngelog', 'ChangeLOG.txt', 'history', 'HistorY', 'HistorY.md', 'license', 'licence', 'LICENSE', 'LICENCE' ), 'README': File(''), 'Readme': File(''), 'readme.md': File(''), 'readme.randomext': File(''), // <---- 'changelog': File(''), 'CHAngelog': File(''), 'ChangeLOG.txt': File(''), 'history': File(''), 'HistorY': File(''), 'HistorY.md': File(''), 'license': File(''), 'licence': File(''), 'LICENSE': File(''), 'LICENCE': File('') }) ) withFixture(t, fixture, function (done) { t.ok(fileExists('package.json'), 'package.json included') t.ok(fileExists('README'), 'README included') t.ok(fileExists('Readme'), 'Readme included') t.ok(fileExists('readme.md'), 'readme.md included') t.ok(fileExists('readme.randomext'), 'readme.randomext included') // <---- t.ok(fileExists('changelog'), 'changelog included') t.ok(fileExists('CHAngelog'), 'CHAngelog included') t.ok(fileExists('ChangeLOG.txt'), 'ChangeLOG.txt included') t.ok(fileExists('license'), 'license included') t.ok(fileExists('licence'), 'licence included') t.ok(fileExists('LICENSE'), 'LICENSE included') t.ok(fileExists('LICENCE'), 'LICENCE included') done() }) })
There are a couple if workarounds you could consider, such as:
The simplest is to rename the file to e.g. read-me.ts
and then specify read-me.ts
in your .npmignore
file. However, as you have *.ts
currently specified in your .npmignore
it's not actually necessary for you to additionally specify read-me.ts
too.
Or, if that's not possible consider:
Add a postpack
script to the scripts section of package.json. For instance:
"scripts": {
"postpack": "node ./fix-it.js",
...
},
Then in fix-it.js
utilize the node-tar package to:
.tgz
).readme.ts
..tgz
).Steps to publishing your package then become:
Run npm pack
The postpack
script will subsequently auto generate the new .tgz
file without the unwanted file(s).
npm publish ./path/to/mypackage.tgz