main 6ca8d7bf55f6 cached
18 files
27.2 KB
6.6k tokens
6 symbols
1 requests
Download .txt
Repository: thepassle/eslint-plugin-barrel-files
Branch: main
Commit: 6ca8d7bf55f6
Files: 18
Total size: 27.2 KB

Directory structure:
gitextract_57vo26pf/

├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs/
│   └── rules/
│       ├── avoid-barrel-files.md
│       ├── avoid-importing-barrel-files.md
│       ├── avoid-namespace-import.md
│       └── avoid-re-export-all.md
├── index.js
├── lib/
│   └── rules/
│       ├── avoid-barrel-files.js
│       ├── avoid-importing-barrel-files.js
│       ├── avoid-namespace-import.js
│       └── avoid-re-export-all.js
├── package.json
└── tests/
    └── lib/
        └── rules/
            ├── avoid-barrel-files-ts.js
            ├── avoid-barrel-files.js
            ├── avoid-namespace-import.js
            └── avoid-re-export-all.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
## editors
/.idea
/.vscode
*.iml

## system files
.DS_Store

## dependencies
/node_modules*
/npm-debug.log

## testing
/coverage/

## temp folders
/.tmp/

# build
/_site/
/dist/

# generic logs
*.log


================================================
FILE: CHANGELOG.md
================================================
# Change Log

This project follows _Semantic Versioning_ (aka SemVer). Visit http://semver.org/ to learn more about it.

## Release 3.0.0 (2025-02-13)

- Actually export the plugin

## Release 3.0.0 (2025-02-13)

- Migrate to ESM and flatconfig format

## Release 1.0.0 (2024-01-05)


================================================
FILE: LICENSE
================================================
MIT License

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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
# eslint-plugin-barrel-files

Barrel files are files that just re-export a bunch of things from other files. If you're using a bundler, bundlers usually apply treeshaking and dead code elimination algorithms to remove any unused code.

In many environments however, like test runners, browsers, CDN environments or server side JavaScript runtimes, treeshaking does not get applied. This means that lots of modules get loaded unnecessarily, which can cause significant performance slowdowns.

For more information, I recommend reading [Speeding up the JavaScript ecosystem - The barrel file debacle](https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/)

## Supported Rules

- [barrel-files/avoid-barrel-files](./docs/rules/avoid-barrel-files.md)
- [barrel-files/avoid-importing-barrel-files](./docs/rules/avoid-importing-barrel-files.md)
- [barrel-files/avoid-namespace-import](./docs/rules/avoid-namespace-import.md)
- [barrel-files/avoid-re-export-all](./docs/rules/avoid-re-export-all.md)


================================================
FILE: docs/rules/avoid-barrel-files.md
================================================
# Avoid barrel files

## Rule Details

This rule disallows _authoring_ barrel files in your project.

Examples of **incorrect** code for this rule:

```js
export { foo } from 'foo';
export { bar } from 'bar';
export { baz } from 'baz';
```

## Configuration

This rule takes an optional configuration:

```json
{
  "rules": {
    "barrel-files/avoid-barrel-files": [
      2,
      {
        "amountOfExportsToConsiderModuleAsBarrel": 5
      }
    ]
  }
}
```


================================================
FILE: docs/rules/avoid-importing-barrel-files.md
================================================
# Avoid importing barrel files

## Rule Details

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.

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';
```

## Configuration

This rule takes an optional configuration:

```json
{
  "rules": {
    "barrel-files/avoid-importing-barrel-files": [
      2,
      {
        "allowList": ["foo"],
        "maxModuleGraphSizeAllowed": 40,
        "amountOfExportsToConsiderModuleAsBarrel": 5,
        "exportConditions": ["node", "import"],
        "mainFields": ["module", "main", "browser"],
        "extensions": [".js", ".ts", ".json", ".node"],
        "tsconfig": {
          "configFile": "./tsconfig.json",
          "references": []
        }
      }
    ]
  }
}
```

### Path Aliases

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
// .eslintrc.cjs
const path = require('path')

export default {
  // ...
  "rules": {
    "barrel-files/avoid-importing-barrel-files": [
      2,
      {
        alias: {
          // "@/foo/bar.js" => "./src/foo/bar.js"
          "@": [path.resolve(".", "src")]
        }
      }
    ]
  }
}
```


================================================
FILE: docs/rules/avoid-namespace-import.md
================================================
# Avoid namespace import

## Rule Details

This rule aims to...

Examples of **incorrect** code for this rule:

```js
import * as foo from 'foo';
```

## Configuration

This rule takes an optional configuration:

```json
{
  "rules": {
    "barrel-files/avoid-namespace-import": [
      2,
      {
        "allowList": ["foo"]
      }
    ]
  }
}
```


================================================
FILE: docs/rules/avoid-re-export-all.md
================================================
# Avoid re-export all

## Rule Details

This rule aims to...

Examples of **incorrect** code for this rule:

```js
export * from 'foo';
export * as foo from 'foo';
```


================================================
FILE: index.js
================================================
import { join, parse } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { dirname } from "node:path";
import { readFile, readdir } from "node:fs/promises";

const __dirname = dirname(fileURLToPath(import.meta.url));
const pkgJson = JSON.parse(await readFile(join(__dirname, "./package.json")));

const rules = {};
const filenames = await readdir(`${__dirname}/lib/rules`);
for (const filename of filenames) {
  const rule = (
    await import(pathToFileURL(`${__dirname}/lib/rules/${filename}`))
  ).default;

  const ruleName = parse(filename).name;
  rules[ruleName] = rule;
}

const plugin = {
  meta: {
    name: pkgJson.name,
    version: pkgJson.version,
  },
  processors: {},
  rules,
  configs: {},
};

// assign configs here so we can reference `plugin`
Object.assign(plugin.configs, {
  recommended: {
    plugins: {
      'barrel-files': plugin,
    },
    rules: {
      'barrel-files/avoid-importing-barrel-files': 'error',
      'barrel-files/avoid-barrel-files': 'error',
      'barrel-files/avoid-namespace-import': 'error',
      'barrel-files/avoid-re-export-all': 'error',
    },
  },
});

export default plugin;

================================================
FILE: lib/rules/avoid-barrel-files.js
================================================
/**
 * @fileoverview avoid-barrel-files
 * @author Pascal Schilp
 */
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: "avoid-barrel-files",
      category: 'Performance',
      recommended: true,
      url: '../../docs/rules/avoid-barrel-files.md',
    },
    schema: [
      {
        amountOfExportsToConsiderModuleAsBarrel: {
          type: 'number',
          description: 'Minimum amount of exports to consider module as barrelfile',
          default: 3,
        },
      },
    ],
  },
  create: context => ({
    //----------------------------------------------------------------------
    // Public
    //----------------------------------------------------------------------
    Program(node) {
      const options = (context.options && context.options[0]) || {};
      const amountOfExportsToConsiderModuleAsBarrel = options?.amountOfExportsToConsiderModuleAsBarrel ?? 3;

      let declarations = 0;
      let exports = 0;

      node.body.forEach((n) => {
        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,
          message: "Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.",
        });
      }
    },
  }),
};


================================================
FILE: lib/rules/avoid-importing-barrel-files.js
================================================
/**
 * @fileoverview avoid-barrel-files
 * @author Pascal Schilp
 */
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

import { readFileSync } from 'fs';
import path from 'path';
import { builtinModules } from 'module';
import {
  count_module_graph_size,
  is_barrel_file,
} from 'eslint-barrel-file-utils/index.cjs';
import { ResolverFactory } from 'oxc-resolver';

/**
 * @fileoverview Avoid importing barrel files
 * @author Pascal Schilp
 */

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

const cache = {};

// custom error class to emulate oxc_resolver ResolveError enum.
// `errorVariant` can be equal to a `ResolveError` enum variant.
class ResolveError extends Error {
  constructor(errorVariant = null, message = "") {
    super(message);
    this.errorVariant = errorVariant;
    this.message = message;
  }
}

export default {
  meta: {
    type: 'problem',
    fixable: null,
    docs: {
      description:
        'Avoid importing barrel files',
      recommended: true,
      url: '../../docs/rules/avoid-importing-barrel-files.md',
    },
    schema: [
      {
        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,
        },
      },
      {
        debug: {
          type: 'boolean',
          description: 'Enabling debug loggin',
          default: false,
        },
      },
      {
        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'
        }
      }
    ],
  },
  create(context) {
    const options = (context.options && context.options[0]) || {};
    const maxModuleGraphSizeAllowed = options.maxModuleGraphSizeAllowed ?? 20;
    const debug = options.debug ?? false;
    const amountOfExportsToConsiderModuleAsBarrel = options.amountOfExportsToConsiderModuleAsBarrel ?? 3;
    const exportConditions = options.exportConditions ?? ["node", "import"];
    const mainFields = options.mainFields ?? ["module", "browser", "main"];
    const extensions = options.extensions ?? [".js", ".ts", ".tsx", ".jsx", ".json", ".node"];
    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
    });

    /**
     * @param {string} specifier
     * @returns {boolean}
     */
    const isBareModuleSpecifier = specifier => !!specifier?.replace(/'/g, '')[0].match(/[@a-zA-Z]/g);

    /**
     * @param {number} amount
     * @returns {string}
     */
    const message = (amount, specifier) => `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`

    return {
      ImportDeclaration(node) {
        const moduleSpecifier = node.source.value;
        const currentFileName = context.getFilename();

        if (options?.allowList?.includes(moduleSpecifier)) {
          return;
        }

        if (node?.importKind === 'type') {
          return;
        }

        if (builtinModules.includes(moduleSpecifier.replace("node:", ""))) {
          return;
        }

        let resolvedPath;
        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 (e) {
          if (!debug) {
            return
          }

          if (e instanceof ResolveError) {
            switch (e.errorVariant) {
              case "NotFound":
                console.error(`Failed to resolve "${moduleSpecifier}" from "${currentFileName}": \n\n${e.stack}`);
                break
              default:
                console.error(`${e.message}: \n\n${e.stack}`);
            }
          }

          console.error(`${e}: \n\n${e.stack}`);
          return
        }

        const fileContent = readFileSync(resolvedPath.path, 'utf8');
        let isBarrelFile;

        /**
         * 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]) {
            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,
                message: message(moduleGraphSize, moduleSpecifier)
              });
            }
          } 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,
                message: message(cache[moduleSpecifier].moduleGraphSize, moduleSpecifier)
              });
            }
          }
        } 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,
              message: message(moduleGraphSize, moduleSpecifier)
            });
          }
        }
      },
    };
  },
};


================================================
FILE: lib/rules/avoid-namespace-import.js
================================================
/**
 * @fileoverview avoid-namespace-import
 * @author Pascal Schilp
 */
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: "avoid-namespace-import",
      category: 'Performance',
      recommended: true,
      url: '../../docs/rules/avoid-namespace-import.md',
    },
    schema: [
      {
        allowList: {
          type: 'array',
          description: 'List of namespace imports to allow',
          default: [],
          uniqueItems: true,
          items: {
            type: 'string',
          },
        },
      },
    ],
  },
  create: context => {
    const options = (context.options && context.options[0]) || {};
    const allowList = options.allowList ?? [];


    return{
      //----------------------------------------------------------------------
      // Public
      //----------------------------------------------------------------------
      ImportNamespaceSpecifier(node) {
        if (node.parent.importKind !== 'type' && !allowList.includes(node.parent.source.value)) {
          context.report({
            node,
            message: "Avoid namespace imports, it leads to unused imports and prevents treeshaking.",
          });
        }
      },
    }
  },
};


================================================
FILE: lib/rules/avoid-re-export-all.js
================================================
/**
 * @fileoverview avoid-re-export-all
 * @author Pascal Schilp
 */
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: "avoid-re-export-all",
      category: 'Performance',
      recommended: true,
      url: '../../docs/rules/avoid-re-export-all.md',
    },
  },
  create: context => ({
    //----------------------------------------------------------------------
    // Public
    //----------------------------------------------------------------------
    ExportAllDeclaration(node) {
      if (node?.exportKind !== 'type') {
        context.report({
          node,
          message: "Avoid re-exporting * from a module, it leads to unused imports and prevents treeshaking.",
        });
      }
    },
  }),
};


================================================
FILE: package.json
================================================
{
  "name": "eslint-plugin-barrel-files",
  "version": "3.0.1",
  "description": "eslint plugin to avoid common errors with barrel files",
  "main": "index.js",
  "directories": {
    "doc": "docs",
    "lib": "lib",
    "test": "tests"
  },
  "type": "module",
  "scripts": {
    "test": "npm run test:node",
    "test:node": "mocha tests --recursive",
    "test:single": "mocha tests/lib/rules/avoid-barrel-files-ts.js --watch"
  },
  "keywords": [
    "eslint",
    "eslintplugin",
    "eslint-plugin",
    "barrel",
    "barrelfiles"
  ],
  "peerDependencies": {
    "eslint": ">= 5"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/thepassle/eslint-plugin-barrel-files"
  },
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "@typescript-eslint/parser": "^6.20.0",
    "@typescript-eslint/rule-tester": "^7.6.0",
    "mocha": "^10.2.0"
  },
  "bundleDependencies": [],
  "dependencies": {
    "eslint-barrel-file-utils": "^0.0.11",
    "oxc-resolver": "^1.9.3"
  }
}


================================================
FILE: tests/lib/rules/avoid-barrel-files-ts.js
================================================
/**
 * @fileoverview avoid-barrel-files-ts
 * @author Pascal Schilp
 */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

import { RuleTester } from '@typescript-eslint/rule-tester';
import { after } from 'node:test';
import rule from '../../../lib/rules/avoid-barrel-files.js';

RuleTester.afterAll = after;

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
  parserOptions: {
    sourceType: "module",
    ecmaVersion: "latest",
  },
});
ruleTester.run('avoid-barrel-files ts', rule, {
  valid: [
    {
      code: `
        type Money = string;
        export type { Money };
      `,
    },
    {
      code: `
        type Money = {
          amount: string;
          currency: string;
        };
        export type { Money };
      `,
    },
    {
      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: [
    {
      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: [{ message: 'Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.' }],
    }
  ],
});


================================================
FILE: tests/lib/rules/avoid-barrel-files.js
================================================
/**
 * @fileoverview avoid-barrel-files
 * @author Pascal Schilp
 */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

import { RuleTester } from 'eslint';
import rule from '../../../lib/rules/avoid-barrel-files.js';

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
  parserOptions: {
    sourceType: "module",
    ecmaVersion: "latest",
  },
});
ruleTester.run('avoid-barrel-files', rule, {
  valid: [
    {
      code: `
        let foo;
        export { foo };
      `,
    },
    {
      code: `
        let foo, bar;
        export { foo, bar };
      `,
    },
    {
      code: `
        let foo, bar, baz;
        export { foo, bar, baz };
      `,
    },
    {
      code: `
        let foo, bar, baz, qux;
        export { foo, bar, baz, qux };
      `,
    },
    {
      code: `
        let foo, bar, baz, qux, quux;
        export { foo, bar, baz, qux };
      `,
    },
    {
      code: `
        export default function Foo() {
          return 'bar';
        }
      `,
      options: [
        {
          amountOfExportsToConsiderModuleAsBarrel: 0,
        },
      ],
    },
    {
      code: `
        export default function bar() {}
      `,
      options: [
        {
          amountOfExportsToConsiderModuleAsBarrel: 0,
        },
      ],
    },
    {
      code: `
        export default defineFoo({});
      `,
      options: [
        {
          amountOfExportsToConsiderModuleAsBarrel: 0,
        },
      ],
    }
  ],

  invalid: [
    {
      code: `
        import { bar, baz, qux} from 'foo';
        let foo;
        export { foo, bar, baz, qux,  };
      `,
      errors: [{ message: 'Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.' }],
    },
    {
      code: `
        export * from 'foo';
        export * from 'bar';
        export * from 'baz';
        export * from 'qux';
      `,
      errors: [{ message: 'Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.' }],
    },
    {
      code: `export { foo, bar, baz } from 'foo';`,
      errors: [{ message: 'Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.' }],
      options: [
        {
          amountOfExportsToConsiderModuleAsBarrel: 2,
        },
      ],
    },
    {
      code: 'export default { var1, var2, var3, var4 };',
      errors: [{ message: 'Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.' }],
    },
  ],
});


================================================
FILE: tests/lib/rules/avoid-namespace-import.js
================================================
/**
 * @fileoverview avoid-namespace-import
 * @author Pascal Schilp
 */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
import { RuleTester } from 'eslint';
import rule from '../../../lib/rules/avoid-namespace-import.js';

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
  // parser: require.resolve('@typescript-eslint/parser'),
  parserOptions: {
    sourceType: "module",
    ecmaVersion: "latest",
  },
});
ruleTester.run('avoid-namespace-import', rule, {
  valid: [
    'import { foo } from "foo";',
    // 'import type { foo } from "foo";',
    // 'import type * as foo from "foo";'
    {
      code: 'import * as foo from "foo";',
      options: [
        {
          allowList: ['foo'],
        },
      ],
    },
  ],

  invalid: [
    {
      code: 'import * as foo from "foo";',
      errors: [{ message: 'Avoid namespace imports, it leads to unused imports and prevents treeshaking.' }],
    },
    {
      code: 'import * as bar from "bar";',
      errors: [{ message: 'Avoid namespace imports, it leads to unused imports and prevents treeshaking.' }],
      options: [
        {
          allowList: ['foo'],
        },
      ],
    },
  ],
});


================================================
FILE: tests/lib/rules/avoid-re-export-all.js
================================================
/**
 * @fileoverview avoid-re-export-all
 * @author Pascal Schilp
 */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
import { RuleTester } from 'eslint';
import rule from '../../../lib/rules/avoid-re-export-all.js';


//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
  // parser: require.resolve('@typescript-eslint/parser'),
  parserOptions: {
    sourceType: "module",
    ecmaVersion: "latest",
  },
});
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: [
    {
      code: 'export * from "foo";',
      errors: [{ message: 'Avoid re-exporting * from a module, it leads to unused imports and prevents treeshaking.' }],
    },
    {
      code: 'export * as foo from "foo";',
      errors: [{ message: 'Avoid re-exporting * from a module, it leads to unused imports and prevents treeshaking.' }],
    },
  ],
});
Download .txt
gitextract_57vo26pf/

├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs/
│   └── rules/
│       ├── avoid-barrel-files.md
│       ├── avoid-importing-barrel-files.md
│       ├── avoid-namespace-import.md
│       └── avoid-re-export-all.md
├── index.js
├── lib/
│   └── rules/
│       ├── avoid-barrel-files.js
│       ├── avoid-importing-barrel-files.js
│       ├── avoid-namespace-import.js
│       └── avoid-re-export-all.js
├── package.json
└── tests/
    └── lib/
        └── rules/
            ├── avoid-barrel-files-ts.js
            ├── avoid-barrel-files.js
            ├── avoid-namespace-import.js
            └── avoid-re-export-all.js
Download .txt
SYMBOL INDEX (6 symbols across 4 files)

FILE: lib/rules/avoid-barrel-files.js
  method Program (line 32) | Program(node) {

FILE: lib/rules/avoid-importing-barrel-files.js
  class ResolveError (line 31) | class ResolveError extends Error {
    method constructor (line 32) | constructor(errorVariant = null, message = "") {
  method create (line 144) | create(context) {

FILE: lib/rules/avoid-namespace-import.js
  method ImportNamespaceSpecifier (line 41) | ImportNamespaceSpecifier(node) {

FILE: lib/rules/avoid-re-export-all.js
  method ExportAllDeclaration (line 23) | ExportAllDeclaration(node) {
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (30K chars).
[
  {
    "path": ".gitignore",
    "chars": 200,
    "preview": "## editors\n/.idea\n/.vscode\n*.iml\n\n## system files\n.DS_Store\n\n## dependencies\n/node_modules*\n/npm-debug.log\n\n## testing\n/"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 283,
    "preview": "# Change Log\n\nThis project follows _Semantic Versioning_ (aka SemVer). Visit http://semver.org/ to learn more about it.\n"
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2020 modern-webdev\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
  },
  {
    "path": "README.md",
    "chars": 1005,
    "preview": "# eslint-plugin-barrel-files\n\nBarrel files are files that just re-export a bunch of things from other files. If you're u"
  },
  {
    "path": "docs/rules/avoid-barrel-files.md",
    "chars": 461,
    "preview": "# Avoid barrel files\n\n## Rule Details\n\nThis rule disallows _authoring_ barrel files in your project.\n\nExamples of **inco"
  },
  {
    "path": "docs/rules/avoid-importing-barrel-files.md",
    "chars": 1510,
    "preview": "# Avoid importing barrel files\n\n## Rule Details\n\nThis rule aims to avoid importing barrel files that lead to loading lar"
  },
  {
    "path": "docs/rules/avoid-namespace-import.md",
    "chars": 351,
    "preview": "# Avoid namespace import\n\n## Rule Details\n\nThis rule aims to...\n\nExamples of **incorrect** code for this rule:\n\n```js\nim"
  },
  {
    "path": "docs/rules/avoid-re-export-all.md",
    "chars": 168,
    "preview": "# Avoid re-export all\n\n## Rule Details\n\nThis rule aims to...\n\nExamples of **incorrect** code for this rule:\n\n```js\nexpor"
  },
  {
    "path": "index.js",
    "chars": 1165,
    "preview": "import { join, parse } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { dirname } fro"
  },
  {
    "path": "lib/rules/avoid-barrel-files.js",
    "chars": 2502,
    "preview": "/**\n * @fileoverview avoid-barrel-files\n * @author Pascal Schilp\n */\n//-------------------------------------------------"
  },
  {
    "path": "lib/rules/avoid-importing-barrel-files.js",
    "chars": 8394,
    "preview": "/**\n * @fileoverview avoid-barrel-files\n * @author Pascal Schilp\n */\n//-------------------------------------------------"
  },
  {
    "path": "lib/rules/avoid-namespace-import.js",
    "chars": 1412,
    "preview": "/**\n * @fileoverview avoid-namespace-import\n * @author Pascal Schilp\n */\n//---------------------------------------------"
  },
  {
    "path": "lib/rules/avoid-re-export-all.js",
    "chars": 933,
    "preview": "/**\n * @fileoverview avoid-re-export-all\n * @author Pascal Schilp\n */\n//------------------------------------------------"
  },
  {
    "path": "package.json",
    "chars": 1009,
    "preview": "{\n  \"name\": \"eslint-plugin-barrel-files\",\n  \"version\": \"3.0.1\",\n  \"description\": \"eslint plugin to avoid common errors w"
  },
  {
    "path": "tests/lib/rules/avoid-barrel-files-ts.js",
    "chars": 1850,
    "preview": "/**\n * @fileoverview avoid-barrel-files-ts\n * @author Pascal Schilp\n */\n\n//---------------------------------------------"
  },
  {
    "path": "tests/lib/rules/avoid-barrel-files.js",
    "chars": 2848,
    "preview": "/**\n * @fileoverview avoid-barrel-files\n * @author Pascal Schilp\n */\n\n//------------------------------------------------"
  },
  {
    "path": "tests/lib/rules/avoid-namespace-import.js",
    "chars": 1451,
    "preview": "/**\n * @fileoverview avoid-namespace-import\n * @author Pascal Schilp\n */\n\n//--------------------------------------------"
  },
  {
    "path": "tests/lib/rules/avoid-re-export-all.js",
    "chars": 1290,
    "preview": "/**\n * @fileoverview avoid-re-export-all\n * @author Pascal Schilp\n */\n\n//-----------------------------------------------"
  }
]

About this extraction

This page contains the full source code of the thepassle/eslint-plugin-barrel-files GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (27.2 KB), approximately 6.6k tokens, and a symbol index with 6 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!