diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9585c194..7c765967 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,9 @@ repos: - repo: local hooks: - - id: eslint - name: eslint - entry: bash -c "cd refl1d/webview/client && npm run lint" + - id: frontend-lint + name: frontend-lint + entry: bash -c 'cd refl1d/webview/client && if command -v bun >/dev/null 2>&1; then bun run lint; elif command -v npm >/dev/null 2>&1; then npm run lint; else echo "Need bun or npm to run lint" >&2; exit 1; fi' language: system - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 @@ -16,7 +16,7 @@ repos: # - id: end-of-file-fixer # - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.7 + rev: v0.14.8 hooks: # - id: ruff # args: [--fix, --exit-non-zero-on-fix] @@ -24,4 +24,4 @@ repos: ci: autoupdate_schedule: monthly - skip: [eslint] + skip: [frontend-lint] diff --git a/refl1d/webview/client/.oxfmtrc.json b/refl1d/webview/client/.oxfmtrc.json new file mode 100644 index 00000000..e73c886a --- /dev/null +++ b/refl1d/webview/client/.oxfmtrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "__note__": "This is the configuration file for oxfmt. See oxc.rs/docs/guide/usage/formatter/config-file-reference.html", + "printWidth": 120, + "trailingComma": "es5" +} diff --git a/refl1d/webview/client/.oxlintrc.json b/refl1d/webview/client/.oxlintrc.json new file mode 100644 index 00000000..10dcc117 --- /dev/null +++ b/refl1d/webview/client/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "extends": [ + "oxlint/recommended", + "oxlint/typescript", + "oxlint/vue" + ], + "files": ["src/**/*.{js,mjs,ts,tsx,vue}"] +} \ No newline at end of file diff --git a/refl1d/webview/client/eslint.config.js b/refl1d/webview/client/eslint.config.js deleted file mode 100644 index 5e5d8e79..00000000 --- a/refl1d/webview/client/eslint.config.js +++ /dev/null @@ -1,59 +0,0 @@ -import url from "url"; -import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; -import pluginVue from "eslint-plugin-vue"; -import { FlatCompat } from "@eslint/eslintrc"; -import js from "@eslint/js"; -import prettierConfig from "@vue/eslint-config-prettier"; -import vueTsEslintConfig from "@vue/eslint-config-typescript"; - -const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - // allConfig: js.configs.all, -}); - -export default [ - /** Extend recommended configs */ - ...compat.extends("plugin:vue/vue3-recommended", "plugin:vuejs-accessibility/recommended", "prettier"), - ...pluginVue.configs["flat/recommended"], - ...vueTsEslintConfig(), - eslintPluginPrettierRecommended, - prettierConfig, - /** Configuration */ - { - languageOptions: { - parserOptions: { - ecmaVersion: "latest", - sourceType: "script", - }, - }, - files: ["src/**/*.js", "src/**/*.mjs", "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], - /** Override rules */ - rules: { - "max-len": ["error", { code: 120 }], - "prefer-const": 0, - "@typescript-eslint/ban-ts-comment": ["error", { "ts-ignore": "allow-with-description" }], - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unused-expressions": [ - "error", - { allowShortCircuit: true, allowTernary: true }, // Temporary fix for indirect dependency @typescript-eslint <= 8.15.0 - ], - "prettier/prettier": [ - "warn", - {}, - { - usePrettierrc: true, - }, - ], - "vuejs-accessibility/label-has-for": [ - "error", - { - required: { - some: ["nesting", "id"], - }, - }, - ], - }, - }, -]; diff --git a/refl1d/webview/client/package.json b/refl1d/webview/client/package.json index 4c34c141..1603174b 100644 --- a/refl1d/webview/client/package.json +++ b/refl1d/webview/client/package.json @@ -1,6 +1,6 @@ { "name": "refl1d-webview-client", - "version": "0.1.26", + "version": "0.1.27", "type": "module", "repository": { "type": "git", @@ -12,44 +12,34 @@ "build": "vite build --emptyOutDir -m development", "build_prod": "vite build --emptyOutDir -m production", "preview": "vite preview --port 4173", - "format": "prettier --write src", - "lint": "eslint src --fix", - "test:lint": "eslint src", + "format": "oxfmt src", + "lint": "oxlint src --fix", + "test:lint": "oxlint src", "test:types": "vue-tsc --noEmit --skipLibCheck -p tsconfig.json --composite false" }, "dependencies": { "@msgpack/msgpack": "^3.1.2", - "bootstrap": "^5.3.7", + "bootstrap": "^5.3.8", "date-fns": "^4.1.0", "json-difference": "^1.16.1", - "mpld3": "^0.5.10", - "plotly.js": "^3.0.1", + "mpld3": "^0.5.11", + "plotly.js": "^3.1.2", "socket.io-client": "^4.8.1", "uuid": "^11.1.0", - "vue": "^3.5.17", - "vue-json-pretty": "^2.5.0" + "vue": "^3.5.22", + "vue-json-pretty": "^2.6.0" }, "devDependencies": { - "@ianvs/prettier-plugin-sort-imports": "^4.4.1", "@tsconfig/node-lts": "^22.0.2", - "@types/node": "^24.5.2", - "@types/plotly.js": "^2.35.1", - "@types/uuid": "^10.0.0", - "@vitejs/plugin-vue": "^5.2.1", - "@vue/eslint-config-prettier": "10.1.0", - "@vue/eslint-config-typescript": "^14.2.0", - "@vue/tsconfig": "^0.7.0", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-vue": "^9.32.0", - "eslint-plugin-vuejs-accessibility": "^2.4.1", - "prettier": "^3.4.2", - "prettier-plugin-css-order": "^2.1.2", - "prettier-plugin-jsdoc": "^1.3.2", - "typescript": "^5.7.3", - "vite": "^6.0.7", + "@types/node": "^24.9.2", + "@types/plotly.js": "^3.0.8", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.8.1", + "oxfmt": "^0.16.0", + "oxlint": "^1.31.0", + "typescript": "^5.9.3", + "vite": "^7.1.12", "vite-svg-loader": "5.1.0", - "vue-tsc": "^2.2.0" + "vue-tsc": "^3.1.2" } } diff --git a/refl1d/webview/client/prettier.config.js b/refl1d/webview/client/prettier.config.js deleted file mode 100644 index f7200637..00000000 --- a/refl1d/webview/client/prettier.config.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @type {import("prettier").Config} - * @see https://prettier.io/docs/en/configuration.html - */ - -const config = { - plugins: [ - "@ianvs/prettier-plugin-sort-imports", - // "./node_modules/prettier-plugin-jsdoc/dist/index.js", - "prettier-plugin-css-order", - ], - importOrder: ["^vue", "^[a-zA-Z]", "^@[a-zA-Z]", "^@/", "^./", "^../"], - cssDeclarationSorterOrder: "smacss", - // jsdocCapitalizeDescription: false, - overrides: [ - { - files: "*.svg", - options: { - parser: "html", - }, - }, - ], - experimentalTernaries: true, - printWidth: 120, - // tabWidth: 4, - // useTabs: false, - // semi: true, - // singleQuote: false, - // jsxSingleQuote: false, - // quoteProps: "as-needed", - trailingComma: "es5", - // bracketSpacing: true, - // bracketSameLine: false, - // arrowParens: "always", - // rangeStart: 0, - // rangeEnd: Infinity, - // parser: "babel", - // requirePragma: false, - // insertPragma: false, - // proseWrap: "preserve", - // htmlWhitespaceSensitivity: "css", - // vueIndentScriptAndStyle: false, - // endOfLine: "lf", - // embeddedLanguageFormatting: "auto", - // singleAttributePerLine: true, -}; - -export default config; diff --git a/refl1d/webview/client/src/components/DataView.vue b/refl1d/webview/client/src/components/DataView.vue index f9c637eb..1a17bfb4 100644 --- a/refl1d/webview/client/src/components/DataView.vue +++ b/refl1d/webview/client/src/components/DataView.vue @@ -95,7 +95,7 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, lin_y ? xs.dR.map((t) => t / intensity_scale) : xs.dR.map((t) => (t / intensity_scale) * local_offset); data_trace.error_y = { type: "data", array: dR, visible: true }; if (calculate_residuals) { - const residuals = xs.R.map((r, i) => (r - xs.theory[i]) / xs.dR![i]); + const residuals = xs.R.map((r, i) => (r - xs.theory[i]!) / xs.dR![i]!); const residuals_trace: Trace = { x: xs.Q, y: residuals, @@ -127,7 +127,7 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, const lin_y = !log_y.value; const background_offset = apply_corrections.value ? xs.background : 0.0; const local_offset = lin_y ? plot_index * offset : Math.pow(10, plot_index * offset); - const theory = xs.theory.map((y, i) => (y - background_offset) / (xs.fresnel[i] - background_offset)); + const theory = xs.theory.map((y, i) => (y - background_offset) / (xs.fresnel![i]! - background_offset)); const offset_theory = lin_y ? theory.map((t) => t + local_offset) : theory.map((t) => t * local_offset); theory_traces.push({ x: xs.Q, @@ -137,7 +137,7 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, line: { width: 2, color }, }); if (xs.R !== undefined) { - const R = xs.R.map((y, i) => (y - background_offset) / (xs.fresnel[i] - background_offset)); + const R = xs.R.map((y, i) => (y - background_offset) / (xs.fresnel![i]! - background_offset)); const offset_R = lin_y ? R.map((t) => t + local_offset) : R.map((t) => t * local_offset); const data_trace: Trace = { x: xs.Q, @@ -152,11 +152,11 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, data_trace.error_x = { type: "data", array: xs.dQ, visible: true }; } if (xs.dR !== undefined) { - const dR = xs.dR.map((dy, i) => dy / (xs.fresnel[i] - background_offset)); + const dR = xs.dR.map((dy, i) => dy / (xs.fresnel![i]! - background_offset)); const dR_offset = lin_y ? dR : dR.map((t) => t * local_offset); data_trace.error_y = { type: "data", array: dR_offset, visible: true }; if (calculate_residuals) { - const residuals = xs.R.map((r, i) => (r - xs.theory[i]) / xs.dR![i]); + const residuals = xs.R.map((r, i) => (r - xs.theory[i]!) / xs.dR![i]!); const residuals_trace: Trace = { x: xs.Q, y: residuals, @@ -192,7 +192,7 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, const intensity_scale = apply_corrections.value ? intensity : 1.0; const background_offset = apply_corrections.value ? background : 0.0; const Q4 = xs.Q.map((qq) => 1e-8 * Math.pow(qq, -4) * intensity_scale); - const theory = xs.theory.map((t, i) => (t - background_offset) / Q4[i]); + const theory = xs.theory.map((t, i) => (t - background_offset) / Q4![i]!); const offset_theory = theory.map((t) => t * local_offset); theory_traces.push({ x: xs.Q, @@ -202,7 +202,7 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, line: { width: 2, color }, }); if (xs.R !== undefined) { - const R = xs.R.map((r, i) => (r - background_offset) / Q4[i]); + const R = xs.R.map((r, i) => (r - background_offset) / Q4![i]!); const offset_R = R.map((t) => t * local_offset); const data_trace: Trace = { x: xs.Q, @@ -217,11 +217,11 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, data_trace.error_x = { type: "data", array: xs.dQ, visible: true }; } if (xs.dR !== undefined) { - const dR = xs.dR.map((dy, i) => dy / Q4[i]); + const dR = xs.dR.map((dy, i) => dy / Q4[i]!); const offset_dR = dR.map((t) => t * local_offset); data_trace.error_y = { type: "data", array: offset_dR, visible: true }; if (calculate_residuals) { - const residuals = xs.R.map((r, i) => (r - xs.theory[i]) / xs.dR![i]); + const residuals = xs.R.map((r, i) => (r - xs.theory[i]!) / xs.dR![i]!); const residuals_trace: Trace = { x: xs.Q, y: residuals, @@ -264,9 +264,9 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, const legendgroup = `group_${plot_index}`; const Tm = interp(pp.Q, mm.Q, mm.theory); - const TSA = Tm.map((m, i) => { - const p_corr = (pp.theory[i] - pp_background_offset) / pp_intensity_scale; - const m_corr = (mm.theory[i] - mm_background_offset) / mm_intensity_scale; + const TSA = Tm.map((_m, i) => { + const p_corr = (pp.theory[i]! - pp_background_offset) / pp_intensity_scale; + const m_corr = (mm.theory[i]! - mm_background_offset) / mm_intensity_scale; return (p_corr - m_corr) / (p_corr + m_corr) + local_offset; }); @@ -277,7 +277,7 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, const p_corr = pp.R.map((r) => (r - pp_background_offset) / pp_intensity_scale); const m_corr = Rm.map((r) => (r - mm_background_offset) / mm_intensity_scale); const SA = m_corr.map((m, i) => { - const p = p_corr[i]; + const p = p_corr[i]!; return (p - m) / (p + m); }); const SA_offset = SA.map((v) => v + local_offset); @@ -297,15 +297,15 @@ function generate_new_traces(model_data: ModelData[][], view: ReflectivityPlot, if (pp.dR !== undefined && mm.dR !== undefined) { const dRm = interp(pp.Q, mm.Q, mm.dR); const dSA = dRm.map((dm, i) => { - const dp_corr = pp.dR![i] / pp_intensity_scale; + const dp_corr = pp.dR![i]! / pp_intensity_scale; const dm_corr = dm / mm_intensity_scale; - const p = p_corr[i]; - const m = m_corr[i]; + const p = p_corr[i]!; + const m = m_corr[i]!; return Math.sqrt((4 * ((p * dm_corr) ** 2 + (m * dp_corr) ** 2)) / (p + m) ** 4); }); data_trace.error_y = { type: "data", array: dSA, visible: true }; if (calculate_residuals) { - const residuals = SA.map((v, i) => (v - TSA[i]) / dSA[i]); + const residuals = SA.map((v, i) => (v - TSA![i]!) / dSA[i]!); const residuals_trace: Trace = { x: pp.Q, y: residuals, @@ -448,13 +448,17 @@ function interp(x: number[], xp: number[], fp: number[]): number[] { throw new Error(`lengths of xp (${xp.length}) and fp (${fp.length}) must match`); } + if (xp.length < 2) { + throw new Error("length of xp, fp must be > 1"); + } + const xpv = xp.values(); const fpv = fp.values(); - const lowest_xp = xp[0]; - const lowest_fp = fp[0]; + const lowest_xp = xp[0]!; + const lowest_fp = fp[0]!; // const highest_xp = xp[xp.length - 1]; - const highest_fp = fp[fp.length - 1]; + const highest_fp = fp[fp.length - 1]!; let lower_xp = xpv.next(); let lower_fp = fpv.next(); @@ -484,7 +488,7 @@ function interp(x: number[], xp: number[], fp: number[]): number[] { lower_fp.value! ); } - }); + }) as number[]; } diff --git a/refl1d/webview/client/src/components/ModelView.vue b/refl1d/webview/client/src/components/ModelView.vue index 6c7bbcc2..72698db7 100644 --- a/refl1d/webview/client/src/components/ModelView.vue +++ b/refl1d/webview/client/src/components/ModelView.vue @@ -1,5 +1,4 @@