I have a project that include a gulp task for building and packaging the sources and release in a directory called dist
. My goal is publish it as a npm package, but only my dist folder. The npm documentation says that I can use the files
tag to specify files to export. It works. But, documentation also says that:
If you name a folder in the array, then it will also include the files inside that folder
The result, is a npm package, which node_modules looks like:
https://i.stack.imgur.com/qWzv7.png
But I'd like to see all my files at the package's root (without that dist
folder). My index.js
file is inside the dist
folder, but should be at root. I've tried to set tag files
as /dist/**/*
but it didn't work.
How can I achieve it?
I have the same desire but I think there is no way of accomplishing this with only using npm tooling. Another script/tool could be used to arrange your package.
Alternative Solution
Currently I am copying my package.json
into the dist
folder and then running npm pack
inside the dist
folder. I think this essentially provides the desired arrangement of our package.
Here is some relevant reading on this npm design: Why no Directories.lib in Node.
It's also interesting to note that jspm DOES respect the directories.lib
option in package.json
and rearranges the files when resolving the npm package. This all has come about for me because I am wanting to build a common library which can be consumed by jspm or npm/webpack.
I have a similar problem to the original poster (@robsonrosa). In my case I use typecript which compiles to a dist
directory. While I could make typescript compile to the root directory I think the best solution is to generate a separate package.json
file in the dist directory.
This is similar to @scvnc suggestion of copying the package.json
but with a twist:
As part of the packaging process you should generate a package.json
for the package which is based on but distinct from the main package.json
file in the root directory
The rationale:
The root package.json file is the development file. It may contain scripts or development dependencies that are of no use to the package user, but may pose security concerns for you. Your packaging procedure may include code that strips that information from the production package.json.
You may want to deploy your package to different environments which may require different package files (e.g. you may want to have different versions or dependencies).
--- EDIT ---
I was asked for a solution in the comments. So here is some code I am using. This should be considered as an example it is not meant to be generic and is specific to my projects.
My setup:
package.json - main package.json with dev dependencies and useful scripts.
.npmignore - files to ignore; copied to 'dist' directory as part of the setup.
/src - directory where my typescript code resides.
/src/SetupPackage.ts - bit of code used to setup the package.
/dist - destination directory for the compiled javascript files.
I want to package only the dist
directory and the directory should be the root directory in the package.
The file SetupPackage.ts
in my src
directory will be compiled to SetupPackage.js
in the dist
directory by typescript:
import fs from "fs";
// DO NOT DELETE THIS FILE
// This file is used by build system to build a clean npm package with the compiled js files in the root of the package.
// It will not be included in the npm package.
function main() {
const source = fs.readFileSync(__dirname + "/../package.json").toString('utf-8');
const sourceObj = JSON.parse(source);
sourceObj.scripts = {};
sourceObj.devDependencies = {};
if (sourceObj.main.startsWith("dist/")) {
sourceObj.main = sourceObj.main.slice(5);
}
fs.writeFileSync(__dirname + "/package.json", Buffer.from(JSON.stringify(sourceObj, null, 2), "utf-8") );
fs.writeFileSync(__dirname + "/version.txt", Buffer.from(sourceObj.version, "utf-8") );
fs.copyFileSync(__dirname + "/../.npmignore", __dirname + "/.npmignore");
}
main();
This file:
Copies the root package.json but removes the scripts and dev dependencies which are not needed in the package. It also fixes the main entry point to the package.
Writes the version of the package from package.json to a file called version.txt.
Copies the .npmignore file from the root.
The .npmignore content is:
*.map
*.spec.*
SetupPackage.*
version.txt
I.e. unit tests (spec files) and typescript map files are ignored as well as the SetupPackage.js
file and the version.txt
file it creates. This leaves a clean package.
Finally the main package.json
file has the following scripts for use by the build system (assumes sh
is used as the shell).
"scripts": {
"compile": "tsc",
"clean": "rm -rf dist",
"prebuildpackage": "npm run clean && npm run compile && node dist/SetupPackage.js",
"buildpackage": "cd dist && npm pack"
},
To build the package the build system clones the repo, does npm install
and then runs npm run buildpackage
which in turn:
Deletes the dist directory ensuring a clean compile.
Compiles the typescript code to javascript.
Executes the SetupPackage.js file which prepares dist for packaging.
cds to the dist directory and builds the package there.
I use the version.txt
file as an easy way to get the version in package.json and to tag my repo. There are countless other ways to do this or you may want to auto-increment the version. Remove this from SetupPackage.ts
and .npmignore
if it is not useful to you.
SetupPackage.ts
file copies files to src
directory instead of dist
. Thanks 👍
If your project has git you can use small hack. Add next scripts to package.json
"prepublishOnly": "npm run build && cp -r ./lib/* . && rm -rf ./lib",
"postpublish": "git clean -fd",
now when you run publish
command npm involve prepublishOnly
. It builds files and saves them to lib
folder (a build script depend on your project). The next command copies files to root folder and removes lib
. After publish postpublish
script returns the project to a previous state.
git clean -fd
.
If you are (and I recommend) using semantic-release, add the pkgRoot
option in the .releaserc.json
file. (You should also install rjp):
{
"pkgRoot": "dist",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/npm",
[
"@semantic-release/exec",
{
"prepareCmd": "npx rjp package.json version nextRelease.version"
}
],
[
"@semantic-release/git",
{
"message": "Release <%= nextRelease.version %> [skip ci]",
"assets": ["package.json", "CHANGELOG.md"]
}
],
"@semantic-release/changelog"
],
}
That'll take care of the problem. Just make sure your dist
folder contains a package.json
file. You can easily do that by adding a cp
to your postbuild
script:
{
"scripts": {
"postbuild": "cp package.json dist"
}
}
npm --no-git-tag-version version <%= nextRelease.version %>
. This will change the version in both package.json and package-lock.json. And by adding the --no-git-tag-version
tag it will NOT create a git tag and NOT commit the files.
I highly recommend you to use .npmignore
instead of moving or copying things around, specially if you are using a CI for deployments, and just add there the files you don't want to publish.
https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package
Example:
#tests
test
coverage
#build tools
.travis.yml
.jenkins.yml
.codeclimate.yml
#linters
.jscsrc
.jshintrc
.eslintrc*
#editor settings
.idea
.editorconfig
Update:
If you want to split your code, into different npm packages using the same repo, I bumped into this project recently: Lerna and looks really good.
Maybe you should take a look
require('mypackage/foo')
instead of require('mypackage/dist/foo')
cd TMPDIR
npm pack /path/to/package.json
The tarball will be created inside the TMPDIR directory.
npm pack
plus the package.json files
field (or .npmignore
) works wonderfully.
You need to publish the dist folder
The natural way to achieve that, according to the npm approach, is to publish the folder which is to be the root. There are several ways to do that, depends on the final environment you want to work with:
npm publish
Prerequisite: in any case above, prior to the publish/install/link, you have to put in your dist
folder at least a proper package.json
file. In your case, you have to have the package name defined in your package.json file as "name": "robsonrosa-ui-alert"
. Typically, you'll want there also some other files like README.md or LICENSE.
Remarks to methods 2 & 3
Typically there are problems with package dependencies when you use the package installed this way. To avoid it, first pack the package with npm pack dist
and then install the package in the target project from the packed tarball, i.e. npm install path/to/package-tarball.tgz
.
Automation example
You can automate the publishing process using the prepare
script, combined with build
script. Additionally, you can protect your package against an accidental publishing of the package root folder with "private": true
field put in the package.json, located in the root directory of your package repo. Here is an example:
"private": true,
"scripts": {
"build": "rm -rf dist && gulp build && cat ./package.json | grep -v '\"private\":' > dist/package.json",
"prepare": "npm run build"
},
This way you won't publish the root folder and get the package built and package.json copied to the dist
folder automatically, within the publishing process.
npm publish dist
it tries to push a package with the name dist
instead of using the dist
folder.
package.json
to your diist
folder?
I have a Typescript project which compiles into a folder called dist
and I have found a solution for Windows.
In my package.json
at the root of my project:
"scripts": {
"prepub": "copy package.json dist",
"pub": "cd dist & npm publish",
"postpub": "cd dist & del package.json"
}
These scripts allow me to publish my dist
folder as the root of my package by running npm run pub
.
prepub copies my package.json into the dist folder.
pub changes the working directory to dist/ and then runs npm publish
postpub deletes package.json from my dist folder to ensure a clean publish every time.
This way, I can still have all the relevant configuration files in my project root but I can copy them to dist
before publishing.
The same approach can probably work for Mac or Linux.
Here is simple package.json
solution:
{
"scripts": {
"pack": "mkdir -p dist && cp package.json dist/package.json && npm pack ./dist",
"pub": "npm publish $(node -p \"p=require('./package.json');p.name+'-'+p.version+'.tgz'\")"
}
}
This works for me
npm run build
cp package.json dist
cp .npmrc dist
cd dist
npm publish
I am using these commands from Jenkins to publish packages.
I achieve this by doing the following:
I set my package.json as private. So I can not publish my package with npm publish I create a gulpfile with the following steps:
import gulp from 'gulp'; import shell from 'gulp-shell'; import replace from 'gulp-replace'; const compile = () => gulp.src('package.json').pipe(shell('npm run compile')); const copy = () => gulp.src(['readme.md']).pipe(gulp.dest('dist')); const update = () => gulp.src('package.json').pipe(replace('"private": true,', '"private": false,')).pipe(gulp.dest('dist')); const publish = () => gulp.src('package.json').pipe(shell('cd dist && npm publish')); export default gulp.series(compile, copy, update, publish);
My situation was similar but hairier at the same time since it involved publishing through a CI pipeline that was mostly out of my control, and which it and only it had publishing permissions through a .npmrc
file it downloaded during its execution. And since it had to be shared with countless other teams and repositories, it had to be agnostic to the specifics of each repository.
After a lot of time wasted trying to copy over the .npmrc
file to the npm's global root, which more often than not ended in permission warnings, trying to use different scripts in package.json
which called upon the global npm instead and getting 403 HTTP errors, the solution finally dawned on me, and it couldn't be simpler if I tried.
All it took was including a shell script (could also be a batch file if the container ran Windows) in the application, checking for its existence in the pipeline and running it instead of npm publish
.
Since nowadays the syntax npm publish [folder]
exists, my script was pretty straightforward, but I can see how in different times it could have looked like @Eli Algranti's script.
So here's how it looked in the end:
publish.sh
# DO NOT EDIT THIS FILE
# THIS OVERRIDES THE DEFAULT PIPELINE PUBLISH SCRIPT TO BE RUN
# IT EXISTS SO ONLY THE BUILD OUTPUT FOLDER GETS PUBLISHED
npm publish ./dist
npm-build.yml
npm run build --if-present
if [ -f "./publish.sh" ]; then
echo "'publish.sh' script has been found. Overriding the publishing with it"
sh publish.sh
else
npm publish
fi
This ran locally, with the current user and the .npmrc
file that had already been copied, and could encapsulate a lot of other logic if necessary
Here is one more approach which I think is the cleanest. It's all configuration based without needing to move files or specify paths in the build and pack scripts:
package.json
Specify the main file.
{
"main": "lib/index.js",
}
Some additional typescript options:
Specify the rootDir. This directory will have all the source code and it should have an index file in it (or some other file you can use as main in the package.json).
Specify the outDir. This is where your tsc command will build to
tsconfig.json
{
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
},
...
}
option 1: Navigate to the folder and execute "npm publish ." command
option 2: Run npm publish /path/directory
You can specify the directory you wish to publish. This is what I came up with for Typescript on Linux.
"scripts": {
"prepub": "rm -rf dist && tsc && cp package*.json dist",
"pub": "npm publish ./dist"
}
Thanks to @EnderShadow8 for the inspiration.
Just create a .npmignore
file and add the following to it :
*.*
!dist/*
Success story sharing
npm pack
, the package.jsonfiles
andmain
fields, and.npmignore
gives a developer everything needed to create a package from a specific, installable directory.