@osmeridian/lint-typecheck-format-test
@osmeridian/lint-typecheck-format-test
Shared lint, format, typecheck defaults, and Vitest factory for Omniswift Vue/Nuxt apps.
Rule changes belong in this package only. Consuming apps keep thin re-exports and allowlisted overlays — do not copy or edit shared rule tables locally.
Copy-paste stubs: examples/nuxt-consumer/.
Engines: Node >=22.18.0 <25, pnpm >=10.
Install
pnpm add -D @osmeridian/lint-typecheck-format-test oxc-nuxt eslint@10.8.1 oxlint-tsgolint@7.0.2001
Or pin an exact version:
{
"devDependencies": {
"@osmeridian/lint-typecheck-format-test": "1.1.2",
"eslint": "10.8.1",
"oxc-nuxt": "^0.1.5",
"oxlint-tsgolint": "7.0.2001"
}
}
| Peer | Required? | Role |
|---|---|---|
eslint |
Yes | Direct install so the editor ESLint extension can resolve the binary and show diagnostics |
oxlint-tsgolint |
Yes | Direct install so Oxlint --type-aware / Nuxt builds can resolve tsgolint from the app |
oxc-nuxt |
Yes (Nuxt) | Registers Nuxt auto-import globals for Oxlint via .nuxt/oxlint.mjs / withNuxt |
vitest |
Optional | Needed if you use /vitest |
@vitejs/plugin-vue |
Optional | Used by the Vitest factory |
happy-dom |
Optional | Vitest environment: "happy-dom" |
Install the exact eslint and oxlint-tsgolint versions listed here — not ^ / >= / “whatever is current.”
ltft runs the copies bundled in this package. The editor and Oxlint type-aware lookup use the copies in the app. Those two paths must be the same version, or IDE squiggles and CI will disagree. oxlint and oxlint-tsgolint are also paired; a newer tsgolint with an older oxlint (or the reverse) breaks --type-aware. Shared rule tables are tested against these pins — bump them in this package, then bump the app to match.
Oxlint, Prettier, and related plugins still ship with this package for ltft lint / ltft format.
Apply in a Nuxt app
1. Register oxc-nuxt
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["oxc-nuxt" /* ... */],
});
This package does not bundle or register that module.
2. Prepare Nuxt
nuxt prepare
This generates .nuxt/oxlint.mjs (withNuxt) so Oxlint knows Nuxt auto-import globals.
3. Add thin config files
ESLint — re-export the shared flat config:
// eslint.config.js
export { default } from "@osmeridian/lint-typecheck-format-test/eslint";
Oxlint — wrap shared rules with withNuxt:
// oxlint.config.ts
import { withNuxt } from "./.nuxt/oxlint.mjs";
import { baseOxlint } from "@osmeridian/lint-typecheck-format-test/oxlint";
export default withNuxt({
...baseOxlint,
overrides: [...(baseOxlint.overrides ?? []), /* app-only overrides */],
});
Prettier — spread shared options; set app Tailwind paths:
// .prettierrc.mjs
import base from "@osmeridian/lint-typecheck-format-test/prettier";
export default {
...base,
tailwindStylesheet: "./app/assets/styles/tailwind.css",
tailwindFunctions: ["cn", "clsx"],
};
The Tailwind plugin path is resolved inside this package — no consumer createRequire needed.
Typecheck defaults (optional):
import { nuxtTypescriptDefaults } from "@osmeridian/lint-typecheck-format-test/typescript/nuxt";
export default defineNuxtConfig({
typescript: {
tsConfig: {
...nuxtTypescriptDefaults,
// app paths / excludes
},
},
});
Vitest (optional):
import { fileURLToPath } from "node:url";
import { createVitestConfig } from "@osmeridian/lint-typecheck-format-test/vitest";
const rootDir = fileURLToPath(new URL(".", import.meta.url));
export default createVitestConfig({
rootDir,
aliases: [
/* app-specific fixture aliases */
],
});
4. Add scripts
{
"scripts": {
"lint": "ltft lint",
"lint:fix": "ltft lint:fix",
"format": "ltft format",
"format:fix": "ltft format:fix",
"typecheck": "nuxt typecheck",
"test": "vitest run"
}
}
CLI
ltft lint # oxlint then eslint; fails on warnings
ltft lint:fix # same with --fix
ltft format # prettier --check
ltft format:fix # prettier --write
ltft format:check # alias for format
ltft help
typecheck and test stay as app scripts (nuxt typecheck, vitest run) — they are not part of ltft.
This package does not ship lint-staged or husky. Wire pre-commit hooks in the app if you want them.
How ltft lint works
ltft lint always runs both tools, in order. Either step exits non-zero on failure; warnings are treated as failures.
| Step | Tool | Scope | Failure policy |
|---|---|---|---|
| 1 | Oxlint | Entire project (.) |
--type-aware --deny-warnings |
| 2 | ESLint | app/**/*.vue, **/utils/**/*.{ts,js,mts,cts,mjs,cjs}, and hexagonal feature/component TS layers |
--max-warnings=0 |
What Oxlint and ESLint do
They are complementary, not alternatives.
| Tool | Scope | Role |
|---|---|---|
| Oxlint | Whole project | Fast primary linter: categories, plugins, and type-aware rules (oxlint-tsgolint). Nuxt globals come from oxc-nuxt / withNuxt. |
| ESLint | Vue SFCs + utils paths + hexagonal layers | Gaps Oxlint does not cover well: custom Vue attribute order, unused vars across script + template in .vue, utils -utils filename convention, and hexagonal no-restricted-imports. |
Rules and config reference
Oxlint (baseOxlint)
Source: src/oxlint/base.js.
Categories
| Category | Level |
|---|---|
correctness |
error |
suspicious |
error |
perf |
warn |
style |
warn |
pedantic |
off |
restriction |
off |
Enabling a category turns on many plugin defaults beyond the explicit rules table below. Those explicit entries are overrides and additions.
Options: denyWarnings: true, typeAware: true.
Plugins: eslint, import, node, oxc, promise, typescript, unicorn, vue (plus JSDoc via a path override).
Explicit top-level rules
| Rule | Config |
|---|---|
eslint/curly |
off |
eslint/eqeqeq |
error, always, { null: "ignore" } |
eslint/func-style |
error, "expression" |
eslint/no-alert |
error |
eslint/no-console |
warn |
eslint/no-debugger |
error |
eslint/no-duplicate-imports |
error |
eslint/no-magic-numbers |
warn |
eslint/no-var |
error |
eslint/no-ternary |
off |
eslint/one-var |
off |
eslint/capitalized-comments |
off |
eslint/prefer-const |
error |
eslint/sort-keys |
off |
import/first |
error |
import/group-exports |
off |
import/no-named-export |
warn |
import/no-cycle |
warn |
import/no-duplicates |
error |
import/no-self-import |
error |
import/no-unassigned-import |
off |
import/consistent-type-specifier-style |
off |
promise/always-return |
off |
promise/no-callback-in-promise |
error |
promise/no-return-wrap |
error |
promise/prefer-await-to-then |
off |
typescript/await-thenable |
error |
typescript/consistent-type-imports |
error; separate-type-imports, prefer type-imports |
typescript/no-explicit-any |
error |
typescript/no-floating-promises |
error |
typescript/no-misused-promises |
error |
typescript/no-unnecessary-type-assertion |
error |
typescript/prefer-nullish-coalescing |
warn |
typescript/prefer-optional-chain |
warn |
unicorn/no-array-for-each |
off |
unicorn/no-null |
off |
unicorn/prefer-node-protocol |
error |
unicorn/prefer-string-slice |
error |
vue/no-export-in-script-setup |
error |
vue/return-in-computed-property |
error |
vue/valid-define-emits |
error |
vue/valid-define-props |
error |
Path overrides
| Files | Effect |
|---|---|
**/*.{spec,test}.{ts,js}, **/__tests__/**, tests/**/*.ts |
Relaxes many eslint/import/typescript/unicorn/vue rules for tests |
app/core/ports/**/*.ts |
import/no-named-export off |
app/stores/**/*.ts |
eslint/max-params off |
app/features/**/application/**/*.ts, app/core/application/**/*.ts |
max-statements / prefer-nullish-coalescing / consistent-function-scoping off |
app/types/**/*.d.ts |
init-declarations, no-var, vars-on-top, no-named-export, require-module-specifiers off |
nuxt.config.ts, server/**/*.ts, vitest.config.ts |
Node env; import/no-nodejs-modules off |
app/utils/magic-numbers-utils.ts |
eslint/no-magic-numbers off |
app/utils/promise-helpers-utils.ts |
promise/avoid-new off |
| Package / local eslint-rules JS | no-continue, no-magic-numbers off |
app/composables/**/*.{ts,js}, app/components/**/*.vue, app/pages/**/*.vue |
JSDoc required (require-jsdoc, require-description) |
ESLint
Source: src/eslint/index.js.
Built on eslint-plugin-vue flat/base plus a local plugin.
Utils files — **/utils/**/*.{ts,js,mts,cts,mjs,cjs} (ignores *.{spec,test}.* and __tests__):
| Rule | Severity |
|---|---|
local/utils-filename-suffix |
error |
Filenames under a utils/ directory must end with -utils (e.g. date-utils.ts). Names of 2 characters or fewer (e.g. cn.ts) are exempt.
Vue SFCs — app/**/*.vue:
| Rule | Severity / notes |
|---|---|
vue/attributes-order |
off (conflicts with local autofix) |
local/static-before-dynamic-attrs |
error; autofixable |
@typescript-eslint/no-unused-vars |
warn; ignore ^_ for args/vars/caught/destructured |
vue/no-unused-vars |
warn; ignorePattern: "^_" |
Hexagonal import bans — no-restricted-imports (source: src/eslint/hexagonal.js):
| Files | Must not import |
|---|---|
app/features/*/application/**/*.ts |
useCustomFetch, endpoint-utils, infrastructure, adapters, composition |
app/features/*/ui/**/*.vue |
useCustomFetch, endpoint-utils, infrastructure, adapters, application |
app/features/*/composition/**/*.ts |
useCustomFetch, endpoint-utils, infrastructure |
app/features/*/adapters/**/*.ts |
useCustomFetch, application, UI, composition |
app/components/**/*.{vue,ts} |
useCustomFetch, endpoint-utils |
app/features/*/domain/**/*.ts |
useCustomFetch, endpoint-utils, infrastructure, adapters, application, composition, UI, vue, nuxt/app, #app, @tanstack/vue-query |
Optional named export: @osmeridian/lint-typecheck-format-test/eslint/hexagonal (already included in the default /eslint config).
Attribute order (local/static-before-dynamic-attrs):
- Definition (
is) - List rendering (
v-for) - Conditionals (
v-if/v-else-if/v-else/v-show/v-cloak) - Render modifiers (
v-pre/v-once) - Global (
id) - Unique / slot (
key,ref,slot) - Two-way binding (
v-model) - Other directives
- Static attributes (
class,role, …) - Dynamic attributes (
:prop,v-bind) - Boolean shorthand
- Events (
@click, …) - Content (
v-text/v-html)
Prettier
Source: src/prettier/index.js.
| Option | Value |
|---|---|
useTabs |
true |
tabWidth |
2 |
semi |
true |
singleQuote |
false |
printWidth |
100 |
trailingComma |
"all" |
plugins |
prettier-plugin-tailwindcss (resolved from this package) |
ltft format always applies:
- This package’s
prettierignore(.nuxt,.output,node_modules,dist, lockfiles,package.json, …) - The project’s
.gitignorewhen present - The project’s
.prettierignorewhen present
Keep app-specific excludes in the project .prettierignore only.
TypeScript defaults
nuxtTypescriptDefaults sets:
{ vueCompilerOptions: { checkUnknownProps: true } }
Oxlint --type-aware is separate from nuxt typecheck — use both.
Vitest factory defaults
createVitestConfig({ rootDir, ... }) defaults:
| Setting | Default |
|---|---|
| Environment | happy-dom |
| Includes | `app/features |
| Setup | tests/setup/vitest-setup.ts |
| Aliases | ~ → ./app, @tests → ./tests |
| Defines | import.meta.client = true, import.meta.server = false |
Pass aliases, include, setupFiles, or overrides for app-specific needs.
Overlay allowlist
Allowed in consumers:
| File | Purpose |
|---|---|
oxlint.config.ts app overrides |
Path-specific rule exceptions |
.prettierrc.mjs |
tailwindStylesheet / tailwindFunctions |
nuxt.config.ts |
Merge typecheck defaults + app tsConfig |
vitest.config.ts |
Root, includes, aliases, setup files |
Not allowed: copying or editing shared rule tables from this package into the app.
Requesting rule changes
Open a PR against osmeridian/lint-typecheck-format-test, bump the consumer dependency pin after the release.
Package exports
| Export | Provides |
|---|---|
@osmeridian/lint-typecheck-format-test/eslint |
Shared ESLint flat config + custom Vue attr rule + utils -utils filename rule + hexagonal import bans |
@osmeridian/lint-typecheck-format-test/eslint/hexagonal |
Hexagonal no-restricted-imports overlay (included in /eslint) |
@osmeridian/lint-typecheck-format-test/oxlint |
baseOxlint object |
@osmeridian/lint-typecheck-format-test/prettier |
Base Prettier options |
@osmeridian/lint-typecheck-format-test/vitest |
createVitestConfig |
@osmeridian/lint-typecheck-format-test/typescript/nuxt |
nuxtTypescriptDefaults |
Exports include TypeScript types — consumers do not need local declare module shims.
Example stubs
See examples/nuxt-consumer/ for ESLint, Oxlint, Prettier, Vitest, and package.json script stubs.
Related Packages
Shared Vite+ and template ESLint configuration for Lupinum Nuxt websites.
Shared ESLint flat configs (base, vue, astro, node, opt-in tailwind) + oxlint sk...
Version History
| Version | Released | License |
|---|---|---|
| 1.1.2 | Proprietary | |
| 1.1.1 | Proprietary | |
| 1.1.0 | Proprietary | |
| 1.0.7 | Proprietary | |
| 1.0.6 | Proprietary | |
| 1.0.5 | Proprietary | |
| 1.0.4 | Proprietary | |
| 1.0.3 | Proprietary | |
| 1.0.2 | Proprietary | |
| 1.0.1 | Proprietary |