Files
bookshelf/eslint.config.js
night 5d5f26c8ae Initial commit
Photo-based book cataloger with AI identification.
Room → Cabinet → Shelf → Book hierarchy; FastAPI + SQLite backend;
vanilla JS SPA; OpenAI-compatible plugin system for boundary
detection, text recognition, and archive search.
2026-03-09 14:16:23 +03:00

128 lines
3.3 KiB
JavaScript

/*
* eslint.config.js (ESLint 9 flat config)
*
* Lints static/js/**\/*.js as plain browser scripts (sourceType:'script').
* All cross-file globals are declared here so no-undef works across the
* multi-file global-scope architecture (no ES modules, no bundler).
*
* Load order and ownership of each global is documented in index.html.
*/
import js from '@eslint/js';
import globals from 'globals';
// ── Globals that cross file boundaries ──────────────────────────────────────
// Declared 'writable' if the variable itself is reassigned across files;
// 'readonly' if only the function/value is consumed by other files.
const appGlobals = {
// state.js — mutable state shared across all modules
S: 'writable',
_plugins: 'writable',
_batchState: 'writable',
_batchPollTimer: 'writable',
_bnd: 'writable',
_photoQueue: 'writable',
// helpers.js
esc: 'readonly',
toast: 'readonly',
isDesktop: 'readonly',
// api.js
req: 'readonly',
// canvas-boundary.js
parseBounds: 'readonly',
parseBndPluginResults: 'readonly',
setupDetailCanvas: 'readonly',
drawBnd: 'readonly',
// tree-render.js
walkTree: 'readonly',
removeNode: 'readonly',
findNode: 'readonly',
pluginsByCategory: 'readonly',
pluginsByTarget: 'readonly',
isLoading: 'readonly',
vPluginBtn: 'readonly',
vBatchBtn: 'readonly',
candidateSugRows: 'readonly',
_STATUS_BADGE: 'readonly',
getBookStats: 'readonly',
vAiProgressBar: 'readonly',
vApp: 'readonly',
mainTitle: 'readonly',
mainHeaderBtns: 'readonly',
// detail-render.js
vDetailBody: 'readonly',
// canvas-crop.js
startCropMode: 'readonly',
// editing.js
attachEditables: 'readonly',
initSortables: 'readonly',
// photo.js
collectQueueBooks: 'readonly',
renderPhotoQueue: 'readonly',
triggerPhoto: 'readonly',
// init.js
render: 'readonly',
renderDetail: 'readonly',
startBatchPolling: 'readonly',
loadTree: 'readonly',
// CDN (SortableJS loaded via <script> in index.html)
Sortable: 'readonly',
};
export default [
{
files: ['static/js/**/*.js'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'script', // browser scripts, not ES modules
globals: {
...globals.browser,
...appGlobals,
},
},
rules: {
...js.configs.recommended.rules,
// Catch typos and missing globals
'no-undef': 'error',
// Unused variables: allow leading-underscore convention for intentional ignores
'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
// Require strict equality
eqeqeq: ['error', 'always', { null: 'ignore' }],
// Disallow var; prefer const/let
'no-var': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
// Warn on console usage (intentional debug left-ins)
'no-console': 'warn',
},
},
{
// Test files run in Node.js, not the browser
files: ['tests/js/**/*.js'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: globals.node,
},
rules: {
...js.configs.recommended.rules,
'no-undef': 'error',
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
},
},
];