Repository: Khan/perseus
Pull request #2414: ESLint config cleanup
Author: login: jeremywiebe
Labels: name: olc-5.0.a8bef
Created: 2025-04-22T19:11:20Z | Merged: 2025-04-24T17:29:48Z

## Summary:

This PR cleans up a bunch of ESLint-related configuration. In it, we:

  * Remove (now) unnecessary configs from each package dir's `.eslintrc.js` (its now properly covered by the root `.eslintrc.js`)
  * Update `package.json` files to properly include dependencies that they use
  * Move some `.d.ts` files from the shared `types` folder into the vendored package folder (and then expose them using the `"types"` key in the `package.json file)

Issue: "none"

## Test plan:

`pnpm lint`
Open your favourite code editor and check if there aren't any invalid lint errors (also try adding an import into a production `.ts` file for a package that isn't in the package's dependencies list and see if the linter flags it)


Diff:
diff --git a/.changeset/gorgeous-owls-agree.md b/.changeset/gorgeous-owls-agree.md
new file mode 100644
index 00000000000..1d73dc01342
--- /dev/null
+++ b/.changeset/gorgeous-owls-agree.md
@@ -0,0 +1,19 @@
+---
+"@khanacademy/perseus-dev-ui": patch
+"@khanacademy/kas": patch
+"@khanacademy/keypad-context": patch
+"@khanacademy/kmath": patch
+"@khanacademy/math-input": patch
+"@khanacademy/perseus": patch
+"@khanacademy/perseus-core": patch
+"@khanacademy/perseus-editor": patch
+"@khanacademy/perseus-linter": patch
+"@khanacademy/perseus-score": patch
+"@khanacademy/perseus-utils": patch
+"@khanacademy/pure-markdown": patch
+"@khanacademy/simple-markdown": patch
+"jsdiff": patch
+"raphael": patch
+---
+
+Fix dependencies so that the package correctly depends on all of the packages it uses
diff --git a/.eslintrc.js b/.eslintrc.js
index a8fcd55520d..52f2c478f64 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -73,18 +73,7 @@ module.exports = {
     settings: {
         "import/resolver": {
             typescript: {
-                project: [
-                    "packages/*/tsconfig.json",
-                    "packages/tsconfig-shared.json",
-                ],
-                extensions: [".js", ".jsx", ".ts", ".tsx"],
-                moduleDirectory: ["node_modules"],
-            },
-            node: {
-                project: [
-                    "packages/*/tsconfig.json",
-                    "packages/tsconfig-shared.json",
-                ],
+                project: ["tsconfig.json"],
                 extensions: [".js", ".jsx", ".ts", ".tsx"],
                 moduleDirectory: ["node_modules"],
             },
@@ -138,6 +127,8 @@ module.exports = {
                 "*.test.tsx",
                 "*.stories.ts",
                 "*.stories.tsx",
+                "**/__testutils__/**",
+                "**/__stories__/**",
             ],
             rules: {
                 "max-lines": "off",
@@ -229,7 +220,8 @@ module.exports = {
          * import
          */
         // "import/no-default-export": "error",
-        "import/no-unresolved": "error",
+        // "import/no-unresolved": "error",
+        "import/no-extraneous-dependencies": "error",
         // NOTE(kevinb): This rule is confused by third-party TypeScript lib defs
         "import/named": "off",
         "import/default": "error",
diff --git a/dev/package.json b/dev/package.json
index b58ac22f347..c03ec8ebaab 100644
--- a/dev/package.json
+++ b/dev/package.json
@@ -37,6 +37,11 @@
         "@khanacademy/wonder-blocks-tooltip": "catalog:",
         "@khanacademy/wonder-stuff-core": "catalog:",
         "@phosphor-icons/core": "catalog:",
-        "aphrodite": "catalog:"
+        "aphrodite": "catalog:",
+        "react": "catalog:",
+        "react-dom": "catalog:"
+    },
+    "devDependencies": {
+        "vite": "catalog:"
     }
 }
diff --git a/package.json b/package.json
index 49dcce434ef..4727186e10d 100644
--- a/package.json
+++ b/package.json
@@ -112,10 +112,10 @@
         "storybook": "^8.6.11",
         "style-loader": "^3.3.3",
         "swc_mut_cjs_exports": "^8.0.1",
-        "tiny-invariant": "^1.3.1",
+        "tiny-invariant": "catalog:",
         "typescript": "5.7.2",
         "typescript-coverage-report": "^0.7.0",
-        "vite": "5.1.0",
+        "vite": "catalog:",
         "vite-plugin-istanbul": "^5.0.0",
         "winston": "^3.7.2",
         "yaml": "^2.7.0"
diff --git a/packages/kas/.eslintrc.js b/packages/kas/.eslintrc.js
index f5a25780336..a233d647453 100644
--- a/packages/kas/.eslintrc.js
+++ b/packages/kas/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/keypad-context/.eslintrc.js b/packages/keypad-context/.eslintrc.js
index bc9af50abb2..a5fe386653a 100644
--- a/packages/keypad-context/.eslintrc.js
+++ b/packages/keypad-context/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/kmath/.eslintrc.js b/packages/kmath/.eslintrc.js
index bc9af50abb2..a5fe386653a 100644
--- a/packages/kmath/.eslintrc.js
+++ b/packages/kmath/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/math-input/.eslintrc.js b/packages/math-input/.eslintrc.js
index f0b4c10308f..656f12d47e9 100644
--- a/packages/math-input/.eslintrc.js
+++ b/packages/math-input/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/perseus-core/.eslintrc.js b/packages/perseus-core/.eslintrc.js
index 6376d5e1dfd..fcc783a707b 100644
--- a/packages/perseus-core/.eslintrc.js
+++ b/packages/perseus-core/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/perseus-core/package.json b/packages/perseus-core/package.json
index 192b898b5a5..c870f6f46df 100644
--- a/packages/perseus-core/package.json
+++ b/packages/perseus-core/package.json
@@ -26,7 +26,8 @@
     },
     "dependencies": {
         "@khanacademy/kas": "workspace:*",
-        "@khanacademy/perseus-utils": "workspace:*"
+        "@khanacademy/perseus-utils": "workspace:*",
+        "tiny-invariant": "catalog:"
     },
     "devDependencies": {
         "@khanacademy/wonder-stuff-core": "catalog:",
diff --git a/packages/perseus-editor/.eslintrc.js b/packages/perseus-editor/.eslintrc.js
index 1c7e3491e67..2d2ea57ee03 100644
--- a/packages/perseus-editor/.eslintrc.js
+++ b/packages/perseus-editor/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/perseus-editor/package.json b/packages/perseus-editor/package.json
index 44cf095132b..a70876d5a99 100644
--- a/packages/perseus-editor/package.json
+++ b/packages/perseus-editor/package.json
@@ -45,7 +45,8 @@
         "@khanacademy/perseus-utils": "workspace:*",
         "@khanacademy/pure-markdown": "workspace:*",
         "katex": "0.11.1",
-        "mafs": "^0.19.0"
+        "mafs": "^0.19.0",
+        "tiny-invariant": "catalog:"
     },
     "devDependencies": {
         "@khanacademy/mathjax-renderer": "catalog:",
@@ -70,6 +71,7 @@
         "aphrodite": "catalog:",
         "classnames": "catalog:",
         "jquery": "catalog:",
+        "jsdiff": "workspace:*",
         "perseus-build-settings": "workspace:*",
         "prop-types": "catalog:",
         "react": "catalog:",
diff --git a/packages/perseus-editor/src/diffs/string-array-diff.ts b/packages/perseus-editor/src/diffs/string-array-diff.ts
index 3ee2f5b8e5f..ff396c12e7b 100644
--- a/packages/perseus-editor/src/diffs/string-array-diff.ts
+++ b/packages/perseus-editor/src/diffs/string-array-diff.ts
@@ -1,4 +1,3 @@
-// eslint-disable-next-line import/no-extraneous-dependencies
 import jsdiff from "jsdiff";
 import _ from "underscore";
 
diff --git a/packages/perseus-editor/src/editor.tsx b/packages/perseus-editor/src/editor.tsx
index 4fa8b5fc377..3bb4a1af08c 100644
--- a/packages/perseus-editor/src/editor.tsx
+++ b/packages/perseus-editor/src/editor.tsx
@@ -13,7 +13,6 @@ import {
     PerseusError,
 } from "@khanacademy/perseus-core";
 import $ from "jquery";
-// eslint-disable-next-line import/no-unresolved, import/no-extraneous-dependencies
 import katex from "katex";
 // ./katex-mhchem is imported for side effects. It adds the mhchem extension
 // to KaTeX, which is needed to render chemistry expressions. This prevents
diff --git a/packages/perseus-linter/.eslintrc.js b/packages/perseus-linter/.eslintrc.js
index bf97a82ee25..f52f68b0103 100644
--- a/packages/perseus-linter/.eslintrc.js
+++ b/packages/perseus-linter/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/perseus-score/.eslintrc.js b/packages/perseus-score/.eslintrc.js
index f86ced62f14..5f1131d4404 100644
--- a/packages/perseus-score/.eslintrc.js
+++ b/packages/perseus-score/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/perseus-utils/.eslintrc.js b/packages/perseus-utils/.eslintrc.js
index f5a25780336..a233d647453 100644
--- a/packages/perseus-utils/.eslintrc.js
+++ b/packages/perseus-utils/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/perseus/.eslintrc.js b/packages/perseus/.eslintrc.js
index bcab19ba385..0fc6630b2ac 100644
--- a/packages/perseus/.eslintrc.js
+++ b/packages/perseus/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/perseus/package.json b/packages/perseus/package.json
index c8f66cb45b1..759afa5e449 100644
--- a/packages/perseus/package.json
+++ b/packages/perseus/package.json
@@ -52,6 +52,7 @@
         "@khanacademy/simple-markdown": "workspace:*",
         "@use-gesture/react": "^10.2.27",
         "mafs": "0.19.0",
+        "tiny-invariant": "catalog:",
         "uuid": "^10.0.0"
     },
     "devDependencies": {
@@ -84,6 +85,7 @@
         "lodash.debounce": "^4.0.8",
         "perseus-build-settings": "workspace:*",
         "prop-types": "catalog:",
+        "raphael": "workspace:*",
         "react": "catalog:",
         "react-dom": "catalog:",
         "react-popper": "^2.2.5",
diff --git a/packages/perseus/src/util/graphie.test.ts b/packages/perseus/src/util/graphie.test.ts
index bd576a9c933..ef90be25a29 100644
--- a/packages/perseus/src/util/graphie.test.ts
+++ b/packages/perseus/src/util/graphie.test.ts
@@ -1,5 +1,4 @@
 import $ from "jquery";
-// eslint-disable-next-line import/no-extraneous-dependencies
 import Raphael from "raphael";
 
 import {testDependencies} from "../../../../testing/test-dependencies";
diff --git a/packages/perseus/src/util/graphie.ts b/packages/perseus/src/util/graphie.ts
index 71958fffc41..46936383c8a 100644
--- a/packages/perseus/src/util/graphie.ts
+++ b/packages/perseus/src/util/graphie.ts
@@ -8,7 +8,6 @@ import {
 import {Errors, PerseusError} from "@khanacademy/perseus-core";
 import {entries} from "@khanacademy/wonder-stuff-core";
 import $ from "jquery";
-// eslint-disable-next-line import/no-extraneous-dependencies
 import Raphael from "raphael";
 
 // Minify Raphael ourselves because IE8 has a problem with the 1.5.2 minified
diff --git a/packages/pure-markdown/.eslintrc.js b/packages/pure-markdown/.eslintrc.js
index a8712ddc84b..1c9606c2d38 100644
--- a/packages/pure-markdown/.eslintrc.js
+++ b/packages/pure-markdown/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/packages/simple-markdown/.eslintrc.js b/packages/simple-markdown/.eslintrc.js
index f5a25780336..a233d647453 100644
--- a/packages/simple-markdown/.eslintrc.js
+++ b/packages/simple-markdown/.eslintrc.js
@@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
         // To avoid circular dependencies,
         // we only allow imports from specific Perseus packages.
         // This is overly strict to prevent accidental interdependencies,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 385a87e7d71..38b3c047fb8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -102,9 +102,15 @@ catalogs:
     react-dom:
       specifier: ^18.2.0
       version: 18.3.1
+    tiny-invariant:
+      specifier: ^1.3.1
+      version: 1.3.3
     underscore:
       specifier: ^1.4.4
       version: 1.6.0
+    vite:
+      specifier: 5.1.0
+      version: 5.1.0
 
 importers:
 
@@ -420,7 +426,7 @@ importers:
         specifier: ^8.0.1
         version: 8.0.1(@swc/core@1.11.13(@swc/helpers@0.5.17))(@swc/jest@0.2.37(@swc/core@1.11.13(@swc/helpers@0.5.17)))
       tiny-invariant:
-        specifier: ^1.3.1
+        specifier: 'catalog:'
         version: 1.3.3
       typescript:
         specifier: 5.7.2
@@ -429,7 +435,7 @@ importers:
         specifier: ^0.7.0
         version: 0.7.0(typescript@5.7.2)
       vite:
-        specifier: 5.1.0
+        specifier: 'catalog:'
         version: 5.1.0(@types/node@20.17.6)(less@4.2.0)(terser@5.39.0)
       vite-plugin-istanbul:
         specifier: ^5.0.0
@@ -517,6 +523,16 @@ importers:
       aphrodite:
         specifier: 'catalog:'
         version: 1.2.5
+      react:
+        specifier: 'catalog:'
+        version: 18.3.1
+      react-dom:
+        specifier: 'catalog:'
+        version: 18.3.1(react@18.3.1)
+    devDependencies:
+      vite:
+        specifier: 'catalog:'
+        version: 5.1.0(@types/node@20.17.6)(less@4.2.0)(terser@5.39.0)
 
   packages/kas:
     dependencies:
@@ -671,6 +687,9 @@ importers:
       mafs:
         specifier: 0.19.0
         version: 0.19.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      tiny-invariant:
+        specifier: 'catalog:'
+        version: 1.3.3
       uuid:
         specifier: ^10.0.0
         version: 10.0.0
@@ -762,6 +781,9 @@ importers:
       prop-types:
         specifier: 'catalog:'
         version: 15.6.1
+      raphael:
+        specifier: workspace:*
+        version: link:../../vendor/raphael
       react:
         specifier: 'catalog:'
         version: 18.3.1
@@ -783,6 +805,9 @@ importers:
       '@khanacademy/perseus-utils':
         specifier: workspace:*
         version: link:../perseus-utils
+      tiny-invariant:
+        specifier: 'catalog:'
+        version: 1.3.3
     devDependencies:
       '@khanacademy/wonder-stuff-core':
         specifier: 'catalog:'
@@ -832,6 +857,9 @@ importers:
       mafs:
         specifier: ^0.19.0
         version: 0.19.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      tiny-invariant:
+        specifier: 'catalog:'
+        version: 1.3.3
     devDependencies:
       '@khanacademy/mathjax-renderer':
         specifier: 'catalog:'
@@ -899,6 +927,9 @@ importers:
       jquery:
         specifier: 'catalog:'
         version: 2.2.4
+      jsdiff:
+        specifier: workspace:*
+        version: link:../../vendor/jsdiff
       perseus-build-settings:
         specifier: workspace:*
         version: link:../../config/build
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index b3563130871..b492e4dd953 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -10,7 +10,9 @@ catalog:
     classnames: 1.1.4
     jquery: ^2.1.1
     prop-types: 15.6.1
+    tiny-invariant: ^1.3.1
     underscore: ^1.4.4
+    vite: 5.1.0
     "@khanacademy/mathjax-renderer": ^2.1.1
     "@khanacademy/wonder-blocks-accordion": 3.1.5
     "@khanacademy/wonder-blocks-banner": 4.1.6
diff --git a/types/jsdiff.d.ts b/vendor/jsdiff/jsdiff.d.ts
similarity index 100%
rename from types/jsdiff.d.ts
rename to vendor/jsdiff/jsdiff.d.ts
diff --git a/vendor/jsdiff/package.json b/vendor/jsdiff/package.json
index 73efb91c52c..700da763663 100644
--- a/vendor/jsdiff/package.json
+++ b/vendor/jsdiff/package.json
@@ -2,5 +2,6 @@
   "name": "jsdiff",
   "private": true,
   "version": "1.0.0",
-  "main": "jsdiff.js"
+  "main": "jsdiff.js",
+  "types": "jsdiff.d.ts"
 }
diff --git a/vendor/raphael/package.json b/vendor/raphael/package.json
index 04f97533724..b66dd157392 100644
--- a/vendor/raphael/package.json
+++ b/vendor/raphael/package.json
@@ -2,5 +2,6 @@
   "name": "raphael",
   "private": "true",
   "version": "1.5.2",
-  "main": "raphael.js"
+  "main": "raphael.js",
+  "types": "raphael.d.ts"
 }
diff --git a/types/raphael.d.ts b/vendor/raphael/raphael.d.ts
similarity index 100%
rename from types/raphael.d.ts
rename to vendor/raphael/raphael.d.ts


Reviews:
user: login: jeremywiebe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T19:36:51Z
user: login: jeremywiebe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T19:37:40Z
user: login: jeremywiebe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T19:38:47Z
user: login: jeremywiebe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T19:39:28Z
user: login: jeremywiebe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T19:41:24Z
user: login: mark-fitzgerald
body: Spring cleaning is so refreshing!

Thank you for the PR comments, also. So helpful!!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T15:36:35Z

Review comments:
diff_hunk: @@ -73,18 +73,7 @@ module.exports = {
     settings: {
         "import/resolver": {
             typescript: {
-                project: [
-                    "packages/*/tsconfig.json",
-                    "packages/tsconfig-shared.json",
-                ],
-                extensions: [".js", ".jsx", ".ts", ".tsx"],
-                moduleDirectory: ["node_modules"],
-            },
-            node: {
path: .eslintrc.js
user: login: jeremywiebe
body: There is no usage of the node resolver in our tests, we always use the Typescript resolver.
created_at: 2025-04-23T19:36:50Z
author_association: COLLABORATOR
diff_hunk: @@ -37,6 +37,11 @@
         "@khanacademy/wonder-blocks-tooltip": "catalog:",
         "@khanacademy/wonder-stuff-core": "catalog:",
         "@phosphor-icons/core": "catalog:",
-        "aphrodite": "catalog:"
+        "aphrodite": "catalog:",
+        "react": "catalog:",
+        "react-dom": "catalog:"
path: dev/package.json
user: login: jeremywiebe
body: These deps are used in this area but weren't explicit. This helps ESLint out when checking extraneous deps.
created_at: 2025-04-23T19:37:40Z
author_association: COLLABORATOR
diff_hunk: @@ -1,16 +1,7 @@
-/* eslint-disable @typescript-eslint/no-require-imports */
 /* eslint-disable import/no-commonjs */
-const path = require("path");
 
 module.exports = {
     rules: {
-        "import/no-extraneous-dependencies": [
-            "error",
-            {
-                packageDir: [__dirname, path.join(__dirname, "../../")],
-                includeTypes: true,
-            },
-        ],
path: packages/kas/.eslintrc.js
user: login: jeremywiebe
body: This is a core part of this PR. We can get rid of this rule in each package's `.eslintrc.js`, making our linting setup simpler (I couldn't figure out how to get rid of the circular imports rules below, so this file needs to stay here still). 
created_at: 2025-04-23T19:38:47Z
author_association: COLLABORATOR
diff_hunk: @@ -45,7 +45,8 @@
         "@khanacademy/perseus-utils": "workspace:*",
         "@khanacademy/pure-markdown": "workspace:*",
         "katex": "0.11.1",
-        "mafs": "^0.19.0"
+        "mafs": "^0.19.0",
+        "tiny-invariant": "catalog:"
path: packages/perseus-editor/package.json
user: login: jeremywiebe
body: More dependencies used by this package that weren't explicit. 
created_at: 2025-04-23T19:39:28Z
author_association: COLLABORATOR
diff_hunk: @@ -2,5 +2,6 @@
   "name": "jsdiff",
   "private": true,
   "version": "1.0.0",
-  "main": "jsdiff.js"
+  "main": "jsdiff.js",
+  "types": "jsdiff.d.ts"
path: vendor/jsdiff/package.json
user: login: jeremywiebe
body: For both of these `vendor/` changes, I've moved the associated `.d.ts` file into the package's folder and defined the `"types"` key in the `package.json.  This allows use to provide types for a vendored package without them being in our shared `types/` folder (eventually, it'd be nice to get rid of that as it's a bandaid for packages that don't ship types and is symlinked to all package folders). 
created_at: 2025-04-23T19:41:23Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-22T19:14:39Z
body: **Size Change:** -15 B (0%) 

**Total Size:** 466 kB

| Filename | Size | Change |
| :--- | :---: | :---: |
| `packages/perseus-core/dist/es/index.js` | 18.5 kB | +17 B (+0.09%) |
| `packages/perseus-editor/dist/es/index.js` | 88.4 kB | -63 B (-0.07%) |
| `packages/perseus/dist/es/index.js` | 199 kB | +31 B (+0.02%) |

<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size |
| :--- | :---: |
| `packages/kas/dist/es/index.js` | 20.7 kB |
| `packages/keypad-context/dist/es/index.js` | 1 kB |
| `packages/kmath/dist/es/index.js` | 5.98 kB |
| `packages/math-input/dist/es/index.js` | 98.6 kB |
| `packages/math-input/dist/es/strings.js` | 1.61 kB |
| `packages/perseus-linter/dist/es/index.js` | 7.05 kB |
| `packages/perseus-score/dist/es/index.js` | 9.04 kB |
| `packages/perseus-utils/dist/es/index.js` | 403 B |
| `packages/perseus/dist/es/strings.js` | 7.49 kB |
| `packages/pure-markdown/dist/es/index.js` | 1.22 kB |
| `packages/simple-markdown/dist/es/index.js` | 6.71 kB |

</details>



<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-23T19:44:09Z
body: # npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (2b71666e) and published it to npm. You
can install it using the tag `PR2414`.

Example:
```sh
pnpm add @khanacademy/perseus@PR2414
```

If you are working in Khan Academy's webapp, you can run:
```sh
./dev/tools/bump_perseus_version.js -t PR2414
```
author_association: CONTRIBUTOR
user: login: jeremywiebe
created_at: 2025-04-23T20:25:29Z
body: @mark-fitzgerald @Myranae I've tagged you both for review as I have recently worked with you on ESLint tooling or problems.  Would you mind giving this branch a test in your environments and see if it behaves in your IDE, please? 
author_association: COLLABORATOR
user: login: Myranae
created_at: 2025-04-23T20:51:25Z
body: Hmm, I ran `pnpm lint` and had no issues in GoLand. It looks like the `kas` package does not have `perseus-core` as a dependency, so I imported something from `perseus-core` into a file in the `kas` package, but the linter didn't flag it. Maybe I did that wrong?
author_association: CONTRIBUTOR
user: login: jeremywiebe
created_at: 2025-04-23T22:53:54Z
body: > Hmm, I ran `pnpm lint` and had no issues in GoLand. It looks like the `kas` package does not have `perseus-core` as a dependency, so I imported something from `perseus-core` into a file in the `kas` package, but the linter didn't flag it. Maybe I did that wrong?

Seems to work for me. 

<img width="842" alt="image" src="https://github.com/user-attachments/assets/acaafb77-e3cc-4fff-bd74-47abaecf75b6" />

author_association: COLLABORATOR