Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to npm publish specific folder but as package root

People also ask

How do I publish a package to npm?

To publish an organization scoped package as public, use npm publish --access public . On the command line, navigate to the package directory. Run npm publish --access public .

Does npm pack publish?

By default npm will publish to the public registry. This can be overridden by specifying a different default registry or using a scope in the name, combined with a scope-configured registry (see package.


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.


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.


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"
  }
}

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


cd TMPDIR
npm pack /path/to/package.json

The tarball will be created inside the TMPDIR directory.


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:

  1. npm publish <folder> from your package repo to an npm registry and then install your package in other project as you install other packages. In your case it would be npm publish dist.
  2. npm install <folder> in some other project if you want to use your package only locally. In your case you go to the other project and run npm install relative/path/to/dist
  3. npm link your folder locally to your node_modules in another project in case you'd like to have the changes in your original package reflected instantly in other project. In your case you first cd dist and run npm link and then go to the other project and run npm link robsonrosa-ui-alert.

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.