diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f24414286c8a..00459abbe701 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -913,6 +913,7 @@ jobs: 'generic-ts3.8', 'node-experimental-fastify-app', 'node-hapi-app', + 'node-exports-test-app', ] build-command: - false diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/.npmrc b/dev-packages/e2e-tests/test-applications/node-exports-test-app/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-exports-test-app/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/README.md b/dev-packages/e2e-tests/test-applications/node-exports-test-app/README.md new file mode 100644 index 000000000000..8af36f6c6dad --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-exports-test-app/README.md @@ -0,0 +1,18 @@ +# Consistent Node Export Test + +This test "app" ensures that we consistently re-export exports from `@sentry/node` in packages depending on +`@sentry/node`. + +## How to add new package + +1. Add package as a dependency to the test app +2. In `scripts/consistentExports.ts`: + - add namespace import + - add `DEPENDENTS` entry + - add any ignores/exclusion entries as necessary + - if the package is still under development, you can also set `skip: true` + +## Limitations: + +- This script only checks top-level exports for now (e.g. `metrics` but no sub-exports like `metrics.increment`) +- This script only checks ESM transpiled code for now, not CJS diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json b/dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json new file mode 100644 index 000000000000..056dd6836e61 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json @@ -0,0 +1,32 @@ +{ + "name": "node-express-app", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc", + "start": "pnpm build && node dist/consistentExports.js", + "test": " node dist/consistentExports.js", + "clean": "npx rimraf node_modules,pnpm-lock.yaml,dist", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test" + }, + "dependencies": { + "@sentry/node": "latest || *", + "@sentry/sveltekit": "latest || *", + "@sentry/remix": "latest || *", + "@sentry/astro": "latest || *", + "@sentry/nextjs": "latest || *", + "@sentry/serverless": "latest || *", + "@sentry/bun": "latest || *", + "@sentry/types": "latest || *", + "@types/node": "18.15.1", + "typescript": "4.9.5" + }, + "devDependencies": { + "ts-node": "10.9.1" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts b/dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts new file mode 100644 index 000000000000..a488ef463412 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts @@ -0,0 +1,103 @@ +import * as SentryAstro from '@sentry/astro'; +// import * as SentryBun from '@sentry/bun'; +import * as SentryNextJs from '@sentry/nextjs'; +import * as SentryNode from '@sentry/node'; +import * as SentryRemix from '@sentry/remix'; +import * as SentryServerless from '@sentry/serverless'; +import * as SentrySvelteKit from '@sentry/sveltekit'; + +/* List of exports that are safe to ignore / we don't require in any depending package */ +const NODE_EXPORTS_IGNORE = [ + 'default', + // Probably generated by transpilation, no need to require it + '__esModule', + // this function was deprecates almost immediately after it was introduced + // due to a name change (startSpan). No need to re-export it IMHO. + 'startActiveSpan', + // this was never meant for external use (and documented as such) + 'trace', + // These Node exports were only made for type definition fixes (see #10339) + 'Undici', + 'Http', + 'DebugSession', + 'AnrIntegrationOptions', + 'LocalVariablesIntegrationOptions', + // deprecated + 'spanStatusfromHttpCode', +]; + +type Dependent = { + package: string; + exports: string[]; + ignoreExports?: string[]; + skip?: boolean; +}; + +const DEPENDENTS: Dependent[] = [ + { + package: '@sentry/astro', + exports: Object.keys(SentryAstro), + }, + { + package: '@sentry/nextjs', + // Next.js doesn't require explicit exports, so we can just merge top level and `default` exports: + // @ts-expect-error: `default` is not in the type definition but it's defined + exports: Object.keys({ ...SentryNextJs, ...SentryNextJs.default }), + ignoreExports: ['withSentryConfig'], + }, + { + package: '@sentry/remix', + exports: Object.keys(SentryRemix), + // TODO: Fix exports in remix + skip: true, + }, + { + package: '@sentry/serverless', + exports: Object.keys(SentryServerless), + ignoreExports: [ + // Deprecated, no need to add this now to serverless + 'extractTraceparentData', + 'getModuleFromFilename', + // TODO: Should these be exported from serverless? + 'cron', + 'enableAnrDetection', + 'runWithAsyncContext', + 'hapiErrorPlugin', + ], + // TODO: Fix exports in serverless + skip: true, + }, + { + package: '@sentry/sveltekit', + exports: Object.keys(SentrySvelteKit), + // TODO: Fix exports in sveltekit + skip: true, + }, +]; + +/* Sanitized list of node exports */ +const nodeExports = Object.keys(SentryNode).filter(e => !NODE_EXPORTS_IGNORE.includes(e)); + +console.log('šŸ”Ž Checking for consistent exports of @sentry/node exports in depending packages'); + +const missingExports: Record = {}; +const dependentsToCheck = DEPENDENTS.filter(d => !d.skip); + +for (const nodeExport of nodeExports) { + for (const dependent of dependentsToCheck) { + if (dependent.ignoreExports?.includes(nodeExport)) { + continue; + } + if (!dependent.exports.includes(nodeExport)) { + missingExports[dependent.package] = [...(missingExports[dependent.package] ?? []), nodeExport]; + } + } +} + +if (Object.keys(missingExports).length > 0) { + console.error('\nāŒ Found missing exports from @sentry/node in the following packages:\n'); + console.log(JSON.stringify(missingExports, null, 2)); + process.exit(1); +} + +console.log('āœ… All good :)'); diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/tsconfig.json b/dev-packages/e2e-tests/test-applications/node-exports-test-app/tsconfig.json new file mode 100644 index 000000000000..fc22710d69dc --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-exports-test-app/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "types": ["node"], + "esModuleInterop": true, + "lib": ["ES6"], + "strict": true, + "outDir": "dist", + "target": "ESNext", + "moduleResolution": "node", + "skipLibCheck": true + }, + "include": ["scripts/**/*.ts"] +}