Phases 0-3 of the JS/JSX -> TS/TSX migration. No runtime behaviour changes:
every converted file emits byte-identical JavaScript, verified file by file.
Phase 0 - harness hardening, before any rename:
- scripts/ssr-resolve.mjs wraps `ssrLoadModule` so the ~170 literal module
paths in the check scripts resolve .js/.jsx/.ts/.tsx. Without it the first
rename would have silently destroyed the 1642-check suite that guards the
Owliver flow.
- eslint.config.js gains a TypeScript block. Its `files` globs listed only
{js,mjs,cjs,jsx}, so a renamed file would have dropped out of the run while
`eslint .` went on exiting 0 - the quietest failure mode available.
- MIGRATION_BASELINE.md records the measured starting point, including the
pre-existing seed-fixture failure and the already-broken standalone
owliver-baseline.mjs, so neither is later mistaken for migration damage.
Phase 1 - tsconfig.json succeeds jsconfig.json, carrying every option across at
its old value. `types` moves from [] to ["vite/client"], which fixes the eight
import.meta errors; @types/node is deliberately excluded so setTimeout stays a
number in browser code. allowJs and checkJs stay on, strict stays off.
Phase 2 - src/types/{api,entities,user}.ts. Transport envelope, error shape,
the entity-name union (the same 18 names are written down twice today, in
httpClient and base44Client, with nothing checking they agree), and the user
record. Every field transcribed from the API contract, the migrations and the
/me projection in me.go - not inferred. Entity record shapes are deliberately
absent: derivable, but nothing consumes them yet.
Phase 3 - nine leaf utilities renamed to .ts with annotations added only where
they could be established from existing usage. Entity records are typed `any`
with a comment naming what they are, rather than a guessed interface.
Verified: tsc 64 errors (65 before; one pre-existing TS2559 genuinely fixed,
none introduced), lint unchanged at 0 errors, skill-check 1641/1642 with only
the known failure, Owliver baseline section 59/59 green, production build
succeeds with the API origin inlined.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
122 lines
3.9 KiB
JavaScript
122 lines
3.9 KiB
JavaScript
import globals from "globals";
|
|
import pluginJs from "@eslint/js";
|
|
import pluginReact from "eslint-plugin-react";
|
|
import pluginReactHooks from "eslint-plugin-react-hooks";
|
|
import pluginUnusedImports from "eslint-plugin-unused-imports";
|
|
import tseslint from "typescript-eslint";
|
|
|
|
/**
|
|
* The rule set, written once and applied to JavaScript and TypeScript alike.
|
|
*
|
|
* Shared rather than duplicated because the two blocks below differ in exactly
|
|
* one thing — which parser reads the file — and a rule that applied to `.jsx`
|
|
* but not to its `.tsx` successor would make the TypeScript migration look like
|
|
* it was tidying the code up. It is not; it renames files and adds types.
|
|
*/
|
|
const rules = {
|
|
"no-unused-vars": "off",
|
|
"react/jsx-uses-vars": "error",
|
|
"react/jsx-uses-react": "error",
|
|
"unused-imports/no-unused-imports": "error",
|
|
"unused-imports/no-unused-vars": [
|
|
"warn",
|
|
{
|
|
vars: "all",
|
|
varsIgnorePattern: "^_",
|
|
args: "after-used",
|
|
argsIgnorePattern: "^_",
|
|
},
|
|
],
|
|
"react/prop-types": "off",
|
|
"react/react-in-jsx-scope": "off",
|
|
"react/no-unknown-property": [
|
|
"error",
|
|
{ ignore: ["cmdk-input-wrapper", "toast-close"] },
|
|
],
|
|
"react-hooks/rules-of-hooks": "error",
|
|
};
|
|
|
|
const plugins = {
|
|
react: pluginReact,
|
|
"react-hooks": pluginReactHooks,
|
|
"unused-imports": pluginUnusedImports,
|
|
};
|
|
|
|
const settings = { react: { version: "detect" } };
|
|
|
|
/**
|
|
* Which files are linted.
|
|
*
|
|
* Unchanged from what this config has always covered — `src/lib` and
|
|
* `src/components/ui` stay out of it — with `ts` and `tsx` added to every
|
|
* pattern. That addition is the point: ESLint matches on extension, so the
|
|
* moment a `.jsx` file became `.tsx` it would have dropped out of the run
|
|
* silently, and `eslint .` would have gone on exiting 0 while linting less and
|
|
* less of the codebase. A lint that passes because it checked nothing is worse
|
|
* than one that fails.
|
|
*/
|
|
const directories = ["src/components", "src/pages", "src/layouts", "src/hooks"];
|
|
const ignores = ["src/lib/**/*", "src/components/ui/**/*"];
|
|
|
|
const filesWith = (extensions) =>
|
|
directories.map((directory) => `${directory}/**/*.{${extensions}}`);
|
|
|
|
export default [
|
|
{
|
|
files: filesWith("js,mjs,cjs,jsx"),
|
|
ignores,
|
|
...pluginJs.configs.recommended,
|
|
...pluginReact.configs.flat.recommended,
|
|
languageOptions: {
|
|
globals: globals.browser,
|
|
parserOptions: {
|
|
ecmaVersion: 2022,
|
|
sourceType: "module",
|
|
ecmaFeatures: { jsx: true },
|
|
},
|
|
},
|
|
settings,
|
|
plugins,
|
|
rules,
|
|
},
|
|
|
|
/**
|
|
* The same block for TypeScript, with the TypeScript parser.
|
|
*
|
|
* A separate block rather than one widened glob, so that nothing about how
|
|
* the existing JavaScript is parsed or reported changes on the day this
|
|
* lands. `typescript-eslint`'s parser accepts plain JavaScript too, and
|
|
* merging the two would have been shorter — but it would also have quietly
|
|
* re-parsed 200-odd existing files, and this phase is meant to be provably
|
|
* inert.
|
|
*
|
|
* Deliberately NOT type-aware (no `projectService`): type errors are
|
|
* `npm run typecheck`'s job, and asking ESLint to build a program as well
|
|
* would make every lint run pay for it twice.
|
|
*/
|
|
{
|
|
files: filesWith("ts,tsx,mts,cts"),
|
|
ignores,
|
|
...pluginReact.configs.flat.recommended,
|
|
languageOptions: {
|
|
globals: globals.browser,
|
|
parser: tseslint.parser,
|
|
parserOptions: {
|
|
ecmaVersion: 2022,
|
|
sourceType: "module",
|
|
ecmaFeatures: { jsx: true },
|
|
},
|
|
},
|
|
settings,
|
|
plugins: { ...plugins, "@typescript-eslint": tseslint.plugin },
|
|
rules: {
|
|
...rules,
|
|
/* TypeScript resolves identifiers itself and reports the ones it cannot,
|
|
with better messages and without ESLint's browser/node globals list
|
|
needing to be right. Leaving the core rule on would report every `type`
|
|
and `interface` name as undefined. */
|
|
"no-undef": "off",
|
|
},
|
|
},
|
|
];
|