From 5e835e3695c7085b09fb9ced07384ba2c4e99531 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Mon, 14 Jul 2025 10:41:14 +0200 Subject: [PATCH 1/6] feat(aws): Create unified lambda layer for ESM and CJS feat(aws): Create unified lambda layer for ESM and CJS --- dev-packages/rollup-utils/bundleHelpers.mjs | 27 ---- .../rollup-utils/plugins/bundlePlugins.mjs | 110 +++++++------- packages/aws-serverless/package.json | 7 +- packages/aws-serverless/rollup.aws.config.mjs | 39 ----- .../scripts/buildLambdaLayer.ts | 139 ++++++++++++++++-- packages/aws-serverless/tsconfig.json | 2 +- packages/aws-serverless/tsconfig.types.json | 2 +- yarn.lock | 36 ++--- 8 files changed, 209 insertions(+), 153 deletions(-) delete mode 100644 packages/aws-serverless/rollup.aws.config.mjs diff --git a/dev-packages/rollup-utils/bundleHelpers.mjs b/dev-packages/rollup-utils/bundleHelpers.mjs index f80b0b7c2e50..1099cb6b6549 100644 --- a/dev-packages/rollup-utils/bundleHelpers.mjs +++ b/dev-packages/rollup-utils/bundleHelpers.mjs @@ -90,32 +90,6 @@ export function makeBaseBundleConfig(options) { plugins: [rrwebBuildPlugin, markAsBrowserBuildPlugin], }; - // used by `@sentry/aws-serverless`, when creating the lambda layer - const awsLambdaBundleConfig = { - output: { - format: 'cjs', - }, - plugins: [ - jsonPlugin, - commonJSPlugin, - // Temporary fix for the lambda layer SDK bundle. - // This is necessary to apply to our lambda layer bundle because calling `new ImportInTheMiddle()` will throw an - // that `ImportInTheMiddle` is not a constructor. Instead we modify the code to call `new ImportInTheMiddle.default()` - // TODO: Remove this plugin once the weird import-in-the-middle exports are fixed, released and we use the respective - // version in our SDKs. See: https://github.com/getsentry/sentry-javascript/issues/12009#issuecomment-2126211967 - { - name: 'aws-serverless-lambda-layer-fix', - transform: code => { - if (code.includes('ImportInTheMiddle')) { - return code.replaceAll(/new\s+(ImportInTheMiddle.*)\(/gm, 'new $1.default('); - } - }, - }, - ], - // Don't bundle any of Node's core modules - external: builtinModules, - }; - const workerBundleConfig = { output: { format: 'esm', @@ -143,7 +117,6 @@ export function makeBaseBundleConfig(options) { const bundleTypeConfigMap = { standalone: standAloneBundleConfig, addon: addOnBundleConfig, - 'aws-lambda': awsLambdaBundleConfig, 'node-worker': workerBundleConfig, }; diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index 9d6edd3157c0..eee7ad2718b8 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -86,8 +86,63 @@ export function makeBrowserBuildPlugin(isBrowserBuild) { }); } -// `terser` options reference: https://github.com/terser/terser#api-reference -// `rollup-plugin-terser` options reference: https://github.com/TrySound/rollup-plugin-terser#options +/** + * Terser options for bundling the SDK. + * + * @see https://github.com/terser/terser#api-reference + * @see https://github.com/TrySound/rollup-plugin-terser#options + * @type {import('terser').MinifyOptions} + */ +export const terserOptions = { + mangle: { + // `captureException` and `captureMessage` are public API methods and they don't need to be listed here, as the + // mangler won't touch user-facing things, but `sentryWrapped` is not user-facing, and would be mangled during + // minification. (We need it in its original form to correctly detect our internal frames for stripping.) All three + // are all listed here just for the clarity's sake, as they are all used in the frames manipulation process. + reserved: ['captureException', 'captureMessage', 'sentryWrapped'], + properties: { + // allow mangling of private field names... + regex: /^_[^_]/, + reserved: [ + // ...except for `_experiments`, which we want to remain usable from the outside + '_experiments', + // We want to keep some replay fields unmangled to enable integration tests to access them + '_replay', + '_canvas', + // We also can't mangle rrweb private fields when bundling rrweb in the replay CDN bundles + '_cssText', + // We want to keep the _integrations variable unmangled to send all installed integrations from replay + '_integrations', + // _meta is used to store metadata of replay network events + '_meta', + // We store SDK metadata in the options + '_metadata', + // Object we inject debug IDs into with bundler plugins + '_sentryDebugIds', + // These are used by instrument.ts in utils for identifying HTML elements & events + '_sentryCaptured', + '_sentryId', + // Keeps the frozen DSC on a Sentry Span + '_frozenDsc', + // These are used to keep span & scope relationships + '_sentryRootSpan', + '_sentryChildSpans', + '_sentrySpan', + '_sentryScope', + '_sentryIsolationScope', + // require-in-the-middle calls `Module._resolveFilename`. We cannot mangle this (AWS lambda layer bundle). + '_resolveFilename', + // Set on e.g. the shim feedbackIntegration to be able to detect it + '_isShim', + // This is used in metadata integration + '_sentryModuleMetadata', + ], + }, + }, + output: { + comments: false, + }, +} /** * Create a plugin to perform minification using `terser`. @@ -95,56 +150,7 @@ export function makeBrowserBuildPlugin(isBrowserBuild) { * @returns An instance of the `terser` plugin */ export function makeTerserPlugin() { - return terser({ - mangle: { - // `captureException` and `captureMessage` are public API methods and they don't need to be listed here, as the - // mangler won't touch user-facing things, but `sentryWrapped` is not user-facing, and would be mangled during - // minification. (We need it in its original form to correctly detect our internal frames for stripping.) All three - // are all listed here just for the clarity's sake, as they are all used in the frames manipulation process. - reserved: ['captureException', 'captureMessage', 'sentryWrapped'], - properties: { - // allow mangling of private field names... - regex: /^_[^_]/, - reserved: [ - // ...except for `_experiments`, which we want to remain usable from the outside - '_experiments', - // We want to keep some replay fields unmangled to enable integration tests to access them - '_replay', - '_canvas', - // We also can't mangle rrweb private fields when bundling rrweb in the replay CDN bundles - '_cssText', - // We want to keep the _integrations variable unmangled to send all installed integrations from replay - '_integrations', - // _meta is used to store metadata of replay network events - '_meta', - // We store SDK metadata in the options - '_metadata', - // Object we inject debug IDs into with bundler plugins - '_sentryDebugIds', - // These are used by instrument.ts in utils for identifying HTML elements & events - '_sentryCaptured', - '_sentryId', - // Keeps the frozen DSC on a Sentry Span - '_frozenDsc', - // These are used to keep span & scope relationships - '_sentryRootSpan', - '_sentryChildSpans', - '_sentrySpan', - '_sentryScope', - '_sentryIsolationScope', - // require-in-the-middle calls `Module._resolveFilename`. We cannot mangle this (AWS lambda layer bundle). - '_resolveFilename', - // Set on e.g. the shim feedbackIntegration to be able to detect it - '_isShim', - // This is used in metadata integration - '_sentryModuleMetadata', - ], - }, - }, - output: { - comments: false, - }, - }); + return terser(terserOptions); } // We don't pass these plugins any options which need to be calculated or changed by us, so no need to wrap them in diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 0bca7574c42c..0e2c195d43a1 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -15,6 +15,7 @@ "/build/loader-hook.mjs" ], "main": "build/npm/cjs/index.js", + "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "exports": { "./package.json": "./package.json", @@ -73,10 +74,12 @@ "@types/aws-lambda": "^8.10.62" }, "devDependencies": { - "@types/node": "^18.19.1" + "@types/node": "^18.19.1", + "@vercel/nft": "^0.29.4", + "terser": "^5.43.1" }, "scripts": { - "build": "run-p build:transpile build:types build:bundle", + "build": "run-p build:transpile build:types", "build:bundle": "yarn build:layer", "build:layer": "yarn ts-node scripts/buildLambdaLayer.ts", "build:dev": "run-p build:transpile build:types", diff --git a/packages/aws-serverless/rollup.aws.config.mjs b/packages/aws-serverless/rollup.aws.config.mjs deleted file mode 100644 index d9f0720886ef..000000000000 --- a/packages/aws-serverless/rollup.aws.config.mjs +++ /dev/null @@ -1,39 +0,0 @@ -import { makeBaseBundleConfig, makeBaseNPMConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils'; - -export default [ - // The SDK - ...makeBundleConfigVariants( - makeBaseBundleConfig({ - // this automatically sets it to be CJS - bundleType: 'aws-lambda', - entrypoints: ['src/index.ts'], - licenseTitle: '@sentry/aws-serverless', - outputFileBase: () => 'index', - packageSpecificConfig: { - output: { - dir: 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs', - sourcemap: false, - }, - }, - }), - // We only need one copy of the SDK, and we pick the minified one because there's a cap on how big a lambda function - // plus its dependencies can be, and we might as well take up as little of that space as is necessary. We'll rename - // it to be `index.js` in the build script, since it's standing in for the index file of the npm package. - { variants: ['.debug.min.js'] }, - ), - makeBaseNPMConfig({ - entrypoints: ['src/awslambda-auto.ts'], - packageSpecificConfig: { - // Normally `makeNPMConfigVariants` sets both of these values for us, but we don't actually want the ESM variant, - // and the directory structure is different than normal, so we have to do it ourselves. - output: { - format: 'cjs', - dir: 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs', - sourcemap: false, - }, - // We only want `awslambda-auto.js`, not the modules that it imports, because they're all included in the bundle - // we generate above - external: ['./index'], - }, - }), -]; diff --git a/packages/aws-serverless/scripts/buildLambdaLayer.ts b/packages/aws-serverless/scripts/buildLambdaLayer.ts index 52c3b50009c5..21e3cf1f1283 100644 --- a/packages/aws-serverless/scripts/buildLambdaLayer.ts +++ b/packages/aws-serverless/scripts/buildLambdaLayer.ts @@ -1,6 +1,11 @@ /* eslint-disable no-console */ +// @ts-expect-error - no types +import { terserOptions } from '@sentry-internal/rollup-utils'; +import { nodeFileTrace } from '@vercel/nft'; import * as childProcess from 'child_process'; import * as fs from 'fs'; +import * as path from 'path'; +import { minify } from 'terser'; import { version } from '../package.json'; /** @@ -11,21 +16,19 @@ function run(cmd: string, options?: childProcess.ExecSyncOptions): string { return String(childProcess.execSync(cmd, { stdio: 'inherit', ...options })); } +/** + * Build the AWS lambda layer by first installing the local package into `build/aws/dist-serverless/nodejs`. + * Then, prune the node_modules directory to remove unused files by first getting all necessary files with + * `@vercel/nft` and then deleting all other files inside `node_modules`. + * Finally, minify the files and create a zip file of the layer. + */ async function buildLambdaLayer(): Promise { - // Create the main SDK bundle - run('yarn rollup --config rollup.aws.config.mjs'); - - // We build a minified bundle, but it's standing in for the regular `index.js` file listed in `package.json`'s `main` - // property, so we have to rename it so it's findable. - fs.renameSync( - 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/index.debug.min.js', - 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/index.js', - ); + console.log('Installing local @sentry/aws-serverless into build/aws/dist-serverless/nodejs.'); + run('npm install . --prefix ./build/aws/dist-serverless/nodejs --install-links --silent'); - // We're creating a bundle for the SDK, but still using it in a Node context, so we need to copy in `package.json`, - // purely for its `main` property. - console.log('Copying `package.json` into lambda layer.'); - fs.copyFileSync('package.json', 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/package.json'); + await pruneNodeModules(); + fs.unlinkSync('./build/aws/dist-serverless/nodejs/package.json'); + fs.unlinkSync('./build/aws/dist-serverless/nodejs/package-lock.json'); // The layer also includes `awslambda-auto.js`, a helper file which calls `Sentry.init()` and wraps the lambda // handler. It gets run when Node is launched inside the lambda, using the environment variable @@ -61,3 +64,113 @@ function fsForceMkdirSync(path: string): void { fs.rmSync(path, { recursive: true, force: true }); fs.mkdirSync(path); } + +async function pruneNodeModules(): Promise { + const entrypoints = [ + './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/esm/index.js', + './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/index.js', + './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/awslambda-auto.js', + './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/esm/awslambda-auto.js', + ]; + + const { fileList } = await nodeFileTrace(entrypoints); + + const allFiles = getAllFiles('./build/aws/dist-serverless/nodejs/node_modules'); + + const filesToDelete = allFiles.filter(file => !fileList.has(file)); + console.log(`Removing ${filesToDelete.length} unused files from node_modules.`); + + for (const file of filesToDelete) { + try { + fs.unlinkSync(file); + } catch { + console.error(`Error deleting ${file}`); + } + } + + console.log('Cleaning up empty directories.'); + + removeEmptyDirs('./build/aws/dist-serverless/nodejs/node_modules'); + + await minifyJavaScriptFiles(fileList); +} + +function removeEmptyDirs(dir: string): void { + try { + const entries = fs.readdirSync(dir); + + for (const entry of entries) { + const fullPath = path.join(dir, entry); + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + removeEmptyDirs(fullPath); + } + } + + const remainingEntries = fs.readdirSync(dir); + + if (remainingEntries.length === 0) { + fs.rmdirSync(dir); + } + } catch (error) { + // Directory might not exist or might not be empty, that's ok + } +} + +function getAllFiles(dir: string): string[] { + const files: string[] = []; + + function walkDirectory(currentPath: string): void { + try { + const entries = fs.readdirSync(currentPath, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(currentPath, entry.name); + const relativePath = path.relative(process.cwd(), fullPath); + + if (entry.isDirectory()) { + walkDirectory(fullPath); + } else { + files.push(relativePath); + } + } + } catch { + console.log(`Skipping directory ${currentPath}`); + } + } + + walkDirectory(dir); + return files; +} + +async function minifyJavaScriptFiles(fileList: Set): Promise { + console.log('Minifying JavaScript files.'); + let minifiedCount = 0; + + for (const file of fileList) { + if (!file.endsWith('.js') && !file.endsWith('.mjs') && !file.endsWith('.cjs')) { + continue; + } + + // Skip minification for OpenTelemetry files to avoid CommonJS/ESM interop issues + if (file.includes('@opentelemetry')) { + continue; + } + + try { + const fullPath = path.resolve(file); + const code = fs.readFileSync(fullPath, 'utf-8'); + + const result = await minify(code, terserOptions); + + if (result.code) { + fs.writeFileSync(fullPath, result.code, 'utf-8'); + minifiedCount++; + } + } catch (error) { + console.error(`Error minifying ${file}`, error); + } + } + + console.log(`Minified ${minifiedCount} files.`); +} diff --git a/packages/aws-serverless/tsconfig.json b/packages/aws-serverless/tsconfig.json index a2731860dfa0..fd68e15254db 100644 --- a/packages/aws-serverless/tsconfig.json +++ b/packages/aws-serverless/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*"], + "include": ["src/**/*", "scripts/**/*"], "compilerOptions": { // package-specific options diff --git a/packages/aws-serverless/tsconfig.types.json b/packages/aws-serverless/tsconfig.types.json index 4c51bd21e64b..03b57fcaa2a7 100644 --- a/packages/aws-serverless/tsconfig.types.json +++ b/packages/aws-serverless/tsconfig.types.json @@ -3,7 +3,7 @@ // We don't ship this in the npm package (it exists purely for controlling what ends up in the AWS lambda layer), so // no need to build types for it - "exclude": ["src/index.awslambda.ts"], + "exclude": ["src/index.awslambda.ts", "scripts/**/*"], "compilerOptions": { "declaration": true, diff --git a/yarn.lock b/yarn.lock index 10c669dc1da4..b77af2d45aee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28318,7 +28318,7 @@ string-template@~0.2.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -28428,13 +28428,6 @@ stringify-object@^3.2.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -28456,6 +28449,13 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -28605,7 +28605,7 @@ stylus@0.59.0, stylus@^0.59.0: sax "~1.2.4" source-map "^0.7.3" -sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills: +sucrase@^3.27.0, sucrase@^3.35.0: version "3.36.0" resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061" dependencies: @@ -28950,7 +28950,7 @@ terser@5.14.2: commander "^2.20.0" source-map-support "~0.5.20" -terser@^5.10.0, terser@^5.17.4, terser@^5.31.1, terser@^5.7.0: +terser@^5.10.0, terser@^5.17.4, terser@^5.31.1, terser@^5.43.1, terser@^5.7.0: version "5.43.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.43.1.tgz#88387f4f9794ff1a29e7ad61fb2932e25b4fdb6d" integrity sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg== @@ -31475,19 +31475,19 @@ wrangler@4.22.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@7.0.0, wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" From c8d2771dcda5bb58592a8684b95b93d78ae72b1e Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Tue, 15 Jul 2025 11:49:18 +0200 Subject: [PATCH 2/6] add e2e test --- .../aws-lambda-layer-esm/.npmrc | 2 + .../aws-lambda-layer-esm/package.json | 22 ++++++ .../aws-lambda-layer-esm/playwright.config.ts | 3 + .../src/lambda-function.mjs | 21 ++++++ .../aws-lambda-layer-esm/src/run-lambda.mjs | 8 +++ .../aws-lambda-layer-esm/src/run.mjs | 17 +++++ .../start-event-proxy.mjs | 6 ++ .../aws-lambda-layer-esm/tests/basic.test.ts | 72 +++++++++++++++++++ yarn.lock | 34 ++++----- 9 files changed, 168 insertions(+), 17 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.npmrc b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/.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/aws-lambda-layer-esm/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json new file mode 100644 index 000000000000..d6d6a97ebcf0 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json @@ -0,0 +1,22 @@ +{ + "name": "node-express-app", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "node src/run.mjs", + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:build": "pnpm install", + "test:assert": "pnpm test" + }, + "dependencies": { + "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless" + }, + "devDependencies": { + "@sentry-internal/test-utils": "link:../../../test-utils", + "@playwright/test": "~1.53.2" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts new file mode 100644 index 000000000000..174593c307df --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/playwright.config.ts @@ -0,0 +1,3 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +export default getPlaywrightConfig(); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs new file mode 100644 index 000000000000..a9cdd48c1197 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/lambda-function.mjs @@ -0,0 +1,21 @@ +import * as Sentry from '@sentry/aws-serverless'; + +import * as http from 'node:http'; + +async function handle() { + await Sentry.startSpan({ name: 'manual-span', op: 'test' }, async () => { + await new Promise(resolve => { + http.get('http://example.com', res => { + res.on('data', d => { + process.stdout.write(d); + }); + + res.on('end', () => { + resolve(); + }); + }); + }); + }); +} + +export { handle }; diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs new file mode 100644 index 000000000000..c30903f9883d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run-lambda.mjs @@ -0,0 +1,8 @@ +import { handle } from './lambda-function.mjs'; + +const event = {}; +const context = { + invokedFunctionArn: 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', + functionName: 'my-lambda', +}; +await handle(event, context); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs new file mode 100644 index 000000000000..4bcd5886a865 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/src/run.mjs @@ -0,0 +1,17 @@ +import child_process from 'node:child_process'; + +child_process.execSync('node ./src/run-lambda.mjs', { + stdio: 'inherit', + env: { + ...process.env, + // On AWS, LAMBDA_TASK_ROOT is usually /var/task but for testing, we set it to the CWD to correctly apply our handler + LAMBDA_TASK_ROOT: process.cwd(), + _HANDLER: 'src/lambda-function.handle', + + NODE_OPTIONS: '--import @sentry/aws-serverless/awslambda-auto', + SENTRY_DSN: 'http://public@localhost:3031/1337', + SENTRY_TRACES_SAMPLE_RATE: '1.0', + SENTRY_DEBUG: 'true', + }, + cwd: process.cwd(), +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs new file mode 100644 index 000000000000..f8d84d924af7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'aws-serverless-lambda-layer-esm', +}); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts new file mode 100644 index 000000000000..91556d938001 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts @@ -0,0 +1,72 @@ +import * as child_process from 'child_process'; +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('Lambda layer SDK bundle sends events', async ({ request }) => { + const transactionEventPromise = waitForTransaction('aws-serverless-lambda-layer-esm', transactionEvent => { + return transactionEvent?.transaction === 'my-lambda'; + }); + + // Waiting for 1s here because attaching the listener for events in `waitForTransaction` is not synchronous + // Since in this test, we don't start a browser via playwright, we don't have the usual delays (page.goto, etc) + // which are usually enough for us to never have noticed this race condition before. + // This is a workaround but probably sufficient as long as we only experience it in this test. + await new Promise(resolve => + setTimeout(() => { + resolve(); + }, 1000), + ); + + child_process.execSync('pnpm start', { + stdio: 'ignore', + }); + + const transactionEvent = await transactionEventPromise; + + // shows the SDK sent a transaction + expect(transactionEvent.transaction).toEqual('my-lambda'); // name should be the function name + expect(transactionEvent.contexts?.trace).toEqual({ + data: { + 'sentry.sample_rate': 1, + 'sentry.source': 'custom', + 'sentry.origin': 'auto.otel.aws-lambda', + 'sentry.op': 'function.aws.lambda', + 'cloud.account.id': '123453789012', + 'faas.id': 'arn:aws:lambda:us-east-1:123453789012:function:my-lambda', + 'faas.coldstart': true, + 'otel.kind': 'SERVER', + }, + op: 'function.aws.lambda', + origin: 'auto.otel.aws-lambda', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + status: 'ok', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }); + + expect(transactionEvent.spans).toHaveLength(2); + + // shows that the Otel Http instrumentation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'http.client', + 'sentry.origin': 'auto.http.otel.http', + url: 'http://example.com/', + }), + description: 'GET http://example.com/', + op: 'http.client', + }), + ); + + // shows that the manual span creation is working + expect(transactionEvent.spans).toContainEqual( + expect.objectContaining({ + data: expect.objectContaining({ + 'sentry.op': 'test', + 'sentry.origin': 'manual', + }), + description: 'manual-span', + op: 'test', + }), + ); +}); diff --git a/yarn.lock b/yarn.lock index b77af2d45aee..69764f183a25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28318,7 +28318,7 @@ string-template@~0.2.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -28428,6 +28428,13 @@ stringify-object@^3.2.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -28449,13 +28456,6 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -28605,7 +28605,7 @@ stylus@0.59.0, stylus@^0.59.0: sax "~1.2.4" source-map "^0.7.3" -sucrase@^3.27.0, sucrase@^3.35.0: +sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills: version "3.36.0" resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061" dependencies: @@ -31475,19 +31475,19 @@ wrangler@4.22.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +wrap-ansi@7.0.0, wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" From a1c28f6643438682cd4de7e38e7c7760bccfcbce Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Tue, 15 Jul 2025 12:10:54 +0200 Subject: [PATCH 3/6] formatting --- dev-packages/rollup-utils/plugins/bundlePlugins.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index eee7ad2718b8..5ed6d8603552 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -142,7 +142,7 @@ export const terserOptions = { output: { comments: false, }, -} +}; /** * Create a plugin to perform minification using `terser`. From dd4971f7af09a3d1e8e68fdae7b39b9873f8e7d4 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Tue, 15 Jul 2025 13:30:19 +0200 Subject: [PATCH 4/6] remove minifying for now --- .../rollup-utils/plugins/bundlePlugins.mjs | 110 +++++++++--------- packages/aws-serverless/package.json | 3 +- .../scripts/buildLambdaLayer.ts | 37 ------ yarn.lock | 2 +- 4 files changed, 54 insertions(+), 98 deletions(-) diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index 5ed6d8603552..9d6edd3157c0 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -86,63 +86,8 @@ export function makeBrowserBuildPlugin(isBrowserBuild) { }); } -/** - * Terser options for bundling the SDK. - * - * @see https://github.com/terser/terser#api-reference - * @see https://github.com/TrySound/rollup-plugin-terser#options - * @type {import('terser').MinifyOptions} - */ -export const terserOptions = { - mangle: { - // `captureException` and `captureMessage` are public API methods and they don't need to be listed here, as the - // mangler won't touch user-facing things, but `sentryWrapped` is not user-facing, and would be mangled during - // minification. (We need it in its original form to correctly detect our internal frames for stripping.) All three - // are all listed here just for the clarity's sake, as they are all used in the frames manipulation process. - reserved: ['captureException', 'captureMessage', 'sentryWrapped'], - properties: { - // allow mangling of private field names... - regex: /^_[^_]/, - reserved: [ - // ...except for `_experiments`, which we want to remain usable from the outside - '_experiments', - // We want to keep some replay fields unmangled to enable integration tests to access them - '_replay', - '_canvas', - // We also can't mangle rrweb private fields when bundling rrweb in the replay CDN bundles - '_cssText', - // We want to keep the _integrations variable unmangled to send all installed integrations from replay - '_integrations', - // _meta is used to store metadata of replay network events - '_meta', - // We store SDK metadata in the options - '_metadata', - // Object we inject debug IDs into with bundler plugins - '_sentryDebugIds', - // These are used by instrument.ts in utils for identifying HTML elements & events - '_sentryCaptured', - '_sentryId', - // Keeps the frozen DSC on a Sentry Span - '_frozenDsc', - // These are used to keep span & scope relationships - '_sentryRootSpan', - '_sentryChildSpans', - '_sentrySpan', - '_sentryScope', - '_sentryIsolationScope', - // require-in-the-middle calls `Module._resolveFilename`. We cannot mangle this (AWS lambda layer bundle). - '_resolveFilename', - // Set on e.g. the shim feedbackIntegration to be able to detect it - '_isShim', - // This is used in metadata integration - '_sentryModuleMetadata', - ], - }, - }, - output: { - comments: false, - }, -}; +// `terser` options reference: https://github.com/terser/terser#api-reference +// `rollup-plugin-terser` options reference: https://github.com/TrySound/rollup-plugin-terser#options /** * Create a plugin to perform minification using `terser`. @@ -150,7 +95,56 @@ export const terserOptions = { * @returns An instance of the `terser` plugin */ export function makeTerserPlugin() { - return terser(terserOptions); + return terser({ + mangle: { + // `captureException` and `captureMessage` are public API methods and they don't need to be listed here, as the + // mangler won't touch user-facing things, but `sentryWrapped` is not user-facing, and would be mangled during + // minification. (We need it in its original form to correctly detect our internal frames for stripping.) All three + // are all listed here just for the clarity's sake, as they are all used in the frames manipulation process. + reserved: ['captureException', 'captureMessage', 'sentryWrapped'], + properties: { + // allow mangling of private field names... + regex: /^_[^_]/, + reserved: [ + // ...except for `_experiments`, which we want to remain usable from the outside + '_experiments', + // We want to keep some replay fields unmangled to enable integration tests to access them + '_replay', + '_canvas', + // We also can't mangle rrweb private fields when bundling rrweb in the replay CDN bundles + '_cssText', + // We want to keep the _integrations variable unmangled to send all installed integrations from replay + '_integrations', + // _meta is used to store metadata of replay network events + '_meta', + // We store SDK metadata in the options + '_metadata', + // Object we inject debug IDs into with bundler plugins + '_sentryDebugIds', + // These are used by instrument.ts in utils for identifying HTML elements & events + '_sentryCaptured', + '_sentryId', + // Keeps the frozen DSC on a Sentry Span + '_frozenDsc', + // These are used to keep span & scope relationships + '_sentryRootSpan', + '_sentryChildSpans', + '_sentrySpan', + '_sentryScope', + '_sentryIsolationScope', + // require-in-the-middle calls `Module._resolveFilename`. We cannot mangle this (AWS lambda layer bundle). + '_resolveFilename', + // Set on e.g. the shim feedbackIntegration to be able to detect it + '_isShim', + // This is used in metadata integration + '_sentryModuleMetadata', + ], + }, + }, + output: { + comments: false, + }, + }); } // We don't pass these plugins any options which need to be calculated or changed by us, so no need to wrap them in diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 0e2c195d43a1..17433e6101e5 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -75,8 +75,7 @@ }, "devDependencies": { "@types/node": "^18.19.1", - "@vercel/nft": "^0.29.4", - "terser": "^5.43.1" + "@vercel/nft": "^0.29.4" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/aws-serverless/scripts/buildLambdaLayer.ts b/packages/aws-serverless/scripts/buildLambdaLayer.ts index 21e3cf1f1283..973652e00180 100644 --- a/packages/aws-serverless/scripts/buildLambdaLayer.ts +++ b/packages/aws-serverless/scripts/buildLambdaLayer.ts @@ -1,11 +1,8 @@ /* eslint-disable no-console */ -// @ts-expect-error - no types -import { terserOptions } from '@sentry-internal/rollup-utils'; import { nodeFileTrace } from '@vercel/nft'; import * as childProcess from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; -import { minify } from 'terser'; import { version } from '../package.json'; /** @@ -91,8 +88,6 @@ async function pruneNodeModules(): Promise { console.log('Cleaning up empty directories.'); removeEmptyDirs('./build/aws/dist-serverless/nodejs/node_modules'); - - await minifyJavaScriptFiles(fileList); } function removeEmptyDirs(dir: string): void { @@ -142,35 +137,3 @@ function getAllFiles(dir: string): string[] { walkDirectory(dir); return files; } - -async function minifyJavaScriptFiles(fileList: Set): Promise { - console.log('Minifying JavaScript files.'); - let minifiedCount = 0; - - for (const file of fileList) { - if (!file.endsWith('.js') && !file.endsWith('.mjs') && !file.endsWith('.cjs')) { - continue; - } - - // Skip minification for OpenTelemetry files to avoid CommonJS/ESM interop issues - if (file.includes('@opentelemetry')) { - continue; - } - - try { - const fullPath = path.resolve(file); - const code = fs.readFileSync(fullPath, 'utf-8'); - - const result = await minify(code, terserOptions); - - if (result.code) { - fs.writeFileSync(fullPath, result.code, 'utf-8'); - minifiedCount++; - } - } catch (error) { - console.error(`Error minifying ${file}`, error); - } - } - - console.log(`Minified ${minifiedCount} files.`); -} diff --git a/yarn.lock b/yarn.lock index 69764f183a25..10c669dc1da4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28950,7 +28950,7 @@ terser@5.14.2: commander "^2.20.0" source-map-support "~0.5.20" -terser@^5.10.0, terser@^5.17.4, terser@^5.31.1, terser@^5.43.1, terser@^5.7.0: +terser@^5.10.0, terser@^5.17.4, terser@^5.31.1, terser@^5.7.0: version "5.43.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.43.1.tgz#88387f4f9794ff1a29e7ad61fb2932e25b4fdb6d" integrity sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg== From 04a8f62969e78950b086a7845b35cdad67a5c8c4 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Fri, 18 Jul 2025 15:10:02 +0200 Subject: [PATCH 5/6] apply suggestions fromcode review --- .../test-applications/aws-lambda-layer-esm/package.json | 3 ++- .../aws-lambda-layer-esm/start-event-proxy.mjs | 2 +- .../aws-lambda-layer-esm/tests/basic.test.ts | 2 +- packages/aws-serverless/scripts/buildLambdaLayer.ts | 9 +++++---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json index d6d6a97ebcf0..d283266bde96 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json @@ -1,5 +1,5 @@ { - "name": "node-express-app", + "name": "aws-lambda-layer-esm", "version": "1.0.0", "private": true, "scripts": { @@ -10,6 +10,7 @@ "test:assert": "pnpm test" }, "dependencies": { + "//": "Linked to the local Lambda layer build", "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs index f8d84d924af7..03fc10269998 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/start-event-proxy.mjs @@ -2,5 +2,5 @@ import { startEventProxyServer } from '@sentry-internal/test-utils'; startEventProxyServer({ port: 3031, - proxyServerName: 'aws-serverless-lambda-layer-esm', + proxyServerName: 'aws-lambda-layer-esm', }); diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts index 91556d938001..2e02fda6486e 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/tests/basic.test.ts @@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; test('Lambda layer SDK bundle sends events', async ({ request }) => { - const transactionEventPromise = waitForTransaction('aws-serverless-lambda-layer-esm', transactionEvent => { + const transactionEventPromise = waitForTransaction('aws-lambda-layer-esm', transactionEvent => { return transactionEvent?.transaction === 'my-lambda'; }); diff --git a/packages/aws-serverless/scripts/buildLambdaLayer.ts b/packages/aws-serverless/scripts/buildLambdaLayer.ts index 973652e00180..ad39948f5b0a 100644 --- a/packages/aws-serverless/scripts/buildLambdaLayer.ts +++ b/packages/aws-serverless/scripts/buildLambdaLayer.ts @@ -17,15 +17,16 @@ function run(cmd: string, options?: childProcess.ExecSyncOptions): string { * Build the AWS lambda layer by first installing the local package into `build/aws/dist-serverless/nodejs`. * Then, prune the node_modules directory to remove unused files by first getting all necessary files with * `@vercel/nft` and then deleting all other files inside `node_modules`. - * Finally, minify the files and create a zip file of the layer. + * Finally, create a zip file of the layer. */ async function buildLambdaLayer(): Promise { + console.log('Building Lambda layer.'); console.log('Installing local @sentry/aws-serverless into build/aws/dist-serverless/nodejs.'); run('npm install . --prefix ./build/aws/dist-serverless/nodejs --install-links --silent'); await pruneNodeModules(); - fs.unlinkSync('./build/aws/dist-serverless/nodejs/package.json'); - fs.unlinkSync('./build/aws/dist-serverless/nodejs/package-lock.json'); + fs.rmSync('./build/aws/dist-serverless/nodejs/package.json', { force: true }); + fs.rmSync('./build/aws/dist-serverless/nodejs/package-lock.json', { force: true }); // The layer also includes `awslambda-auto.js`, a helper file which calls `Sentry.init()` and wraps the lambda // handler. It gets run when Node is launched inside the lambda, using the environment variable @@ -107,7 +108,7 @@ function removeEmptyDirs(dir: string): void { if (remainingEntries.length === 0) { fs.rmdirSync(dir); } - } catch (error) { + } catch { // Directory might not exist or might not be empty, that's ok } } From 4ad4e49808d64d8ebe3746713e45ed7d790c55fc Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Fri, 18 Jul 2025 15:39:02 +0200 Subject: [PATCH 6/6] fix package.json comment --- .../test-applications/aws-lambda-layer-esm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json index d283266bde96..7a25061dde1c 100644 --- a/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json +++ b/dev-packages/e2e-tests/test-applications/aws-lambda-layer-esm/package.json @@ -9,8 +9,8 @@ "test:build": "pnpm install", "test:assert": "pnpm test" }, + "//": "Link from local Lambda layer build", "dependencies": { - "//": "Linked to the local Lambda layer build", "@sentry/aws-serverless": "link:../../../../packages/aws-serverless/build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless" }, "devDependencies": {