Repository: eslint/eslint
Pull request #14706 (2021-06-14 18:21:05)

Breaking: Strict package exports (refs #13654)
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[x] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)

This implements the [strict package exports RFC](https://github.com/eslint/rfcs/tree/main/designs/2021-package-exports) by updating `api.js` and implementing `unsupported-api.js`.

This does **not** implement `ESLint#getRulesMetaForReport()` method. I'll do a separate PR for that piece.

#### Is there anything you'd like reviewers to focus on?

Please check that this matches the RFC. Also, how do we want to document the `use-at-your-own-risk` entrypoint? Should that be on the Node.js API page, or should we just omit it and trust that people who want it will find it?


nzakas: Hmmm, there's a ton of linting errors on the master branch right now.
btmills: I'm not able to repro any failures on `master` locally, and the last commit to `master` passed checks: https://github.com/eslint/eslint/runs/2809970160. Your first commit changed a bunch of rule tests, which I wasn't expecting. Were those changes intentional?
mdjermanovic: You probably have an old version of `eslint-plugin-eslint-plugin` locally installed.
nzakas: Ah ok, weird. I definitely didn’t intend to change all of those files it must have happened when I ran auto fix. I’ll try updating the plugin
nzakas: Thanks @mdjermanovic that did the trick!
nzakas: Forgot to mention: the more I think about it, the more I feel like the `use-at-your-own-risk` entrypoint should not be documented. People were already digging around the `eslint` package and finding APIs to use that weren't documented before. I don't want to implicitly "bless" stuff in `use-at-your-own-risk` by documenting it, but would like to hear other thoughts.
ljharb: By documented, you mean in prose? as long as it's in package.json "exports" that seems fine.
nzakas: @ljharb yes, in prose. Forgot people could just look in package.json 🤦 
mdjermanovic: > Forgot to mention: the more I think about it, the more I feel like the `use-at-your-own-risk` entrypoint should not be documented. People were already digging around the `eslint` package and finding APIs to use that weren't documented before. I don't want to implicitly "bless" stuff in `use-at-your-own-risk` by documenting it, but would like to hear other thoughts.

I agree that we shouldn't document the `use-at-your-own-risk` entrypoint, as we don't want to encourage its use.

btw it seems that `exports` in package.json doesn't restrict `require(<absolute path>)`, so all internal modules still can be loaded from other packages, like this:

```js
const accessorPairs = require(
  path.resolve(path.dirname(require.resolve("eslint")), "rules", "accessor-pairs")
);
```
nzakas: Thanks for catching those docs issues. 

Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 9ebb5f6b3..640ac2db7 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -110,6 +110,7 @@ module.exports = {
         // Restrict relative path imports
         {
             files: ["lib/*"],
+            excludedFiles: ["lib/unsupported-api.js"],
             rules: {
                 "node/no-restricted-require": ["error", [
                     ...createInternalFilesPatterns()
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index 593a1f71b..dd47ec9b4 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -31,11 +31,8 @@ While ESLint is designed to be run on the command line, it's possible to use ESL
     * [getRules()](#lintergetrules)
     * [defineParser()](#linterdefineparser)
     * [version](#linterversionlinterversion)
-* [linter (deprecated)](#linter-1)
-* [CLIEngine (deprecated)](#cliengine)
 * [RuleTester](#ruletester)
     * [Customizing RuleTester](#customizing-ruletester)
-* [Deprecated APIs](#deprecated-apis)
 
 ---
 
@@ -482,8 +479,8 @@ Those run on `linter2` will get `process.cwd()` if the global `process` object i
 The most important method on `Linter` is `verify()`, which initiates linting of the given text. This method accepts three arguments:
 
 * `code` - the source code to lint (a string or instance of `SourceCode`).
-* `config` - a configuration object that has been processed and normalized by CLIEngine using eslintrc files and/or other configuration arguments.
-    * **Note**: If you want to lint text and have your configuration be read and processed, use CLIEngine's [`executeOnFiles`](#cliengineexecuteonfiles) or [`executeOnText`](#cliengineexecuteontext) instead.
+* `config` - a configuration object that has been processed and normalized by `ESLint` using eslintrc files and/or other configuration arguments.
+    * **Note**: If you want to lint text and have your configuration be read and processed, use [`ESLint#lintFiles()`][eslint-lintfiles] or [`ESLint#lintText()`][eslint-linttext] instead.
 * `options` - (optional) Additional options for this run.
     * `filename` - (optional) the filename to associate with the source code.
     * `preprocess` - (optional) A function that [Processors in Plugins](/docs/developer-guide/working-with-plugins.md#processors-in-plugins) documentation describes as the `preprocess` method.
@@ -701,507 +698,6 @@ const Linter = require("eslint").Linter;
 Linter.version; // => '4.5.0'
 ```
 
-## linter
-
-The `eslint.linter` object (deprecated) is an instance of the `Linter` class as defined [above](#linter). `eslint.linter` exists for backwards compatibility, but we do not recommend using it because any mutations to it are shared among every module that uses `eslint`. Instead, please create your own instance of `eslint.Linter`.
-
-```js
-const linter = require("eslint").linter;
-
-const messages = linter.verify("var foo;", {
-    rules: {
-        semi: 2
-    }
-}, { filename: "foo.js" });
-```
-
-Note: This API is deprecated as of 4.0.0.
-
----
-
-## CLIEngine
-
-⚠️ The `CLIEngine` class has been deprecated in favor of the `ESLint` class as of v7.0.0.
-
-The primary Node.js API is `CLIEngine`, which is the underlying utility that runs the ESLint command line interface. This object will read the filesystem for configuration and file information but will not output any results. Instead, it allows you direct access to the important information so you can deal with the output yourself.
-
-You can get a reference to the `CLIEngine` by doing the following:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-```
-
-The `CLIEngine` is a constructor, and you can create a new instance by passing in the options you want to use. The available options are:
-
-* `allowInlineConfig` - Set to `false` to disable the use of configuration comments (such as `/*eslint-disable*/`). Corresponds to `--no-inline-config`.
-* `baseConfig` - Can optionally be set to a config object that has the same schema as `.eslintrc.*`. This will used as a default config, and will be merged with any configuration defined in `.eslintrc.*` files, with the `.eslintrc.*` files having precedence.
-* `cache` - Operate only on changed files (default: `false`). Corresponds to `--cache`.
-* `cacheFile` - Name of the file where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-file`. Deprecated: use `cacheLocation` instead.
-* `cacheLocation` - Name of the file or directory where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-location`.
-* `configFile` - The configuration file to use (default: null). If `useEslintrc` is true or not specified, this configuration will be merged with any configuration defined in `.eslintrc.*` files, with options in this configuration having precedence. Corresponds to `-c`.
-* `cwd` - Path to a directory that should be considered as the current working directory.
-* `envs` - An array of environments to load (default: empty array). Corresponds to `--env`. Note: This differs from `.eslintrc.*` / `baseConfig`, where instead the option is called `env` and is an object.
-* `errorOnUnmatchedPattern` - Set to `false` to prevent errors when pattern is unmatched. Corresponds to `--no-error-on-unmatched-pattern`.
-* `extensions` - An array of filename extensions that should be checked for code. The default is an array containing just `".js"`. Corresponds to `--ext`. It is only used in conjunction with directories, not with filenames, glob patterns or when using `executeOnText()`.
-* `fix` - A boolean or a function (default: `false`). If a function, it will be passed each linting message and should return a boolean indicating whether the fix should be included with the output report (errors and warnings will not be listed if fixed). Files on disk are never changed regardless of the value of `fix`. To persist changes to disk, call [`outputFixes()`](#cliengineoutputfixes).
-* `fixTypes` - An array of rule types for which fixes should be applied (default: `null`). This array acts like a filter, only allowing rules of the given types to apply fixes. Possible array values are `"problem"`, `"suggestion"`, and `"layout"`.
-* `globals` - An array of global variables to declare (default: empty array). Corresponds to `--global`, and similarly supports passing `'name:true'` to denote a writeable global. Note: This differs from `.eslintrc.*` / `baseConfig`, where `globals` is an object.
-* `ignore` - False disables use of `.eslintignore`, `ignorePath` and `ignorePattern` (default: true). Corresponds to `--no-ignore`.
-* `ignorePath` - The ignore file to use instead of `.eslintignore` (default: null). Corresponds to `--ignore-path`.
-* `ignorePattern` - Glob patterns for paths to ignore. String or array of strings.
-* `parser` - Specify the parser to be used (default: `espree`). Corresponds to `--parser`.
-* `parserOptions` - An object containing parser options (default: empty object). Corresponds to `--parser-options`.
-* `plugins` - An array of plugins to load (default: empty array). Corresponds to `--plugin`.
-* `reportUnusedDisableDirectives` - When set to `true`, adds reported errors for unused `eslint-disable` directives when no problems would be reported in the disabled area anyway (default: false). Corresponds to `--report-unused-disable-directives`.
-* `resolvePluginsRelativeTo` - Determines the folder where plugins should be resolved from. Should be used when an integration installs plugins and uses those plugins to lint code on behalf of the end user. Corresponds to `--resolve-plugins-relative-to`.
-* `rulePaths` - An array of directories to load custom rules from (default: empty array). Corresponds to `--rulesdir`.
-* `rules` - An object of rules to use (default: null). Corresponds to `--rule`.
-* `useEslintrc` - Set to false to disable use of `.eslintrc` files (default: true). Corresponds to `--no-eslintrc`.
-* `globInputPaths` - Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
-
-To programmatically set `.eslintrc.*` options not supported above (such as `extends`,
-`overrides` and `settings`), define them in a config object passed to `baseConfig` instead.
-
-For example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    baseConfig: {
-        extends: ["eslint-config-shared"],
-        settings: {
-            sharedData: "Hello"
-        }
-    },
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-```
-
-In this example, a new `CLIEngine` instance is created that extends a configuration called
-`"eslint-config-shared"`, a setting named `"sharedData"` and two environments (`"browser"`
-and `"mocha"`) are defined, loading of `.eslintrc` and `package.json` files are disabled,
-and the `semi` rule enabled as an error. You can then call methods on `cli` and these options
-will be used to perform the correct action.
-
-Note: Currently `CLIEngine` does not validate options passed to it, but may start doing so in the future.
-
-### CLIEngine#executeOnFiles()
-
-If you want to lint one or more files, use the `executeOnFiles()` method. This method accepts a single argument, which is an array of files and/or directories to traverse for files. You can pass the same values as you would using the ESLint command line interface, such as `"."` to search all JavaScript files in the current directory. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-```
-
-The return value is an object containing the results of the linting operation. Here's an example of a report object:
-
-```js
-{
-    results: [
-        {
-            filePath: "/Users/eslint/project/myfile.js",
-            messages: [{
-                ruleId: "semi",
-                severity: 2,
-                message: "Missing semicolon.",
-                line: 1,
-                column: 13,
-                nodeType: "ExpressionStatement",
-                fix: { range: [12, 12], text: ";" }
-            }, {
-                ruleId: "no-useless-escape",
-                severity: 1,
-                message: "disallow unnecessary escape characters",
-                line: 1,
-                column: 10,
-                nodeType: "ExpressionStatement",
-                suggestions: [{
-                    desc: "Remove unnecessary escape. This maintains the current functionality.",
-                    fix: { range: [9, 10], text: "" }
-                }, {
-                    desc: "Escape backslash to include it in the RegExp.",
-                    fix: { range: [9, 9], text: "\\" }
-                }]
-            }],
-            errorCount: 1,
-            warningCount: 1,
-            fixableErrorCount: 1,
-            fixableWarningCount: 0,
-            source: "\"use strict\"\n"
-        }
-    ],
-    errorCount: 1,
-    warningCount: 0,
-    fixableErrorCount: 1,
-    fixableWarningCount: 0,
-    usedDeprecatedRules: []
-}
-```
-
-You can also pass `fix: true` when instantiating the `CLIEngine` in order to have it figure out what fixes can be applied.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    fix: true, // difference from last example
-    useEslintrc: false,
-    rules: {
-        semi: 2,
-        quotes: [2, "double"]
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-```
-
-```js
-{
-    results: [
-        {
-            filePath: "/Users/eslint/project/myfile.js",
-            messages: [
-                {
-                    ruleId: "semi",
-                    severity: 2,
-                    message: "Missing semicolon.",
-                    line: 1,
-                    column: 13,
-                    nodeType: "ExpressionStatement",
-                    fix: { range: [12, 12], text: ";" }
-                },
-                {
-                    ruleId: "func-name-matching",
-                    severity: 2,
-                    message: "Function name `bar` should match variable name `foo`",
-                    line: 2,
-                    column: 5,
-                    nodeType: "VariableDeclarator"
-                }
-            ],
-            errorCount: 2,
-            warningCount: 0,
-            fixableErrorCount: 1,
-            fixableWarningCount: 0,
-            output: "\"use strict\";\nvar foo = function bar() {};\nfoo();\n"
-        }
-    ],
-    errorCount: 2,
-    warningCount: 0,
-    fixableErrorCount: 1,
-    fixableWarningCount: 0,
-    usedDeprecatedRules: []
-}
-```
-
-If the operation ends with a parsing error, you will get a single message for this file, with `fatal: true` added as an extra property.
-
-```js
-{
-    results: [
-        {
-            filePath: "./myfile.js",
-            messages: [
-                {
-                    ruleId: null,
-                    fatal: true,
-                    severity: 2,
-                    message: "Parsing error: Unexpected token foo",
-                    line: 1,
-                    column: 10
-                }
-            ],
-            errorCount: 1,
-            warningCount: 0,
-            fixableErrorCount: 0,
-            fixableWarningCount: 0,
-            source: "function foo() {}"
-        }
-    ],
-    errorCount: 1,
-    warningCount: 0,
-    fixableErrorCount: 0,
-    fixableWarningCount: 0,
-    usedDeprecatedRules: []
-}
-```
-
-The top-level report object has a `results` array containing all linting results for files that had warnings or errors (any files that did not produce a warning or error are omitted). Each file result includes:
-
-* `filePath` - Path to the given file.
-* `messages` - Array containing the result of calling `linter.verify()` on the given file.
-* `errorCount` and `warningCount` - The exact number of errors and warnings respectively on the given file.
-* `source` - The source code for the given file. This property is omitted if this file has no errors/warnings or if the `output` property is present.
-* `output` - The source code for the given file with as many fixes applied as possible, so you can use that to rewrite the files if necessary. This property is omitted if no fix is available.
-
-The top-level report object also has `errorCount` and `warningCount` which give the exact number of errors and warnings respectively on all the files. Additionally, `usedDeprecatedRules` signals any deprecated rules used and their replacement (if available). Specifically, it is an array of objects with properties like so:
-
-* `ruleId` - The name of the rule (e.g. `indent-legacy`).
-* `replacedBy` - An array of rules that replace the deprecated rule (e.g. `["indent"]`).
-
-Once you get a report object, it's up to you to determine how to output the results. Fixes will not be automatically applied to the files, even if you set `fix: true` when constructing the `CLIEngine` instance. To apply fixes to the files, call [`outputFixes`](#cliengineoutputfixes).
-
-### CLIEngine#resolveFileGlobPatterns()
-
-You can pass filesystem-style or glob patterns to ESLint and have it function properly. In order to achieve this, ESLint must resolve non-glob patterns into glob patterns before determining which files to execute on. The `resolveFileGlobPatterns()` methods uses the current settings from `CLIEngine` to resolve non-glob patterns into glob patterns. Pass an array of patterns that might be passed to the ESLint CLI and it will return an array of glob patterns that mean the same thing. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-});
-
-// pass an array of patterns
-const globPatterns = cli.resolveFileGlobPatterns(["."]);
-console.log(globPatterns[i]);       // ["**/*.js"]
-```
-
-### CLIEngine#getConfigForFile()
-
-If you want to retrieve a configuration object for a given file, use the `getConfigForFile()` method. This method accepts one argument, a file path, and returns an object represented the calculated configuration of the file. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-const config = cli.getConfigForFile("myfile.js");
-```
-
-Once you have the configuration information, you can pass it into the `linter` object:
-
-```js
-const CLIEngine = require("eslint").CLIEngine,
-    Linter = require("eslint").Linter;
-
-const linter = new Linter();
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-const config = cli.getConfigForFile("myfile.js");
-
-const messages = linter.verify('var foo;', config);
-```
-
-### CLIEngine#executeOnText()
-
-If you already have some text to lint, then you can use the `executeOnText()` method to lint that text. The linter will assume that the text is a file in the current working directory, and so will still obey any `.eslintrc` and `.eslintignore` files that may be present. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// Lint the supplied text and optionally set a filename that is displayed in the report
-const report = cli.executeOnText("var foo = 'bar';", "foo.js");
-
-// In addition to the above, warn if the resolved file name is ignored.
-const reportAndWarnOnIgnoredFile = cli.executeOnText("var foo = 'bar';", "foo.js", true);
-```
-
-The `report` returned from `executeOnText()` is in the same format as from `executeOnFiles()`, but there is only ever one result in `report.results`.
-
-If a filename in the optional second parameter matches a file that is configured to be ignored, then this function returns no errors or warnings. The method includes an additional optional boolean third parameter. When `true`, a resolved file name that is ignored will return a warning.
-
-### CLIEngine#addPlugin()
-
-Loads a plugin from configuration object with specified name. Name can include plugin prefix ("eslint-plugin-")
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-const cli = new CLIEngine({
-    ignore: true
-});
-cli.addPlugin("eslint-plugin-processor", {
-    processors: {
-        ".txt": {
-            preprocess: function(text) {
-                return [text];
-            },
-            postprocess: function(messages) {
-                return messages[0];
-            }
-        }
-    }
-});
-```
-
-### CLIEngine#isPathIgnored()
-
-Checks if a given path is ignored by ESLint.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    ignore: true,
-    ignorePath: ".customIgnoreFile"
-});
-
-const isIgnored = cli.isPathIgnored("foo/bar.js");
-```
-
-### CLIEngine#getFormatter()
-
-Retrieves a formatter, which you can then use to format a report object. The argument is either the name of a built-in formatter:
-
-* "[checkstyle](../user-guide/formatters#checkstyle)"
-* "[compact](../user-guide/formatters#compact)"
-* "[html](../user-guide/formatters#html)"
-* "[jslint-xml](../user-guide/formatters#jslint-xml)"
-* "[json](../user-guide/formatters#json)"
-* "[junit](../user-guide/formatters#junit)"
-* "[stylish](../user-guide/formatters#stylish)" (the default)
-* "[tap](../user-guide/formatters#tap)"
-* "[unix](../user-guide/formatters#unix)"
-* "[visualstudio](../user-guide/formatters#visualstudio)"
-
-or the full path to a JavaScript file containing a custom formatter. You can also omit the argument to retrieve the default formatter.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-
-// get the default formatter
-const formatter = cli.getFormatter();
-
-// Also could do...
-// const formatter = cli.getFormatter("compact");
-// const formatter = cli.getFormatter("./my/formatter.js");
-
-// output to console
-console.log(formatter(report.results));
-```
-
-**Note:** Also available as a static function on `CLIEngine`.
-
-```js
-// get the default formatter by calling the static function
-const formatter = CLIEngine.getFormatter();
-```
-
-**Important:** You must pass in the `results` property of the report. Passing in `report` directly will result in an error.
-
-### CLIEngine#getErrorResults()
-
-This is a static function on `CLIEngine`. It can be used to filter out all the non error messages from the report object.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-
-// only get the error messages
-const errorReport = CLIEngine.getErrorResults(report.results)
-```
-
-**Important:** You must pass in the `results` property of the report. Passing in `report` directly will result in an error.
-
-### CLIEngine#outputFixes()
-
-This is a static function on `CLIEngine` that is used to output fixes from `report` to disk. It does by looking for files that have an `output` property in their results. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    fix: true,
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-
-// output fixes to disk
-CLIEngine.outputFixes(report);
-```
-
-### CLIEngine#getRules()
-
-This method returns a map of all loaded rules. Under the hood, it calls [Linter#getRules](#lintergetrules).
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-const cli = new CLIEngine();
-
-cli.getRules();
-
-/*
-Map {
-  'accessor-pairs' => { meta: { docs: [Object], schema: [Array] }, create: [Function: create] },
-  'array-bracket-newline' => { meta: { docs: [Object], schema: [Array] }, create: [Function: create] },
-  ...
-}
-*/
-```
-
-
-### CLIEngine.version
-
-`CLIEngine` has a static `version` property containing the semantic version number of ESLint that it comes from.
-
-```js
-require("eslint").CLIEngine.version; // '4.5.0'
-```
-
 ---
 
 ## RuleTester
@@ -1398,14 +894,6 @@ ruleTester.run("my-rule", myRule, {
 
 ---
 
-## Deprecated APIs
-
-* `cli` - the `cli` object has been deprecated in favor of `CLIEngine`. As of v1.0.0, `cli` is no longer exported and should not be used by external tools.
-* `linter` - the `linter` object has been deprecated in favor of `Linter` as of v4.0.0.
-* `CLIEngine` - the `CLIEngine` class has been deprecated in favor of the `ESLint` class as of v7.0.0.
-
----
-
 [configuration object]: ../user-guide/configuring
 [builtin-formatters]: https://eslint.org/docs/user-guide/formatters/
 [thirdparty-formatters]: https://www.npmjs.com/search?q=eslintformatter
diff --git a/lib/api.js b/lib/api.js
index e4b6643b4..3dde09855 100644
--- a/lib/api.js
+++ b/lib/api.js
@@ -5,30 +5,22 @@
 
 "use strict";
 
-const { CLIEngine } = require("./cli-engine");
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
 const { ESLint } = require("./eslint");
 const { Linter } = require("./linter");
 const { RuleTester } = require("./rule-tester");
 const { SourceCode } = require("./source-code");
 
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
+
 module.exports = {
     Linter,
-    CLIEngine,
     ESLint,
     RuleTester,
     SourceCode
 };
-
-// DOTO: remove deprecated API.
-let deprecatedLinterInstance = null;
-
-Object.defineProperty(module.exports, "linter", {
-    enumerable: false,
-    get() {
-        if (!deprecatedLinterInstance) {
-            deprecatedLinterInstance = new Linter();
-        }
-
-        return deprecatedLinterInstance;
-    }
-});
diff --git a/lib/unsupported-api.js b/lib/unsupported-api.js
new file mode 100644
index 000000000..110b35a47
--- /dev/null
+++ b/lib/unsupported-api.js
@@ -0,0 +1,23 @@
+/**
+ * @fileoverview APIs that are not officially supported by ESLint.
+ *      These APIs may change or be removed at any time. Use at your
+ *      own risk.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const { FileEnumerator } = require("./cli-engine/file-enumerator");
+
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
+
+module.exports = {
+    builtinRules: require("./rules"),
+    FileEnumerator
+};
diff --git a/package.json b/package.json
index 593bebf07..6b10d99e7 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,11 @@
     "eslint": "./bin/eslint.js"
   },
   "main": "./lib/api.js",
+  "exports": {
+    "./package.json": "./package.json",
+    ".": "./lib/api.js",
+    "./use-at-your-own-risk": "./lib/unsupported-api.js"
+  },
   "scripts": {
     "test": "node Makefile.js test",
     "test:cli": "mocha",


Test patch:
diff --git a/tests/lib/api.js b/tests/lib/api.js
index f943f526c..074d206e5 100644
--- a/tests/lib/api.js
+++ b/tests/lib/api.js
@@ -5,21 +5,33 @@
 
 "use strict";
 
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
 const assert = require("chai").assert,
     api = require("../../lib/api");
 
+//-----------------------------------------------------------------------------
+// Tests
+//-----------------------------------------------------------------------------
+
 describe("api", () => {
 
     it("should have RuleTester exposed", () => {
         assert.isFunction(api.RuleTester);
     });
 
-    it("should have CLIEngine exposed", () => {
-        assert.isFunction(api.CLIEngine);
+    it("should not have CLIEngine exposed", () => {
+        assert.isUndefined(api.CLIEngine);
+    });
+
+    it("should not have linter exposed", () => {
+        assert.isUndefined(api.linter);
     });
 
-    it("should have linter exposed", () => {
-        assert.isObject(api.linter);
+    it("should have Linter exposed", () => {
+        assert.isFunction(api.Linter);
     });
 
     it("should have SourceCode exposed", () => {
diff --git a/tests/lib/unsupported-api.js b/tests/lib/unsupported-api.js
new file mode 100644
index 000000000..dd88be5e6
--- /dev/null
+++ b/tests/lib/unsupported-api.js
@@ -0,0 +1,30 @@
+/**
+ * @fileoverview Tests for unsupported-api.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const assert = require("chai").assert,
+    { LazyLoadingRuleMap } = require("../../lib/rules/utils/lazy-loading-rule-map"),
+    api = require("../../lib/unsupported-api");
+
+//-----------------------------------------------------------------------------
+// Tests
+//-----------------------------------------------------------------------------
+
+describe("unsupported-api", () => {
+
+    it("should have FileEnumerator exposed", () => {
+        assert.isFunction(api.FileEnumerator);
+    });
+
+    it("should have builtinRules exposed", () => {
+        assert.instanceOf(api.builtinRules, LazyLoadingRuleMap);
+    });
+
+});