@ms-cloudpack/eslint-plugin v0.4.1
@ms-cloudpack/eslint-plugin
@ms-cloudpack/eslint-plugin
provides a shared config and custom rules to help encourage Cloudpack-friendly coding practices, specifically regarding imports and exports.
Included configs
Recommended
The @ms-cloudpack/recommended
config includes rules that are recommended for helping Cloudpack work better in your repo. None of these rules require type checking. To enable:
{
"plugins": ["@ms-cloudpack"],
"extends": ["plugin:@ms-cloudpack/recommended"]
}
@ms-cloudpack/recommended
includes the following rules:
@ms-cloudpack/no-unsupported-imports
- `@rnx-kit/no-const-enum
- `@rnx-kit/no-export-all
Recommended requiring type checking
The @ms-cloudpack/recommended-requiring-type-checking
config includes additional rules which are helpful for Cloudpack but require type information. Following the pattern of typescript-eslint, this config does not include the rules from the basic "recommended" config.
To enable, follow the instructions for linting with type information, then add the config to extends
:
{
"plugins": ["@ms-cloudpack"],
"extends": [
// Both of these should be included
"plugin:@ms-cloudpack/recommended",
"plugin:@ms-cloudpack/recommended-requiring-type-checking",
],
"parserOptions": {
// `project` must be specified; see link above
},
}
@ms-cloudpack/recommended-requiring-type-checking
includes the following rules:
Onboarding
The @ms-cloudpack/onboarding
config includes the same rules as @ms-cloudpack/recommended
, but at warn
level.
Onboarding requiring type checking
The @ms-cloudpack/onboarding-requiring-type-checking
config includes the same rules as @ms-cloudpack/recommended-requiring-type-checking
, but at warn
level. Following the pattern of typescript-eslint, this config does not include the rules from the basic "onboarding" config.
Included rules
- ✓: Enabled with
@ms-cloudpack/recommended
- 🔧: Fixable with
--fix
✓ | 🔧 | Rule | Description |
---|---|---|---|
@ms-cloudpack/no-test-exports | Ban test, mock, and fixture exports from index files | ||
✓ | @ms-cloudpack/no-unsupported-imports | Ban deep imports that are not defined in an exports map |
no-test-exports
This rule bans exporting paths or names which appear to be test-related from index files (unless the index file is also under a test-related path).
Note that this rule checks the filename being linted and will only run on index.*
files. Files under paths that appear test-related (based on the testPathPatterns
setting) If you have the rule enabled for a whole repo, you'll need to disable it for any test-related packages.
Mixing test and "production" exports in a single package entry point is not recommended for several reasons:
- Especially if the package is published, it's not clear to consumers what should actually be considered part of the main public API.
- For Cloudpack, it will cause traversal of additional test-related dependencies such as
jest
and its dependency tree. These packages are often not intended for use in the browser and may fail to bundle, which causes time to be wasted debugging issues that won't be relevant at runtime. - Even for other bundlers such as Webpack (or any tool that traverses code), traversing into test dependency code can cause a parse time penalty even if the exports are ultimately tree-shaken out.
The rule is unfortunately not auto-fixable because ESLint is designed to operate on single files. The fix is as follows:
- At the package source root, create a new file with a name such as
index.mock.ts
orindex.test.ts
and move all test, mock, and fixture exports into that file. - If the packages uses an exports map, add this path to the exports map.
- Update consumers to import from the new path, e.g.
my-pkg/index.mock
.
Options
For any of the array options, a provided array overwrites the defaults. Include ...
in your custom array to also include the defaults.
testPathPatterns
(string[]
): Ban importing or exporting from paths matching these regular expressions in index files. Also, index files underneath matching paths will be ignored. Default as of writing:- File or directory starting with
mock|test|fixture
(optionally with__
before) - File or directory containing
Mock|Test|Fixture
- File ending with
.(mock|test|fixture)
(optionally with extension)
- File or directory starting with
testExportNamePatterns
(string[]
): Ban exporting identifiers matching these regular expressions in index files. Default as of writing:- Name starting with
mock|test|fixture
- Name containing
Mock|Test|Fixture
- Name starting with
maxDepth
(number
): Maximum depth of index files to check relative to the package root (zero-indexed). By default this is set to infinity in case of re-exports. Set to 0 to only check<package root>/index.ts
or 1 to also checksrc/index.ts
.
Examples
These examples applies to any index.*
file which is within the configured maxDepth
from the package root:
// ✅ OK
export { foo } from './foo';
// With testPathPatterns: []
export { foo } from './mocks/foo';
// With testExportNamePatterns: []
export { mockFoo } from './foo';
// ❌ Error
// These examples use "mock" to be concise, but the same rules apply by default to "test" and "fixture"
export { foo } from './foo.mock';
export { foo } from './mock-foo';
export { foo } from './mocks/foo';
export { foo } from './__mocks__/foo';
export * as foo from './foo.mock';
export { mockFoo, bar as mockBar } from './foo';
export const mockFoo = 'foo';
export { fooMock };
// Importing from test-related files in an index file is also banned
import { foo } from './foo.mock';
no-unsupported-imports
This rule bans deep imports from paths which aren't defined in a package's exports map. It also bans all deep imports from packages which don't define exports maps.
Features:
- Checks all common "import-like" syntax variants (
import
,export
from paths,require
) - Basic support for wildcards and directories in exports maps
- Caches resolved package info to reduce disk access
- Provides suggestions (not auto-fixes) for importing from package root in some cases
Current limitations (handling for some of these could be added if needed):
- Not sensitive to conditions: it passes if a path is exported for any condition
- Can't auto-fix. This would require either type information (to figure out where symbols are defined and whether they're exported from the root) or complete manual traversal of a package's exported paths and symbols.
- Limited handling of later exclusions underneath wildcards or directories
- Limited support for checking template strings in
import()
orrequire()
(non-literal strings are ignored) - Doesn't verify that the resolved path exists on disk (reduces cost and complexity)
- Does no verification of top-level package imports. This is intentional for performance and usually correct, but it would fail to detect a package that only defines deep imports in its exports map. (Importing specific packages with this issue could be banned with ESLint's
no-restricted-imports
.)
Options
ignorePatterns
(string[]
): Don't check imports matching these regular expression patterns. Examples:"^foo/"
: Ignore all imports underfoo
"^foo/bar/
: Ignore imports underfoo/bar
"^foo/bar/baz$"
: Ignore the specific pathfoo/bar/baz
Examples
For this package without an exports map:
{
"name": "no-exports",
"main": "lib/index.js"
}
The rule bans all deep imports, regardless of whether the files being imported exist on disk:
// ✅ OK
import { foo } from 'no-exports';
// and other variants of import/export syntax
// ❌ Error
import { foo } from 'no-exports/lib/foo';
import { foo } from 'no-exports/lib/index';
const packageJson = require('no-exports/package.json');
// ✅ OK with ignorePatterns such as ["^no-exports/"] or ["^no-exports/lib/foo$"]
import { foo } from 'no-exports/lib/foo';
For this package with an exports map:
{
"name": "with-exports",
"main": "lib/index.js",
"exports": {
".": "./lib/index.js",
"./lib/foo": "./lib/foo.js",
"./lib/utils/*": "./lib/utils/*.js"
}
}
The rule allows root imports or those matching keys in the exports map:
// ✅ OK
import { foo } from 'with-exports';
import { foo } from 'with-exports/lib/foo';
import { foo } from 'with-exports/lib/utils/bar';
// ❌ Error
// Not included in exports
import { foo } from 'with-exports/lib/nope';
const packageJson = require('with-exports/package.json');
// Path in exports doesn't have extension
import { foo } from 'with-exports/lib/foo.js';
// ✅ OK with ignorePatterns such as ["^with-exports/"] or ["^with-exports/lib/nope$"]
import { foo } from 'with-exports/lib/nope';
9 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago