npm.io
0.1.4 • Published 11h ago

eslint-plugin-ai-context-header

Licence
MIT
Version
0.1.4
Deps
0
Size
61 kB
Vulns
0
Weekly
0

eslint-plugin-ai-context-header

An ESLint plugin that enforces structured file header comments for Vue 2/3 / React / TypeScript / JavaScript projects.

Supported file extensions: .vue, .ts, .js, .tsx, .jsx.

By requiring standardized annotation fields at the top of every file, it enables AI tools and developers to instantly understand a file's role, platform constraints, state dependencies, async boundaries, and side effects — without reading the implementation.


Motivation

In AI-assisted development, context windows are limited. IDEs and AI tools prioritize the beginning of each file. Structured file header comments deliver the most critical constraints at the lowest cost, effectively replacing design documents that tend to go stale.


Installation

npm install --save-dev eslint-plugin-ai-context-header

Quick Setup

ESLint 9 defaults to flat config; ESLint 10 removed .eslintrc entirely. Use the built-in flat/* presets:

// eslint.config.js
import aiContextHeader from 'eslint-plugin-ai-context-header';

export default [
  aiContextHeader.configs['flat/recommended-vue'],
  // or: aiContextHeader.configs['flat/recommended-react']
  // or: aiContextHeader.configs['flat/recommended-universal']
];

Manual flat config:

import aiContextHeader from 'eslint-plugin-ai-context-header';

export default [
  {
    plugins: { 'ai-context-header': aiContextHeader },
    rules: {
      'ai-context-header/require-file-header': ['warn', { framework: 'vue' }],
      'ai-context-header/no-deprecated-import': 'warn',
    },
  },
];

Vue .vue files require vue-eslint-parser in your ESLint config:

import aiContextHeader from 'eslint-plugin-ai-context-header';
import vueParser from 'vue-eslint-parser';

export default [
  {
    files: ['**/*.vue'],
    languageOptions: {
      parser: vueParser,
      parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
    },
    ...aiContextHeader.configs['flat/recommended-vue'],
  },
  {
    files: ['**/*.{js,ts,tsx,jsx}'],
    ...aiContextHeader.configs['flat/recommended-vue'],
  },
];

Compatibility: ESLint ^7 || ^8 || ^9 || ^10. ESLint 10 requires Node.js ^20.19.0 || ^22.13.0 || >=24 (the plugin itself runs on Node 14+ with older ESLint versions).

Vue Project — ESLint 7 / 8 only (.eslintrc.cjs)
module.exports = {
  plugins: ['ai-context-header'],

  // Option A: use a preset (warn level, good for gradual adoption)
  extends: ['plugin:ai-context-header/recommended-vue'],

  // Option B: manual config
  rules: {
    'ai-context-header/require-file-header': ['warn', { framework: 'vue' }],
  },
};
React Project — ESLint 7 / 8 only (.eslintrc.cjs)
module.exports = {
  plugins: ['ai-context-header'],

  extends: ['plugin:ai-context-header/recommended-react'],
};
Monorepo (Vue + React) — ESLint 7 / 8 only
module.exports = {
  plugins: ['ai-context-header'],

  extends: ['plugin:ai-context-header/recommended-universal'],
};
Available Presets

All presets include both require-file-header and no-deprecated-import rules.

Legacy presets (.eslintrc, ESLint 7–8; ESLint 9 with @eslint/eslintrc FlatCompat):

Preset Framework Level Description
plugin:ai-context-header/recommended Vue warn Backward-compatible alias for recommended-vue
plugin:ai-context-header/strict Vue error Backward-compatible alias for strict-vue
plugin:ai-context-header/recommended-vue Vue warn Explicit Vue preset
plugin:ai-context-header/strict-vue Vue error Explicit Vue preset
plugin:ai-context-header/recommended-react React warn React preset
plugin:ai-context-header/strict-react React error React preset
plugin:ai-context-header/recommended-universal Vue + React warn Monorepo preset
plugin:ai-context-header/strict-universal Vue + React error Monorepo preset

Flat presets (eslint.config.js, ESLint 9–10):

Config key Framework Level
configs['flat/recommended'] Vue warn
configs['flat/strict'] Vue error
configs['flat/recommended-vue'] Vue warn
configs['flat/strict-vue'] Vue error
configs['flat/recommended-react'] React warn
configs['flat/strict-react'] React error
configs['flat/recommended-universal'] Vue + React warn
configs['flat/strict-universal'] Vue + React error

Field Reference

@type (required)

Declares the file's responsibility type. This is the most important field — it tells AI tools and developers the design intent and which rules apply.

Value Framework Description
page Vue / React Route-level page component; responsible for data orchestration and page layout
component Vue / React Reusable UI component; not directly responsible for routing or data fetching
composable Vue Logic reuse unit built with Vue Composition API
hook React React custom Hook encapsulating reusable state and side effect logic
api All Request wrapper functions for backend communication
util All Pure utility functions with no side effects
store All Global state management module (Pinia / Vuex / Zustand / Redux, etc.)

@platform (required for page / component)

Declares the target platform. Helps AI determine available APIs, styling approaches, and compatibility constraints.

Value Description
wap Mobile H5
pc Desktop Web
miniapp Mini Program (WeChat / Alipay, etc.)
all Cross-platform compatible
web General Web (common in React projects)
mobile Mobile (React Native, etc.)
<!-- Single platform -->
<!--
  @type: page
  @platform: wap
-->

<!-- Multiple platforms: use a comma, not | (| in field values triggers unfilledField) -->
<!--
  @type: component
  @platform: wap, pc
-->

@scope (required for component)

Declares the component's usage scope. Helps AI assess the impact radius before refactoring.

Value Description
internal Used only within the current module; safe to refactor freely
shared Shared across modules; consumers should be notified of changes
global Used across the entire project; changes require extra caution

@route (required for page)

Declares the page's route path. Helps AI understand the page's position in the application and assists with routing analysis and navigation logic generation.

// Static route
@route: /user/profile

// Dynamic route
@route: /article/:id

// Nested route
@route: /dashboard/settings/account

@state (required for page / component / composable / hook / store)

Declares the external state sources the file depends on. Helps AI build a dependency graph to quickly locate affected files when refactoring a store or state logic.

Example Value Description
props-only No external state; all data comes from props (ideal for components)
none No state dependency (suitable for pure utility functions)
userStore Depends on a state module named userStore
userStore, settingsStore Depends on multiple state modules, comma-separated

@async (required for page / composable / hook / api)

Declares whether the file contains async operations and lists key async function names. Helps AI determine the scope of error handling, loading states, and test mocks.

Example Value Description
true Contains async operations (without listing specifics)
false No async operations
getUserInfo, updateProfile Lists key async function names

@sideEffect (required for page / component / composable / hook / api)

Declares the side effects produced by the file. This is the most valuable field for AI — side effects are hard to infer from code structure alone, and a single line lets AI avoid accidentally breaking them during refactoring.

Example Value Description
none No side effects (explicitly stating this is encouraged)
localStorage Reads or writes local storage
cookie Reads or writes cookies
analytics Triggers analytics / tracking events
localStorage, analytics Multiple side effects, comma-separated
modifies global CSS variables Free-form description of the side effect

@description (required for all types)

A brief description of the file's functionality. Especially important when the file name alone does not fully convey the intent.

// Good: reveals non-obvious implementation detail
@description: Manages paginated list scroll-loading with debounce and reset support

// Redundant: just repeats the file name, adds no value
@description: User list component

@deprecated (optional)

Marks the file as deprecated and describes the replacement. The plugin will automatically warn in two places:

  1. When editing the deprecated file itself (warning at the header comment)
  2. When another file imports the deprecated file (warning at the import statement)
/*
 * @type: util
 * @description: Legacy date formatter
 * @deprecated: Use ./dateFormat instead — supports i18n
 */

Comment Templates

Templates use /* (single-star block comment) for .ts / .js / .tsx / .jsx files, and <!-- --> for .vue files. This avoids conflicts with JSDoc plugins while still being parsed by the rule.

When --fix inserts a new header, every field includes an inline hint in [...] describing what value to write. Fill in the actual value and remove the brackets. This makes it easy for both humans and AI tools to complete the header correctly.

Vue: page type (.vue)
<!--
  @type: page
  @description: [one-line summary of this page's purpose]
  @platform: [wap | pc | miniapp | all — target runtime platform]
  @route: [route path, e.g. /user/profile or /article/:id]
  @state: [store names this page reads/writes, e.g. userStore — or "none"]
  @async: [key async function names — or true/false]
  @sideEffect: [side effects: localStorage | cookie | analytics | none]
-->
Vue: component type (.vue)
<!--
  @type: component
  @description: [one-line summary of this component's purpose]
  @platform: [wap | pc | miniapp | all — target runtime platform]
  @scope: [internal — local only | shared — cross-module | global — project-wide]
  @state: [store names this component depends on — or "props-only" / "none"]
  @sideEffect: [side effects: localStorage | cookie | analytics | none]
-->
Vue: composable type (.ts)
/*
 * @type: composable
 * @description: [one-line summary of what this composable encapsulates]
 * @state: [store names this composable reads/writes — or "none"]
 * @async: [key async function names — or true/false]
 * @sideEffect: [side effects: localStorage | cookie | analytics | none]
 */
React: page type (.tsx)
/*
 * @type: page
 * @description: [one-line summary of this page's purpose]
 * @platform: [web | mobile | all — target runtime platform]
 * @route: [route path, e.g. /user/profile or /article/:id]
 * @state: [store/hook names this page depends on — or "none"]
 * @async: [key async function names — or true/false]
 * @sideEffect: [side effects: localStorage | cookie | analytics | none]
 */
React: component type (.tsx)
/*
 * @type: component
 * @description: [one-line summary of this component's purpose]
 * @platform: [web | mobile | all — target runtime platform]
 * @scope: [internal — local only | shared — cross-module | global — project-wide]
 * @state: [store/hook names this component depends on — or "props-only" / "none"]
 * @sideEffect: [side effects: localStorage | cookie | analytics | none]
 */
React: hook type (.ts)
/*
 * @type: hook
 * @description: [one-line summary of what this hook encapsulates]
 * @state: [store names this hook reads/writes — or "none"]
 * @async: [key async function names — or true/false]
 * @sideEffect: [side effects: localStorage | cookie | analytics | none]
 */
General: api type (.ts)
/*
 * @type: api
 * @description: [one-line summary of what API endpoints this file wraps]
 * @async: true
 * @sideEffect: [side effects: cache | analytics | none]
 */
General: util type (.ts)
/*
 * @type: util
 * @description: [one-line summary of the utility functions in this file]
 */
General: store type (.ts)
/*
 * @type: store
 * @description: [one-line summary of what state this module manages]
 * @state: [brief description of the core state shape or key properties]
 */

Default Required Fields

Vue (framework: 'vue', default)
type Required fields
page @description, @platform, @route, @state, @async, @sideEffect
component @description, @platform, @scope, @state, @sideEffect
composable @description, @state, @async, @sideEffect
api @description, @async, @sideEffect
util @description
store @description, @state
React (framework: 'react')
type Required fields
page @description, @platform, @route, @state, @async, @sideEffect
component @description, @platform, @scope, @state, @sideEffect
hook @description, @state, @async, @sideEffect
api @description, @async, @sideEffect
util @description
store @description, @state

composable is not a valid type in React mode; hook is not a valid type in Vue mode. Universal mode (framework: 'universal') supports all types from both frameworks.


Rule Behavior Summary

require-file-header reports these message types:

Message When
missingHeader No header comment block at the top of the file
missingType Header exists but @type is missing or still a placeholder (e.g. page | component)
invalidType @type value is not in validTypes
missingField A required field for the declared @type is absent
unfilledField A required field exists but its value still contains | (template placeholder not replaced)
deprecatedFile File header contains @deprecated (warning only; field checks continue)

Note: \| in field values is treated as an unfilled placeholder. Use commas for multiple items (e.g. @platform: wap, pc), not pipe separators. Pipe characters only appear in --fix template hints like [wap \| pc \| all].


Deprecated File Detection

The plugin provides two complementary deprecation rules, enabled by default in all presets.

File-level: warning when editing a deprecated file
/*
 * @type: util
 * @description: Legacy date formatter
 * @deprecated: Use ./dateFormat instead
 */
export function formatDate() {}
// ↑ ESLint warning: This file is marked as deprecated: Use ./dateFormat instead
Import-level: warning when importing a deprecated file
import { formatDate } from './oldFormat';
//                         ^^^^^^^^^^^
// ESLint warning: The imported file "./oldFormat" is deprecated: Use ./dateFormat instead

no-deprecated-import only handles relative imports starting with ./ or ../. Node modules and path aliases (e.g. @/) are automatically ignored.

Control the rule level independently:

rules: {
  'ai-context-header/no-deprecated-import': 'error',
}

Advanced Configuration

Full Option Reference
rules: {
  'ai-context-header/require-file-header': ['warn', {
    // ① Target framework — controls which default types and templates are loaded.
    //    'vue' (default) | 'react' | 'universal'
    //    Omit this field entirely when using a fully custom setup (see below).
    framework: 'vue',

    // ② Per-type required fields (shallow-merged with framework defaults).
    //    key   = the @type value written in the file header
    //    value = list of fields that MUST be present when @type matches the key
    //    An empty array [] means "only @type is required; no other fields enforced".
    typeFieldMap: {
      service: ['@description', '@async', '@sideEffect'],
    },

    // ③ Per-type --fix templates (shallow-merged with framework defaults).
    //    Used when the file has no header at all and ESLint --fix is run.
    //    Embed inline hints in [...] so AI can fill values without extra context.
    templates: {
      service: `/*\n * @type: service\n * @description: [one-line summary]\n * @async: [true/false]\n * @sideEffect: [none | ...]\n */\n`,
    },

    // ④ Allowed @type values. Defaults to all keys in typeFieldMap.
    //    Writing a @type value not in this list triggers an `invalidType` error.
    //    Set to [] to allow any @type value (disables type validation entirely).
    validTypes: ['page', 'component', 'service'],

    // ⑤ When true, files with no @type field are silently skipped.
    //    Useful for gradual adoption on legacy codebases.
    ignoreIfNoType: false,

    // ⑥ Hint text shown in ESLint error messages for each missing field.
    //    Built-in hints exist for standard fields (@description, @state, etc.).
    //    Add entries here for any custom fields in your typeFieldMap.
    //    Merged with built-in hints; your values take priority.
    fieldHints: {
      '@method': 'HTTP method: GET | POST | PUT | DELETE | PATCH.',
      '@path':   'Route path this handler is mounted at, e.g. /api/users/:id.',
      '@env':    'Required environment variables, e.g. DB_URL, REDIS_HOST.',
    },
  }]
}

How typeFieldMap Works

Important: typeFieldMap keys are validators, not file classifiers.

The plugin does not automatically infer which type a file belongs to by reading its filename or directory path. Instead:

  1. The developer writes @type: router in the file header manually (or via --fix).
  2. The plugin reads the @type value, looks up typeFieldMap['router'], and checks that all listed fields are present.
File has @type: router  →  plugin checks @description, @method, @path are present
File has @type: service →  plugin checks @description, @async, @sideEffect are present
File has @type: unknown →  plugin reports invalidType error (not in validTypes)

The typeFieldMap answers: "given that a file declared itself as X, what fields must it have?"

It does not answer: "which files are X?"


Pattern 1 — Extend the Built-in Types

Add custom types on top of a framework preset. Useful when most of your files fit the built-in types, but you have a few special categories.

// eslint.config.js
export default [
  {
    plugins: { 'ai-context-header': aiContextHeader },
    rules: {
      'ai-context-header/require-file-header': ['warn', {
        framework: 'vue',
        // Merged with Vue defaults; adds 'service' without removing 'page', 'composable', etc.
        typeFieldMap: {
          service: ['@description', '@async', '@sideEffect'],
        },
        templates: {
          service: `/*\n * @type: service\n * @description: [one-line summary]\n * @async: [true/false]\n * @sideEffect: [none | cache | analytics]\n */\n`,
        },
      }],
    },
  },
];

Use ESLint's files glob patterns to apply different rule configurations to different directories. This is the correct way to make --fix automatically insert the right template for each directory.

// eslint.config.js
export default [
  // Route handlers: src/router/**
  {
    files: ['src/router/**/*.js'],
    plugins: { 'ai-context-header': aiContextHeader },
    rules: {
      'ai-context-header/require-file-header': ['warn', {
        validTypes: ['router'],
        typeFieldMap: {
          router: ['@description', '@method', '@path'],
        },
        templates: {
          router: `/*\n * @type: router\n * @description: [one-line summary]\n * @method: [GET | POST | PUT | DELETE]\n * @path: [route path, e.g. /api/users/:id]\n */\n`,
        },
      }],
    },
  },

  // Middleware: src/middleware/**
  {
    files: ['src/middleware/**/*.js'],
    plugins: { 'ai-context-header': aiContextHeader },
    rules: {
      'ai-context-header/require-file-header': ['warn', {
        validTypes: ['middleware'],
        typeFieldMap: {
          middleware: ['@description', '@sideEffect'],
        },
        templates: {
          middleware: `/*\n * @type: middleware\n * @description: [one-line summary]\n * @sideEffect: [none | localStorage | analytics]\n */\n`,
        },
      }],
    },
  },

  // Scripts: scripts/**
  {
    files: ['scripts/**/*.js'],
    plugins: { 'ai-context-header': aiContextHeader },
    rules: {
      'ai-context-header/require-file-header': ['warn', {
        validTypes: ['script'],
        typeFieldMap: {
          script: ['@description'],
        },
        templates: {
          script: `/*\n * @type: script\n * @description: [one-line summary of what this script does]\n */\n`,
        },
      }],
    },
  },
];

With this setup:

  • Running --fix on src/router/userRouter.js inserts the router template automatically.
  • Writing @type: middleware in a router file triggers an invalidType error.
  • Each directory has its own enforced contract.

Pattern 3 — Fully Custom Type Taxonomy

Replace the built-in type list by providing a complete typeFieldMap and matching validTypes. Custom entries are shallow-merged with framework defaults — to use only your own types, set validTypes explicitly and override every type you do not want:

rules: {
  'ai-context-header/require-file-header': ['warn', {
    // framework defaults to 'vue' if omitted; pass only the types you need
    validTypes: ['controller', 'service', 'repository', 'model', 'config'],
    typeFieldMap: {
      controller: ['@description', '@async', '@sideEffect'],
      service:    ['@description', '@async'],
      repository: ['@description'],
      model:      ['@description'],
      config:     ['@description'],
    },
  }],
}

Built-in types like page / composable remain in the merged map unless you restrict validTypes. Use validTypes as the whitelist to enforce only your taxonomy.


Pattern 4 — Only Enforce Header Existence (No Field Validation)

Use this when you just want every file to have a comment block, without enforcing specific fields.

rules: {
  'ai-context-header/require-file-header': ['warn', {
    typeFieldMap: {},    // No field requirements for any type
    validTypes: [],      // Any @type value is accepted
    ignoreIfNoType: true, // Files without @type are silently skipped
  }],
}

Pattern 5 — Partial Strictness (Some Types Strict, Others Loose)
rules: {
  'ai-context-header/require-file-header': ['warn', {
    typeFieldMap: {
      page:    ['@description', '@route', '@state'],  // strict
      service: ['@description'],                      // minimal
      legacy:  [],                                    // @type presence only; no field check
    },
  }],
}

Pattern 6 — Gradual Adoption on a Legacy Codebase
// Phase 1: warn only; skip files with no @type entirely
rules: {
  'ai-context-header/require-file-header': ['warn', {
    framework: 'vue',
    ignoreIfNoType: true,
  }],
}

// Phase 2: add a pre-commit hook to enforce headers on new/modified files only
// (use lint-staged to run ESLint --fix on staged files)

// Phase 3: once all files are annotated, remove ignoreIfNoType and switch to 'error'
rules: {
  'ai-context-header/require-file-header': ['error', {
    framework: 'vue',
  }],
}

AI-Assisted Workflow

The plugin is designed to work seamlessly with AI coding assistants. The recommended workflow for completing file headers is:

Step 1 — Run --fix to insert the template
eslint src/router/userRouter.js --fix

The plugin inserts a template with inline hints for every field:

/*
 * @type: router
 * @description: [one-line summary of what this router handles]
 * @method: [HTTP method: GET | POST | PUT | DELETE | PATCH]
 * @path: [route path, e.g. /api/users/:id]
 */
Step 2 — Ask AI to fill in the values

Give the file (with the template) to your AI tool and say:

"Fill in the file header. Replace each [...] placeholder with the actual value based on the code below."

The AI reads the inline hints directly from the file — no extra documentation needed, minimal token cost.

Step 3 — Verify with ESLint
eslint src/router/userRouter.js

If any field still contains [...] or | (unfilled placeholder), the plugin reports an unfilledField error.


Built-in Field Hints (shown in ESLint error messages)

When a required field is missing, the error message includes a hint explaining what to write:

[@type: composable] Missing required field: @state. Store/hook names this file depends on (e.g. userStore), or "none" if stateless.
Field Built-in hint
@description Write a one-line summary of what this file does.
@platform Target runtime: wap | pc | miniapp | all (Vue) or web | mobile | all (React).
@scope Usage range: internal (this module only) | shared (cross-module) | global (project-wide).
@route Route path this page is mounted at, e.g. /user/profile or /article/:id.
@state Store/hook names this file depends on (e.g. userStore), or "none" if stateless.
@async List key async function names, or just true/false.
@sideEffect Describe side effects: localStorage | cookie | analytics | none.

For custom fields, add hints via the fieldHints option:

'ai-context-header/require-file-header': ['warn', {
  typeFieldMap: {
    router: ['@description', '@method', '@path'],
  },
  fieldHints: {
    '@method': 'HTTP method: GET | POST | PUT | DELETE | PATCH.',
    '@path':   'Route path this handler is mounted at, e.g. /api/users/:id.',
  },
}]

Error message for custom fields with hints:

[@type: router] Missing required field: @method. HTTP method: GET | POST | PUT | DELETE | PATCH.

Gradual Adoption Strategy

  1. Phase 1warn level + ignoreIfNoType: true: only report files that have a comment block but are missing required fields
  2. Phase 2 — Enforce on new files via a pre-commit hook (e.g. lint-staged with eslint --fix)
  3. Phase 3 — After backfilling legacy files, remove ignoreIfNoType and switch to error level

See Pattern 6 in Advanced Configuration for example configs per phase.

Keywords