diff --git a/eslint.config.js b/eslint.config.js index 4df1ad7..fecb9e9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,4 +3,7 @@ import { defineConfig } from 'readable'; export default defineConfig({ strict: true, tsconfig: '/home/work/Documents/Repositories/Readable/tsconfig.json', + environment: { + node: true, + }, }); diff --git a/packages/core/src/env.d.ts b/packages/core/src/env.d.ts new file mode 100644 index 0000000..865d8e4 --- /dev/null +++ b/packages/core/src/env.d.ts @@ -0,0 +1,22 @@ +/** + * @see {@link https://github.com/sindresorhus/globals} + * @summary Global identifiers from different Javascript environments + * --- + * **Note:** + * + * The `globals` package type declarations doesn't seems to be working properly, + * so the used globals are declared here so the type-checking doesn't error. + * + * Also, because of this manual declaration, we can filter just the globals that + * are recommended/should be used in normal development/environments today. + */ +declare module 'globals' { + const globals: { + builtin: Record + browser: Record + node: Record + nodeBuiltin: Record + commonjs: Record + }; + export default globals; +} diff --git a/packages/core/src/environments.js b/packages/core/src/environments.js new file mode 100644 index 0000000..2825432 --- /dev/null +++ b/packages/core/src/environments.js @@ -0,0 +1,56 @@ +import globals from 'globals'; + +/** + * @param {import('./types').Config['environment']} environment + * + * @returns {import('./types').ESConfig[]} +*/ +export function setEnvironments(environment) { + + environment ||= { + node: + typeof window === 'undefined' && + typeof process !== 'undefined' && + typeof require !== 'function', + deno: + typeof window !== 'undefined' && + // @ts-expect-error because this package is develop in node + typeof Deno !== 'undefined', + browser: + typeof window !== 'undefined', + }; + + return [ + { + files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'], + languageOptions: { + ecmaVersion: environment.ecmaVersion ?? 'latest', + globals: { + ...globals.builtin, + ...environment.customGlobals, + }, + }, + }, + { + files: ['**/*.cjs', '**/*.cts'], + languageOptions: { + sourceType: 'commonjs', + globals: { + ...globals.node, + ...globals.commonjs, + }, + }, + }, + { + files: ['**/*.js', '**/*.mjs', '**/*.ts', '**/*.mts'], + languageOptions: { + sourceType: 'module', + globals: { + ...(environment.node ? globals.nodeBuiltin : {}), + ...(environment.browser || environment.deno ? globals.browser : {}), + ...(environment.deno ? { Deno: true } : {}), + }, + }, + }, + ]; +} diff --git a/packages/core/src/index.js b/packages/core/src/index.js index 3013a89..85bb70f 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -3,8 +3,8 @@ import tsEslint from '@typescript-eslint/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; import js from '@eslint/js'; import * as configs from './configs/index.js'; -import globals from 'globals'; import { getUserRules } from './userOptions.js'; +import { setEnvironments } from './environments.js'; /** * @param {import('./types').Config} userConfig @@ -35,14 +35,8 @@ export function defineConfig(userConfig) { parser: tsParser, parserOptions: { project: userConfig.tsconfig, - // eslint-disable-next-line no-undef tsconfigRootDir: process.cwd(), }, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - globals: { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - ...globals.nodeBuiltin, - }, }, // @ts-expect-error The `@typescript-eslint/eslint-plugin` package doesn't export // rules as `RulesRecord` type. @@ -57,6 +51,7 @@ export function defineConfig(userConfig) { configs.formatting, configs.typescript, ...getUserRules(userConfig.options), + ...setEnvironments(userConfig.environment), ]; } diff --git a/packages/core/src/types.d.ts b/packages/core/src/types.d.ts index d62316a..93d7469 100644 --- a/packages/core/src/types.d.ts +++ b/packages/core/src/types.d.ts @@ -2,21 +2,63 @@ import type { Linter } from 'eslint'; export type ESConfig = Readonly; -export type inferrableTypesOptions = [ - 'never' | 'always' | 'ts-never' | 'js-never', - { - /** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreparameters} */ - parameters?: boolean - /** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreproperties} */ - properties?: boolean - /** @see {@link https://typescript-eslint.io/rules/explicit-function-return-type} */ - returnValues?: boolean - }, -] | 'never' | 'always' | 'ts-never' | 'js-never'; - export interface Config { tsconfig?: string | string[] strict?: boolean + /** + * @summary + * Environment and language settings + * + * If no globals/environments are defined, the configuration tries to detect the + * environment using `typeof`. See each option for more explanation + */ + environment?: { + /** + * @summary + * Enables NodeJS environment globals. + * + * **Note:** this does not enables CommonJS globals, if you are using + * CommonJS, use a file ending in `.cjs` or `.cts` + * + * + * @example // Detects if + * typeof window === 'undefined' && + * typeof process !== 'undefined' && + * typeof require !== 'undefined' + */ + node?: boolean + /** + * @summary + * Enables the global `Deno` namespace and browser/web standards globals + * + * @example // Detects if + * typeof window !== 'undefined' && + * typeof Deno !== 'undefined' + */ + deno?: boolean + /** + * @summary + * Enables browser/web standards globals + * + * @example // Detects if + * typeof window !== 'undefined' + */ + browser?: boolean + /** + * @summary + * What JavaScript (ECMAScript) that will be evaluated + * + * **Defaults to `latest`** + */ + ecmaVersion?: Linter.ParserOptions['ecmaVersion'] + /** + * @summary + * User defined globals for edge-cases or if available aren't enough + * + * **Does not overrides previous enabled ones** + */ + customGlobals?: Record + } options?: { indent?: 'tab' | 'space' quotes?: 'single' | 'double' @@ -98,3 +140,15 @@ export interface Config { inferrableTypes?: inferrableTypesOptions } } + +export type inferrableTypesOptions = [ + 'never' | 'always' | 'ts-never' | 'js-never', + { + /** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreparameters} */ + parameters?: boolean + /** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreproperties} */ + properties?: boolean + /** @see {@link https://typescript-eslint.io/rules/explicit-function-return-type} */ + returnValues?: boolean + }, +] | 'never' | 'always' | 'ts-never' | 'js-never';