| |

npm 16 ๐ŸŒ .npmignore vs .gitignore and files Field

โฑ๏ธ Estimated read: 13 min | |

Publishing a package to the npm registry involves a decision that is easy to get wrong: which files from the project directory end up in the published tarball. Three mechanisms compete for this decisionโ€”.gitignore, .npmignore, and the files field in package.jsonโ€”and their precedence rules are not intuitive. A .gitignore entry that excludes a secrets file from version control does not necessarily exclude it from the published package. An .npmignore that overrides .gitignore can silently reintroduce a file that the developer thought was excluded. The files field inverts the logic entirely, treating the listed entries as the only files that should be published . The difference between these mechanisms is the difference between an allowlist and a blocklist, and that difference determines which failure mode a package is exposed to.

Key point: The files field is an allowlist and fails closed. .npmignore is a blocklist and fails open. When both are present, the files field takes precedence. When .npmignore is present, .gitignore is ignored for publishing. The only reliable way to confirm what will be published is npm pack --dry-run.


Why the three mechanisms exist

The version control problem. .gitignore tells Git which files should not be tracked. It exists because build artifacts, node_modules, environment files, and editor configuration should not be committed to the repository. The file is designed for the repository’s needs, not the registry’s needs. A file that is correctly ignored by Git can still be a legitimate part of the published packageโ€”a compiled dist directory, for example, is often gitignored during development but must be published for consumers to use the package.

The registry problem. .npmignore tells npm which files should not be included in the published tarball. It was designed as the registry-side counterpart to .gitignore, with the same syntax and the same blocklist semantics. The problem is that the two files can drift: a developer adds an entry to .gitignore but forgets .npmignore, and the file that was excluded from the repository is still published to the registry . This drift is a security issue when the file contains credentials or internal configuration.

The allowlist problem. The files field in package.json reverses the logic. Instead of listing what to exclude, it lists what to include. Everything not listed is excluded by default. This means that a new file added to the project is not published unless it is explicitly added to the files array. The failure mode is a missing file in the published package, which is immediately visible to consumers, rather than a leaked file, which may not be noticed until it is exploited . GitHub’s own community discussion on the topic recommends files for this reason: “files in package.json is an allowlist, which is safer because new files are excluded by default” .


a. The Precedence Rules

The interaction between the three mechanisms follows a specific order. Understanding this order is the difference between a correct package and a leaked secret.

If files is present, it defines the allowlist. npm includes only the files and directories listed in files, plus a set of files that are always included regardless of the field. Those always-included files are package.json, the README, the LICENSE, and the file referenced by the main field . The files field takes precedence over both .npmignore and .gitignore. If a file is listed in files, it is published even if .npmignore excludes it. If a file is not listed in files, it is not published even if .npmignore would allow it.

If files is absent and .npmignore is present, .npmignore defines the blocklist. npm publishes everything in the project directory except what .npmignore lists, plus the always-included files. In this case, .gitignore is ignored entirely for the purpose of publishing . The presence of .npmignore is a signal to npm that the developer has taken explicit control over the registry contents, and the Git ignore file is not consulted.

If neither files nor .npmignore is present, .gitignore is used as the blocklist. npm falls back to the Git ignore file because it is the only signal available about what the developer considers disposable . This fallback is convenient for small packages that do not need separate ignore files, but it creates the drift problem: the .gitignore was written for Git, not for npm, and its exclusions may not match the registry’s needs.

The OWASP cheat sheet warns explicitly about the drift problem: “if a project is utilising both .npmignore and .gitignore files, everything which isn’t in .npmignore is published to the registry (i.e. the .npmignore file overrides the .gitignore). This condition is a common source of confusion and is a problem that can lead to leaking secrets” . A developer who adds a secrets file to .gitignore and assumes it will not be published is wrong if .npmignore exists and does not list the same file.


b. What Is Always Included and Always Excluded

Regardless of which mechanism is used, npm includes and excludes certain files automatically. The always-included files are package.json, the README (with any of the common extensions: .md, .txt, .rst), the LICENSE (or LICENCE), and the file referenced by the main field . These files are essential for the package to be usable: package.json defines the package, the README describes it, the LICENSE states the terms, and the main file is the entry point. Excluding them would produce a package that is broken or unusable.

The always-excluded files include .git, .npmrc, node_modules, and various lock files and configuration files that are specific to the development environment . The .git directory is excluded because it contains the repository history and would bloat the package. .npmrc is excluded because it can contain registry credentials or tokens. node_modules is excluded because dependencies are installed by the consumer, not bundled with the package.

The files field does not override the always-included files. A package with "files": ["dist"] still includes package.json, the README, the LICENSE, and the main file. The files field adds to the allowlist; it does not replace the built-in list.


c. Verifying the Tarball Contents

The only way to know what will be published is to ask npm to show the tarball contents without actually publishing. The npm pack --dry-run command performs the packaging step, applies all the inclusion and exclusion rules, and prints the resulting file list and size without writing the tarball or contacting the registry . The npm publish --dry-run command does the same and additionally shows what would be uploaded.

# Show exactly which files would be included in the tarball
npm pack --dry-run

# The output lists every file and the total package size

This verification step is not optional for packages that contain anything that should not be public. The precedence rules are complex enough that reasoning about them is error-prone. The dry run applies the rules mechanically and shows the result. A file that appears in the dry-run output will be published; a file that does not appear will not.

The files field can be configured interactively with a tool like create-include, which prompts for the files and directories to include and writes the resulting array to package.json . This is a convenience, not a substitute for verification. The dry run remains the authoritative check.


Complete Example Session

// ============================================
// PART 1: PROJECT DIRECTORY
// ============================================
// my-package/
// โ”œโ”€โ”€ src/
// โ”‚   โ””โ”€โ”€ index.ts
// โ”œโ”€โ”€ dist/
// โ”‚   โ””โ”€โ”€ index.js
// โ”œโ”€โ”€ tests/
// โ”‚   โ””โ”€โ”€ index.test.ts
// โ”œโ”€โ”€ .env
// โ”œโ”€โ”€ .gitignore
// โ”œโ”€โ”€ README.md
// โ”œโ”€โ”€ LICENSE
// โ””โ”€โ”€ package.json


// ============================================
// PART 2: .GITIGNORE (FOR VERSION CONTROL)
// ============================================
// .gitignore
// node_modules/
// dist/
// .env
// *.log
// The dist/ directory is gitignored because it is a build artifact.
// The .env file is gitignored because it contains secrets.


// ============================================
// PART 3: THE PROBLEM WITH FALLING BACK TO .GITIGNORE
// ============================================
// If there is no .npmignore and no files field,
// npm uses .gitignore.
// This means dist/ is excluded from the published package.
// The package will be published without its compiled code.
// Consumers cannot use it.


// ============================================
// PART 4: .NPMIGNORE AS A BLOCKLIST
// ============================================
// .npmignore
// src/
// tests/
// .env
// *.log
// This publishes dist/, README, LICENSE, package.json.
// It excludes src/ (TypeScript source), tests/, .env.
// The .env is excluded because it is listed in .npmignore.


// ============================================
// PART 5: THE DRIFT PROBLEM
// ============================================
// Later, a developer adds a new secret file: .env.production
// They add it to .gitignore.
// They forget to add it to .npmignore.
// The file is excluded from Git but included in the package.
// This is the security failure that OWASP warns about.


// ============================================
// PART 6: THE FILES FIELD AS AN ALLOWLIST
// ============================================
// package.json
{
  "name": "my-package",
  "version": "1.0.0",
  "main": "dist/index.js",
  "files": [
    "dist"
  ]
}
// Only dist/ is published, plus package.json, README, LICENSE,
// and the main file (dist/index.js).
// src/, tests/, .env, and .env.production are all excluded
// because they are not listed in files.
// A new file added to the project is not published unless
// it is explicitly added to files.


// ============================================
// PART 7: THE FILES FIELD TAKES PRECEDENCE
// ============================================
// Even if .npmignore exists and lists dist/,
// the files field includes dist/.
// The files field is the allowlist; it wins.


// ============================================
// PART 8: VERIFY WITH NPM PACK --DRY-RUN
// ============================================
npm pack --dry-run
// Output lists:
// package.json
// README.md
// LICENSE
// dist/index.js
// The .env and .env.production files are not listed.
// The tests/ directory is not listed.


// ============================================
// PART 9: WHAT IS ALWAYS INCLUDED
// ============================================
// Even with "files": ["dist"],
// npm always includes:
// - package.json
// - README.md
// - LICENSE
// - dist/index.js (the main file)
// These cannot be excluded by files or ignore files.


// ============================================
// PART 10: WHAT IS ALWAYS EXCLUDED
// ============================================
// npm always excludes:
// - .git/
// - .npmrc
// - node_modules/
// These are excluded regardless of files or ignore files.

The ten parts trace the three mechanisms and their precedence. Parts 2 and 3 show the fallback to .gitignore and its failure mode. Part 5 shows the drift problem that makes .npmignore unsafe for secrets. Part 6 shows the files field as the allowlist that prevents the drift.


Quick Reference

Precedence Rules

files Field.npmignore.gitignoreEffective Rule
PresentIgnoredIgnoredfiles is the allowlist
AbsentPresentIgnored.npmignore is the blocklist
AbsentAbsentPresent.gitignore is the blocklist
AbsentAbsentAbsentEverything is published

Always Included

FileReason
package.jsonDefines the package
README (.md, .txt, .rst)Describes the package
LICENSE / LICENCEStates the terms
File in main fieldEntry point

Always Excluded

FileReason
.git/Repository history
.npmrcMay contain credentials
node_modules/Installed by consumer

Verification Commands

CommandPurpose
npm pack --dry-runList files in tarball, no publish
npm publish --dry-runList files and publish target

Best Practices

โœ… Do This:

// Use files as an allowlist
"files": ["dist"]                                                      // โœ…
# Verify the tarball contents before every publish
npm pack --dry-run                                                     # โœ…
# Include the compiled output in files
"files": ["dist", "bin"]                                               # โœ…
# Use .npmignore only when the blocklist is small and stable
# and when files is not practical                                       // โœ…

โŒ Don’t Do This:

# Rely on .gitignore for publishing exclusions
# It is ignored if .npmignore exists                                    # โŒ
# Assume a file in .gitignore is not published
# Add it to files exclusion or .npmignore explicitly                    # โŒ
# Forget to rebuild dist/ before publishing
# files publishes the existing dist/ content                            # โŒ
# Publish without a dry run
# The tarball may contain secrets or missing files                      # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Secrets published.gitignore entry not in .npmignoreUse files allowlist
Package missing dist/.gitignore fallback excluded itAdd files with dist
files ignoredTypo in field nameVerify files is in package.json
README missingNot a recognized extensionUse .md, .txt, or .rst
.npmrc publishedCannot be excluded by filesAlways excluded by npm

Real-World Examples

1. Allowlist with Files

"files": ["dist"]

2. Multiple Directories

"files": ["dist", "bin", "types"]

3. Blocklist with .npmignore

src/
tests/
.env

4. Fallback to .gitignore

# No .npmignore, no files field
# .gitignore is used as the blocklist

5. Verify Tarball

npm pack --dry-run

6. Always Included: README

README.md is included even if not in files

7. Always Included: Main

"main": "dist/index.js" โ†’ dist/index.js is included

8. Always Excluded: .npmrc

.npmrc is never published

9. Drift Scenario

.env added to .gitignore, not .npmignore โ†’ published

10. Precedence Check

files present โ†’ files wins, ignore files ignored

Visual

Precedence Decision Tree

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  IS "files" IN package.json?                                 โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ YES             โ”‚โ”€โ”€โ”€โ”€โ–บ Use files as allowlist            โ”‚
โ”‚  โ”‚                 โ”‚      Ignore .npmignore and .gitignore  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ NO              โ”‚โ”€โ”€โ”€โ”€โ–บ Is .npmignore present?           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚           โ”‚                                                  โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”                                           โ”‚
โ”‚     โ–ผ           โ–ผ                                           โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”                                       โ”‚
โ”‚  โ”‚ YES  โ”‚   โ”‚ NO   โ”‚                                       โ”‚
โ”‚  โ”‚      โ”‚   โ”‚      โ”‚                                       โ”‚
โ”‚  โ”‚Use   โ”‚   โ”‚Use   โ”‚                                       โ”‚
โ”‚  โ”‚.npm  โ”‚   โ”‚.git  โ”‚                                       โ”‚
โ”‚  โ”‚ignoreโ”‚   โ”‚ignoreโ”‚                                       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                       โ”‚
โ”‚                                                              โ”‚
โ”‚  The files field always wins.                                โ”‚
โ”‚  .npmignore overrides .gitignore.                            โ”‚
โ”‚  .gitignore is the fallback.                                 โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Allowlist vs. Blocklist Failure Modes

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ALLOWLIST (files)              BLOCKLIST (.npmignore)       โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
โ”‚  โ”‚ New file added  โ”‚            โ”‚ New file added  โ”‚         โ”‚
โ”‚  โ”‚ to project      โ”‚            โ”‚ to project      โ”‚         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ”‚
โ”‚           โ–ผ                              โ–ผ                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
โ”‚  โ”‚ NOT published   โ”‚            โ”‚ PUBLISHED       โ”‚         โ”‚
โ”‚  โ”‚ (must be added  โ”‚            โ”‚ (unless added   โ”‚         โ”‚
โ”‚  โ”‚ to files)       โ”‚            โ”‚ to .npmignore)  โ”‚         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ”‚
โ”‚                                                              โ”‚
โ”‚  Failure mode: missing file    Failure mode: leaked file     โ”‚
โ”‚  Visible to consumers          May be invisible until        โ”‚
โ”‚  (package broken)              exploited                     โ”‚
โ”‚                                                              โ”‚
โ”‚  Allowlist fails closed. Blocklist fails open.               โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Drift Problem

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  DEVELOPER ADDS .env.production                              โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ .gitignore      โ”‚โ”€โ”€โ”€โ”€โ–บ .env.production added             โ”‚
โ”‚  โ”‚                 โ”‚      Excluded from Git                 โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚           โ–ผ                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ .npmignore      โ”‚โ”€โ”€โ”€โ”€โ–บ NOT updated                       โ”‚
โ”‚  โ”‚                 โ”‚      .env.production not listed        โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚           โ–ผ                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ npm publish     โ”‚โ”€โ”€โ”€โ”€โ–บ .env.production PUBLISHED         โ”‚
โ”‚  โ”‚                 โ”‚      Secret leaked to registry         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚                                                              โ”‚
โ”‚  This is why files (allowlist) is safer.                     โ”‚
โ”‚  A new file is not published unless explicitly listed.       โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Always Included and Excluded

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ALWAYS INCLUDED              ALWAYS EXCLUDED                โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ package.json    โ”‚          โ”‚ .git/           โ”‚           โ”‚
โ”‚  โ”‚ README.md       โ”‚          โ”‚ .npmrc          โ”‚           โ”‚
โ”‚  โ”‚ LICENSE         โ”‚          โ”‚ node_modules/   โ”‚           โ”‚
โ”‚  โ”‚ main file       โ”‚          โ”‚                 โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                              โ”‚
โ”‚  These rules apply regardless of files or ignore files.      โ”‚
โ”‚  The main file is included even if not in files.             โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
.gitignoreGit exclusion list; fallback for npm if no other file
.npmignorenpm blocklist; overrides .gitignore
files fieldnpm allowlist; overrides both ignore files
Precedencefiles > .npmignore > .gitignore
Always includedpackage.json, README, LICENSE, main file
Always excluded.git, .npmrc, node_modules
Verificationnpm pack --dry-run

Key takeaways:

  • files is an allowlist; .npmignore is a blocklist. The allowlist fails closedโ€”a new file is not published unless it is added to the list. The blocklist fails openโ€”a new file is published unless it is added to the ignore file. For security, the allowlist is the safer default .
  • files takes precedence over both ignore files. If files is present, .npmignore and .gitignore are ignored for publishing. If files is absent and .npmignore is present, .gitignore is ignored. The fallback to .gitignore only happens when neither of the other two mechanisms is present.
  • The drift between .gitignore and .npmignore is a security risk. A file added to .gitignore but not to .npmignore is excluded from the repository but included in the published package. This is the failure mode that OWASP warns about, and it is the reason files is recommended for packages that contain anything sensitive .
  • Certain files are always included and always excluded. package.json, the README, the LICENSE, and the main file are always published. .git, .npmrc, and node_modules are never published. These rules apply regardless of the mechanism used.
  • npm pack --dry-run is the authoritative verification. The precedence rules are complex enough that reasoning about them is unreliable. The dry run applies the rules mechanically and shows exactly which files will be in the tarball .

Remember: The choice between .npmignore and files is not a matter of preference. It is a choice between a blocklist that fails open and an allowlist that fails closed. The blocklist requires the developer to remember every file that should not be published, and to update the list every time a new sensitive file is added. The allowlist requires the developer to remember every file that should be published, and to update the list every time a new distributable file is added. The allowlist’s failure mode is a missing file, which is visible to consumers immediately. The blocklist’s failure mode is a leaked secret, which may not be visible until it is exploited. For packages that contain compiled output and source code, files is the safer and more maintainable choice.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!