Skip to content

noPrivateImports

Restricts imports of private exports.

In JavaScript and TypeScript, as soon as you export a symbol, such as a type, function, or anything else that can be exported, it is considered public and can be imported from anywhere else. Unfortunately, this makes it hard to enforce module boundaries, or to prevent importing things that were only exported for test purposes, for instance.

This rule recognizes the JSDoc tags @public, @package, and @private so that you are free to set the visibility of exports. Exports without tag have a default visibility of public, but this can be configured.

The @access tag is also supported if it’s used with one of the values public, package, or private.

Public visibility is the default and means there are no restrictions for importing a given symbol. In other words, without this rule, all exported symbols are implicitly public.

Within the context of this rule, package visibility means that a symbol is visible within the same “package”, which means that any module that resides in the same folder, or one of its subfolders, is allowed to import the symbol. Modules that only share a common folder higher up in the hierarchy are not allowed to import the symbol.

For a visual explanation, see this illustration.

Private visibility means that a symbol may not be imported from other modules.

The key thing to understanding the usefulness of @private is that this rule doesn’t treat modules and files as one and the same thing. While files are indeed modules, folders are considered modules too, with their files and subfolders being submodules. Therefore, symbols exported as @private from an index file, such as index.js, can still be imported from other submodules in that same module.

Another reason why private visibility may still be useful is that it allows you to choose specific exceptions. For example, using overrides, you may want to disable this rule in all files with a .test.js extension. This way, symbols marked private cannot be imported from outside modules, with the exception of test files.

  • This rule currently only looks at the JSDoc comments that are attached to the export statement nearest to the symbol’s definition. If the symbol isn’t exported in the same statement as in which it is defined, the visibility as specified in the export statement is used, not that of the symbol definition. Re-exports cannot (currently) override the visibility from the original export.
  • This rule only applies to imports from JavaScript and TypeScript files. Imports for resources such as images or CSS files are exempted regardless of the default visibility setting.

sub/foo.js

/**
* @package
*/
export const fooPackageVariable = 1;

bar.js

// Attempt to import package private variable from `sub/foo.js` from
// outside its `sub` module:
import { fooPackageVariable } from "./sub/foo.js";
/**
* @private For test purposes only!
*/
export function getTestStuff() {}

bar.test.js // Attempt to import a private export. To allow this, you probably want // to configure an override to disable this rule in test files. // See: https://biomejs.dev/reference/configuration/#overrides

import { getTestStuff } from "./bar.js";

sub/index.js

// Package-private exports can be imported from inside the same module.
import { fooPackageVariable } from "./foo.js";
// Resources (anything other than JS/TS files) are always exempt.
import { barResource } from "../resources/bar.png";
/** @private */
export const subPrivateVariable = 2;

sub/deep/index.js

// Private exports are accessible within the same module only, but
// modules can be nested. So the following works because you can always
// import from the index file of a parent module:
import { subPrivateVariable } from "../index.js";
biome.json
{
"linter": {
"rules": {
"correctness": {
"noPrivateImports": "error"
}
}
}
}