diff --git a/.vscode/settings.json b/.vscode/settings.json index 76de0a81ded..a6e68687d9b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,6 +15,7 @@ { "rule": "object-shorthand", "severity": "off", "fixable": true } ], "typescript.tsdk": "node_modules/typescript/lib", + "typescript.tsserver.pluginPaths": ["${workspaceFolder}/tools/ts-plugin-inherit-doc"], // Load .git-blame-ignore-revs file "gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"], "jestrunner.jestCommand": "pnpm exec cross-env NODE_OPTIONS=\"--no-deprecation\" node 'node_modules/jest/bin/jest.js'", diff --git a/package.json b/package.json index be2ce9d023a..8c5b03fd2f5 100644 --- a/package.json +++ b/package.json @@ -140,6 +140,7 @@ "@payloadcms/eslint-config": "workspace:*", "@payloadcms/eslint-plugin": "workspace:*", "@payloadcms/live-preview-react": "workspace:*", + "@payloadcms/ts-plugin-inherit-doc": "workspace:*", "@playwright/test": "1.54.1", "@sentry/nextjs": "^8.33.1", "@sentry/node": "^8.33.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95a4f18c84d..bfc5611ec42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,12 +39,15 @@ importers: '@payloadcms/live-preview-react': specifier: workspace:* version: link:packages/live-preview-react + '@payloadcms/ts-plugin-inherit-doc': + specifier: workspace:* + version: link:tools/ts-plugin-inherit-doc '@playwright/test': specifier: 1.54.1 version: 1.54.1 '@sentry/nextjs': specifier: ^8.33.1 - version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29)) + version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29)) '@sentry/node': specifier: ^8.33.1 version: 8.37.1 @@ -134,7 +137,7 @@ importers: version: 10.1.4(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) next: specifier: 15.4.4 - version: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + version: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) open: specifier: ^10.1.0 version: 10.1.0 @@ -1167,7 +1170,7 @@ importers: dependencies: next: specifier: ^15.2.3 - version: 15.2.3(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + version: 15.2.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) devDependencies: '@payloadcms/eslint-config': specifier: workspace:* @@ -1232,7 +1235,7 @@ importers: dependencies: '@sentry/nextjs': specifier: ^8.33.1 - version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29)) + version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29)) '@sentry/types': specifier: ^8.33.1 version: 8.37.1 @@ -1620,7 +1623,7 @@ importers: version: link:../plugin-cloud-storage uploadthing: specifier: 7.3.0 - version: 7.3.0(express@5.0.1)(next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(tailwindcss@4.1.13) + version: 7.3.0(express@5.0.1)(next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(tailwindcss@4.1.13) devDependencies: payload: specifier: workspace:* @@ -1819,7 +1822,7 @@ importers: version: 16.9.0 next: specifier: 15.4.4 - version: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + version: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) payload: specifier: workspace:* version: link:../../packages/payload @@ -1961,7 +1964,7 @@ importers: version: 8.6.0(react@19.1.1) geist: specifier: ^1.3.0 - version: 1.4.2(next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)) + version: 1.4.2(next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)) graphql: specifier: ^16.8.1 version: 16.9.0 @@ -1973,7 +1976,7 @@ importers: version: 0.477.0(react@19.1.1) next: specifier: ^15.5.4 - version: 15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + version: 15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) next-themes: specifier: 0.4.6 version: 0.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -2163,7 +2166,7 @@ importers: version: 16.4.7 geist: specifier: ^1.3.0 - version: 1.4.2(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)) + version: 1.4.2(next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)) graphql: specifier: ^16.8.1 version: 16.9.0 @@ -2172,10 +2175,10 @@ importers: version: 0.378.0(react@19.1.1) next: specifier: 15.4.4 - version: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + version: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) next-sitemap: specifier: ^4.2.3 - version: 4.2.3(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)) + version: 4.2.3(next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)) payload: specifier: workspace:* version: link:../../packages/payload @@ -2407,7 +2410,7 @@ importers: version: link:../packages/ui '@sentry/nextjs': specifier: ^8.33.1 - version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29)) + version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29)) '@sentry/react': specifier: ^7.77.0 version: 7.119.2(react@19.1.1) @@ -2479,7 +2482,7 @@ importers: version: 8.15.1(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) next: specifier: 15.4.4 - version: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + version: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) nodemailer: specifier: 7.0.9 version: 7.0.9 @@ -2623,6 +2626,22 @@ importers: specifier: workspace:* version: link:../../packages/translations + tools/ts-plugin-inherit-doc: + dependencies: + typescript: + specifier: 5.7.3 + version: 5.7.3 + devDependencies: + '@types/jest': + specifier: ^29.5.12 + version: 29.5.12 + '@types/node': + specifier: ^22.15.30 + version: 22.15.30 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.15.30)(babel-plugin-macros@3.1.0) + packages: '@alloc/quick-lru@5.2.0': @@ -20472,7 +20491,7 @@ snapshots: '@sentry/utils': 7.119.2 localforage: 1.10.0 - '@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29))': + '@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) @@ -20488,7 +20507,7 @@ snapshots: '@sentry/vercel-edge': 8.37.1 '@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.11.29)) chalk: 3.0.0 - next: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + next: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 @@ -20501,7 +20520,7 @@ snapshots: - supports-color - webpack - '@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29))': + '@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(react@19.1.1)(webpack@5.96.1(@swc/core@1.11.29))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) @@ -20517,7 +20536,7 @@ snapshots: '@sentry/vercel-edge': 8.37.1 '@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.11.29)) chalk: 3.0.0 - next: 15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + next: 15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 @@ -21968,7 +21987,7 @@ snapshots: '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 22.5.4 + '@types/node': 22.15.30 '@types/busboy@1.5.4': dependencies: @@ -25108,13 +25127,13 @@ snapshots: - encoding - supports-color - geist@1.4.2(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)): + geist@1.4.2(next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)): dependencies: - next: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + next: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) - geist@1.4.2(next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)): + geist@1.4.2(next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)): dependencies: - next: 15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + next: 15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) gel@2.0.1: dependencies: @@ -27029,13 +27048,13 @@ snapshots: transitivePeerDependencies: - supports-color - next-sitemap@4.2.3(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)): + next-sitemap@4.2.3(next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.11 fast-glob: 3.3.2 minimist: 1.2.8 - next: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + next: 15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) next-themes@0.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: @@ -27071,7 +27090,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.2.3(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): + next@15.2.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): dependencies: '@next/env': 15.2.3 '@swc/counter': 0.1.3 @@ -27099,33 +27118,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): - dependencies: - '@next/env': 15.4.4 - '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001720 - postcss: 8.4.31 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - styled-jsx: 5.1.6(@babel/core@7.27.4)(babel-plugin-macros@3.1.0)(react@19.1.1) - optionalDependencies: - '@next/swc-darwin-arm64': 15.4.4 - '@next/swc-darwin-x64': 15.4.4 - '@next/swc-linux-arm64-gnu': 15.4.4 - '@next/swc-linux-arm64-musl': 15.4.4 - '@next/swc-linux-x64-gnu': 15.4.4 - '@next/swc-linux-x64-musl': 15.4.4 - '@next/swc-win32-arm64-msvc': 15.4.4 - '@next/swc-win32-x64-msvc': 15.4.4 - '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.54.1 - sass: 1.77.4 - sharp: 0.34.3 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - - next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): + next@15.4.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): dependencies: '@next/env': 15.4.4 '@swc/helpers': 0.5.15 @@ -27152,7 +27145,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): + next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): dependencies: '@next/env': 15.5.4 '@swc/helpers': 0.5.15 @@ -27178,7 +27171,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): + next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4): dependencies: '@next/env': 15.5.4 '@swc/helpers': 0.5.15 @@ -29703,7 +29696,7 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - uploadthing@7.3.0(express@5.0.1)(next@15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(tailwindcss@4.1.13): + uploadthing@7.3.0(express@5.0.1)(next@15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4))(tailwindcss@4.1.13): dependencies: '@effect/platform': 0.69.8(effect@3.10.3) '@uploadthing/mime-types': 0.3.2 @@ -29711,7 +29704,7 @@ snapshots: effect: 3.10.3 optionalDependencies: express: 5.0.1 - next: 15.5.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) + next: 15.5.4(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.77.4) tailwindcss: 4.1.13 uri-js@4.4.1: diff --git a/tools/ts-plugin-inherit-doc/README.md b/tools/ts-plugin-inherit-doc/README.md new file mode 100644 index 00000000000..d0b1b7a1775 --- /dev/null +++ b/tools/ts-plugin-inherit-doc/README.md @@ -0,0 +1,48 @@ +# TypeScript JSDoc Inheritance Plugin + +A TypeScript Language Service Plugin that enables documentation inheritance via `@inheritDoc` tag. + +## Usage + +1. Build the plugin: + +```bash +cd tools/ts-plugin-inherit-doc +pnpm install +pnpm build +``` + +2. Add to your `tsconfig.json`: + +```json +{ + "compilerOptions": { + "plugins": [{ "name": "@payloadcms/ts-plugin-inherit-doc" }] + } +} +``` + +3. Use in your code: + +```javascript +/** + * Represents a user in the system + * @typedef {Object} User + * @property {string} id - Unique identifier + * @property {string} name - User's full name + */ + +/** + * @typedef {Object} UserResponse + * @property {User} user - @inheritDoc User + * @property {string} token - Auth token + */ +``` + +When you hover over `user` property, you'll see the inherited documentation from `User`. + +## Notes + +- The plugin rebuilds the documentation cache on each hover (optimization needed for production) +- Only works with `@typedef` JSDoc declarations +- VSCode requires TypeScript workspace version to use plugins diff --git a/tools/ts-plugin-inherit-doc/jest.config.js b/tools/ts-plugin-inherit-doc/jest.config.js new file mode 100644 index 00000000000..4a997307ffa --- /dev/null +++ b/tools/ts-plugin-inherit-doc/jest.config.js @@ -0,0 +1,20 @@ +/** @type {import('jest').Config} */ +const customJestConfig = { + extensionsToTreatAsEsm: ['.ts', '.tsx'], + // setupFilesAfterEnv: ['/jest.setup.js'], + moduleNameMapper: { + '\\.(css|scss)$': '/helpers/mocks/emptyModule.js', + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': + '/test/helpers/mocks/fileMock.js', + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + testEnvironment: 'node', + testMatch: ['/**/*spec.ts'], + testTimeout: 160000, + transform: { + '^.+\\.(t|j)sx?$': ['@swc/jest'], + }, + verbose: true, +} + +export default customJestConfig diff --git a/tools/ts-plugin-inherit-doc/package.json b/tools/ts-plugin-inherit-doc/package.json new file mode 100644 index 00000000000..962da9aaaa7 --- /dev/null +++ b/tools/ts-plugin-inherit-doc/package.json @@ -0,0 +1,25 @@ +{ + "name": "@payloadcms/ts-plugin-inherit-doc", + "version": "1.0.0", + "description": "TypeScript Language Service Plugin for JSDoc documentation inheritance", + "keywords": [ + "typescript", + "plugin", + "jsdoc" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "jest", + "watch": "tsc --watch" + }, + "dependencies": { + "typescript": "^5.7.2" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "^22.15.30", + "jest": "^29.7.0" + } +} diff --git a/tools/ts-plugin-inherit-doc/src/index.spec.ts b/tools/ts-plugin-inherit-doc/src/index.spec.ts new file mode 100644 index 00000000000..a7b69163602 --- /dev/null +++ b/tools/ts-plugin-inherit-doc/src/index.spec.ts @@ -0,0 +1,156 @@ +import * as fs from 'fs' +import * as os from 'os' +import * as path from 'path' +import * as ts from 'typescript/lib/tsserverlibrary' + +// eslint-disable-next-line @typescript-eslint/no-require-imports +const pluginFactory = require('./index') + +describe('ts-plugin-inherit-doc', () => { + let tempDir: string + let testFilePath: string + + beforeEach(() => { + // Create temporary directory for test files + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ts-plugin-test-')) + testFilePath = path.join(tempDir, 'test.ts') + }) + + afterEach(() => { + // Clean up + fs.rmSync(tempDir, { recursive: true, force: true }) + }) + + function createLanguageService(fileContent: string): ts.LanguageService { + fs.writeFileSync(testFilePath, fileContent) + + const servicesHost: ts.LanguageServiceHost = { + getScriptFileNames: () => [testFilePath], + getScriptVersion: () => '1', + getScriptSnapshot: (fileName) => { + if (fileName === testFilePath) { + const text = fs.readFileSync(fileName, 'utf8') + return ts.ScriptSnapshot.fromString(text) + } + return undefined + }, + getCurrentDirectory: () => tempDir, + getCompilationSettings: () => ({ + target: ts.ScriptTarget.ES2020, + module: ts.ModuleKind.CommonJS, + }), + getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options), + fileExists: ts.sys.fileExists, + readFile: ts.sys.readFile, + readDirectory: ts.sys.readDirectory, + directoryExists: ts.sys.directoryExists, + getDirectories: ts.sys.getDirectories, + } + + const languageService = ts.createLanguageService(servicesHost) + + // Initialize plugin + const plugin = pluginFactory({ typescript: ts }) + const pluginInfo: ts.server.PluginCreateInfo = { + languageService, + languageServiceHost: servicesHost, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + project: {} as any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + serverHost: {} as any, + config: {}, + } + + return plugin.create(pluginInfo) + } + + it('plugin adds debug marker to all hovers', () => { + const content = ` +type MyType = { + prop: string +} +` + const ls = createLanguageService(content) + const position = content.indexOf('prop') + const quickInfo = ls.getQuickInfoAtPosition(testFilePath, position) + + expect(quickInfo).toBeDefined() + expect(quickInfo!.documentation).toBeDefined() + }) + + it('inherits documentation from referenced type', () => { + const content = ` +/** + * Base type with documentation + */ +type BaseType = { + /** + * A property + */ + prop: string +} + +type DerivedType = { + /** + * @inheritDoc BaseType + */ + field: BaseType +} +` + const ls = createLanguageService(content) + const position = content.indexOf('field:') + const quickInfo = ls.getQuickInfoAtPosition(testFilePath, position) + + expect(quickInfo).toBeDefined() + const docText = quickInfo!.documentation!.map((p) => p.text).join('') + expect(docText).toContain('Base type with documentation') + expect(docText).toContain('prop: A property') + }) + + it('shows debug message when type not found', () => { + const content = ` +type MyType = { + /** + * @inheritDoc NonExistentType + */ + field: string +} +` + const ls = createLanguageService(content) + const position = content.indexOf('field:') + const quickInfo = ls.getQuickInfoAtPosition(testFilePath, position) + + expect(quickInfo).toBeDefined() + const docText = quickInfo!.documentation!.map((p) => p.text).join('') + expect(docText).toContain('NonExistentType') + expect(docText).toContain('not found') + }) + + it('works with interface declarations', () => { + const content = ` +/** + * Interface documentation + */ +interface MyInterface { + /** + * Method docs + */ + method(): void +} + +type MyType = { + /** + * @inheritDoc MyInterface + */ + field: MyInterface +} +` + const ls = createLanguageService(content) + const position = content.lastIndexOf('field:') + const quickInfo = ls.getQuickInfoAtPosition(testFilePath, position) + + expect(quickInfo).toBeDefined() + const docText = quickInfo!.documentation!.map((p) => p.text).join('') + expect(docText).toContain('Interface documentation') + }) +}) diff --git a/tools/ts-plugin-inherit-doc/src/index.ts b/tools/ts-plugin-inherit-doc/src/index.ts new file mode 100644 index 00000000000..968a742301a --- /dev/null +++ b/tools/ts-plugin-inherit-doc/src/index.ts @@ -0,0 +1,273 @@ +import type * as ts from 'typescript/lib/tsserverlibrary' + +interface DocCache { + [typeName: string]: { + documentation: string + properties: Map + } +} + +function init(modules: { typescript: typeof import('typescript/lib/tsserverlibrary') }) { + const ts = modules.typescript + let docCache: DocCache = {} + + function create(info: ts.server.PluginCreateInfo) { + const proxy: ts.LanguageService = Object.create(null) + const oldLS = info.languageService + const program = oldLS.getProgram() + + // Build documentation cache + function buildDocCache() { + if (!program) { + return + } + + docCache = {} + const checker = program.getTypeChecker() + const sourceFiles = program.getSourceFiles().filter((sf) => !sf.isDeclarationFile) + + for (const sourceFile of sourceFiles) { + ts.forEachChild(sourceFile, visitNode) + } + + function visitNode(node: ts.Node) { + let typeName: string | undefined + let jsDoc: readonly ts.JSDoc[] | undefined + + // Handle type alias declarations + if (ts.isTypeAliasDeclaration(node)) { + typeName = node.name.text + jsDoc = (node as any).jsDoc + } + // Handle interface declarations + else if (ts.isInterfaceDeclaration(node)) { + typeName = node.name.text + jsDoc = (node as any).jsDoc + } + // Handle @typedef JSDoc declarations + else if (ts.isVariableStatement(node) || ts.isExpressionStatement(node)) { + const jsDocTags = ts.getJSDocTags(node) + const typedefTag = jsDocTags.find((tag) => tag.tagName.text === 'typedef') + if (typedefTag && ts.isJSDocTypedefTag(typedefTag)) { + typeName = typedefTag.name?.getText() + jsDoc = [node as any] + .filter((n: any) => n.jsDoc) + .flatMap((n: any) => n.jsDoc) as ts.JSDoc[] + } + } + + if (typeName && jsDoc && jsDoc.length > 0) { + const fullComment = ts.getTextOfJSDocComment(jsDoc[0].comment) + const properties = new Map() + + // Extract property documentation from JSDoc tags + for (const doc of jsDoc) { + if (doc.tags) { + for (const tag of doc.tags) { + if (ts.isJSDocPropertyTag(tag)) { + const propName = tag.name.getText() + const propDoc = ts.getTextOfJSDocComment(tag.comment) || '' + properties.set(propName, propDoc) + } + } + } + } + + // Also extract properties from type literal or interface members + if (ts.isTypeAliasDeclaration(node) && node.type && ts.isTypeLiteralNode(node.type)) { + for (const member of node.type.members) { + if (ts.isPropertySignature(member) && member.name) { + const propName = member.name.getText() + const propJsDoc = (member as any).jsDoc as ts.JSDoc[] | undefined + if (propJsDoc && propJsDoc.length > 0) { + const propDoc = ts.getTextOfJSDocComment(propJsDoc[0].comment) || '' + properties.set(propName, propDoc) + } + } + } + } else if (ts.isInterfaceDeclaration(node)) { + for (const member of node.members) { + if (ts.isPropertySignature(member) && member.name) { + const propName = member.name.getText() + const propJsDoc = (member as any).jsDoc as ts.JSDoc[] | undefined + if (propJsDoc && propJsDoc.length > 0) { + const propDoc = ts.getTextOfJSDocComment(propJsDoc[0].comment) || '' + properties.set(propName, propDoc) + } + } + } + } + + docCache[typeName] = { + documentation: fullComment || '', + properties, + } + } + + ts.forEachChild(node, visitNode) + } + } + + // Parse @inheritDoc tag from JSDoc comment + function parseInheritDoc(comment: string): null | string { + const match = comment.match(/@inheritDoc\s+(\w+)/) + return match ? match[1] : null + } + + // Merge documentation from referenced type + function mergeDocumentation( + original: string | ts.SymbolDisplayPart[] | undefined, + inheritFrom: string, + ): ts.SymbolDisplayPart[] { + if (!docCache[inheritFrom]) { + const result: ts.SymbolDisplayPart[] = + typeof original === 'string' ? [{ text: original, kind: 'text' }] : original || [] + result.push({ + text: `\n\n[DEBUG: Type "${inheritFrom}" not found. Available: ${Object.keys(docCache).join(', ')}]`, + kind: 'text', + }) + return result + } + + const inherited = docCache[inheritFrom] + const result: ts.SymbolDisplayPart[] = [] + + // Add inherited documentation + if (inherited.documentation) { + result.push({ text: inherited.documentation, kind: 'text' }) + } + + // Add inherited properties + if (inherited.properties.size > 0) { + result.push({ text: '\n\nProperties:\n', kind: 'text' }) + inherited.properties.forEach((doc, name) => { + result.push({ text: `• ${name}: ${doc}\n`, kind: 'text' }) + }) + } + + return result + } + + // Intercept hover requests + proxy.getQuickInfoAtPosition = (fileName: string, position: number) => { + const quickInfo = oldLS.getQuickInfoAtPosition(fileName, position) + if (!quickInfo) { + return quickInfo + } + + // Rebuild cache on each request (in production, you'd want smarter invalidation) + buildDocCache() + + // Get the source file and node at position to check for JSDoc + const sourceFile = program?.getSourceFile(fileName) + if (!sourceFile) { + return quickInfo + } + + // Find the node at the cursor position + const node = findNodeAtPosition(sourceFile, position) + if (!node) { + return quickInfo + } + + // Get JSDoc from the node or its parent (for property signatures) + let inheritFrom: null | string = null + + // Check current node and parent for JSDoc + const nodesToCheck = [node, node.parent].filter(Boolean) + + for (const n of nodesToCheck) { + if (!n) {continue} + + // Check if node has jsDoc property (TypeScript internal) + const jsDoc = (n as any).jsDoc as ts.JSDoc[] | undefined + if (jsDoc && jsDoc.length > 0) { + for (const doc of jsDoc) { + if (doc.tags) { + for (const tag of doc.tags) { + if (tag.tagName.text === 'inheritDoc') { + const commentText = + typeof tag.comment === 'string' + ? tag.comment + : ts.getTextOfJSDocComment(tag.comment) + + if (commentText) { + inheritFrom = commentText.trim() + break + } + } + // Also check if the whole comment contains @inheritDoc + if (tag.comment) { + const commentText = + typeof tag.comment === 'string' + ? tag.comment + : ts.getTextOfJSDocComment(tag.comment) + + if (commentText) { + const parsed = parseInheritDoc(commentText) + if (parsed) { + inheritFrom = parsed + break + } + } + } + } + } + // Check the doc comment itself + if (!inheritFrom && doc.comment) { + const commentText = + typeof doc.comment === 'string' + ? doc.comment + : ts.getTextOfJSDocComment(doc.comment) + + if (commentText) { + inheritFrom = parseInheritDoc(commentText) + } + } + if (inheritFrom) {break} + } + } + if (inheritFrom) {break} + } + + // If no @inheritDoc in tags, check the documentation text as fallback + if (!inheritFrom) { + const docParts = quickInfo.documentation || [] + const docText = docParts.map((part) => part.text).join('') + inheritFrom = parseInheritDoc(docText) + } + + if (inheritFrom) { + return { + ...quickInfo, + documentation: mergeDocumentation(quickInfo.documentation, inheritFrom), + } + } + + return quickInfo + } + + function findNodeAtPosition(sourceFile: ts.SourceFile, position: number): ts.Node | undefined { + function find(node: ts.Node): ts.Node | undefined { + if (position >= node.getStart() && position < node.getEnd()) { + return ts.forEachChild(node, find) || node + } + return undefined + } + return find(sourceFile) + } + + // Proxy all other methods + return new Proxy(oldLS, { + get: (target, prop) => { + return prop in proxy + ? proxy[prop as keyof ts.LanguageService] + : target[prop as keyof ts.LanguageService] + }, + }) + } + + return { create } +} + +export = init diff --git a/tools/ts-plugin-inherit-doc/tsconfig.json b/tools/ts-plugin-inherit-doc/tsconfig.json new file mode 100644 index 00000000000..eb56c8eb160 --- /dev/null +++ b/tools/ts-plugin-inherit-doc/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/tsconfig.json b/tsconfig.json index 58c299a78ee..e303c9fff82 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,11 @@ "composite": false, "noEmit": true, "baseUrl": ".", + "plugins": [ + { + "name": "@payloadcms/ts-plugin-inherit-doc" + } + ] }, "references": [ {