如何 npm 发布特定文件夹但作为包根目录

How to npm publish specific folder but as package root

我有一个项目,其中包含一个 gulp 任务,用于构建和打包源代码并在名为 dist 的目录中发布。我的目标是将其发布为 npm 包,但仅限于我的 dist 文件夹。 npm documentation 表示我可以使用 files 标签来指定要导出的文件。有用。但是,文档还说:

If you name a folder in the array, then it will also include the files inside that folder

结果是一个 npm 包,node_modules 看起来像:

但我想在包的根目录下查看我的所有文件(没有那个 dist 文件夹)。我的 index.js 文件在 dist 文件夹中,但应该在根目录下。我试图将标签 files 设置为 /dist/**/* 但它没有用。

如何实现?

我有同样的愿望,但我认为 仅使用 npm 工具是无法实现的。另一个 script/tool 可以用来安排您的包裹。

备选方案

目前我正在将 package.json 复制到 dist 文件夹中,然后将 运行 npm pack 复制到 dist 文件夹中。我认为这基本上提供了我们所需的包裹安排。

这里是一些关于这个 npm 设计的相关读物:Why no Directories.lib in Node

值得注意的是,jspm 确实尊重 package.json 中的 directories.lib 选项,并在解析 npm 包时重新排列文件。这一切都是为我而来的,因为我想构建一个可以被 jspm 或 npm/webpack.

使用的公共库

我强烈建议您使用 .npmignore 而不是四处移动或复制东西,特别是如果您使用 CI 进行部署,只需将您不想添加的文件添加到那里发布。

https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package

示例:

#tests
test
coverage

#build tools
.travis.yml
.jenkins.yml
.codeclimate.yml

#linters
.jscsrc
.jshintrc
.eslintrc*

#editor settings
.idea
.editorconfig

更新:

如果你想使用同一个 repo 将代码拆分到不同的 npm 包中,我最近遇到了这个项目:Lerna 看起来非常好。

也许你应该看看

我有一个与原始发布者 (@robsonrosa) 类似的问题。在我的例子中,我使用编译到 dist 目录的 typecript。虽然我可以将打字稿编译到根目录,但我认为最好的解决方案是在 dist 目录中生成一个单独的 package.json 文件。
这类似于@scvnc 建议复制 package.json 但有一个转折:

作为打包过程的一部分,您应该为包生成一个 package.json,它基于但不同于根目录中的主要 package.json 文件

理由:

  • package.json文件是开发文件。它可能包含对包用户无用的脚本或开发依赖项,但可能会给您带来安全问题。您的打包程序可能包含从生产中剥离该信息的代码 package.json
  • 您可能希望将包部署到可能需要不同包文件的不同环境(例如,您可能希望拥有不同的版本或依赖项)。

--- 编辑 ---

我在评论中被要求提供解决方案。所以这是我正在使用的一些代码。这应该被视为一个例子,它并不意味着通用,而是特定于我的项目。

我的设置:

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.

我只想打包dist目录,该目录应该是包中的根目录。

我的src目录下的文件SetupPackage.ts会被typescript编译成dist目录下的SetupPackage.js:

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();

这个文件:

  • 复制根 package.json 但删除包中不需要的脚本和开发依赖项。它还修复了包的主要入口点。
  • 将包的版本从 package.json 写入名为 version.txt 的文件。
  • 从根目录复制 .npmignore 文件。

.npmignore 内容为:

*.map
*.spec.*
SetupPackage.*
version.txt

即单元测试(规范文件)和打字稿映射文件以及它创建的 SetupPackage.js 文件和 version.txt 文件都将被忽略。这留下了一个干净的包。

最后 package.json 主文件包含以下供构建系统使用的脚本(假设 sh 用作 shell)。

"scripts": {
    "compile": "tsc",
    "clean": "rm -rf dist",
    "prebuildpackage": "npm run clean && npm run compile && node dist/SetupPackage.js",
    "buildpackage": "cd dist && npm pack"
  },

要构建包,构建系统会克隆 repo,执行 npm install 然后运行 ​​npm run buildpackage,后者依次为:

  • 删除 dist 目录以确保干净的编译。
  • 将 typescript 代码编译为 javascript。
  • 执行准备 dist 打包的 SetupPackage.js 文件。
  • cds 到 dist 目录并在那里构建包。

我使用 version.txt 文件作为在 package.json 中获取版本并标记我的 repo 的简单方法。有无数其他方法可以做到这一点,或者您可能想要自动增加版本。如果对您没有用,请将其从 SetupPackage.ts.npmignore 中删除。

如果您的项目有 git,您可以使用 small hack。 将下一个脚本添加到 package.json

    "prepublishOnly": "npm run build && cp -r ./lib/* . && rm -rf ./lib",
    "postpublish": "git clean -fd",

现在,当您 运行 publish 命令 npm 涉及 prepublishOnly 时。它构建文件并将它们保存到 lib 文件夹(构建脚本取决于您的项目)。下一个命令将文件复制到根文件夹并删除 lib。在发布 postpublish 脚本 returns 项目到以前的状态后。

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

tarball 将在 TMPDIR 目录中创建。

这是我认为最干净的另一种方法。全部基于配置,无需移动文件或在构建和打包脚本中指定路径:

package.json 指定主文件。

{
    "main": "lib/index.js",
}

一些额外的打字稿选项:

  • 指定 rootDir。该目录将包含所有源代码,并且其中应该包含一个 index 文件(或者您可以在 package.json 中用作主要文件的其他文件)。
  • 指定 outDir。这是您的 tsc 命令将构建到
  • 的地方

tsconfig.json

{
    "compilerOptions": {
        "rootDir": "src",
        "outDir": "lib",
    },
    ...

}

只需创建一个 .npmignore 文件并向其中添加以下内容:

*.*
!dist/*

选项 1:导航到文件夹并执行 "npm publish ." 命令

选项 2:运行 npm publish /path/directory

您需要发布 dist 文件夹

根据 npm 方法,实现此目的的自然方法是发布将成为根目录的文件夹。有几种方法可以做到这一点,具体取决于您要使用的最终环境:

  1. npm publish <folder> 从您的包仓库到 npm 注册表,然后在安装其他包时将您的包安装到其他项目中。在您的情况下,它将是 npm publish dist.
  2. npm install <folder> 如果您只想在本地使用您的包,请在其他项目中使用。在你的情况下你去另一个项目和 运行 npm install relative/path/to/dist
  3. npm link 您本地的文件夹到另一个项目中的 node_modules,以防您希望原始包中的更改立即反映在其他项目中。在你的情况下,你首先 cd dist 和 运行 npm link 然后去另一个项目和 运行 npm link robsonrosa-ui-alert.

先决条件:在上述任何情况下,在 publish/install/link 之前,您必须至少在 dist 文件夹中放置一个合适的 package.json 文件。在您的情况下,您必须在 package.json 文件中将包名称定义为 "name": "robsonrosa-ui-alert"。通常,您还需要一些其他文件,例如 README.md 或 LICENSE。

对方法 2 和 3 的备注

当您使用以这种方式安装的包时,包依赖性通常会出现问题。为避免这种情况,首先使用 npm pack dist 打包软件包,然后从打包的 tarball 中将软件包安装到目标项目中,即 npm install path/to/package-tarball.tgz.

自动化示例

您可以使用位于 package.json 中的 prepare script, combined with build script. Additionally, you can protect your package against an accidental publishing of the package root folder with "private": true 字段自动发布过程,该字段位于包存储库的根目录中。这是一个例子:

  "private": true,
  "scripts": {
    "build": "rm -rf dist && gulp build && cat ./package.json | grep -v '\"private\":' > dist/package.json",
    "prepare": "npm run build"
  },

这样您就不会发布根文件夹并在发布过程中自动生成包并package.json复制到dist文件夹。

如果您(我推荐)使用 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"
  ],
}

这样就可以解决问题了。只需确保您的 dist 文件夹包含 package.json 文件。您可以通过在 postbuild 脚本中添加 cp 来轻松做到这一点:

{
  "scripts": {
    "postbuild": "cp package.json dist"
  }
}

我通过执行以下操作实现了这一点:

  1. 我将 package.json 设置为 private。所以我不能用 npm publish
  2. 发布我的包
  3. 我通过以下步骤创建了一个 gulpfile

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);

这对我有用

npm run build
cp package.json dist
cp .npmrc dist
cd dist
npm publish 

我正在使用 Jenkins 中的这些命令来发布包。

这是简单的package.json解决方案:

{
    "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'\")"
    }
}

我有一个 Typescript 项目,它编译到一个名为 dist 的文件夹中,我找到了 Windows.

的解决方案

在我的 package.json 项目根目录中:

  "scripts": {
    "prepub": "copy package.json dist",
    "pub": "cd dist & npm publish",
    "postpub": "cd dist & del package.json"
  }

这些脚本允许我通过 运行 npm run pub.

将我的 dist 文件夹发布为我的包的根目录
  • prepub 将我的 package.json 复制到 dist 文件夹中。
  • pub 将工作目录更改为 dist/,然后运行 ​​npm publish
  • postpub 从我的 dist 文件夹中删除 package.json 以确保每次发布都干净利落。

这样,我仍然可以在我的项目根目录中拥有所有相关的配置文件,但我可以在发布之前将它们复制到 dist

同样的方法可能适用于 Mac 或 Linux。

您可以指定要发布的目录。这是我在 Linux.

上为 Typescript 想出的
  "scripts": {
    "prepub": "rm -rf dist && tsc && cp package*.json dist",
    "pub": "npm publish ./dist"
  }

感谢@EnderShadow8 的启发。

我的情况类似,但同时更毛茸茸,因为它涉及通过 CI 管道进行发布,这在很大程度上是我无法控制的,并且只有它通过 [=12= 具有发布权限] 它在执行期间下载的文件。由于它必须与无数其他团队和存储库共享,因此它必须与每个存储库的具体细节无关。

在尝试将 .npmrc 文件复制到 npm 的全局根目录(通常以权限警告告终)浪费了大量时间后,尝试在 package.json 中使用不同的脚本改为调用全局 npm 并收到 403 HTTP 错误,解决方案终于让我明白了,如果我尝试的话,它再简单不过了。

它所要做的只是在应用程序中包含一个 shell 脚本(如果容器 运行 Windows,也可以是一个批处理文件),检查它是否存在于管道中,并且运行 它而不是 npm publish.

自从现在语法 npm publish [folder] 存在以来,我的脚本非常简单,但我可以看到在不同的时间它看起来像 @Eli Algranti's 脚本。

最后是这样的:

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

这个 运行 在本地,包含当前用户和已经复制的 .npmrc 文件,如果需要可以封装很多其他逻辑