From 52d279858203e561faa97642a5110283d3956a07 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 4 Dec 2025 18:40:59 -0500 Subject: [PATCH] chore: Update to next 16, react 19, add react compiler (#1434) fixes: HDX-2956 Co-authored-by: Brandon Pereira <7552738+brandon-pereira@users.noreply.github.com> --- .changeset/fair-points-float.md | 7 + .gitignore | 1 + .nvmrc | 2 +- .vscode/settings.json | 9 +- agent_docs/development.md | 2 +- docker/hyperdx/Dockerfile | 2 +- nx.json | 19 +- package.json | 18 +- packages/api/.eslintignore | 5 - packages/api/.eslintrc.js | 44 - packages/api/eslint.config.mjs | 99 + ...-add_accessKey_field_to_user_collection.ts | 4 +- packages/app/.eslintrc.js | 64 - packages/app/.storybook/main.ts | 9 +- packages/app/.storybook/preview.tsx | 13 +- packages/app/Dockerfile | 6 +- packages/app/eslint.config.mjs | 114 + packages/app/next-env.d.ts | 5 - packages/app/next.config.js | 67 - packages/app/next.config.mjs | 84 + packages/app/package.json | 57 +- packages/app/pages/_document.tsx | 2 + packages/app/src/AppNav.tsx | 10 +- packages/app/src/AutocompleteInput.tsx | 2 +- packages/app/src/DBChartPage.tsx | 2 +- packages/app/src/DBDashboardPage.tsx | 8 +- packages/app/src/DBSearchPage.tsx | 2 +- .../src/HDXMultiSeriesTableChart.stories.tsx | 2 +- .../app/src/LogSidePanelElements.stories.tsx | 2 +- packages/app/src/ServicesDashboardPage.tsx | 9 +- packages/app/src/TimelineChart.tsx | 2 +- packages/app/src/UserPreferencesModal.tsx | 2 +- .../app/src/__mocks__/react-json-tree.tsx | 4 + .../app/src/components/AlertPreviewChart.tsx | 8 +- .../components/ColorSwatchInput.stories.tsx | 2 +- .../src/components/DBEditTimeChartForm.tsx | 48 +- .../app/src/components/DBHistogramChart.tsx | 4 +- .../app/src/components/DBListBarChart.tsx | 4 +- .../app/src/components/DBRowJsonViewer.tsx | 4 +- .../DBTable/DBRowTableFieldWithPopover.tsx | 6 +- .../src/components/DBTable/TableHeader.tsx | 1 + .../src/components/ErrorBoundary.stories.tsx | 2 +- .../app/src/components/HyperJson.module.scss | 2 +- .../app/src/components/HyperJson.stories.tsx | 2 +- .../app/src/components/MetricNameSelect.tsx | 8 +- packages/app/src/components/NumberFormat.tsx | 4 +- packages/app/src/components/SourceSelect.tsx | 2 +- packages/app/src/components/Table.tsx | 2 +- packages/app/src/components/Tags.tsx | 4 +- .../TimePicker/TimePicker.stories.tsx | 2 +- .../__tests__/InputControlled.test.tsx | 8 +- packages/app/src/sessions.ts | 2 +- packages/app/src/stories/AppNav.stories.tsx | 4 +- packages/app/src/stories/Button.stories.tsx | 2 +- packages/app/src/timeQuery.ts | 4 +- packages/app/src/utils.ts | 2 +- .../app/tests/e2e/features/dashboard.spec.ts | 7 +- packages/app/tsconfig.json | 33 +- packages/common-utils/.eslintignore | 7 - packages/common-utils/.eslintrc.js | 43 - packages/common-utils/eslint.config.mjs | 94 + packages/common-utils/src/types.ts | 1 + yarn.lock | 8679 ++++++----------- 63 files changed, 3710 insertions(+), 5959 deletions(-) create mode 100644 .changeset/fair-points-float.md delete mode 100644 packages/api/.eslintignore delete mode 100644 packages/api/.eslintrc.js create mode 100644 packages/api/eslint.config.mjs delete mode 100644 packages/app/.eslintrc.js create mode 100644 packages/app/eslint.config.mjs delete mode 100644 packages/app/next-env.d.ts delete mode 100644 packages/app/next.config.js create mode 100644 packages/app/next.config.mjs create mode 100644 packages/app/src/__mocks__/react-json-tree.tsx delete mode 100644 packages/common-utils/.eslintignore delete mode 100644 packages/common-utils/.eslintrc.js create mode 100644 packages/common-utils/eslint.config.mjs diff --git a/.changeset/fair-points-float.md b/.changeset/fair-points-float.md new file mode 100644 index 00000000..1d42440a --- /dev/null +++ b/.changeset/fair-points-float.md @@ -0,0 +1,7 @@ +--- +"@hyperdx/common-utils": minor +"@hyperdx/api": minor +"@hyperdx/app": minor +--- + +chore: Upgrade nextjs, react, and eslint + add react compiler diff --git a/.gitignore b/.gitignore index 41ca8e7c..180f8b26 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ packages/app/.pnp.js packages/app/.vercel packages/app/coverage packages/app/out +packages/app/next-env.d.ts # optional npm cache directory **/.npm diff --git a/.nvmrc b/.nvmrc index aebd91c5..5767036a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v22.16.0 +22.21.1 diff --git a/.vscode/settings.json b/.vscode/settings.json index dd85da40..07ac540b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -35,6 +35,13 @@ "**/yarn.lock": true, "**/yarn-*.cjs": true }, - "cSpell.words": ["micropip", "opamp", "pyimport", "pyodide"], + "cSpell.words": [ + "clickhouse", + "hyperdx", + "micropip", + "opamp", + "pyimport", + "pyodide" + ], "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/agent_docs/development.md b/agent_docs/development.md index 4f2a61cf..4d97692f 100644 --- a/agent_docs/development.md +++ b/agent_docs/development.md @@ -101,7 +101,7 @@ yarn run lint ## File Locations Quick Reference -- **Config**: `packages/api/src/config.ts`, `packages/app/next.config.js`, `docker-compose.dev.yml` +- **Config**: `packages/api/src/config.ts`, `packages/app/next.config.mjs`, `docker-compose.dev.yml` - **Models**: `packages/api/src/models/` - **API Routes**: `packages/api/src/routers/` - **Controllers**: `packages/api/src/controllers/` diff --git a/docker/hyperdx/Dockerfile b/docker/hyperdx/Dockerfile index 13faaee2..8f5cc400 100644 --- a/docker/hyperdx/Dockerfile +++ b/docker/hyperdx/Dockerfile @@ -25,7 +25,7 @@ COPY .yarn ./.yarn COPY .yarnrc.yml yarn.lock package.json nx.json .prettierrc .prettierignore ./tsconfig.base.json ./ COPY ./packages/common-utils ./packages/common-utils COPY ./packages/api/jest.config.js ./packages/api/tsconfig.json ./packages/api/tsconfig.build.json ./packages/api/package.json ./packages/api/ -COPY ./packages/app/jest.config.js ./packages/app/tsconfig.json ./packages/app/tsconfig.build.json ./packages/app/package.json ./packages/app/next.config.js ./packages/app/mdx.d.ts ./packages/app/.eslintrc.js ./packages/app/ +COPY ./packages/app/jest.config.js ./packages/app/tsconfig.json ./packages/app/tsconfig.build.json ./packages/app/package.json ./packages/app/next.config.mjs ./packages/app/mdx.d.ts ./packages/app/eslint.config.mjs ./packages/app/ # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat diff --git a/nx.json b/nx.json index f5796747..48343419 100644 --- a/nx.json +++ b/nx.json @@ -8,35 +8,26 @@ }, "targetDefaults": { "build": { - "dependsOn": [ - "^build" - ] + "dependsOn": ["^build"] } }, "tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default", "options": { - "cacheableOperations": [ - "build", - "lint", - "test" - ] + "cacheableOperations": ["build", "lint", "test"] } } }, "namedInputs": { - "default": [ - "{projectRoot}/**/*", - "sharedGlobals" - ], + "default": ["{projectRoot}/**/*", "sharedGlobals"], "production": [ "default", "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", "!{projectRoot}/tsconfig.spec.json", "!{projectRoot}/jest.config.[jt]s", - "!{projectRoot}/.eslintrc.json" + "!{projectRoot}/eslint.config.mjs" ], "sharedGlobals": [] } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 91211ac8..f67ddd98 100644 --- a/package.json +++ b/package.json @@ -9,17 +9,19 @@ "devDependencies": { "@changesets/cli": "^2.26.2", "@nx/workspace": "21.3.11", - "@typescript-eslint/eslint-plugin": "^8.7.0", - "@typescript-eslint/parser": "^8.7.0", + "@typescript-eslint/eslint-plugin": "^8.48.1", + "@typescript-eslint/parser": "^8.48.1", + "babel-plugin-react-compiler": "^1.0.0", "concurrently": "^9.1.2", "dotenv": "^16.4.7", "dotenv-cli": "^8.0.0", "dotenv-expand": "^12.0.1", - "eslint": "^8.57.0", - "eslint-config-next": "13", + "eslint": "^9.39.1", + "eslint-config-next": "16.0.7", "eslint-config-prettier": "^9.1.0", "eslint-plugin-n": "^16.4.0", "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-security": "^2.1.0", "eslint-plugin-simple-import-sort": "^12.1.1", "husky": "^8.0.3", @@ -43,12 +45,12 @@ "lint-staged": { "packages/api/src/routers/external-api/**/*.ts": [ "prettier --write --ignore-unknown", - "eslint --fix --quiet", + "eslint --flag v10_config_lookup_from_file --fix --quiet", "sh -c 'cd packages/api && yarn run docgen && git add openapi.json'" ], "**/*.{ts,tsx}": [ "prettier --write --ignore-unknown", - "eslint --fix --quiet" + "eslint --flag v10_config_lookup_from_file --fix --quiet" ], "**/*.{mdx,json,yml}": [ "prettier --write --ignore-unknown" @@ -56,8 +58,8 @@ }, "packageManager": "yarn@4.5.1", "resolutions": { - "@types/react": "18.3.1", - "@types/react-dom": "18.3.1", + "@types/react": "19.0.7", + "@types/react-dom": "19.0.3", "@types/express": "4.17.21", "@types/express-serve-static-core": "4.17.43" } diff --git a/packages/api/.eslintignore b/packages/api/.eslintignore deleted file mode 100644 index 37e98a58..00000000 --- a/packages/api/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -keys -node_modules -archive -migrate-mongo-config.ts -jest.setup.ts diff --git a/packages/api/.eslintrc.js b/packages/api/.eslintrc.js deleted file mode 100644 index 120f52a0..00000000 --- a/packages/api/.eslintrc.js +++ /dev/null @@ -1,44 +0,0 @@ -module.exports = { - root: true, - ignorePatterns: ['migrate-mongo-config.ts'], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint', 'prettier', 'simple-import-sort'], - parserOptions: { - tsconfigRootDir: __dirname, - project: ['./tsconfig.json'], - }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:n/recommended', - 'plugin:prettier/recommended', - 'plugin:security/recommended-legacy', - ], - rules: { - '@typescript-eslint/ban-ts-comment': 'warn', - '@typescript-eslint/no-empty-interface': 'off', - '@typescript-eslint/no-empty-object-type': 'warn', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/no-namespace': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - 'n/no-process-exit': 'warn', - 'n/no-missing-import': 'off', - 'n/no-unpublished-import': [ - 'error', - { - allowModules: ['mongodb', 'supertest'], - }, - ], - 'n/no-unsupported-features/es-syntax': [ - 'error', - { - ignores: ['modules'], - }, - ], - 'prettier/prettier': 'error', - 'simple-import-sort/imports': 'error', - 'simple-import-sort/exports': 'error', - }, -}; diff --git a/packages/api/eslint.config.mjs b/packages/api/eslint.config.mjs new file mode 100644 index 00000000..e5c2f447 --- /dev/null +++ b/packages/api/eslint.config.mjs @@ -0,0 +1,99 @@ +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import prettierConfig from 'eslint-config-prettier'; +import prettierPlugin from 'eslint-plugin-prettier'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; +import nodePlugin from 'eslint-plugin-n'; +import securityPlugin from 'eslint-plugin-security'; + +export default [ + js.configs.recommended, + ...tseslint.configs.recommended, + prettierConfig, + { + ignores: [ + 'node_modules/**', + 'build/**', + 'dist/**', + 'coverage/**', + 'src/coverage/**', + 'migrations/**', + 'migrate-mongo-config.ts', + '**/*.config.js', + '**/*.config.mjs', + 'jest.config.js', + 'jest.setup.ts', + ], + }, + { + files: ['src/**/*.ts', '!migrations/**'], + plugins: { + '@typescript-eslint': tseslint.plugin, + 'simple-import-sort': simpleImportSort, + 'prettier': prettierPlugin, + 'n': nodePlugin, + 'security': securityPlugin, + }, + rules: { + ...nodePlugin.configs.recommended.rules, + ...securityPlugin.configs['recommended-legacy'].rules, + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-empty-object-type': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-namespace': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'n/no-process-exit': 'warn', + 'n/no-missing-import': 'off', + 'n/no-unpublished-import': [ + 'error', + { + allowModules: ['mongodb', 'supertest'], + }, + ], + 'n/no-unsupported-features/es-syntax': [ + 'error', + { + ignores: ['modules'], + }, + ], + 'prettier/prettier': 'error', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.json', + tsconfigRootDir: import.meta.dirname, + }, + globals: { + console: 'readonly', + process: 'readonly', + module: 'readonly', + require: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + exports: 'readonly', + Buffer: 'readonly', + setTimeout: 'readonly', + clearTimeout: 'readonly', + setInterval: 'readonly', + clearInterval: 'readonly', + setImmediate: 'readonly', + clearImmediate: 'readonly', + global: 'readonly', + }, + }, + }, +]; + diff --git a/packages/api/migrations/mongo/20231130053610-add_accessKey_field_to_user_collection.ts b/packages/api/migrations/mongo/20231130053610-add_accessKey_field_to_user_collection.ts index 94712698..b1bd1fda 100644 --- a/packages/api/migrations/mongo/20231130053610-add_accessKey_field_to_user_collection.ts +++ b/packages/api/migrations/mongo/20231130053610-add_accessKey_field_to_user_collection.ts @@ -2,12 +2,12 @@ import { Db, MongoClient } from 'mongodb'; import { v4 as uuidv4 } from 'uuid'; module.exports = { - async up(db: Db, client: MongoClient) { + async up(db: Db, _client: MongoClient) { await db .collection('users') .updateMany({}, { $set: { accessKey: uuidv4() } }); }, - async down(db: Db, client: MongoClient) { + async down(db: Db, _client: MongoClient) { await db.collection('users').updateMany({}, { $unset: { accessKey: '' } }); }, }; diff --git a/packages/app/.eslintrc.js b/packages/app/.eslintrc.js deleted file mode 100644 index 550b33de..00000000 --- a/packages/app/.eslintrc.js +++ /dev/null @@ -1,64 +0,0 @@ -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: [ - 'simple-import-sort', - '@typescript-eslint', - 'prettier', - 'react-hooks', - ], - parserOptions: { - tsconfigRootDir: __dirname, - project: ['./tsconfig.json'], - }, - extends: [ - 'next', - 'eslint:recommended', - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - rules: { - '@typescript-eslint/ban-ts-comment': 'warn', - '@typescript-eslint/no-empty-function': 'warn', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-function-type': 'warn', - '@typescript-eslint/no-unused-expressions': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - 'react/display-name': 'off', - 'simple-import-sort/exports': 'error', - 'simple-import-sort/imports': 'error', - 'react-hooks/exhaustive-deps': 'error', - 'no-console': ['error', { allow: ['warn', 'error'] }], - }, - overrides: [ - { - files: ['**/*.js', '**/*.ts', '**/*.tsx'], - rules: { - 'simple-import-sort/imports': [ - 'error', - { - groups: [ - ['^react$', '^next', '^[a-z]', '^@'], - ['^@/'], - ['^\\.\\.(?!/?$)', '^\\.\\./?$'], - ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'], - ['^.+\\.s?css$'], - ['^\\u0000'], - ], - }, - ], - }, - }, - { - // Disable strict rules for E2E test files - files: ['tests/e2e/**/*.ts', 'tests/e2e/**/*.js'], - rules: { - 'no-console': 'off', - 'no-empty': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@next/next/no-html-link-for-pages': 'off', - }, - }, - ], -}; diff --git a/packages/app/.storybook/main.ts b/packages/app/.storybook/main.ts index 7f9c6227..a1eed137 100644 --- a/packages/app/.storybook/main.ts +++ b/packages/app/.storybook/main.ts @@ -1,6 +1,10 @@ -import { join, dirname } from 'path'; +// This file has been automatically migrated to valid ESM format by Storybook. +import { createRequire } from 'node:module'; +import { dirname, join } from 'path'; import type { StorybookConfig } from '@storybook/nextjs'; +const require = createRequire(import.meta.url); + function getAbsolutePath(value: string): any { return dirname(require.resolve(join(value, 'package.json'))); } @@ -9,9 +13,8 @@ const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ getAbsolutePath('@storybook/addon-links'), - getAbsolutePath('@storybook/addon-essentials'), - getAbsolutePath('@storybook/addon-interactions'), getAbsolutePath('@storybook/addon-styling-webpack'), + getAbsolutePath('@storybook/addon-docs'), ], framework: { name: getAbsolutePath('@storybook/nextjs'), diff --git a/packages/app/.storybook/preview.tsx b/packages/app/.storybook/preview.tsx index b80f6abd..e698f59d 100644 --- a/packages/app/.storybook/preview.tsx +++ b/packages/app/.storybook/preview.tsx @@ -1,21 +1,20 @@ import React from 'react'; -import type { Preview } from '@storybook/react'; +import { NextAdapter } from 'next-query-params'; import { initialize, mswLoader } from 'msw-storybook-addon'; import { QueryClient, QueryClientProvider } from 'react-query'; import { QueryParamProvider } from 'use-query-params'; -import { NextAdapter } from 'next-query-params'; +import type { Preview } from '@storybook/nextjs'; + +import { meHandler } from '../src/mocks/handlers'; +import { ThemeWrapper } from '../src/ThemeWrapper'; import '@mantine/core/styles.css'; import '@mantine/notifications/styles.css'; import '@mantine/dates/styles.css'; import '@mantine/dropzone/styles.css'; - import '../styles/globals.css'; import '../styles/app.scss'; -import { meHandler } from '../src/mocks/handlers'; -import { ThemeWrapper } from '../src/ThemeWrapper'; - export const parameters = { layout: 'fullscreen', options: { @@ -61,7 +60,7 @@ const preview: Preview = { msw: { handlers: [meHandler], }, - backgrounds: { disable: true }, + backgrounds: { disabled: true }, }, }; diff --git a/packages/app/Dockerfile b/packages/app/Dockerfile index 4c7525d2..b8cec077 100644 --- a/packages/app/Dockerfile +++ b/packages/app/Dockerfile @@ -7,7 +7,7 @@ WORKDIR /app COPY .yarn ./.yarn COPY .yarnrc.yml yarn.lock package.json nx.json .prettierrc .prettierignore ./tsconfig.base.json ./ COPY ./packages/common-utils ./packages/common-utils -COPY ./packages/app/jest.config.js ./packages/app/tsconfig.json ./packages/app/tsconfig.build.json ./packages/app/package.json ./packages/app/next.config.js ./packages/app/mdx.d.ts ./packages/app/.eslintrc.js ./packages/app/ +COPY ./packages/app/jest.config.js ./packages/app/tsconfig.json ./packages/app/tsconfig.build.json ./packages/app/package.json ./packages/app/next.config.mjs ./packages/app/mdx.d.ts ./packages/app/eslint.config.mjs ./packages/app/ RUN yarn install --mode=skip-build && yarn cache clean @@ -51,8 +51,8 @@ ENV NODE_ENV production RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 -# You only need to copy next.config.js if you are NOT using the default configuration -COPY --from=builder /app/packages/app/next.config.js ./ +# You only need to copy next.config.mjs if you are NOT using the default configuration +COPY --from=builder /app/packages/app/next.config.mjs ./ COPY --from=builder --chown=nextjs:nodejs /app/packages/app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/packages/app/.next ./.next COPY --from=builder /app/node_modules ./node_modules diff --git a/packages/app/eslint.config.mjs b/packages/app/eslint.config.mjs new file mode 100644 index 00000000..9bc7310f --- /dev/null +++ b/packages/app/eslint.config.mjs @@ -0,0 +1,114 @@ +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import storybook from 'eslint-plugin-storybook'; +import nextPlugin from '@next/eslint-plugin-next'; +import reactPlugin from 'eslint-plugin-react'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; +import prettierConfig from 'eslint-config-prettier'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; + +export default [ + js.configs.recommended, + ...tseslint.configs.recommended, + prettierConfig, + { + ignores: [ + 'playwright-report/**', + '.next/**', + 'node_modules/**', + 'out/**', + 'build/**', + 'coverage/**', + 'dist/**', + '**/*.config.js', + '**/*.config.ts', + '**/*.config.cjs', + '**/*.config.mjs', + 'eslint.config.mjs', + 'public/__ENV.js', + ], + }, + { + files: ['**/*.{js,jsx,ts,tsx}'], + plugins: { + '@next/next': nextPlugin, + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + 'simple-import-sort': simpleImportSort, + }, + rules: { + ...nextPlugin.configs.recommended.rules, + ...nextPlugin.configs['core-web-vitals'].rules, + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/no-empty-function': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unsafe-function-type': 'warn', + '@typescript-eslint/no-unused-expressions': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'react/display-name': 'off', + 'simple-import-sort/exports': 'error', + 'simple-import-sort/imports': [ + 'error', + { + groups: [ + ['^react$', '^next', '^[a-z]', '^@'], + ['^@/'], + ['^\\.\\.(?!/?$)', '^\\.\\./?$'], + ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'], + ['^.+\\.s?css$'], + ['^\\u0000'], + ], + }, + ], + 'react-hooks/exhaustive-deps': 'error', + 'no-console': ['error', { allow: ['warn', 'error'] }], + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + globals: { + React: 'readonly', + JSX: 'readonly', + NodeJS: 'readonly', + window: 'readonly', + document: 'readonly', + navigator: 'readonly', + console: 'readonly', + setTimeout: 'readonly', + clearTimeout: 'readonly', + setInterval: 'readonly', + clearInterval: 'readonly', + fetch: 'readonly', + process: 'readonly', + module: 'readonly', + require: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + exports: 'readonly', + Buffer: 'readonly', + }, + }, + }, + { + files: ['tests/e2e/**/*.{ts,js}'], + rules: { + 'no-console': 'off', + 'no-empty': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@next/next/no-html-link-for-pages': 'off', + }, + }, + ...storybook.configs['flat/recommended'], +]; diff --git a/packages/app/next-env.d.ts b/packages/app/next-env.d.ts deleted file mode 100644 index a4a7b3f5..00000000 --- a/packages/app/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/packages/app/next.config.js b/packages/app/next.config.js deleted file mode 100644 index ee8a943a..00000000 --- a/packages/app/next.config.js +++ /dev/null @@ -1,67 +0,0 @@ -const { configureRuntimeEnv } = require('next-runtime-env/build/configure'); -const { version } = require('./package.json'); - -configureRuntimeEnv(); - -const withNextra = require('nextra')({ - theme: 'nextra-theme-docs', - themeConfig: './src/nextra.config.tsx', -}); - -const basePath = process.env.NEXT_PUBLIC_HYPERDX_BASE_PATH; - -module.exports = { - basePath: basePath, - experimental: { - instrumentationHook: true, - // External packages to prevent bundling issues with Next.js 14 - // https://github.com/open-telemetry/opentelemetry-js/issues/4297#issuecomment-2285070503 - serverComponentsExternalPackages: [ - '@opentelemetry/instrumentation', - '@opentelemetry/sdk-node', - '@opentelemetry/auto-instrumentations-node', - '@hyperdx/node-opentelemetry', - '@hyperdx/instrumentation-sentry-node', - ], - }, - typescript: { - tsconfigPath: 'tsconfig.build.json', - }, - // Ignore otel pkgs warnings - // https://github.com/open-telemetry/opentelemetry-js/issues/4173#issuecomment-1822938936 - webpack: ( - config, - { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }, - ) => { - if (isServer) { - config.ignoreWarnings = [{ module: /opentelemetry/ }]; - } - return config; - }, - ...withNextra({ - async headers() { - return [ - { - source: '/(.*)?', // Matches all pages - headers: [ - { - key: 'X-Frame-Options', - value: 'DENY', - }, - ], - }, - ]; - }, - // This slows down builds by 2x for some reason... - swcMinify: false, - publicRuntimeConfig: { - version, - }, - productionBrowserSourceMaps: false, - ...(process.env.NEXT_OUTPUT_STANDALONE === 'true' - ? { - output: 'standalone', - } - : {}), - }), -}; diff --git a/packages/app/next.config.mjs b/packages/app/next.config.mjs new file mode 100644 index 00000000..001f208e --- /dev/null +++ b/packages/app/next.config.mjs @@ -0,0 +1,84 @@ +import { configureRuntimeEnv } from 'next-runtime-env/build/configure.js'; +import nextra from 'nextra'; +import { readFileSync } from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Read version from package.json +const packageJson = JSON.parse( + readFileSync(join(__dirname, 'package.json'), 'utf-8') +); +const { version } = packageJson; + +// Support legacy consumers of next-runtime-env that expect this value under window.__ENV +process.env.NEXT_PUBLIC_APP_VERSION = version; + +configureRuntimeEnv(); + +const withNextra = nextra({ + theme: 'nextra-theme-docs', + themeConfig: './src/nextra.config.tsx', +}); + +const basePath = process.env.NEXT_PUBLIC_HYPERDX_BASE_PATH; + +const nextConfig = { + reactCompiler: true, + basePath: basePath, + env: { + // Ensures bundler-time replacements for client/server code that references this env var + NEXT_PUBLIC_APP_VERSION: version, + }, + // External packages to prevent bundling issues (moved from experimental in Next.js 15+) + // https://github.com/open-telemetry/opentelemetry-js/issues/4297#issuecomment-2285070503 + serverExternalPackages: [ + '@opentelemetry/instrumentation', + '@opentelemetry/sdk-node', + '@opentelemetry/auto-instrumentations-node', + '@hyperdx/node-opentelemetry', + '@hyperdx/instrumentation-sentry-node', + ], + typescript: { + tsconfigPath: 'tsconfig.build.json', + }, + // NOTE: Using Webpack instead of Turbopack (Next.js 16 default) + // Reason: Turbopack has CSS module parsing issues with nested :global syntax + // used in styles/SearchPage.module.scss and other SCSS files. + // The --webpack flag is added to dev and build scripts in package.json. + // TODO: Re-evaluate when Turbopack CSS module support improves + // Ignore otel pkgs warnings + // https://github.com/open-telemetry/opentelemetry-js/issues/4173#issuecomment-1822938936 + webpack: (config, { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }) => { + if (isServer) { + config.ignoreWarnings = [{ module: /opentelemetry/ }]; + } + return config; + }, + ...withNextra({ + async headers() { + return [ + { + source: '/(.*)?', // Matches all pages + headers: [ + { + key: 'X-Frame-Options', + value: 'DENY', + }, + ], + }, + ]; + }, + productionBrowserSourceMaps: false, + ...(process.env.NEXT_OUTPUT_STANDALONE === 'true' + ? { + output: 'standalone', + } + : {}), + }), +}; + +export default nextConfig; + diff --git a/packages/app/package.json b/packages/app/package.json index b3aef603..9a118376 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -7,9 +7,9 @@ "node": ">=22.16.0" }, "scripts": { - "dev": "npx dotenv -e .env.development -- next dev", - "dev:local": "NEXT_PUBLIC_IS_LOCAL_MODE=true npx dotenv -e .env.development -- next dev", - "build": "next build", + "dev": "npx dotenv -e .env.development -- next dev --webpack", + "dev:local": "NEXT_PUBLIC_IS_LOCAL_MODE=true npx dotenv -e .env.development -- next dev --webpack", + "build": "next build --webpack", "start": "next start", "lint": "npx eslint --quiet . --ext .ts,.tsx", "lint:fix": "npx eslint . --ext .ts,.tsx --fix", @@ -33,13 +33,13 @@ "@hyperdx/common-utils": "^0.8.0", "@hyperdx/node-opentelemetry": "^0.9.0", "@lezer/highlight": "^1.2.0", - "@mantine/core": "7.9.2", - "@mantine/dates": "^7.11.2", - "@mantine/dropzone": "^8.3.1", - "@mantine/form": "^7.11.2", - "@mantine/hooks": "7.9.2", - "@mantine/notifications": "^7.9.2", - "@mantine/spotlight": "7.9.2", + "@mantine/core": "^7.17.8", + "@mantine/dates": "^7.17.8", + "@mantine/dropzone": "^7.17.8", + "@mantine/form": "^7.17.8", + "@mantine/hooks": "^7.17.8", + "@mantine/notifications": "^7.17.8", + "@mantine/spotlight": "^7.17.8", "@microsoft/fetch-event-source": "^2.0.1", "@tabler/icons-react": "^3.5.0", "@tanstack/react-query": "^5.56.2", @@ -56,6 +56,7 @@ "crypto-randomuuid": "^1.0.0", "date-fns": "^2.28.0", "date-fns-tz": "^2.0.0", + "dayjs": "^1.11.19", "flat": "^5.0.2", "fuse.js": "^6.6.2", "http-proxy-middleware": "^3.0.5", @@ -65,7 +66,7 @@ "ky-universal": "^0.10.1", "lodash": "^4.17.21", "ms": "^2.1.3", - "next": "^14.2.32", + "next": "^16.0.7", "next-query-params": "^4.3.1", "next-runtime-env": "1", "next-seo": "^4.28.1", @@ -74,16 +75,16 @@ "numbro": "^2.4.0", "nuqs": "1.17.0", "object-hash": "^3.0.0", - "react": "18.3.1", + "react": "^19.2.1", "react-copy-to-clipboard": "^5.1.0", - "react-dom": "18.3.1", + "react-dom": "^19.2.1", "react-error-boundary": "^3.1.4", "react-grid-layout": "^1.3.4", "react-hook-form": "^7.43.8", "react-hook-form-mantine": "^3.1.3", "react-hotkeys-hook": "^4.3.7", - "react-json-tree": "^0.17.0", - "react-markdown": "^8.0.4", + "react-json-tree": "^0.20.0", + "react-markdown": "^10.1.0", "react-papaparse": "^4.4.0", "react-query": "^3.39.3", "react-sortable-hoc": "^2.0.0", @@ -103,19 +104,16 @@ "zod": "3.25" }, "devDependencies": { - "@chromatic-com/storybook": "^1.5.0", + "@chromatic-com/storybook": "^4.1.3", "@hookform/devtools": "^4.3.1", "@jedmao/location": "^3.0.0", "@playwright/test": "^1.47.0", - "@storybook/addon-essentials": "^8.1.5", - "@storybook/addon-interactions": "^8.1.5", - "@storybook/addon-links": "^8.1.5", - "@storybook/addon-styling-webpack": "^1.0.0", - "@storybook/addon-themes": "^10.0.8", - "@storybook/blocks": "^8.1.5", - "@storybook/nextjs": "^8.1.5", - "@storybook/react": "^8.1.5", - "@storybook/test": "^8.1.5", + "@storybook/addon-docs": "^10.1.4", + "@storybook/addon-links": "^10.1.4", + "@storybook/addon-styling-webpack": "^3.0.0", + "@storybook/addon-themes": "^10.1.4", + "@storybook/nextjs": "^10.1.4", + "@storybook/react": "^10.1.4", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^16.3.0", @@ -129,14 +127,15 @@ "@types/ms": "^0.7.31", "@types/object-hash": "^2.2.1", "@types/pluralize": "^0.0.29", - "@types/react": "18.3.1", + "@types/react": "^19.2.7", "@types/react-copy-to-clipboard": "^5.0.2", - "@types/react-dom": "18.3.1", + "@types/react-dom": "^19.2.3", "@types/react-grid-layout": "^1.3.2", "@types/react-syntax-highlighter": "^13.5.2", "@types/react-table": "^7.7.14", "@types/sqlstring": "^2.3.2", - "eslint-config-next": "^14.2.29", + "eslint-config-next": "^16.0.7", + "eslint-plugin-storybook": "10.1.4", "identity-obj-proxy": "^3.0.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", @@ -146,7 +145,7 @@ "postcss": "^8.4.38", "postcss-preset-mantine": "^1.15.0", "prettier": "^3.3.2", - "storybook": "^8.1.5", + "storybook": "^10.1.4", "stylelint": "^16.6.1", "stylelint-config-standard-scss": "^13.1.0", "stylelint-prettier": "^5.0.0", diff --git a/packages/app/pages/_document.tsx b/packages/app/pages/_document.tsx index bc8bef17..582e5539 100644 --- a/packages/app/pages/_document.tsx +++ b/packages/app/pages/_document.tsx @@ -4,7 +4,9 @@ export default function Document() { return ( + {/* eslint-disable-next-line @next/next/no-sync-scripts */} onChange(e.currentTarget.value)} + onChange={(e: React.ChangeEvent) => + onChange(e.currentTarget.value) + } leftSection={} onKeyDown={handleKeyDown} rightSection={ @@ -880,7 +886,7 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) { }} > ; + inputRef: React.RefObject; value?: string; onChange: (value: string) => void; onSubmit?: () => void; diff --git a/packages/app/src/DBChartPage.tsx b/packages/app/src/DBChartPage.tsx index cefe165f..44dab3b7 100644 --- a/packages/app/src/DBChartPage.tsx +++ b/packages/app/src/DBChartPage.tsx @@ -210,7 +210,7 @@ function DBChartExplorerPage() { // showRelativeInterval: isLive, }); - const submitRef = useRef<() => void>(); + const submitRef = useRef<(() => void) | undefined>(undefined); const { data: sources } = useSources(); const { data: me } = api.useMe(); diff --git a/packages/app/src/DBDashboardPage.tsx b/packages/app/src/DBDashboardPage.tsx index 9519d967..e21c4e86 100644 --- a/packages/app/src/DBDashboardPage.tsx +++ b/packages/app/src/DBDashboardPage.tsx @@ -469,7 +469,9 @@ function DashboardName({ setEditedName(e.target.value)} + onChange={(e: React.ChangeEvent) => + setEditedName(e.target.value) + } placeholder="Dashboard Name" />