diff --git a/LICENSE b/LICENSE index c6ade2a4..de415124 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2015 Ben Mosher +Copyright (c) 2020 modern-webdev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/rules/avoid-barrel-files.md b/docs/rules/avoid-barrel-files.md new file mode 100644 index 00000000..76e934fb --- /dev/null +++ b/docs/rules/avoid-barrel-files.md @@ -0,0 +1,27 @@ +# import-x/avoid-barrel-files + +This rule disallows _authoring_ barrel files in your project. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +export { foo } from 'foo' +export { bar } from 'bar' +export { baz } from 'baz' +``` + +## Options + +This rule has the following options, with these defaults: + +```js +"import-x/avoid-barrel-files": ["error", { + "amountOfExportsToConsiderModuleAsBarrel": 5 +}] +``` + +### `amountOfExportsToConsiderModuleAsBarrel` + +This option sets the number of exports that will be considered a barrel file. Anything over will trigger the rule. diff --git a/docs/rules/avoid-importing-barrel-files.md b/docs/rules/avoid-importing-barrel-files.md new file mode 100644 index 00000000..aa3df3f1 --- /dev/null +++ b/docs/rules/avoid-importing-barrel-files.md @@ -0,0 +1,87 @@ +# import-x/avoid-importing-barrel-files + +This rule aims to avoid importing barrel files that lead to loading large module graphs. This rule is different from the `avoid-barrel-files` rule, which lints against _authoring_ barrel files. This rule lints against _importing_ barrel files. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +// If `foo` is a barrel file leading to a module graph of more than 20 modules +export { foo } from 'foo' +``` + +## Options + +This rule has the following options, with these defaults: + +```js +"import-x/avoid-importing-barrel-files": ["error", { + allowList: [], + maxModuleGraphSizeAllowed: 20, + amountOfExportsToConsiderModuleAsBarrel: 3, + exportConditions: ["node", "import"], + mainFields: ["module", "browser", "main"], + extensions: [".js", ".ts", ".tsx", ".jsx", ".json", ".node"], +}] +``` + +### `allowList` + +List of modules from which to allow barrel files being imported. + +### `maxModuleGraphSizeAllowed` + +Maximum allowed module graph size. If an imported module results in loading more than this many modules, it will be considered a barrel file. + +### `amountOfExportsToConsiderModuleAsBarrel` + +Amount of exports to consider a module as a barrel file. + +### `exportConditions` + +Export conditions to use when resolving modules. + +### `mainFields` + +Main fields to use when resolving modules. + +### `extensions` + +Extensions to use when resolving modules. + +### `tsconfig` + +TSConfig options to pass to the underlying resolver when resolving modules. + +This can be useful when resolving project references in TypeScript. + +### `alias` + +The rule can accept an `alias` option whose value can be an object that matches Webpack's [resolve.alias](https://webpack.js.org/configuration/resolve/) configuration. + +```js +// eslint.config.js +import path from 'node:path' +import { defineConfig } from 'eslint/config' +import importPlugin from 'eslint-plugin-import-x' + +export default defineConfig([ + { + plugins: { + 'import-x': importPlugin, + }, + rules: { + 'import-x/avoid-importing-barrel-files': [ + 'error', + { + alias: { + // "@/foo/bar.js" => "./src/foo/bar.js" + '@': [path.resolve('.', 'src')], + }, + }, + ], + }, + }, +]) +``` diff --git a/docs/rules/avoid-namespace-import.md b/docs/rules/avoid-namespace-import.md new file mode 100644 index 00000000..075f1468 --- /dev/null +++ b/docs/rules/avoid-namespace-import.md @@ -0,0 +1,25 @@ +# import-x/avoid-namespace-import + +This rule forbids the use of namespace imports as they can lead to unused imports and prevent treeshaking. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +import * as foo from 'foo' +``` + +## Options + +This rule has the following options, with these defaults: + +```js +"import-x/avoid-namespace-import": ["error", { + allowList: [] +}] +``` + +### `allowList` + +A list of namespace imports that are allowed. diff --git a/docs/rules/avoid-re-export-all.md b/docs/rules/avoid-re-export-all.md new file mode 100644 index 00000000..44d3af54 --- /dev/null +++ b/docs/rules/avoid-re-export-all.md @@ -0,0 +1,12 @@ +# import-x/avoid-re-export-all + +This rule forbids exporting `*` from a module as it can lead to unused imports and prevent treeshaking. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +export * from 'foo' +export * as foo from 'foo' +``` diff --git a/package.json b/package.json index 34c731c3..1cf776d6 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "@typescript-eslint/utils": "^8.31.0", "comment-parser": "^1.4.1", "debug": "^4.4.0", + "eslint-barrel-file-utils": "^0.0.11", "eslint-import-resolver-node": "^0.3.9", "get-tsconfig": "^4.10.0", "is-glob": "^4.0.3", diff --git a/src/index.ts b/src/index.ts index 720c030e..3f77886e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,6 +20,10 @@ import warnings from './config/warnings.js' import { meta } from './meta.js' import { createNodeResolver } from './node-resolver.js' import { cjsRequire } from './require.js' +import avoidBarrelFiles from './rules/avoid-barrel-files.js' +import avoidImportingBarrelFiles from './rules/avoid-importing-barrel-files.js' +import avoidNamespaceImport from './rules/avoid-namespace-import.js' +import avoidReExportAll from './rules/avoid-re-export-all.js' import consistentTypeSpecifierStyle from './rules/consistent-type-specifier-style.js' import default_ from './rules/default.js' import dynamicImportChunkname from './rules/dynamic-import-chunkname.js' @@ -129,6 +133,12 @@ const rules = { // deprecated aliases to rules 'imports-first': importsFirst, + + // barrel files + 'avoid-barrel-files': avoidBarrelFiles, + 'avoid-importing-barrel-files': avoidImportingBarrelFiles, + 'avoid-namespace-import': avoidNamespaceImport, + 'avoid-re-export-all': avoidReExportAll, } satisfies Record> const configs = { diff --git a/src/rules/avoid-barrel-files.ts b/src/rules/avoid-barrel-files.ts new file mode 100644 index 00000000..5a127529 --- /dev/null +++ b/src/rules/avoid-barrel-files.ts @@ -0,0 +1,100 @@ +import { createRule } from '../utils/index.js' + +export interface Options { + amountOfExportsToConsiderModuleAsBarrel: number +} + +export type MessageId = 'avoidBarrel' + +const defaultOptions: Options = { + amountOfExportsToConsiderModuleAsBarrel: 3, +} + +export default createRule<[Options?], MessageId>({ + name: 'avoid-barrel-files', + meta: { + type: 'suggestion', + docs: { + description: 'Forbid authoring of barrel files.', + category: 'Performance', + recommended: true, + }, + schema: [ + { + type: 'object', + properties: { + amountOfExportsToConsiderModuleAsBarrel: { + type: 'number', + description: + 'Minimum amount of exports to consider module as barrelfile', + default: 3, + }, + }, + additionalProperties: false, + }, + ], + messages: { + avoidBarrel: + 'Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.', + }, + }, + defaultOptions: [defaultOptions], + create(context) { + const options = context.options[0] || defaultOptions + const amountOfExportsToConsiderModuleAsBarrel = + options.amountOfExportsToConsiderModuleAsBarrel + + return { + Program(node) { + let declarations = 0 + let exports = 0 + + for (const n of node.body) { + if (n.type === 'VariableDeclaration') { + declarations += n.declarations.length + } + + if ( + n.type === 'FunctionDeclaration' || + n.type === 'ClassDeclaration' || + n.type === 'TSTypeAliasDeclaration' || + n.type === 'TSInterfaceDeclaration' + ) { + declarations += 1 + } + + if (n.type === 'ExportNamedDeclaration') { + exports += n.specifiers.length + } + + if (n.type === 'ExportAllDeclaration' && n?.exportKind !== 'type') { + exports += 1 + } + + if (n.type === 'ExportDefaultDeclaration') { + if ( + n.declaration.type === 'FunctionDeclaration' || + n.declaration.type === 'CallExpression' + ) { + declarations += 1 + } else if (n.declaration.type === 'ObjectExpression') { + exports += n.declaration.properties.length + } else { + exports += 1 + } + } + } + + if ( + exports > declarations && + exports > amountOfExportsToConsiderModuleAsBarrel + ) { + context.report({ + node, + messageId: 'avoidBarrel', + }) + } + }, + } + }, +}) diff --git a/src/rules/avoid-importing-barrel-files.ts b/src/rules/avoid-importing-barrel-files.ts new file mode 100644 index 00000000..b620ede0 --- /dev/null +++ b/src/rules/avoid-importing-barrel-files.ts @@ -0,0 +1,310 @@ +import { readFileSync } from 'node:fs' +import { builtinModules } from 'node:module' +import path from 'node:path' + +import { + count_module_graph_size, + is_barrel_file, +} from 'eslint-barrel-file-utils/index.cjs' +import { ResolverFactory } from 'unrs-resolver' +import type { ResolveResult } from 'unrs-resolver' + +import { createRule } from '../utils/index.js' + +export interface Options { + allowList: string[] + maxModuleGraphSizeAllowed: number + amountOfExportsToConsiderModuleAsBarrel: number + exportConditions: string[] + mainFields: string[] + extensions: string[] + tsconfig?: { + configFile: string + references: string[] + } + alias?: Record> +} + +export type MessageId = 'avoidImport' + +const defaultOptions: Options = { + allowList: [], + maxModuleGraphSizeAllowed: 20, + amountOfExportsToConsiderModuleAsBarrel: 3, + exportConditions: ['node', 'import'], + mainFields: ['module', 'browser', 'main'], + extensions: ['.js', '.ts', '.tsx', '.jsx', '.json', '.node'], + tsconfig: undefined, + alias: undefined, +} + +const cache: Record< + string, + { isBarrelFile: boolean; moduleGraphSize: number } +> = {} + +/** + * @param {string} specifier + * @returns {boolean} + */ +const isBareModuleSpecifier = (specifier: string): boolean => { + if (specifier && specifier.length > 0) { + return /[@a-zA-Z]/.test(specifier.replaceAll("'", '')[0]) + } + return false +} + +// custom error class to emulate oxc_resolver ResolveError enum. +// `errorVariant` can be equal to a `ResolveError` enum variant. +class ResolveError extends Error { + public errorVariant: string | null + + constructor(errorVariant: string | null = null, message: string = '') { + super(message) + this.errorVariant = errorVariant + this.message = message + } +} + +export default createRule<[Options?], MessageId>({ + name: 'avoid-importing-barrel-files', + meta: { + type: 'problem', + docs: { + description: 'Forbid importing barrel files.', + recommended: true, + }, + schema: [ + { + type: 'object', + properties: { + allowList: { + type: 'array', + description: 'List of modules from which to allow barrel files', + default: [], + uniqueItems: true, + items: { + type: 'string', + }, + }, + maxModuleGraphSizeAllowed: { + type: 'number', + description: 'Maximum allowed module graph size', + default: 20, + }, + amountOfExportsToConsiderModuleAsBarrel: { + type: 'number', + description: + 'Amount of exports to consider a module as barrel file', + default: 3, + }, + exportConditions: { + type: 'array', + description: + 'Export conditions to use to resolve bare module specifiers', + default: [], + uniqueItems: true, + items: { + type: 'string', + }, + }, + mainFields: { + type: 'array', + description: 'Main fields to use to resolve modules', + default: [], + uniqueItems: true, + items: { + type: 'string', + }, + }, + extensions: { + type: 'array', + description: 'Extensions to use to resolve modules', + default: [], + uniqueItems: true, + items: { + type: 'string', + }, + }, + // schema to match oxc-resolver's TsconfigOptions + tsconfig: { + type: 'object', + description: 'Options to TsconfigOptions', + properties: { + configFile: { + type: 'string', + description: 'Relative path to the configuration file', + }, + references: { + type: 'array', + description: 'Typescript Project References', + items: { + type: 'string', + }, + }, + }, + }, + // NapiResolveOptions.alias + alias: { + type: 'object', + description: 'Webpack aliases used in imports or requires', + }, + }, + }, + ], + messages: { + avoidImport: + 'The imported module "{{specifier}}" is a barrel file, which leads to importing a module graph of {{amount}} modules, which exceeds the maximum allowed size of {{maxModuleGraphSizeAllowed}} modules', + }, + }, + defaultOptions: [defaultOptions], + create(context) { + const options = context.options[0] || defaultOptions + const maxModuleGraphSizeAllowed = options.maxModuleGraphSizeAllowed + const amountOfExportsToConsiderModuleAsBarrel = + options.amountOfExportsToConsiderModuleAsBarrel + const exportConditions = options.exportConditions + const mainFields = options.mainFields + const extensions = options.extensions + const tsconfig = options.tsconfig + const alias = options.alias + + const resolutionOptions = { + exportConditions, + mainFields, + extensions, + tsconfig, + alias, + } + + const resolver = new ResolverFactory({ + tsconfig, + alias, + conditionNames: exportConditions, + mainFields, + extensions, + }) + + return { + ImportDeclaration(node) { + const moduleSpecifier = node.source.value + const currentFileName = context.filename + + if (options?.allowList?.includes(moduleSpecifier)) { + return + } + + if (node?.importKind === 'type') { + return + } + + if (builtinModules.includes(moduleSpecifier.replace('node:', ''))) { + return + } + + let resolvedPath: ResolveResult + try { + resolvedPath = resolver.sync( + path.dirname(currentFileName), + moduleSpecifier, + ) + + if (resolvedPath.error) { + // assuming ResolveError::NotFound if ResolveResult's path value is None + if (!resolvedPath.path) { + throw new ResolveError('NotFound', resolvedPath.error) + } + + throw new ResolveError(null, resolvedPath.error) + } + } catch { + // do nothing since we couldn't resolve it + return + } + + if (!resolvedPath.path) { + throw new ResolveError('NotFound', resolvedPath.error) + } + + const fileContent = readFileSync(resolvedPath.path, 'utf8') + let isBarrelFile: boolean + + /** Only cache bare module specifiers, as local files can change */ + if (isBareModuleSpecifier(moduleSpecifier)) { + /** + * The module specifier is not cached yet, so we need to analyze and + * cache it + */ + if (cache[moduleSpecifier] === undefined) { + isBarrelFile = is_barrel_file( + fileContent, + amountOfExportsToConsiderModuleAsBarrel, + ) + const moduleGraphSize = isBarrelFile + ? count_module_graph_size(resolvedPath.path, resolutionOptions) + : -1 + + cache[moduleSpecifier] = { + isBarrelFile, + moduleGraphSize, + } + + if (moduleGraphSize > maxModuleGraphSizeAllowed) { + context.report({ + node: node.source, + messageId: 'avoidImport', + data: { + amount: moduleGraphSize, + specifier: moduleSpecifier, + maxModuleGraphSizeAllowed, + }, + }) + } + } else { + /** + * It is a bare module specifier, but cached, so we can use the + * cached value + */ + + if ( + cache[moduleSpecifier].moduleGraphSize > maxModuleGraphSizeAllowed + ) { + context.report({ + node: node.source, + messageId: 'avoidImport', + data: { + amount: cache[moduleSpecifier].moduleGraphSize, + specifier: moduleSpecifier, + maxModuleGraphSizeAllowed, + }, + }) + } + } + } else { + /** + * Its not a bare module specifier, but local module, so we need to + * analyze it + */ + const isBarrelFile = is_barrel_file( + fileContent, + amountOfExportsToConsiderModuleAsBarrel, + ) + const moduleGraphSize = isBarrelFile + ? count_module_graph_size(resolvedPath.path, resolutionOptions) + : -1 + if (moduleGraphSize > maxModuleGraphSizeAllowed) { + context.report({ + node: node.source, + messageId: 'avoidImport', + data: { + amount: moduleGraphSize, + specifier: moduleSpecifier, + maxModuleGraphSizeAllowed, + }, + }) + } + } + }, + } + }, +}) diff --git a/src/rules/avoid-namespace-import.ts b/src/rules/avoid-namespace-import.ts new file mode 100644 index 00000000..6fa7626d --- /dev/null +++ b/src/rules/avoid-namespace-import.ts @@ -0,0 +1,63 @@ +import { createRule } from '../utils/index.js' + +export interface Options { + allowList: string[] +} + +export type MessageId = 'avoidNamespace' + +const defaultOptions: Options = { + allowList: [], +} + +export default createRule<[Options?], MessageId>({ + name: 'avoid-namespace-import', + meta: { + type: 'suggestion', + docs: { + description: 'Forbid namespace imports.', + category: 'Performance', + recommended: true, + }, + schema: [ + { + type: 'object', + properties: { + allowList: { + type: 'array', + description: 'List of namespace imports to allow', + default: [], + uniqueItems: true, + items: { + type: 'string', + }, + }, + }, + additionalProperties: false, + }, + ], + messages: { + avoidNamespace: + 'Avoid namespace imports, it leads to unused imports and prevents treeshaking.', + }, + }, + defaultOptions: [defaultOptions], + create: context => { + const options = context.options[0] || defaultOptions + const allowList = options.allowList + + return { + ImportNamespaceSpecifier(node) { + if ( + node.parent.importKind !== 'type' && + !allowList.includes(node.parent.source.value) + ) { + context.report({ + node, + messageId: 'avoidNamespace', + }) + } + }, + } + }, +}) diff --git a/src/rules/avoid-re-export-all.ts b/src/rules/avoid-re-export-all.ts new file mode 100644 index 00000000..a4c28d43 --- /dev/null +++ b/src/rules/avoid-re-export-all.ts @@ -0,0 +1,33 @@ +import { createRule } from '../utils/index.js' + +export type MessageId = 'avoidReExport' + +export default createRule<[], MessageId>({ + name: 'avoid-re-export-all', + meta: { + type: 'suggestion', + docs: { + description: 'Forbid re-exporting * from a module.', + category: 'Performance', + recommended: true, + }, + schema: [], + messages: { + avoidReExport: + 'Avoid re-exporting * from a module, it leads to unused imports and prevents treeshaking.', + }, + }, + defaultOptions: [], + create(context) { + return { + ExportAllDeclaration(node) { + if (node?.exportKind !== 'type') { + context.report({ + node, + messageId: 'avoidReExport', + }) + } + }, + } + }, +}) diff --git a/test/rules/avoid-barrel-files-ts.spec.ts b/test/rules/avoid-barrel-files-ts.spec.ts new file mode 100644 index 00000000..2c043172 --- /dev/null +++ b/test/rules/avoid-barrel-files-ts.spec.ts @@ -0,0 +1,61 @@ +import { RuleTester } from '@typescript-eslint/rule-tester' + +import { createRuleTestCaseFunctions } from '../utils.js' + +import rule from 'eslint-plugin-import-x/rules/avoid-barrel-files' + +const ruleTester = new RuleTester() + +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +ruleTester.run('avoid-barrel-files ts', rule, { + valid: [ + tValid({ + code: ` + type Money = string; + export type { Money }; + `, + }), + tValid({ + code: ` + type Money = { + amount: string; + currency: string; + }; + export type { Money }; + `, + }), + tValid({ + code: ` + interface Money { + amount: string; + currency: string; + }; + type Country = string; + type State = { + name: string; + }; + const newSouthWales = { + name: "New South Wales" + }; + export { newSouthWales } + export type { Money, Country, State }; + `, + }), + ], + + invalid: [ + tInvalid({ + code: ` + import { Country } from 'geo'; + type Money = string; + type State = { + name: string; + }; + interface Person { name: string; age: number; } + export type { Money, Country, Person, State }; + `, + errors: [{ messageId: 'avoidBarrel' }], + }), + ], +}) diff --git a/test/rules/avoid-barrel-files.spec.ts b/test/rules/avoid-barrel-files.spec.ts new file mode 100644 index 00000000..bd52e580 --- /dev/null +++ b/test/rules/avoid-barrel-files.spec.ts @@ -0,0 +1,109 @@ +import { RuleTester } from '@typescript-eslint/rule-tester' + +import { createRuleTestCaseFunctions } from '../utils.js' + +import rule from 'eslint-plugin-import-x/rules/avoid-barrel-files' + +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +const ruleTester = new RuleTester() + +ruleTester.run('avoid-barrel-files', rule, { + valid: [ + tValid({ + code: ` + let foo; + export { foo }; + `, + }), + tValid({ + code: ` + let foo, bar; + export { foo, bar }; + `, + }), + tValid({ + code: ` + let foo, bar, baz; + export { foo, bar, baz }; + `, + }), + tValid({ + code: ` + let foo, bar, baz, qux; + export { foo, bar, baz, qux }; + `, + }), + tValid({ + code: ` + let foo, bar, baz, qux, quux; + export { foo, bar, baz, qux }; + `, + }), + tValid({ + code: ` + export default function Foo() { + return 'bar'; + } + `, + options: [ + { + amountOfExportsToConsiderModuleAsBarrel: 0, + }, + ], + }), + tValid({ + code: ` + export default function bar() {} + `, + options: [ + { + amountOfExportsToConsiderModuleAsBarrel: 0, + }, + ], + }), + tValid({ + code: ` + export default defineFoo({}); + `, + options: [ + { + amountOfExportsToConsiderModuleAsBarrel: 0, + }, + ], + }), + ], + + invalid: [ + tInvalid({ + code: ` + import { bar, baz, qux} from 'foo'; + let foo; + export { foo, bar, baz, qux, }; + `, + errors: [{ messageId: 'avoidBarrel' }], + }), + tInvalid({ + code: ` + export * from 'foo'; + export * from 'bar'; + export * from 'baz'; + export * from 'qux'; + `, + errors: [{ messageId: 'avoidBarrel' }], + }), + tInvalid({ + code: `export { foo, bar, baz } from 'foo';`, + errors: [{ messageId: 'avoidBarrel' }], + options: [ + { + amountOfExportsToConsiderModuleAsBarrel: 2, + }, + ], + }), + tInvalid({ + code: 'export default { var1, var2, var3, var4 };', + errors: [{ messageId: 'avoidBarrel' }], + }), + ], +}) diff --git a/test/rules/avoid-namespace-import.spec.ts b/test/rules/avoid-namespace-import.spec.ts new file mode 100644 index 00000000..4b29b862 --- /dev/null +++ b/test/rules/avoid-namespace-import.spec.ts @@ -0,0 +1,41 @@ +import { RuleTester } from '@typescript-eslint/rule-tester' + +import { createRuleTestCaseFunctions } from '../utils.js' + +import rule from 'eslint-plugin-import-x/rules/avoid-namespace-import' + +const ruleTester = new RuleTester() + +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +ruleTester.run('avoid-namespace-import', rule, { + valid: [ + tValid({ code: 'import { foo } from "foo";' }), + // 'import type { foo } from "foo";', + // 'import type * as foo from "foo";' + tValid({ + code: 'import * as foo from "foo";', + options: [ + { + allowList: ['foo'], + }, + ], + }), + ], + + invalid: [ + tInvalid({ + code: 'import * as foo from "foo";', + errors: [{ messageId: 'avoidNamespace' }], + }), + tInvalid({ + code: 'import * as bar from "bar";', + errors: [{ messageId: 'avoidNamespace' }], + options: [ + { + allowList: ['foo'], + }, + ], + }), + ], +}) diff --git a/test/rules/avoid-re-export-all.spec.ts b/test/rules/avoid-re-export-all.spec.ts new file mode 100644 index 00000000..dfb7ccf6 --- /dev/null +++ b/test/rules/avoid-re-export-all.spec.ts @@ -0,0 +1,29 @@ +import { RuleTester } from '@typescript-eslint/rule-tester' + +import { createRuleTestCaseFunctions } from '../utils.js' + +import rule from 'eslint-plugin-import-x/rules/avoid-re-export-all' + +const ruleTester = new RuleTester() + +const { tInvalid } = createRuleTestCaseFunctions() + +ruleTester.run('avoid-re-export-all', rule, { + valid: [ + // 'export type { foo } from "foo";', + // 'export type * as foo from "foo";', + 'export { foo } from "foo";', + 'export { foo as bar } from "foo";', + ], + + invalid: [ + tInvalid({ + code: 'export * from "foo";', + errors: [{ messageId: 'avoidReExport' }], + }), + tInvalid({ + code: 'export * as foo from "foo";', + errors: [{ messageId: 'avoidReExport' }], + }), + ], +}) diff --git a/yarn.lock b/yarn.lock index 46163320..594fd409 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6163,6 +6163,145 @@ __metadata: languageName: node linkType: hard +"eslint-barrel-file-utils-android-arm-eabi@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-android-arm-eabi@npm:0.0.11" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"eslint-barrel-file-utils-android-arm64@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-android-arm64@npm:0.0.11" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"eslint-barrel-file-utils-darwin-arm64@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-darwin-arm64@npm:0.0.11" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"eslint-barrel-file-utils-darwin-x64@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-darwin-x64@npm:0.0.11" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"eslint-barrel-file-utils-freebsd-x64@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-freebsd-x64@npm:0.0.11" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"eslint-barrel-file-utils-linux-arm-gnueabihf@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-linux-arm-gnueabihf@npm:0.0.11" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"eslint-barrel-file-utils-linux-arm64-gnu@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-linux-arm64-gnu@npm:0.0.11" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"eslint-barrel-file-utils-linux-arm64-musl@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-linux-arm64-musl@npm:0.0.11" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"eslint-barrel-file-utils-linux-x64-gnu@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-linux-x64-gnu@npm:0.0.11" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"eslint-barrel-file-utils-linux-x64-musl@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-linux-x64-musl@npm:0.0.11" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"eslint-barrel-file-utils-win32-arm64-msvc@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-win32-arm64-msvc@npm:0.0.11" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"eslint-barrel-file-utils-win32-ia32-msvc@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-win32-ia32-msvc@npm:0.0.11" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"eslint-barrel-file-utils-win32-x64-msvc@npm:0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils-win32-x64-msvc@npm:0.0.11" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"eslint-barrel-file-utils@npm:^0.0.11": + version: 0.0.11 + resolution: "eslint-barrel-file-utils@npm:0.0.11" + dependencies: + eslint-barrel-file-utils-android-arm-eabi: "npm:0.0.11" + eslint-barrel-file-utils-android-arm64: "npm:0.0.11" + eslint-barrel-file-utils-darwin-arm64: "npm:0.0.11" + eslint-barrel-file-utils-darwin-x64: "npm:0.0.11" + eslint-barrel-file-utils-freebsd-x64: "npm:0.0.11" + eslint-barrel-file-utils-linux-arm-gnueabihf: "npm:0.0.11" + eslint-barrel-file-utils-linux-arm64-gnu: "npm:0.0.11" + eslint-barrel-file-utils-linux-arm64-musl: "npm:0.0.11" + eslint-barrel-file-utils-linux-x64-gnu: "npm:0.0.11" + eslint-barrel-file-utils-linux-x64-musl: "npm:0.0.11" + eslint-barrel-file-utils-win32-arm64-msvc: "npm:0.0.11" + eslint-barrel-file-utils-win32-ia32-msvc: "npm:0.0.11" + eslint-barrel-file-utils-win32-x64-msvc: "npm:0.0.11" + dependenciesMeta: + eslint-barrel-file-utils-android-arm-eabi: + optional: true + eslint-barrel-file-utils-android-arm64: + optional: true + eslint-barrel-file-utils-darwin-arm64: + optional: true + eslint-barrel-file-utils-darwin-x64: + optional: true + eslint-barrel-file-utils-freebsd-x64: + optional: true + eslint-barrel-file-utils-linux-arm-gnueabihf: + optional: true + eslint-barrel-file-utils-linux-arm64-gnu: + optional: true + eslint-barrel-file-utils-linux-arm64-musl: + optional: true + eslint-barrel-file-utils-linux-x64-gnu: + optional: true + eslint-barrel-file-utils-linux-x64-musl: + optional: true + eslint-barrel-file-utils-win32-arm64-msvc: + optional: true + eslint-barrel-file-utils-win32-ia32-msvc: + optional: true + eslint-barrel-file-utils-win32-x64-msvc: + optional: true + checksum: 10c0/e7491b1d87f6975635e1a4e05c70ff0cbec0fe749bd03e8a48f679fd6523871e15b05b2a40445dda7ec2904eaed45185bd4d4261de9e35eca0cf5d2e17473083 + languageName: node + linkType: hard + "eslint-compat-utils@npm:^0.5.1": version: 0.5.1 resolution: "eslint-compat-utils@npm:0.5.1" @@ -6390,6 +6529,7 @@ __metadata: comment-parser: "npm:^1.4.1" debug: "npm:^4.4.0" eslint: "npm:^9.25.1" + eslint-barrel-file-utils: "npm:^0.0.11" eslint-config-prettier: "npm:^10.1.2" eslint-doc-generator: "npm:^2.1.2" eslint-import-resolver-node: "npm:^0.3.9"