0.4.1 • Published 9 months ago

@ms-cloudpack/eslint-plugin v0.4.1

Weekly downloads
-
License
MIT
Repository
-
Last release
9 months ago

@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:

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
🔧RuleDescription
@ms-cloudpack/no-test-exportsBan test, mock, and fixture exports from index files
@ms-cloudpack/no-unsupported-importsBan 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 or index.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)
  • 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
  • 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 check src/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() or require() (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 under foo
    • "^foo/bar/: Ignore imports under foo/bar
    • "^foo/bar/baz$": Ignore the specific path foo/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';
0.4.1

9 months ago

0.4.0

1 year ago

0.3.15

1 year ago

0.3.14

1 year ago

0.3.13

1 year ago

0.3.12

1 year ago

0.3.11

1 year ago

0.3.10

2 years ago

0.3.9

2 years ago

0.3.0

2 years ago

0.3.6

2 years ago

0.3.5

2 years ago

0.3.8

2 years ago

0.3.7

2 years ago

0.3.2

2 years ago

0.3.1

2 years ago

0.3.4

2 years ago

0.3.3

2 years ago

0.2.2

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago