1
1
#!/usr/bin/env node
2
- import { exec as child_exec } from 'child_process'
3
- import util from 'util'
2
+
3
+ import { Command } from 'commander'
4
+ import { mkdirSync , rmSync , symlinkSync , writeFileSync } from 'fs'
5
+ import { tmpdir } from 'os'
4
6
import path from 'path'
5
- import packageJson from '../package.json'
6
7
import { simpleGit } from 'simple-git'
7
- import { Command } from 'commander'
8
- import { bumpCalculator , bumpMapping , BumpType , isValidTag , replaceVersionInCommonFiles } from './utils'
9
-
10
- const exec = util . promisify ( child_exec )
8
+ import packageJson from '../package.json'
9
+ import { bumpCalculator , bumpMapping , BumpType , findInFile , isValidTag , replaceVersionInCommonFiles , zipFolder , zipMultipleFoldersOrFiles } from './utils'
11
10
12
11
const skipCiFlag = '[skip ci]'
13
-
12
+ const commandCwd = process . cwd ( )
13
+ const nextServerConfigRegex = / (?< = c o n f : ) ( .* ) (? = , ) /
14
14
const scriptDir = path . dirname ( __filename )
15
- const scriptPath = path . resolve ( `${ scriptDir } /../../scripts/pack-nextjs.sh` )
16
- const handlerPath = path . resolve ( `${ scriptDir } /../server-handler/index.js` )
17
15
18
16
const program = new Command ( )
19
17
@@ -26,21 +24,107 @@ program
26
24
program
27
25
. command ( 'pack' )
28
26
. description ( 'Package standalone Next12 build into Lambda compatible ZIPs.' )
29
- . option ( '--output' , 'folder where to save output' , 'next.out' )
30
- . option ( '--publicFolder' , 'folder where public assets are located' , 'public' )
31
- . option ( '--handler' , 'custom handler to deal with ApiGw events' , handlerPath )
32
- . option ( '--grepBy' , 'keyword to identify configuration inside server.js' , 'webpack' )
33
- . action ( async ( str , options ) => {
34
- // @TODO : Ensure path exists.
35
- // @TODO : Ensure.next folder exists with standalone folder inside.
36
-
37
- // @TODO : Transform into code, move away from script.
38
- // Also, pass parameters and options.
39
- console . log ( 'Starting packaging of your NextJS project!' )
40
-
41
- await exec ( `chmod +x ${ scriptPath } && ${ scriptPath } ` )
42
- . then ( ( { stdout } ) => console . log ( stdout ) )
43
- . catch ( console . error )
27
+ . option (
28
+ '--standaloneFolder' ,
29
+ 'Folder including NextJS standalone build. Parental folder should include more folders as well.' ,
30
+ path . resolve ( commandCwd , '.next/standalone' ) ,
31
+ )
32
+ . option (
33
+ '--publicFolder' ,
34
+ 'Folder where public assets are located, typically this folder is located in root of the project.' ,
35
+ path . resolve ( commandCwd , './public' ) ,
36
+ )
37
+ . option (
38
+ '--handlerPath' ,
39
+ 'Path to custom handler to be used to handle ApiGw events. By default this is provided for you.' ,
40
+ path . resolve ( scriptDir , './../server-handler/index.js' ) ,
41
+ )
42
+ . option (
43
+ '--outputFolder' ,
44
+ 'Path to folder which should be used for outputting bundled ZIP files for your Lambda. It will be cleared before every script run.' ,
45
+ path . resolve ( commandCwd , './next.out' ) ,
46
+ )
47
+ . action ( async ( options ) => {
48
+ const { standaloneFolder, publicFolder, handlerPath, outputFolder } = options
49
+
50
+ // Dependencies layer configuration
51
+ const nodeModulesFolderPath = path . resolve ( standaloneFolder , './node_modules' )
52
+ const depsLambdaFolder = 'nodejs/node_modules'
53
+ const lambdaNodeModulesPath = path . resolve ( '/opt' , depsLambdaFolder )
54
+ const dependenciesOutputPath = path . resolve ( outputFolder , 'dependenciesLayer.zip' )
55
+
56
+ // Code layer configuration
57
+ const generatedNextServerPath = path . resolve ( standaloneFolder , './server.js' )
58
+ const codeOutputPath = path . resolve ( outputFolder , 'code.zip' )
59
+
60
+ // Assets bundle configuration
61
+ const generatedStaticContentPath = path . resolve ( commandCwd , '.next/static' )
62
+ const generatedStaticRemapping = '_next/static'
63
+ const assetsOutputPath = path . resolve ( outputFolder , 'assetsLayer.zip' )
64
+
65
+ // Clean output directory before continuing
66
+ rmSync ( outputFolder , { force : true , recursive : true } )
67
+ mkdirSync ( outputFolder )
68
+
69
+ // Zip dependencies from standalone output in a layer-compatible format.
70
+ await zipFolder ( {
71
+ outputName : dependenciesOutputPath ,
72
+ folderPath : nodeModulesFolderPath ,
73
+ dir : depsLambdaFolder ,
74
+ } )
75
+
76
+ // Zip staticly generated assets and public folder.
77
+ await zipMultipleFoldersOrFiles ( {
78
+ outputName : assetsOutputPath ,
79
+ inputDefinition : [
80
+ {
81
+ path : publicFolder ,
82
+ } ,
83
+ {
84
+ path : generatedStaticContentPath ,
85
+ dir : generatedStaticRemapping ,
86
+ } ,
87
+ ] ,
88
+ } )
89
+
90
+ // Create a symlink for node_modules so they point to the separately packaged layer.
91
+ // We need to create symlink because we are not using NodejsFunction in CDK as bundling is custom.
92
+ const tmpFolder = tmpdir ( )
93
+
94
+ const symlinkPath = path . resolve ( tmpFolder , `./node_modules_${ Math . random ( ) } ` )
95
+ symlinkSync ( lambdaNodeModulesPath , symlinkPath )
96
+
97
+ const nextConfig = findInFile ( generatedNextServerPath , nextServerConfigRegex )
98
+ const configPath = path . resolve ( tmpFolder , `./config.json_${ Math . random ( ) } ` )
99
+ writeFileSync ( configPath , nextConfig , 'utf-8' )
100
+
101
+ // Zip codebase including symlinked node_modules and handler.
102
+ await zipMultipleFoldersOrFiles ( {
103
+ outputName : codeOutputPath ,
104
+ inputDefinition : [
105
+ {
106
+ isGlob : true ,
107
+ cwd : standaloneFolder ,
108
+ path : '**/*' ,
109
+ ignore : [ '**/node_modules/**' , '*.zip' ] ,
110
+ } ,
111
+ {
112
+ isFile : true ,
113
+ path : handlerPath ,
114
+ name : 'handler.js' ,
115
+ } ,
116
+ {
117
+ isFile : true ,
118
+ path : symlinkPath ,
119
+ name : 'node_modules' ,
120
+ } ,
121
+ {
122
+ isFile : true ,
123
+ path : configPath ,
124
+ name : 'config.json' ,
125
+ } ,
126
+ ] ,
127
+ } )
44
128
45
129
console . log ( 'Your NextJS project was succefully prepared for Lambda.' )
46
130
} )
0 commit comments