1
1
/* eslint-disable no-console */
2
+ // @ts -expect-error - no types
3
+ import { terserOptions } from '@sentry-internal/rollup-utils' ;
4
+ import { nodeFileTrace } from '@vercel/nft' ;
2
5
import * as childProcess from 'child_process' ;
3
6
import * as fs from 'fs' ;
7
+ import * as path from 'path' ;
8
+ import { minify } from 'terser' ;
4
9
import { version } from '../package.json' ;
5
10
6
11
/**
@@ -11,21 +16,19 @@ function run(cmd: string, options?: childProcess.ExecSyncOptions): string {
11
16
return String ( childProcess . execSync ( cmd , { stdio : 'inherit' , ...options } ) ) ;
12
17
}
13
18
19
+ /**
20
+ * Build the AWS lambda layer by first installing the local package into `build/aws/dist-serverless/nodejs`.
21
+ * Then, prune the node_modules directory to remove unused files by first getting all necessary files with
22
+ * `@vercel/nft` and then deleting all other files inside `node_modules`.
23
+ * Finally, minify the files and create a zip file of the layer.
24
+ */
14
25
async function buildLambdaLayer ( ) : Promise < void > {
15
- // Create the main SDK bundle
16
- run ( 'yarn rollup --config rollup.aws.config.mjs' ) ;
17
-
18
- // We build a minified bundle, but it's standing in for the regular `index.js` file listed in `package.json`'s `main`
19
- // property, so we have to rename it so it's findable.
20
- fs . renameSync (
21
- 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/index.debug.min.js' ,
22
- 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/index.js' ,
23
- ) ;
26
+ console . log ( 'Installing local @sentry/aws-serverless into build/aws/dist-serverless/nodejs.' ) ;
27
+ run ( 'npm install . --prefix ./build/aws/dist-serverless/nodejs --install-links --silent' ) ;
24
28
25
- // We're creating a bundle for the SDK, but still using it in a Node context, so we need to copy in `package.json`,
26
- // purely for its `main` property.
27
- console . log ( 'Copying `package.json` into lambda layer.' ) ;
28
- fs . copyFileSync ( 'package.json' , 'build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/package.json' ) ;
29
+ await pruneNodeModules ( ) ;
30
+ fs . unlinkSync ( './build/aws/dist-serverless/nodejs/package.json' ) ;
31
+ fs . unlinkSync ( './build/aws/dist-serverless/nodejs/package-lock.json' ) ;
29
32
30
33
// The layer also includes `awslambda-auto.js`, a helper file which calls `Sentry.init()` and wraps the lambda
31
34
// handler. It gets run when Node is launched inside the lambda, using the environment variable
@@ -61,3 +64,113 @@ function fsForceMkdirSync(path: string): void {
61
64
fs . rmSync ( path , { recursive : true , force : true } ) ;
62
65
fs . mkdirSync ( path ) ;
63
66
}
67
+
68
+ async function pruneNodeModules ( ) : Promise < void > {
69
+ const entrypoints = [
70
+ './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/esm/index.js' ,
71
+ './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/index.js' ,
72
+ './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/cjs/awslambda-auto.js' ,
73
+ './build/aws/dist-serverless/nodejs/node_modules/@sentry/aws-serverless/build/npm/esm/awslambda-auto.js' ,
74
+ ] ;
75
+
76
+ const { fileList } = await nodeFileTrace ( entrypoints ) ;
77
+
78
+ const allFiles = getAllFiles ( './build/aws/dist-serverless/nodejs/node_modules' ) ;
79
+
80
+ const filesToDelete = allFiles . filter ( file => ! fileList . has ( file ) ) ;
81
+ console . log ( `Removing ${ filesToDelete . length } unused files from node_modules.` ) ;
82
+
83
+ for ( const file of filesToDelete ) {
84
+ try {
85
+ fs . unlinkSync ( file ) ;
86
+ } catch {
87
+ console . error ( `Error deleting ${ file } ` ) ;
88
+ }
89
+ }
90
+
91
+ console . log ( 'Cleaning up empty directories.' ) ;
92
+
93
+ removeEmptyDirs ( './build/aws/dist-serverless/nodejs/node_modules' ) ;
94
+
95
+ await minifyJavaScriptFiles ( fileList ) ;
96
+ }
97
+
98
+ function removeEmptyDirs ( dir : string ) : void {
99
+ try {
100
+ const entries = fs . readdirSync ( dir ) ;
101
+
102
+ for ( const entry of entries ) {
103
+ const fullPath = path . join ( dir , entry ) ;
104
+ const stat = fs . statSync ( fullPath ) ;
105
+ if ( stat . isDirectory ( ) ) {
106
+ removeEmptyDirs ( fullPath ) ;
107
+ }
108
+ }
109
+
110
+ const remainingEntries = fs . readdirSync ( dir ) ;
111
+
112
+ if ( remainingEntries . length === 0 ) {
113
+ fs . rmdirSync ( dir ) ;
114
+ }
115
+ } catch ( error ) {
116
+ // Directory might not exist or might not be empty, that's ok
117
+ }
118
+ }
119
+
120
+ function getAllFiles ( dir : string ) : string [ ] {
121
+ const files : string [ ] = [ ] ;
122
+
123
+ function walkDirectory ( currentPath : string ) : void {
124
+ try {
125
+ const entries = fs . readdirSync ( currentPath , { withFileTypes : true } ) ;
126
+
127
+ for ( const entry of entries ) {
128
+ const fullPath = path . join ( currentPath , entry . name ) ;
129
+ const relativePath = path . relative ( process . cwd ( ) , fullPath ) ;
130
+
131
+ if ( entry . isDirectory ( ) ) {
132
+ walkDirectory ( fullPath ) ;
133
+ } else {
134
+ files . push ( relativePath ) ;
135
+ }
136
+ }
137
+ } catch {
138
+ console . log ( `Skipping directory ${ currentPath } ` ) ;
139
+ }
140
+ }
141
+
142
+ walkDirectory ( dir ) ;
143
+ return files ;
144
+ }
145
+
146
+ async function minifyJavaScriptFiles ( fileList : Set < string > ) : Promise < void > {
147
+ console . log ( 'Minifying JavaScript files.' ) ;
148
+ let minifiedCount = 0 ;
149
+
150
+ for ( const file of fileList ) {
151
+ if ( ! file . endsWith ( '.js' ) && ! file . endsWith ( '.mjs' ) && ! file . endsWith ( '.cjs' ) ) {
152
+ continue ;
153
+ }
154
+
155
+ // Skip minification for OpenTelemetry files to avoid CommonJS/ESM interop issues
156
+ if ( file . includes ( '@opentelemetry' ) ) {
157
+ continue ;
158
+ }
159
+
160
+ try {
161
+ const fullPath = path . resolve ( file ) ;
162
+ const code = fs . readFileSync ( fullPath , 'utf-8' ) ;
163
+
164
+ const result = await minify ( code , terserOptions ) ;
165
+
166
+ if ( result . code ) {
167
+ fs . writeFileSync ( fullPath , result . code , 'utf-8' ) ;
168
+ minifiedCount ++ ;
169
+ }
170
+ } catch ( error ) {
171
+ console . error ( `Error minifying ${ file } ` , error ) ;
172
+ }
173
+ }
174
+
175
+ console . log ( `Minified ${ minifiedCount } files.` ) ;
176
+ }
0 commit comments