├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .release-it.json ├── .yarnclean ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs └── monorepo-config.png ├── examples ├── .eslintrc.yml ├── basic │ ├── .vercelignore │ ├── README.md │ ├── api │ │ ├── contents │ │ │ └── [id].js │ │ └── hello.js │ ├── nuxt.config.js │ ├── package.json │ ├── pages │ │ └── _.vue │ ├── server-middleware │ │ └── log.js │ ├── static │ │ └── test.txt │ ├── vercel-settings.png │ ├── vercel.dev.json │ ├── vercel.json │ └── yarn.lock └── side-by-side │ ├── .nowignore │ ├── README.md │ ├── admin │ ├── layouts │ │ └── default.vue │ ├── nuxt.config.js │ ├── package.json │ ├── pages │ │ ├── index.vue │ │ └── testing.vue │ ├── static │ │ └── favicon.ico │ └── yarn.lock │ ├── app │ ├── components │ │ ├── LinksInline.vue │ │ └── LinksList.vue │ ├── layouts │ │ └── default.vue │ ├── nuxt.config.js │ ├── package.json │ ├── pages │ │ ├── first.vue │ │ ├── fourth.vue │ │ ├── index.vue │ │ ├── second.vue │ │ └── third.vue │ ├── static │ │ └── favicon.ico │ └── yarn.lock │ ├── now.json │ └── shared │ └── components │ └── Logo.vue ├── jest.config.js ├── package.json ├── renovate.json ├── siroc.config.ts ├── src ├── build.ts ├── config.ts ├── index.ts ├── launcher.ts ├── prepare-cache.ts ├── typescript.ts └── utils.ts ├── test ├── .eslintrc.yml ├── build.test.js ├── fixture-generated │ ├── now.json │ └── www │ │ ├── custom │ │ └── foo.js │ │ ├── nuxt.config.js │ │ ├── package.json │ │ ├── pages │ │ ├── dynamic │ │ │ └── _id.vue │ │ └── index.vue │ │ ├── static │ │ └── test.txt │ │ └── yarn.lock ├── fixture-ts │ ├── now.json │ └── www │ │ ├── another.ts │ │ ├── custom │ │ └── foo.js │ │ ├── modules │ │ ├── further.ts │ │ └── module.ts │ │ ├── nuxt.config.ts │ │ ├── package.json │ │ ├── pages │ │ └── index.vue │ │ ├── static │ │ └── test.txt │ │ ├── tsconfig.json │ │ └── yarn.lock ├── fixture │ ├── now.json │ └── www │ │ ├── custom │ │ └── foo.js │ │ ├── nuxt.config.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── pages │ │ └── index.vue │ │ └── static │ │ └── test.txt └── utils │ └── run-build-lambda.js ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | 9 | [*.js] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [{package.json,*.yml,*.cjson}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules_dev 3 | node_modules_prod 4 | lib 5 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | extends: 2 | - "plugin:@typescript-eslint/recommended" 3 | - "@nuxtjs" 4 | rules: 5 | no-console: 1 6 | no-undef: "off" 7 | "@typescript-eslint/no-var-requires": "off" 8 | "vue/multi-word-component-names": "off" 9 | plugins: 10 | - "@typescript-eslint" 11 | parserOptions: 12 | parser: "@typescript-eslint/parser" 13 | tsconfigRootDir: "./src" 14 | ecmaFeatures: 15 | jsx: false 16 | useJSXTextNode: false 17 | ecmaVersion: 11 18 | sourceType: "module" 19 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release/* 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.event.number || github.sha }} 14 | cancel-in-progress: ${{ github.event_name != 'push' }} 15 | 16 | jobs: 17 | test: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | node: [16, 18] 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - uses: actions/cache@v4 27 | id: cache 28 | with: 29 | path: "node_modules" 30 | key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('yarn.lock') }} 31 | 32 | - name: Install dependencies 33 | if: steps.cache.outputs.cache-hit != 'true' 34 | run: yarn 35 | 36 | - name: Lint 37 | run: yarn lint 38 | 39 | - name: Build 40 | run: yarn build 41 | 42 | - uses: actions/setup-node@v4 43 | with: 44 | node-version: ${{ matrix.node }} 45 | 46 | - name: Test 47 | run: yarn test 48 | 49 | - name: Coverage 50 | run: yarn codecov 51 | env: 52 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | node_modules_dev 4 | node_modules_prod 5 | *.log 6 | .DS_Store 7 | coverage 8 | .nuxt 9 | dist 10 | .cache 11 | .tmp 12 | lib 13 | .vercel 14 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore: release v${version}" 4 | }, 5 | "github": { 6 | "release": true, 7 | "releaseName": "v${version}" 8 | }, 9 | "npm": { 10 | "skipChecks": true 11 | }, 12 | "plugins": { 13 | "@release-it/conventional-changelog": { 14 | "preset": "conventionalcommits", 15 | "infile": "CHANGELOG.md" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.yarnclean: -------------------------------------------------------------------------------- 1 | *.yaml 2 | *.md 3 | *.lock 4 | LICENSE 5 | CHANGELOG 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## [0.25.0](https://github.com/nuxt/vercel-builder/compare/v0.24.0...v0.25.0) (2024-04-12) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * do not inline `@vercel/build-utils` ([c72143a](https://github.com/nuxt/vercel-builder/commit/c72143ab37a422837051b5749b0e936ed4372abf)) 9 | * respect `router.base` if set ([#682](https://github.com/nuxt/vercel-builder/issues/682)) ([6d87f55](https://github.com/nuxt/vercel-builder/commit/6d87f55e08d253db2cfdd546c1a1adccf0e50430)) 10 | 11 | ## [0.24.0](https://github.com/nuxt/vercel-builder/compare/v0.23.0...v0.24.0) (2022-12-02) 12 | 13 | 14 | ### Bug Fixes 15 | 16 | * **deps:** upgrade rc9 to latest patch version ([#742](https://github.com/nuxt/vercel-builder/issues/742)) ([28a5bb9](https://github.com/nuxt/vercel-builder/commit/28a5bb90f47fd7254982d7fe17905706752c2f31)) 17 | * handle npm v7 overwriting `node_modules` symlink ([#636](https://github.com/nuxt/vercel-builder/issues/636)) ([ff20a34](https://github.com/nuxt/vercel-builder/commit/ff20a34dbc1fcfc576c745ff5db824adc81e1cdf)) 18 | 19 | ## [0.23.0](https://github.com/nuxt/vercel-builder/compare/v0.22.1...v0.23.0) (2022-10-12) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * move `@vercel/build-utils` to a devDependency ([#674](https://github.com/nuxt/vercel-builder/issues/674)) ([feca866](https://github.com/nuxt/vercel-builder/commit/feca866e2ad69760eb3ed517f3a0706a9a7d3d31)) 25 | 26 | ### [0.22.1](https://github.com/nuxt/vercel-builder/compare/v0.22.0...v0.22.1) (2021-09-28) 27 | 28 | 29 | ### Bug Fixes 30 | 31 | * revert `@vercel/build-utils` upgrade (hotfix) ([c34432f](https://github.com/nuxt/vercel-builder/commit/c34432fc948564790196583fa3ef2c55adbc4e20)), closes [nuxt/vercel-builder#633](https://github.com/nuxt/vercel-builder/issues/633) 32 | 33 | ## [0.22.0](https://github.com/nuxt/vercel-builder/compare/v0.21.3...v0.22.0) (2021-09-25) 34 | 35 | 36 | ### ⚠ BREAKING CHANGES 37 | 38 | * upgrade `@vercel/node-bridge` 39 | 40 | ### Features 41 | 42 | * allow disabling cache with `NUXT_CACHE_DISABLED` flag ([#628](https://github.com/nuxt/vercel-builder/issues/628)) ([e993410](https://github.com/nuxt/vercel-builder/commit/e993410575f39d55c6abda7861804018f82532fd)) 43 | 44 | 45 | ### Miscellaneous Chores 46 | 47 | * upgrade `@vercel/node-bridge` ([73af3f9](https://github.com/nuxt/vercel-builder/commit/73af3f941eee2eb782eec468c1538e6b46e819b5)) 48 | 49 | ### [0.21.3](https://github.com/nuxt/vercel-builder/compare/v0.21.2...v0.21.3) (2021-06-10) 50 | 51 | 52 | ### Bug Fixes 53 | 54 | * only use custom `publicPath` if it is not absolute ([d8df9af](https://github.com/nuxt/vercel-builder/commit/d8df9aff618c322617523c6b07ba65b45e69be59)), closes [#578](https://github.com/nuxt/vercel-builder/issues/578) 55 | 56 | ### [0.21.2](https://github.com/nuxt/vercel-builder/compare/v0.21.1...v0.21.2) (2021-03-02) 57 | 58 | ### [0.21.1](https://github.com/nuxt/vercel-builder/compare/v0.21.0...v0.21.1) (2021-03-02) 59 | 60 | 61 | ### Bug Fixes 62 | 63 | * improve error messages when failing to load nuxt config ([4874361](https://github.com/nuxt/vercel-builder/commit/48743613505eba308a98b3ade7ba0880599a3b2c)) 64 | 65 | ## [0.21.0](https://github.com/nuxt/vercel-builder/compare/v0.20.3...v0.21.0) (2021-02-25) 66 | 67 | 68 | ### Features 69 | 70 | * zero-config integration with vercel analytics ([#518](https://github.com/nuxt/vercel-builder/issues/518)) ([5001a4a](https://github.com/nuxt/vercel-builder/commit/5001a4ac8a30162c8a4a2855750e27bd8318073f)) 71 | 72 | ### [0.20.3](https://github.com/nuxt/vercel-builder/compare/v0.20.2...v0.20.3) (2021-02-15) 73 | 74 | 75 | ### Bug Fixes 76 | 77 | * remove needless default ([455ff1a](https://github.com/nuxt/vercel-builder/commit/455ff1a7aa3185cfb5db1de6930e38fa6faa7a47)) 78 | 79 | ### [0.20.2](https://github.com/nuxt/vercel-builder/compare/v0.20.1...v0.20.2) (2021-02-15) 80 | 81 | 82 | ### Bug Fixes 83 | 84 | * de-default the jiti import ([bce0a26](https://github.com/nuxt/vercel-builder/commit/bce0a26f253aca14362ae1825f517c439c019baa)) 85 | 86 | ### [0.20.1](https://github.com/nuxt/vercel-builder/compare/v0.20.0...v0.20.1) (2021-02-15) 87 | 88 | 89 | ### Bug Fixes 90 | 91 | * add workaround for esm for Nuxt < 2.15 ([a7a2e49](https://github.com/nuxt/vercel-builder/commit/a7a2e49a20d70766b7eace060b305baeb6fecc14)) 92 | 93 | ## [0.20.0](https://github.com/nuxt/vercel-builder/compare/v0.19.0...v0.20.0) (2021-02-15) 94 | 95 | 96 | ### ⚠ BREAKING CHANGES 97 | 98 | * upgrade to nuxt 2.15 99 | 100 | ### Miscellaneous Chores 101 | 102 | * upgrade to nuxt 2.15 ([1588c3b](https://github.com/nuxt/vercel-builder/commit/1588c3b5b1a4383176b47d2a60cdc73cada1554a)) 103 | 104 | ## [0.19.0](https://github.com/nuxt/vercel-builder/compare/v0.18.1...v0.19.0) (2021-01-30) 105 | 106 | 107 | ### Features 108 | 109 | * add support for `NPM_RC` and `NPM_TOKEN` ([a223f15](https://github.com/nuxt/vercel-builder/commit/a223f15ccfb4ed39f3292d946b2c0a81289f2ecc)), closes [#488](https://github.com/nuxt/vercel-builder/issues/488) 110 | 111 | ### [0.18.1](https://github.com/nuxt/vercel-builder/compare/v0.18.0...v0.18.1) (2020-11-30) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * collect all static generated files ([fe4e507](https://github.com/nuxt/vercel-builder/commit/fe4e50701bd9d8ad855c5fcdcf87ff4fb210a243)), closes [#391](https://github.com/nuxt/vercel-builder/issues/391) 117 | 118 | ## [0.18.0](https://github.com/nuxt/vercel-builder/compare/v0.17.12...v0.18.0) (2020-11-09) 119 | 120 | 121 | ### Features 122 | 123 | * add internal server for serverMiddleware ([#375](https://github.com/nuxt/vercel-builder/issues/375)) ([4935cab](https://github.com/nuxt/vercel-builder/commit/4935cab146a04366beb31fc14c35f78cb779d202)) 124 | 125 | ### [0.17.12](https://github.com/nuxt/vercel-builder/compare/v0.17.11...v0.17.12) (2020-09-28) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * improve cache handling ([bec1d02](https://github.com/nuxt/vercel-builder/commit/bec1d02cd8d41ce91f0fccca944f0c77cdafe948)) 131 | * print debugging info and don't use `repoRootPath` for `node_modules` ([2626f7f](https://github.com/nuxt/vercel-builder/commit/2626f7f068dde19f146b82270ad3a8a153164f18)) 132 | 133 | ### [0.17.11](https://github.com/nuxt/vercel-builder/compare/v0.17.10...v0.17.11) (2020-09-27) 134 | 135 | ### [0.17.10](https://github.com/nuxt/vercel-builder/compare/v0.17.9...v0.17.10) (2020-09-27) 136 | 137 | 138 | ### Bug Fixes 139 | 140 | * preserve contents of `node_modules` folder if it exists ([b9318b8](https://github.com/nuxt/vercel-builder/commit/b9318b8f98f4e807b90babdbeb2db1759d843258)) 141 | 142 | ### [0.17.9](https://github.com/nuxt/vercel-builder/compare/v0.17.8...v0.17.9) (2020-09-27) 143 | 144 | 145 | ### Bug Fixes 146 | 147 | * recursively delete `node_modules` if it exists ([461e2ff](https://github.com/nuxt/vercel-builder/commit/461e2ffb5c3e16c6ca1d76f2d422f702d5db2b05)) 148 | 149 | ### [0.17.8](https://github.com/nuxt/vercel-builder/compare/v0.17.7...v0.17.8) (2020-09-27) 150 | 151 | 152 | ### Bug Fixes 153 | 154 | * hotfix to address unlinking `node_modules` issue ([9334118](https://github.com/nuxt/vercel-builder/commit/9334118fa58ed20abd6b38a9c56e530ef3f1a353)), closes [#369](https://github.com/nuxt/vercel-builder/issues/369) 155 | 156 | ### [0.17.7](https://github.com/nuxt/vercel-builder/compare/v0.17.6...v0.17.7) (2020-09-22) 157 | 158 | 159 | ### Bug Fixes 160 | 161 | * revert vercel monorepo support ([#366](https://github.com/nuxt/vercel-builder/issues/366)) ([f7d4ff4](https://github.com/nuxt/vercel-builder/commit/f7d4ff4e27fd1e5f175e45792a631c81abf6f976)) 162 | 163 | ### [0.17.6](https://github.com/nuxt/vercel-builder/compare/v0.17.5...v0.17.6) (2020-09-22) 164 | 165 | ### [0.17.5](https://github.com/nuxt/vercel-builder/compare/v0.17.4...v0.17.5) (2020-09-06) 166 | 167 | 168 | ### Bug Fixes 169 | 170 | * add support for UTF-16 LE encoded `tsconfig.json` ([#354](https://github.com/nuxt/vercel-builder/issues/354)) ([ac40dd2](https://github.com/nuxt/vercel-builder/commit/ac40dd2fe9b151521f1f1d88eaa98499f913b95e)), closes [#245](https://github.com/nuxt/vercel-builder/issues/245) 171 | 172 | ### [0.17.4](https://github.com/nuxt/vercel-builder/compare/v0.17.3...v0.17.4) (2020-08-26) 173 | 174 | ### [0.17.3](https://github.com/nuxt/now-builder/compare/v0.17.2...v0.17.3) (2020-07-02) 175 | 176 | ### [0.17.2](https://github.com/nuxt/now-builder/compare/v0.17.1...v0.17.2) (2020-05-14) 177 | 178 | ### [0.17.1](https://github.com/nuxt/now-builder/compare/v0.17.0...v0.17.1) (2020-02-27) 179 | 180 | 181 | ### Features 182 | 183 | * add option to include static generated pages ([#185](https://github.com/nuxt/now-builder/issues/185)) ([a488cb6](https://github.com/nuxt/now-builder/commit/a488cb68917c6b70d32e77f43b73a8d0199a8010)) 184 | 185 | ## [0.17.0](https://github.com/nuxt/now-builder/compare/v0.16.6...v0.17.0) (2020-01-23) 186 | 187 | 188 | ### ⚠ BREAKING CHANGES 189 | 190 | * @nuxt/now-builder no longer supports node v8 191 | 192 | * docs: correct default version 193 | 194 | * chore: upgrade now deps 195 | 196 | Co-authored-by: Steven 197 | 198 | ### Features 199 | 200 | * add support for now-build script ([#168](https://github.com/nuxt/now-builder/issues/168)) ([5266188](https://github.com/nuxt/now-builder/commit/526618864ffe2e0dd8fc3ea6cab99b674fc4088b)) 201 | * support new config options ([#158](https://github.com/nuxt/now-builder/issues/158)) ([01a241a](https://github.com/nuxt/now-builder/commit/01a241a52f485d973b541d843777bca6331bb133)) 202 | 203 | 204 | ### Bug Fixes 205 | 206 | * pin old version of nuxt for compat with node 8 ([d4408fd](https://github.com/nuxt/now-builder/commit/d4408fdddcb1b41e4945ab706e93a1a6bcf1d68f)) 207 | * use srcDir to retrieve static folder ([#150](https://github.com/nuxt/now-builder/issues/150)) ([e23b4e5](https://github.com/nuxt/now-builder/commit/e23b4e5c5c1760179d217c6d22a871a7bf8b8e75)) 208 | * use srcDir when compiling build files ([#152](https://github.com/nuxt/now-builder/issues/152)) ([6e25975](https://github.com/nuxt/now-builder/commit/6e25975bd33de83db2f4a24c8964af63667c6273)) 209 | 210 | ### [0.16.6](https://github.com/nuxt/now-builder/compare/v0.16.5...v0.16.6) (2019-10-23) 211 | 212 | 213 | ### Bug Fixes 214 | 215 | * install devDependencies with npm ([c14deee](https://github.com/nuxt/now-builder/commit/c14deee)), closes [#143](https://github.com/nuxt/now-builder/issues/143) 216 | 217 | ### [0.16.5](https://github.com/nuxt/now-builder/compare/v0.16.4...v0.16.5) (2019-10-11) 218 | 219 | 220 | ### Bug Fixes 221 | 222 | * prepare cache against workCache ([#136](https://github.com/nuxt/now-builder/issues/136)) ([8dfac11](https://github.com/nuxt/now-builder/commit/8dfac11)), closes [#133](https://github.com/nuxt/now-builder/issues/133) 223 | * remove boilerplate routes from example ([#140](https://github.com/nuxt/now-builder/issues/140)) ([d0a50cb](https://github.com/nuxt/now-builder/commit/d0a50cb)) 224 | * revert to using cachePath ([#135](https://github.com/nuxt/now-builder/issues/135)) ([da7409e](https://github.com/nuxt/now-builder/commit/da7409e)), closes [#133](https://github.com/nuxt/now-builder/issues/133) 225 | * test for presence of nuxt.config.ts before compile ([a218681](https://github.com/nuxt/now-builder/commit/a218681)) 226 | 227 | ### [0.16.4](https://github.com/nuxt/now-builder/compare/v0.16.3...v0.16.4) (2019-09-11) 228 | 229 | 230 | ### Bug Fixes 231 | 232 | * force production NODE_ENV ([7333afa](https://github.com/nuxt/now-builder/commit/7333afa)) 233 | * set NODE_ENV to production before building nuxt ([9bbaeac](https://github.com/nuxt/now-builder/commit/9bbaeac)), closes [#126](https://github.com/nuxt/now-builder/issues/126) 234 | * set NODE_ENV to production before building nuxt ([#127](https://github.com/nuxt/now-builder/issues/127)) ([db76f1c](https://github.com/nuxt/now-builder/commit/db76f1c)) 235 | 236 | ### [0.16.3](https://github.com/nuxt/now-builder/compare/v0.16.2...v0.16.3) (2019-09-10) 237 | 238 | 239 | ### Bug Fixes 240 | 241 | * consume default export of nuxt config file ([#124](https://github.com/nuxt/now-builder/issues/124)) ([22dff63](https://github.com/nuxt/now-builder/commit/22dff63)), closes [#43](https://github.com/nuxt/now-builder/issues/43) 242 | 243 | ### [0.16.2](https://github.com/nuxt/now-builder/compare/v0.16.1...v0.16.2) (2019-09-10) 244 | 245 | 246 | ### Bug Fixes 247 | 248 | * use new object property from Now ([#122](https://github.com/nuxt/now-builder/issues/122)) ([d01e992](https://github.com/nuxt/now-builder/commit/d01e992)), closes [#111](https://github.com/nuxt/now-builder/issues/111) 249 | 250 | ### [0.16.1](https://github.com/nuxt/now-builder/compare/v0.16.0...v0.16.1) (2019-08-30) 251 | 252 | 253 | ### Bug Fixes 254 | 255 | * fix typescript dependency test ([#118](https://github.com/nuxt/now-builder/issues/118)) ([5d41eee](https://github.com/nuxt/now-builder/commit/5d41eee)) 256 | 257 | 258 | 259 | ## [0.16.0](https://github.com/nuxt/now-builder/compare/v0.15.3...v0.16.0) (2019-08-30) 260 | 261 | 262 | ### Bug Fixes 263 | 264 | * allow installing node 10 dependencies ([#109](https://github.com/nuxt/now-builder/issues/109)) ([20962de](https://github.com/nuxt/now-builder/commit/20962de)) 265 | * test for presence of dependencies before accessing ([#116](https://github.com/nuxt/now-builder/issues/116)) ([4fc181d](https://github.com/nuxt/now-builder/commit/4fc181d)) 266 | 267 | 268 | ### Features 269 | 270 | * add required options for typescript projects ([#110](https://github.com/nuxt/now-builder/issues/110)) ([100e6f4](https://github.com/nuxt/now-builder/commit/100e6f4)) 271 | * expose static files as routes ([#106](https://github.com/nuxt/now-builder/issues/106)) ([d66931d](https://github.com/nuxt/now-builder/commit/d66931d)) 272 | 273 | 274 | 275 | ### [0.15.3](https://github.com/nuxt/now-builder/compare/v0.15.2...v0.15.3) (2019-08-22) 276 | 277 | 278 | ### Features 279 | 280 | * add support for yarn workspaces ([#103](https://github.com/nuxt/now-builder/issues/103)) ([a4a7e94](https://github.com/nuxt/now-builder/commit/a4a7e94)) 281 | 282 | ### [0.14.6](https://github.com/nuxt/now-builder/compare/v0.14.5...v0.14.6) (2019-06-29) 283 | 284 | 285 | ### Bug Fixes 286 | 287 | * downgrade execa to 1.x ([55b01bb](https://github.com/nuxt/now-builder/commit/55b01bb)) 288 | 289 | 290 | 291 | ### [0.14.5](https://github.com/nuxt/now-builder/compare/v0.14.4...v0.14.5) (2019-06-29) 292 | 293 | 294 | ### Bug Fixes 295 | 296 | * pin @now/node-bridge dependency to 1.2.1 ([#67](https://github.com/nuxt/now-builder/issues/67)) ([72090c6](https://github.com/nuxt/now-builder/commit/72090c6)) 297 | * pin all non dev dependencies ([f386391](https://github.com/nuxt/now-builder/commit/f386391)) 298 | * use preferLocal: true for execa ([00318b4](https://github.com/nuxt/now-builder/commit/00318b4)) 299 | 300 | 301 | 302 | ## [0.14.4](https://github.com/nuxt/now-builder/compare/v0.14.3...v0.14.4) (2019-05-27) 303 | 304 | 305 | 306 | ## [0.14.3](https://github.com/nuxt/now-builder/compare/v0.14.2...v0.14.3) (2019-04-30) 307 | 308 | 309 | 310 | ## [0.14.2](https://github.com/nuxt/now-builder/compare/v0.14.1...v0.14.2) (2019-04-30) 311 | 312 | 313 | ### Bug Fixes 314 | 315 | * add package.json to serverFiles ([#21](https://github.com/nuxt/now-builder/issues/21)) ([3b4e3d9](https://github.com/nuxt/now-builder/commit/3b4e3d9)) 316 | * allow using nuxt 2.4.0 ([#29](https://github.com/nuxt/now-builder/issues/29)) ([2e096c2](https://github.com/nuxt/now-builder/commit/2e096c2)) 317 | 318 | 319 | 320 | ## [0.14.1](https://github.com/nuxt/now-builder/compare/v0.14.0...v0.14.1) (2019-04-03) 321 | 322 | 323 | ### Bug Fixes 324 | 325 | * read nuxt.config after yarn install. fixes [#26](https://github.com/nuxt/now-builder/issues/26) ([a5a1068](https://github.com/nuxt/now-builder/commit/a5a1068)) 326 | 327 | 328 | 329 | # [0.14.0](https://github.com/nuxt/now-builder/compare/v0.13.1...v0.14.0) (2019-04-02) 330 | 331 | 332 | ### Bug Fixes 333 | 334 | * resolve buildDir relative to rootDir ([4d56202](https://github.com/nuxt/now-builder/commit/4d56202)) 335 | 336 | 337 | ### Features 338 | 339 | * wait for nuxt to be ready before render ([#21](https://github.com/nuxt/now-builder/issues/21), [#24](https://github.com/nuxt/now-builder/issues/24)) ([75151ca](https://github.com/nuxt/now-builder/commit/75151ca)) 340 | 341 | 342 | 343 | ## [0.13.1](https://github.com/nuxt/now-builder/compare/v0.13.0...v0.13.1) (2019-03-23) 344 | 345 | 346 | ### Bug Fixes 347 | 348 | * use --no-lock. fixes [#19](https://github.com/nuxt/now-builder/issues/19). ([52b8a89](https://github.com/nuxt/now-builder/commit/52b8a89)) 349 | 350 | 351 | 352 | # Change Log 353 | 354 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 355 | 356 | # [0.13.0](https://github.com/nuxt/now-builder/compare/v0.12.3...v0.13.0) (2019-02-24) 357 | 358 | 359 | ### Features 360 | 361 | * working cache! ([b942a5d](https://github.com/nuxt/now-builder/commit/b942a5d)) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Nuxt.js Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![vercel-builder](https://user-images.githubusercontent.com/904724/61308402-7a752d00-a7f0-11e9-9502-23731ccd00fd.png) 2 | 3 | # Nuxt Vercel Builder 4 | 5 | [![npm version][npm-version-src]][npm-version-href] 6 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 7 | [![packagephobia][packagephobia-src]][packagephobia-href] 8 | [![Github actions status][github-ci-src]][github-ci-href] 9 | [![Codecov][codecov-src]][codecov-href] 10 | [![Dependencies][david-dm-src]][david-dm-href] 11 | [![Standard JS][standard-js-src]][standard-js-href] 12 | 13 | > ⚠️ This is a _legacy builder_ and only works for Nuxt 2. We'd strongly recommend using [Nuxt Bridge](https://github.com/nuxt/bridge) or [Nuxt 3](https://nuxt.com/docs/getting-started/introduction), which use the latest Vercel features instead. 14 | 15 | --- 16 | 17 | `@nuxtjs/vercel-builder` allows you ship a fast, production-ready [Nuxt 2 application](https://nuxtjs.org) that scales automatically on Vercel when using SSR rendering. 18 | 19 | ### When to use it 20 | 21 | **This package is only made for Nuxt 2 SSR applications - and you probably do not need to use it.** 22 | 23 | 👉 If you want to deploy a statically generated Nuxt 2 application instead, Vercel is a zero configuration provider. Check [this guide from Vercel](https://vercel.com/guides/deploying-nuxtjs-with-vercel) for more information. 24 | 25 | 👉 If you want to deploy a Nuxt Bridge or Nuxt 3 app, Vercel deployment will work with zero configuration. Check [this guide](https://nitro.unjs.io/deploy/providers/vercel) for more information. 26 | 27 | ⚠️ We would advise you migrate your app to Nuxt 3, which features a much superior integration with Vercel using their latest Build API. 28 | 29 | ### How it works 30 | 31 | This Vercel builder takes a Nuxt application defined by a `nuxt.config.js` (or `.ts`) entrypoint and deploys it as a serverless function in a Vercel environment. 32 | 33 | It features built-in caching of `node_modules` and the global yarn cache (even when dependencies change) and a multi-stage build for fast and small deployments. 34 | 35 | ## Setup 36 | 37 | All you need is a [Nuxt](https://nuxtjs.org) application and a [Vercel](https://vercel.com) account. 38 | 39 | Then, simply create a `vercel.json` file at the root of your project: 40 | 41 | ```json 42 | { 43 | "version": 2, 44 | "builds": [ 45 | { 46 | "src": "nuxt.config.js", 47 | "use": "@nuxtjs/vercel-builder" 48 | } 49 | ] 50 | } 51 | ``` 52 | 53 | **NOTE:** When installing your dependencies, Vercel will use the same package manager that is used in the project. Using `yarn` is **highly** recommended due to its [autoclean](https://yarnpkg.com/lang/en/docs/cli/autoclean) functionality, which can decrease lambda size. 54 | 55 | ## Examples 56 | 57 | See [Basic example](./examples/basic) for a more complete deployable example, including an example of how to set up `vercel dev` support. 58 | 59 | See [Deploying two Nuxt apps side-by-side](./examples/side-by-side/README.md) for details on deploying two Nuxt apps in one monorepo. 60 | 61 | ## Configuration 62 | 63 | ### `serverFiles` 64 | 65 | - Type: `Array` 66 | 67 | If you need to include files in the server lambda that are not built by webpack (or within `static/`), such as a local module or serverMiddleware, you may specify them with this option. Each item can be a glob pattern. 68 | 69 | **Example** 70 | 71 | ```json 72 | { 73 | "builds": [ 74 | { 75 | "src": "nuxt.config.js", 76 | "use": "@nuxtjs/vercel-builder", 77 | "config": { 78 | "serverFiles": ["server-middleware/**"] 79 | } 80 | } 81 | ] 82 | } 83 | ``` 84 | 85 | ### `internalServer` 86 | 87 | - Type: `Boolean` 88 | - Default: `false` 89 | 90 | If you have defined `serverMiddleware` in your `nuxt.config`, this builder will automatically enable an internal server within the lambda so you can access your own endpoints via `http://localhost:3000`. (This does not affect how you call your endpoints from client-side.) 91 | 92 | If you need to enable or disable the internal server manually (for example, if you are adding server middleware via a module), just set `internalServer` within the builder options. 93 | 94 | ### `generateStaticRoutes` 95 | 96 | - Type: `Boolean` 97 | - Default: `false` 98 | 99 | To pre-render routes during the build using `nuxt generate` set this to true. Routes that are not generated will fallback to the server lambda. You will need to [specify the routes to be generated](https://nuxtjs.org/api/configuration-generate/#routes) in your `nuxt.config`. 100 | 101 | **Example** 102 | 103 | ```json 104 | { 105 | "builds": [ 106 | { 107 | "src": "nuxt.config.js", 108 | "use": "@nuxtjs/vercel-builder", 109 | "config": { 110 | "generateStaticRoutes": true 111 | } 112 | } 113 | ] 114 | } 115 | ``` 116 | 117 | ### `tscOptions` 118 | 119 | - Type: `Object` 120 | 121 | If you need to pass TypeScript compiler options to override your `tsconfig.json`, you can pass them here. See [the TypeScript documentation](https://www.typescriptlang.org/docs/handbook/compiler-options.html) for valid options. 122 | 123 | **Example** 124 | 125 | ```json 126 | { 127 | "src": "nuxt.config.ts", 128 | "use": "@nuxtjs/vercel-builder", 129 | "config": { 130 | "tscOptions": { 131 | "sourceMap": false 132 | } 133 | } 134 | } 135 | ``` 136 | 137 | You can also include a `tsconfig.vercel.json` file alongside your `tsconfig.json` file. The `compilerOptions` from those files, along with any `tscOptions` passed through vercel.json, will be merged and the resulting options used to compile your `nuxt.config.ts`, local modules and serverMiddleware. 138 | 139 | ### `memory` 140 | 141 | - Type: `Number` 142 | 143 | Pass this option if you need to customize the default memory limit of the serverless function that renders your pages. 144 | 145 | ### `maxDuration` 146 | 147 | - Type: `Number` 148 | 149 | Pass this option if you need to customize the max duration of the serverless function that renders your pages. 150 | 151 | ### Environment variables 152 | 153 | #### `env` 154 | 155 | If you are [accessing environment variables via `env`](https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-env#process-env-) within your Nuxt build, then they will be **baked in at build time**. This means that if you update the variables in the Vercel dashboard, you will need to trigger a deployment again for the changes to take effect. You must include these variables in `build.env` in your `vercel.json` (see below). 156 | 157 | #### `runtimeConfig` 158 | 159 | If you are using Nuxt 2.13+, it is recommended to use the [new runtimeConfig options](https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-runtime-config) instead. 160 | 161 | #### Exposing variables 162 | 163 | There are two environments where you may need to expose environment variables within `vercel.json`. They are `env` (for _runtime_ variables) and `build.env` (for _build-time_ variables, which may not be required for `runtimeConfig`). See [Vercel documentation](https://vercel.com/docs/cli#project-configuration/env). For example: 164 | 165 | ```json 166 | { 167 | "env": { 168 | "MY_VARIABLE": true 169 | }, 170 | "build": { 171 | "env": { 172 | "MY_VARIABLE": true 173 | } 174 | } 175 | } 176 | ``` 177 | 178 | Finally, note that if you want to access Vercel's [system environment variables](https://vercel.com/docs/environment-variables#system-environment-variables), you may want ensure that system environment variables are automatically exposed. 179 | 180 | ## Usage with Typescript 181 | 182 | `vercel-builder` supports TypeScript runtime compilation. It adds in a pre-compilation step as part of building the lambda for files not compiled by Webpack, such as `nuxt.config.ts`, local modules and serverMiddleware. 183 | 184 | References to original TS files in strings outside of `modules` or `serverMiddleware` may therefore cause unexpected errors. 185 | 186 | Don't forget to update your Nuxt config filename in your `vercel.json` file. 187 | 188 | ## Technical details 189 | 190 | ### Monorepos 191 | 192 | Just enable the "Include source files outside of the Root Directory in the Build Step" option in the **Root Directory** section within the project settings. 193 | 194 | ![Vercel monorepo config](/docs/monorepo-config.png) 195 | 196 | ### Private npm modules 197 | 198 | To install private npm modules, define `NPM_AUTH_TOKEN` or `NPM_TOKEN` as a [build environment variable](https://vercel.com/docs/configuration#project/build-env) in `vercel.json`. 199 | 200 | Alternatively, you can inline your entire `.npmrc` file in a `NPM_RC` environment variable. 201 | 202 | ### Node.js version 203 | 204 | The newest available Node.js version is automatically selected. If your packages depend on a particular major release Node.js version, you can specify the version in your `package.json` - see [Vercel documentation](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/node-js#node.js-version). 205 | 206 | ### `vercel-build` script support 207 | 208 | This builder will run a given [custom build step](https://vercel.com/docs/runtimes#advanced-usage/advanced-node-js-usage/custom-build-step-for-node-js) if you have added a `vercel-build` key under `scripts` in `package.json`. 209 | 210 | ## Deploying additional serverless functions 211 | 212 | You can also deploy additional serverless functions _alongside_ your Nuxt application. 213 | 214 | ### serverMiddleware 215 | 216 | Create an `api` folder at the root of your project, and then create a file in it, for example `hello.js`: 217 | 218 | ```js 219 | import express from 'express' 220 | import bodyParser from 'body-parser' 221 | 222 | const app = express() 223 | app.use(bodyParser.json()) 224 | 225 | // It is important that the full path is specified here 226 | app.post('/api/hello', function(req, res) { 227 | const { info } = req.body 228 | console.log(info) 229 | res 230 | .status(200) 231 | .json({ info }) 232 | .end() 233 | }) 234 | 235 | export default app 236 | ``` 237 | 238 | ### Setup the Vercel config 239 | 240 | In your `vercel.json`, add your additional endpoints: 241 | 242 | ```json 243 | { 244 | "version": 2, 245 | "routes": [ 246 | { 247 | "src": "/api/hello", 248 | "dest": "/api/hello.js" 249 | } 250 | ], 251 | "builds": [ 252 | { 253 | "src": "api/**/*.js", 254 | "use": "@vercel/node" 255 | }, 256 | { 257 | "src": "nuxt.config.ts", 258 | "use": "@nuxtjs/vercel-builder", 259 | "config": { 260 | "serverFiles": ["api/**"] 261 | } 262 | } 263 | ] 264 | } 265 | ``` 266 | 267 | ### Add it to the Nuxt config 268 | 269 | If you want to interact with this API whilst developing your Nuxt app, you can add it to your `serverMiddleware` conditionally. 270 | 271 | ```js 272 | export default { 273 | serverMiddleware: 274 | process.env.NODE_ENV === 'production' ? [] : ['~/api/hello.js'], 275 | } 276 | ``` 277 | 278 | And that's it! You can now go to `http://locahost:3000/api/hello` and see the result! In production the endpoint will be handled with Vercel, but locally Nuxt will manage it for you. 279 | 280 | # License 281 | 282 | [MIT License](./LICENSE) 283 | 284 | Documentation and builder inspired by [Next.js](https://nextjs.org) by [Vercel](https://vercel.com) 285 | 286 | Copyright (c) Nuxt Community 287 | 288 | 289 | 290 | [npm-version-src]: https://flat.badgen.net/npm/dt/@nuxtjs/vercel-builder 291 | [npm-version-href]: https://npmjs.com/package/@nuxtjs/vercel-builder 292 | [npm-downloads-src]: https://flat.badgen.net/npm/v/@nuxtjs/vercel-builder 293 | [npm-downloads-href]: https://npmjs.com/package/@nuxtjs/vercel-builder 294 | [github-ci-src]: https://flat.badgen.net//github/checks/nuxt/vercel-builder 295 | [github-ci-href]: https://github.com/nuxt/vercel-builder/actions 296 | [codecov-src]: https://flat.badgen.net/codecov/c/github/nuxt/vercel-builder 297 | [codecov-href]: https://codecov.io/gh/nuxt/vercel-builder 298 | [david-dm-src]: https://flat.badgen.net/david/dep/nuxt/vercel-builder 299 | [david-dm-href]: https://david-dm.org/nuxt/vercel-builder 300 | [standard-js-src]: https://flat.badgen.net/badge/code%20style/standard/f2a 301 | [standard-js-href]: https://standardjs.com 302 | [packagephobia-src]: https://flat.badgen.net/packagephobia/install/@nuxtjs/vercel-builder 303 | [packagephobia-href]: https://packagephobia.now.sh/result?p=@nuxtjs/vercel-builder 304 | -------------------------------------------------------------------------------- /docs/monorepo-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/docs/monorepo-config.png -------------------------------------------------------------------------------- /examples/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | rules: 2 | "@typescript-eslint/explicit-function-return-type": 0 3 | -------------------------------------------------------------------------------- /examples/basic/.vercelignore: -------------------------------------------------------------------------------- 1 | .nuxt 2 | node_modules 3 | *.log 4 | README.md 5 | -------------------------------------------------------------------------------- /examples/basic/README.md: -------------------------------------------------------------------------------- 1 | # Vercel example 2 | 3 | ## Development 4 | 5 | You need to have package `vercel` globally installed: 6 | 7 | ```sh 8 | yarn global add vercel 9 | ``` 10 | 11 | Make sure to add `yarn run dev` on the _Development command_, on the settings of your project on vercel.com: 12 | 13 | ![vercel settings](vercel-settings.png) 14 | 15 | Then you can run the following command to have a local development experience 16 | 17 | ```sh 18 | vc dev -A vercel.dev.json 19 | ``` 20 | 21 | ## Deployment 22 | 23 | Any lambdas added to `api/` must be added to the `routes` config in the `vercel.json` file. 24 | -------------------------------------------------------------------------------- /examples/basic/api/contents/[id].js: -------------------------------------------------------------------------------- 1 | export default (req, res) => { 2 | const id = req.query.id 3 | res.json({ message: 'dynamic route', id }) 4 | } 5 | -------------------------------------------------------------------------------- /examples/basic/api/hello.js: -------------------------------------------------------------------------------- 1 | export default (req, res) => { 2 | res.json({ message: 'hello' }) 3 | } 4 | -------------------------------------------------------------------------------- /examples/basic/nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | head: { 3 | title: 'Nuxt + Now2' 4 | }, 5 | serverMiddleware: [ 6 | '~/server-middleware/log.js' 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MIT", 4 | "scripts": { 5 | "dev": "nuxt --port $PORT", 6 | "local-dev": "vercel dev -A vercel.dev.json" 7 | }, 8 | "dependencies": { 9 | "access-log": "^0.4.1" 10 | }, 11 | "devDependencies": { 12 | "nuxt-edge": "latest" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/basic/pages/_.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 180 | 181 | 190 | -------------------------------------------------------------------------------- /examples/basic/server-middleware/log.js: -------------------------------------------------------------------------------- 1 | const accesslog = require('access-log') 2 | 3 | module.exports = (req, res, next) => { 4 | accesslog(req, res) 5 | next() 6 | } 7 | -------------------------------------------------------------------------------- /examples/basic/static/test.txt: -------------------------------------------------------------------------------- 1 | test.txt 2 | -------------------------------------------------------------------------------- /examples/basic/vercel-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/examples/basic/vercel-settings.png -------------------------------------------------------------------------------- /examples/basic/vercel.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2 3 | } 4 | -------------------------------------------------------------------------------- /examples/basic/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "nuxt.config.js", 6 | "use": "@nuxtjs/vercel-builder", 7 | "config": { 8 | "serverFiles": ["server-middleware/**"] 9 | } 10 | }, 11 | { 12 | "src": "api/**/*.js", 13 | "use": "@vercel/node" 14 | } 15 | ], 16 | "routes": [ 17 | { 18 | "src": "/api/contents/(?[^/]+)", 19 | "dest": "/api/contents/[id].js?id=$id", 20 | "methods": ["GET", "OPTIONS"], 21 | "headers": { 22 | "Access-Control-Allow-Methods": "GET", 23 | "Access-Control-Allow-Headers": "Content-Type" 24 | } 25 | }, 26 | { 27 | "src": "/api/(.*)", 28 | "dest": "/api/$1.js", 29 | "methods": ["GET", "OPTIONS"], 30 | "headers": { 31 | "Access-Control-Allow-Methods": "GET", 32 | "Access-Control-Allow-Headers": "Content-Type" 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /examples/side-by-side/.nowignore: -------------------------------------------------------------------------------- 1 | .nuxt 2 | node_modules 3 | *.log 4 | README.md 5 | -------------------------------------------------------------------------------- /examples/side-by-side/README.md: -------------------------------------------------------------------------------- 1 | # Deploying two Nuxt apps side-by-side 2 | 3 | `@nuxtjs/vercel-builder` works out of the box for a single Nuxt app. If you're trying to deploy two Nuxt apps side-by-side, you'll need to add some extra configuration options. 4 | 5 | The goal of this, is to run multiple Nuxt apps in one Vercel deployment, and allow them to use shared components. 6 | 7 | To see this working altogether, check out [a deployable example of two side-by-side apps](./). 8 | 9 | ## Setting up this example. 10 | 11 | Lets walk through the setup of this side-by-side app. In our case, main app, and separate admin app. 12 | 13 | 1. vercel.json should exist at root level. 14 | 2. Each app should be in its own folder, at root level. 15 | 3. Each individual app should have their own package.json, facilitating their own Nuxt build (each app can have different dependancies) 16 | 4. If you're using shared components, they should be available in the root folder. 17 | 18 | Your project structure should roughly look like so: 19 | 20 | ``` 21 | vercel.json 22 | 23 | -- app/ 24 | ---- app files... 25 | ---- nuxt.config.json 26 | 27 | -- admin/ 28 | ---- app files... 29 | ---- nuxt.config.json 30 | 31 | -- shared/ 32 | ---- shared mixins, components, etc 33 | ``` 34 | 35 | ### Additional nuxt.config.js setup 36 | 37 | In order to customize where each of these apps is built in our `@nuxtjs/vercel-builder`, we need to update our `nuxt.config.js`. 38 | 39 | The goal here is to change our single app from building to `_nuxt` into two apps, building into `_nuxt/app` and `_nuxt/admin` respectively. 40 | 41 | For this to work, we need to update: 42 | 43 | - **srcDir** for the app, to match our Vercel builder 44 | - **buildDir** to point to our new build folder 45 | - **lambdaName** there is no need to set this for the main app, but secondary app needs to have a unique lambda name. 46 | - **build.publicPath** this needs to match the route found in our `vercel.json` 47 | - **build.extend** to include support for our shared components 48 | 49 | #### Updating `app/nuxt.config.js` 50 | 51 | This will build our main app to `_nuxt/app`, and serve it at `/` in our `vercel.json` routes. 52 | 53 | ```js 54 | { 55 | "srcDir": __dirname, 56 | "buildDir": "_nuxt/app", 57 | "lambdaName": "index", // main app should be index, not needed here 58 | "build": { 59 | // publicPath matches our vercel.json routes 60 | "publicPath": "_nuxt/app", 61 | extend(config) { 62 | // Add '~/shared' as an alias. 63 | config.resolve.alias.shared = resolve(__dirname, "../shared"); 64 | config.resolve.alias["~shared"] = resolve(__dirname, "../shared"); 65 | } 66 | } 67 | } 68 | ``` 69 | 70 | #### Updating `admin/nuxt.config.json` 71 | 72 | This is our secondary app, meant to run side-by-side with our main app. This will build to `_nuxt/admin` and serve it at `/admin` in our `vercel.json` routes. 73 | 74 | ```js 75 | { 76 | "srcDir": __dirname, 77 | "buildDir": "_nuxt/admin", 78 | "lambdaName": "admin", // if we don't name our secondary app, builds two index lambdas 79 | "router": { 80 | // gotta match our url routing at the app level 81 | "base": "/admin/" 82 | }, 83 | "build": { 84 | // publicPath matches our vercel.json routes 85 | "publicPath": "_nuxt/admin", 86 | extend(config) { 87 | // Add '~/shared' as an alias. 88 | config.resolve.alias.shared = resolve(__dirname, "../shared"); 89 | config.resolve.alias["~shared"] = resolve(__dirname, "../shared"); 90 | } 91 | } 92 | } 93 | ``` 94 | 95 | ### Setting up `vercel.json` for the deploy 96 | 97 | The goal here, is two set up two sets of nuxt catch all routes, and forward them to their respective apps. 98 | 99 | We have **two builds**, one for each of our apps. These should point to the nuxt.config.js files you prepped above. 100 | 101 | Our routes will include: 102 | 103 | - Catch all for `/admin/_nuxt/admin/` build file lookups. This will point it to the root `_nuxt/admin` files we built. 104 | - Catch all for `/_nuxt/` routes for main app 105 | - admin main route, for hitting the app directly. 106 | - catch all for admin sub routes, for hitting app pages 107 | - generic catch all, for remaining routes, to feed to our core app. 108 | 109 | ```json 110 | { 111 | "version": 2, 112 | "builds": [ 113 | { "src": "app/nuxt.config.js", "use": "@nuxtjs/vercel-builder" }, 114 | { "src": "admin/nuxt.config.js", "use": "@nuxtjs/vercel-builder" } 115 | ], 116 | "routes": [ 117 | { 118 | "src": "/admin/_nuxt/(.*)", 119 | "dest": "/_nuxt/$1", 120 | "headers": { 121 | "Cache-Control": "max-age=31557600" 122 | } 123 | }, 124 | { 125 | "src": "/_nuxt/.+", 126 | "headers": { 127 | "Cache-Control": "max-age=31557600" 128 | } 129 | }, 130 | { 131 | "src": "^/admin", 132 | "dest": "/admin" 133 | }, 134 | { 135 | "src": "^/admin/(.*)", 136 | "dest": "/admin" 137 | }, 138 | { 139 | "src": "^/(.*)", 140 | "dest": "/" 141 | } 142 | ] 143 | } 144 | ``` 145 | 146 | ## Deploy the app. 147 | 148 | With this set up, you should be able to `vc` and see your two apps live. 149 | -------------------------------------------------------------------------------- /examples/side-by-side/admin/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 56 | -------------------------------------------------------------------------------- /examples/side-by-side/admin/nuxt.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | 3 | module.exports = { 4 | mode: 'universal', 5 | 6 | // We want our admin build to be in a different src than our app build 7 | srcDir: __dirname, 8 | buildDir: '_nuxt/admin', 9 | lambdaName: 'admin', 10 | router: { 11 | base: '/admin/' 12 | }, 13 | 14 | build: { 15 | // I have set this up to be specifically at root... 16 | publicPath: '_nuxt/admin', 17 | 18 | // add support for ~/shared 19 | extend (config) { 20 | // Add '~/shared' as an alias. 21 | config.resolve.alias.shared = resolve(__dirname, '../shared') 22 | config.resolve.alias['~shared'] = resolve(__dirname, '../shared') 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/side-by-side/admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nuxt --config-file app/nuxt.config.app.js", 6 | "build": "nuxt build --config-file app/nuxt.config.app.js", 7 | "start": "nuxt start --config-file app/nuxt.config.app.js" 8 | }, 9 | "dependencies": { 10 | "nuxt-edge": "latest" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/side-by-side/admin/pages/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | 26 | 58 | -------------------------------------------------------------------------------- /examples/side-by-side/admin/pages/testing.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | 26 | 58 | -------------------------------------------------------------------------------- /examples/side-by-side/admin/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/examples/side-by-side/admin/static/favicon.ico -------------------------------------------------------------------------------- /examples/side-by-side/app/components/LinksInline.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 39 | 40 | 69 | -------------------------------------------------------------------------------- /examples/side-by-side/app/components/LinksList.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /examples/side-by-side/app/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 56 | -------------------------------------------------------------------------------- /examples/side-by-side/app/nuxt.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | 3 | module.exports = { 4 | mode: 'universal', 5 | 6 | // We want our admin build to be in a different src than our app build 7 | srcDir: __dirname, 8 | buildDir: '_nuxt/app', 9 | lambdaName: 'index', // main app should be index 10 | 11 | build: { 12 | publicPath: '_nuxt/app', 13 | 14 | extend (config) { 15 | // Support ~shared alias 16 | config.resolve.alias.shared = resolve(__dirname, '../shared') 17 | config.resolve.alias['~shared'] = resolve(__dirname, '../shared') 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/side-by-side/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "private": true, 4 | "scripts": { 5 | "dev": "nuxt --config-file app/nuxt.config.app.js", 6 | "build": "nuxt build --config-file app/nuxt.config.app.js", 7 | "start": "nuxt start --config-file app/nuxt.config.app.js" 8 | }, 9 | "dependencies": { 10 | "nuxt-edge": "latest" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/side-by-side/app/pages/first.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /examples/side-by-side/app/pages/fourth.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /examples/side-by-side/app/pages/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 33 | 34 | 66 | -------------------------------------------------------------------------------- /examples/side-by-side/app/pages/second.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /examples/side-by-side/app/pages/third.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /examples/side-by-side/app/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/examples/side-by-side/app/static/favicon.ico -------------------------------------------------------------------------------- /examples/side-by-side/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": false, 3 | "version": 2, 4 | "builds": [ 5 | { "src": "app/nuxt.config.js", "use": "@nuxtjs/vercel-builder" }, 6 | { "src": "admin/nuxt.config.js", "use": "@nuxtjs/vercel-builder" } 7 | ], 8 | "routes": [ 9 | { 10 | "src": "/admin/_nuxt/(.*)", 11 | "dest": "/_nuxt/$1", 12 | "headers": { 13 | "Cache-Control": "max-age=31557600" 14 | } 15 | }, 16 | { 17 | "src": "/_nuxt/.+", 18 | "headers": { 19 | "Cache-Control": "max-age=31557600" 20 | } 21 | }, 22 | { 23 | "src": "^/admin", 24 | "dest": "/admin" 25 | }, 26 | { 27 | "src": "^/admin/(.*)", 28 | "dest": "/admin" 29 | }, 30 | { 31 | "src": "^/(.*)", 32 | "dest": "/" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /examples/side-by-side/shared/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 80 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | collectCoverage: true, 4 | collectCoverageFrom: [ 5 | 'lib/**/*.js', 6 | '!lib/launcher.js' 7 | ], 8 | modulePathIgnorePatterns: [ 9 | '/node_modules/', 10 | '/node_modules_dev/', 11 | '/node_modules_prod/' 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nuxtjs/vercel-builder", 3 | "version": "0.25.0", 4 | "repository": "nuxt/vercel-builder", 5 | "license": "MIT", 6 | "exports": { 7 | ".": "./lib/index.js", 8 | "./launcher": "./lib/launcher.js" 9 | }, 10 | "main": "lib/index.js", 11 | "files": [ 12 | "lib", 13 | ".yarnclean" 14 | ], 15 | "scripts": { 16 | "build": "siroc build", 17 | "lint": "eslint --ext .vue,.js,.ts src examples test", 18 | "prepare": "yarn build", 19 | "refresh": "run-s refresh:*", 20 | "refresh:example-basic": "cd examples/basic && rm yarn.lock && yarn && rm -rf node_modules", 21 | "refresh:example-side-by-side-admin": "cd examples/side-by-side/admin && rm yarn.lock && yarn && rm -rf node_modules", 22 | "refresh:example-side-by-side-app": "cd examples/side-by-side/app && rm yarn.lock && yarn && rm -rf node_modules", 23 | "refresh:fixture": "cd test/fixture/www && rm package-lock.json && npm install && rm -rf node_modules", 24 | "refresh:fixture-generated": "cd test/fixture-generated/www && rm yarn.lock && yarn && rm -rf node_modules", 25 | "refresh:fixture-ts": "cd test/fixture-ts/www && rm yarn.lock && yarn && rm -rf node_modules", 26 | "release": "release-it", 27 | "test": "jest test" 28 | }, 29 | "resolutions": { 30 | "@types/mime": "^3.0.4" 31 | }, 32 | "dependencies": { 33 | "@nuxtjs/web-vitals": "^0.2.7", 34 | "@vercel/node-bridge": "4.0.1", 35 | "consola": "2.15.3", 36 | "execa": "^5.1.1", 37 | "fs-extra": "11.2.0", 38 | "jiti": "^1.21.0", 39 | "rc9": "^2.1.2", 40 | "replace-in-file": "^6.3.5", 41 | "resolve-from": "^5.0.0", 42 | "semver": "7.6.0", 43 | "ufo": "^1.5.3" 44 | }, 45 | "devDependencies": { 46 | "@nuxt/types": "^2.17.3", 47 | "@nuxtjs/eslint-config": "^12.0.0", 48 | "@release-it/conventional-changelog": "^8.0.1", 49 | "@types/aws-lambda": "^8.10.137", 50 | "@types/fs-extra": "^11.0.4", 51 | "@types/glob": "^8.1.0", 52 | "@types/node": "^18.19.31", 53 | "@types/semver": "^7.5.8", 54 | "@typescript-eslint/eslint-plugin": "^5.62.0", 55 | "@typescript-eslint/parser": "^5.62.0", 56 | "@vercel/build-utils": "7.11.0", 57 | "@vercel/routing-utils": "3.1.0", 58 | "codecov": "^3.8.3", 59 | "eslint": "^8.57.0", 60 | "eslint-config-standard": "^17.1.0", 61 | "eslint-plugin-import": "^2.29.1", 62 | "eslint-plugin-jest": "^27.9.0", 63 | "eslint-plugin-n": "^14.0.0", 64 | "eslint-plugin-promise": "^6.1.1", 65 | "eslint-plugin-standard": "^5.0.0", 66 | "eslint-plugin-vue": "^9.24.1", 67 | "jest": "29.7.0", 68 | "npm-run-all2": "^5.0.2", 69 | "nuxt": "2.17.3", 70 | "release-it": "^15.11.0", 71 | "siroc": "^0.16.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@nuxtjs", 4 | "packages:linters", 5 | ":maintainLockFilesWeekly", 6 | ":automergeLinters", 7 | ":automergeTypes", 8 | ":automergePatch" 9 | ], 10 | "packageRules": [ 11 | { 12 | "packagePatterns": ["lint"], 13 | "groupName": "all lint dependencies", 14 | "groupSlug": "all-lint" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /siroc.config.ts: -------------------------------------------------------------------------------- 1 | import { defineSirocConfig } from 'siroc' 2 | 3 | export default defineSirocConfig({ 4 | rollup: { 5 | external: ['@vercel/build-utils'] 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /src/build.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | import { createLambda, BuildOptions, download, File, FileBlob, FileFsRef, glob, getNodeVersion, getSpawnOptions, Lambda, runNpmInstall, runPackageJsonScript } from '@vercel/build-utils' 4 | import type { Route } from '@vercel/routing-utils' 5 | import consola from 'consola' 6 | import fs from 'fs-extra' 7 | import resolveFrom from 'resolve-from' 8 | import { gte, gt } from 'semver' 9 | import { update as updaterc } from 'rc9' 10 | import { hasProtocol, withTrailingSlash, withoutLeadingSlash } from 'ufo' 11 | 12 | import { endStep, exec, getNuxtConfig, getNuxtConfigName, globAndPrefix, MutablePackageJson, prepareNodeModules, backupNodeModules, preparePkgForProd, readJSON, startStep, validateEntrypoint } from './utils' 13 | import { prepareTypescriptEnvironment, compileTypescriptBuildFiles, JsonOptions } from './typescript' 14 | 15 | interface BuilderOutput { 16 | watch?: string[]; 17 | output: Record; 18 | routes: Route[]; 19 | } 20 | 21 | interface NuxtBuilderConfig { 22 | maxDuration?: number 23 | memory?: number 24 | tscOptions?: JsonOptions 25 | generateStaticRoutes?: boolean 26 | includeFiles?: string[] | string 27 | serverFiles?: string[] 28 | internalServer?: boolean 29 | } 30 | 31 | export async function build (opts: BuildOptions & { config: NuxtBuilderConfig }): Promise { 32 | const { files, entrypoint, workPath, config = {}, meta = {} } = opts 33 | // ---------------- Debugging context -------------- 34 | consola.log('Running with @nuxt/vercel-builder version', require('../package.json').version) 35 | 36 | // ----------------- Prepare build ----------------- 37 | startStep('Prepare build') 38 | 39 | // Validate entrypoint 40 | validateEntrypoint(entrypoint) 41 | 42 | // Get Nuxt directory 43 | const entrypointDirname = path.dirname(entrypoint) 44 | // Get Nuxt path 45 | const entrypointPath = path.join(workPath, entrypointDirname) 46 | // Get folder where we'll store node_modules 47 | const modulesPath = path.join(entrypointPath, 'node_modules') 48 | 49 | // Create a real filesystem 50 | consola.log('Downloading files...') 51 | await download(files, workPath, meta) 52 | 53 | // Change current working directory to entrypointPath 54 | process.chdir(entrypointPath) 55 | consola.log('Working directory:', process.cwd()) 56 | 57 | // Read package.json 58 | let pkg: MutablePackageJson 59 | try { 60 | pkg = await readJSON('package.json') 61 | } catch (e) { 62 | throw new Error(`Can not read package.json from ${entrypointPath}`) 63 | } 64 | 65 | // Node version 66 | const nodeVersion = await getNodeVersion(entrypointPath, undefined, {}, meta) 67 | const spawnOpts = getSpawnOptions(meta, nodeVersion) 68 | 69 | // Prepare TypeScript environment if required. 70 | const usesTypescript = (pkg.devDependencies && Object.keys(pkg.devDependencies).includes('@nuxt/typescript-build')) || (pkg.dependencies && Object.keys(pkg.dependencies).includes('@nuxt/typescript')) 71 | const needsTypescriptBuild = getNuxtConfigName(entrypointPath) === 'nuxt.config.ts' 72 | 73 | if (usesTypescript) { 74 | await prepareTypescriptEnvironment({ 75 | pkg, spawnOpts, rootDir: entrypointPath 76 | }) 77 | } 78 | 79 | // Detect npm (prefer yarn) 80 | const isYarn = !fs.existsSync('package-lock.json') 81 | consola.log('Using', isYarn ? 'yarn' : 'npm') 82 | 83 | // Write .npmrc 84 | if (process.env.NPM_RC) { 85 | consola.log('Found NPM_RC in environment; creating .npmrc') 86 | await fs.writeFile('.npmrc', process.env.NPM_RC) 87 | } else if (process.env.NPM_AUTH_TOKEN || process.env.NPM_TOKEN) { 88 | consola.log('Found NPM_AUTH_TOKEN or NPM_TOKEN in environment; creating .npmrc') 89 | await fs.writeFile('.npmrc', `//registry.npmjs.org/:_authToken=${process.env.NPM_AUTH_TOKEN || process.env.NPM_TOKEN}`) 90 | } 91 | 92 | // Write .yarnclean 93 | if (isYarn && !fs.existsSync('../.yarnclean')) { 94 | await fs.copyFile(path.join(__dirname, '../.yarnclean'), '.yarnclean') 95 | } 96 | 97 | // Cache dir 98 | const cachePath = path.resolve(entrypointPath, '.vercel_cache') 99 | await fs.mkdirp(cachePath) 100 | 101 | const yarnCachePath = path.join(cachePath, 'yarn') 102 | await fs.mkdirp(yarnCachePath) 103 | 104 | // Detect vercel analytics 105 | if (process.env.VERCEL_ANALYTICS_ID) { 106 | consola.log('Vercel Analytics Detected. Adding @nuxtjs/web-vitals to .nuxtrc') 107 | updaterc( 108 | { 'buildModules[]': require.resolve('@nuxtjs/web-vitals') }, 109 | { dir: entrypointPath, name: '.nuxtrc' } 110 | ) 111 | } 112 | 113 | // ----------------- Install devDependencies ----------------- 114 | startStep('Install devDependencies') 115 | 116 | // Prepare node_modules 117 | await prepareNodeModules(entrypointPath, 'node_modules_dev') 118 | 119 | // Install all dependencies 120 | await runNpmInstall(entrypointPath, [ 121 | '--prefer-offline', 122 | '--frozen-lockfile', 123 | '--non-interactive', 124 | '--production=false', 125 | `--modules-folder=${modulesPath}`, 126 | `--cache-folder=${yarnCachePath}` 127 | ], { ...spawnOpts, env: { ...spawnOpts.env, NODE_ENV: 'development' } }, meta) 128 | 129 | // ----------------- Pre build ----------------- 130 | const buildSteps = ['vercel-build', 'now-build'] 131 | for (const step of buildSteps) { 132 | if (pkg.scripts && Object.keys(pkg.scripts).includes(step)) { 133 | startStep(`Pre build (${step})`) 134 | await runPackageJsonScript(entrypointPath, step, spawnOpts) 135 | break 136 | } 137 | } 138 | 139 | // ----------------- Nuxt build ----------------- 140 | startStep('Nuxt build') 141 | 142 | let compiledTypescriptFiles: { [filePath: string]: FileFsRef } = {} 143 | if (needsTypescriptBuild) { 144 | const { tscOptions } = config 145 | compiledTypescriptFiles = await compileTypescriptBuildFiles({ rootDir: entrypointPath, spawnOpts, tscOptions }) 146 | } 147 | 148 | // Read nuxt.config.js 149 | const nuxtConfigName = 'nuxt.config.js' 150 | const nuxtConfigFile = getNuxtConfig(entrypointPath, nuxtConfigName) 151 | 152 | // Read options from nuxt.config.js otherwise set sensible defaults 153 | const staticDir = (nuxtConfigFile.dir && nuxtConfigFile.dir.static) ? nuxtConfigFile.dir.static : 'static' 154 | let publicPath = (nuxtConfigFile.build && nuxtConfigFile.build.publicPath) 155 | ? withTrailingSlash(withoutLeadingSlash(nuxtConfigFile.build.publicPath)) 156 | : (nuxtConfigFile.router && nuxtConfigFile.router.base) 157 | ? `${withTrailingSlash(withoutLeadingSlash(nuxtConfigFile.router.base))}_nuxt/` 158 | : '_nuxt/' 159 | if (hasProtocol(publicPath)) { 160 | publicPath = '_nuxt/' 161 | } 162 | const buildDir = nuxtConfigFile.buildDir ? path.relative(entrypointPath, nuxtConfigFile.buildDir) : '.nuxt' 163 | const srcDir = nuxtConfigFile.srcDir ? path.relative(entrypointPath, nuxtConfigFile.srcDir) : '.' 164 | const lambdaName = nuxtConfigFile.lambdaName ? nuxtConfigFile.lambdaName : 'index' 165 | const usesServerMiddleware = config.internalServer !== undefined ? config.internalServer : !!nuxtConfigFile.serverMiddleware 166 | 167 | await exec('nuxt', [ 168 | 'build', 169 | '--standalone', 170 | '--no-lock', // #19 171 | `--config-file "${nuxtConfigName}"`, 172 | entrypointPath 173 | ], spawnOpts) 174 | 175 | if (config.generateStaticRoutes) { 176 | await exec('nuxt', [ 177 | 'generate', 178 | '--no-build', 179 | '--no-lock', // #19 180 | `--config-file "${nuxtConfigName}"`, 181 | entrypointPath 182 | ], spawnOpts) 183 | } 184 | 185 | await backupNodeModules(entrypointPath, 'node_modules_dev') 186 | 187 | // ----------------- Install dependencies ----------------- 188 | startStep('Install dependencies') 189 | 190 | // Use node_modules_prod 191 | await prepareNodeModules(entrypointPath, 'node_modules_prod') 192 | 193 | // Only keep core dependency 194 | const nuxtDep = preparePkgForProd(pkg) 195 | await fs.writeJSON('package.json', pkg) 196 | 197 | await runNpmInstall(entrypointPath, [ 198 | '--prefer-offline', 199 | '--pure-lockfile', 200 | '--non-interactive', 201 | '--production=true', 202 | `--modules-folder=${modulesPath}`, 203 | `--cache-folder=${yarnCachePath}` 204 | ], { 205 | ...spawnOpts, 206 | env: { 207 | ...spawnOpts.env, 208 | NPM_ONLY_PRODUCTION: 'true' 209 | } 210 | }, meta) 211 | 212 | // Validate nuxt version 213 | const nuxtPkg = require(resolveFrom(entrypointPath, `@nuxt/core${nuxtDep.suffix}/package.json`)) 214 | if (!gte(nuxtPkg.version, '2.4.0')) { 215 | throw new Error(`nuxt >= 2.4.0 is required, detected version ${nuxtPkg.version}`) 216 | } 217 | if (gt(nuxtPkg.version, '3.0.0')) { 218 | consola.warn('WARNING: nuxt >= 3.0.0 is not tested against this builder!') 219 | } 220 | 221 | // Cleanup .npmrc 222 | if (process.env.NPM_AUTH_TOKEN) { 223 | await fs.unlink('.npmrc') 224 | } 225 | 226 | await backupNodeModules(entrypointPath, 'node_modules_prod') 227 | 228 | // ----------------- Collect artifacts ----------------- 229 | startStep('Collect artifacts') 230 | 231 | // Static files 232 | const staticFiles = await glob('**', path.join(entrypointPath, srcDir, staticDir)) 233 | 234 | // Client dist files 235 | const clientDistDir = path.join(entrypointPath, buildDir, 'dist/client') 236 | const clientDistFiles = await globAndPrefix('**', clientDistDir, publicPath) 237 | 238 | // Server dist files 239 | const serverDistDir = path.join(entrypointPath, buildDir, 'dist/server') 240 | const serverDistFiles = await globAndPrefix('**', serverDistDir, path.join(buildDir, 'dist/server')) 241 | 242 | // Generated static files 243 | const generatedDir = path.join(entrypointPath, 'dist') 244 | const generatedPagesFiles = config.generateStaticRoutes ? await globAndPrefix('**/*.*', generatedDir, './') : {} 245 | 246 | // node_modules_prod 247 | const nodeModulesDir = path.join(entrypointPath, 'node_modules_prod') 248 | const nodeModules = await globAndPrefix('**', nodeModulesDir, 'node_modules') 249 | 250 | // Lambdas 251 | const lambdas: Record = {} 252 | 253 | const launcherPath = path.join(__dirname, 'launcher.js') 254 | const launcherSrc = (await fs.readFile(launcherPath, 'utf8')) 255 | .replace(/__NUXT_SUFFIX__/g, nuxtDep.suffix) 256 | .replace(/__NUXT_CONFIG__/g, './' + nuxtConfigName) 257 | .replace(/\/\* __ENABLE_INTERNAL_SERVER__ \*\/ *true/g, String(usesServerMiddleware)) 258 | 259 | const launcherFiles = { 260 | 'vercel__launcher.js': new FileBlob({ data: launcherSrc }), 261 | 'vercel__bridge.js': new FileFsRef({ fsPath: require('@vercel/node-bridge') }), 262 | [nuxtConfigName]: new FileFsRef({ fsPath: path.resolve(entrypointPath, nuxtConfigName) }), 263 | ...serverDistFiles, 264 | ...compiledTypescriptFiles, 265 | ...nodeModules 266 | } 267 | 268 | // Extra files to be included in lambda 269 | const serverFiles = [ 270 | ...(Array.isArray(config.includeFiles) ? config.includeFiles : config.includeFiles ? [config.includeFiles] : []), 271 | ...(Array.isArray(config.serverFiles) ? config.serverFiles : []), 272 | 'package.json' 273 | ] 274 | 275 | for (const pattern of serverFiles) { 276 | const files = await glob(pattern, entrypointPath) 277 | Object.assign(launcherFiles, files) 278 | } 279 | 280 | // lambdaName will be titled index, unless specified in nuxt.config.js 281 | lambdas[lambdaName] = await createLambda({ 282 | handler: 'vercel__launcher.launcher', 283 | runtime: nodeVersion.runtime, 284 | files: launcherFiles, 285 | environment: { 286 | NODE_ENV: 'production' 287 | }, 288 | // 289 | maxDuration: config.maxDuration, 290 | memory: config.memory 291 | }) 292 | 293 | // await download(launcherFiles, rootDir) 294 | 295 | endStep() 296 | 297 | return { 298 | output: { 299 | ...lambdas, 300 | ...clientDistFiles, 301 | ...staticFiles, 302 | ...generatedPagesFiles 303 | }, 304 | routes: [ 305 | { src: `/${publicPath}.+`, headers: { 'Cache-Control': 'max-age=31557600' } }, 306 | ...Object.keys(staticFiles).map(file => ({ src: `/${file}`, headers: { 'Cache-Control': 'max-age=31557600' } })), 307 | { handle: 'filesystem' }, 308 | { src: '/(.*)', dest: '/index' } 309 | ] 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@vercel/build-utils' 2 | 3 | const config: Config = { 4 | maxLambdaSize: '50mb' 5 | } 6 | 7 | export default config 8 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { build } from './build' 2 | import config from './config' 3 | import prepareCache from './prepare-cache' 4 | 5 | // Docs: https://github.com/zeit/now/blob/master/DEVELOPING_A_RUNTIME.md 6 | module.exports = { 7 | version: 2, 8 | build, 9 | config, 10 | prepareCache 11 | } 12 | -------------------------------------------------------------------------------- /src/launcher.ts: -------------------------------------------------------------------------------- 1 | import type { RequestListener } from 'http' 2 | 3 | const startTime = process.hrtime() 4 | 5 | let nuxtConfig 6 | 7 | // Load Config 8 | const loaders = [{ name: 'jiti', args: [] }, { 9 | name: 'esm', 10 | args: [module, { 11 | cjs: { 12 | dedefault: true 13 | } 14 | }] 15 | }] 16 | for (const { name, args } of loaders) { 17 | try { 18 | const load = require(name)(...args) 19 | const config = load('__NUXT_CONFIG__') 20 | nuxtConfig = config.default || config 21 | break 22 | } catch (err) { 23 | if (name === 'esm') { 24 | throw new Error(`Could not load Nuxt configuration. Make sure all dependencies are listed in package.json dependencies or in serverFiles within builder options:\n ${err}`) 25 | } 26 | } 27 | } 28 | 29 | // Create nuxt 30 | const { Nuxt } = require('@nuxt/core__NUXT_SUFFIX__') 31 | const nuxt = new Nuxt({ 32 | _start: true, 33 | ...nuxtConfig 34 | }) 35 | 36 | // Start nuxt initialization process 37 | let isReady = false 38 | const readyPromise = nuxt.ready().then(() => { 39 | isReady = true 40 | const hrTime = process.hrtime(startTime) 41 | const hrTimeMs = ((hrTime[0] * 1e9) + hrTime[1]) / 1e6 42 | // eslint-disable-next-line no-console 43 | console.log(`λ Cold start took: ${hrTimeMs}ms`) 44 | }).catch((error: string | Error) => { 45 | // eslint-disable-next-line no-console 46 | console.error('λ Error while initializing nuxt:', error) 47 | process.exit(1) 48 | }) 49 | 50 | // Create bridge and start listening 51 | const { Server } = require('http') as typeof import('http') // eslint-disable-line import/order 52 | const { Bridge } = require('./vercel__bridge.js') as typeof import('@vercel/node-bridge/bridge') 53 | 54 | const requestListener: RequestListener = async (req, res) => { 55 | if (!isReady) { 56 | await readyPromise 57 | } 58 | nuxt.server.app(req, res) 59 | } 60 | 61 | // This is used by Vercel 62 | const server = new Server(requestListener) 63 | const bridge = new Bridge(server) 64 | bridge.listen() 65 | 66 | // eslint-disable-next-line 67 | if (/* __ENABLE_INTERNAL_SERVER__ */true) { 68 | // Allow internal calls from Nuxt to endpoints registered as serverMiddleware 69 | const internalServer = new Server(requestListener) 70 | internalServer.listen(3000, '127.0.0.1') 71 | } 72 | 73 | export const launcher: typeof bridge.launcher = bridge.launcher 74 | -------------------------------------------------------------------------------- /src/prepare-cache.ts: -------------------------------------------------------------------------------- 1 | import { PrepareCacheOptions, glob, Files } from '@vercel/build-utils' 2 | import consola from 'consola' 3 | 4 | import { startStep, endStep } from './utils' 5 | 6 | async function prepareCache ({ workPath }: PrepareCacheOptions): Promise { 7 | startStep('Collect cache') 8 | 9 | const dirs = process.env.NUXT_CACHE_DISABLED === '1' 10 | ? [] 11 | : ['.nuxt', '.vercel_cache', 'node_modules_dev', 'node_modules_prod'] 12 | 13 | const cache: Files = {} 14 | for (const dir of dirs) { 15 | const files = await glob(`**/${dir}/**`, workPath) 16 | consola.info(`${Object.keys(files).length} files collected from ${dir}`) 17 | Object.assign(cache, files) 18 | } 19 | endStep() 20 | 21 | return cache 22 | } 23 | 24 | export default prepareCache 25 | -------------------------------------------------------------------------------- /src/typescript.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import type { SpawnOptions } from 'child_process' 3 | 4 | import { glob, FileFsRef, PackageJson } from '@vercel/build-utils' 5 | import fs from 'fs-extra' 6 | import replaceInFile from 'replace-in-file' 7 | 8 | import { exec, getNuxtConfig, getNuxtConfigName, readJSON } from './utils' 9 | 10 | export interface JsonOptions { [key: string]: number | boolean | string | Array } 11 | 12 | interface CompileTypescriptOptions { 13 | spawnOpts: SpawnOptions; 14 | rootDir: string; 15 | tscOptions?: JsonOptions; 16 | } 17 | 18 | interface PrepareTypescriptOptions { 19 | pkg: PackageJson; 20 | spawnOpts: SpawnOptions; 21 | rootDir: string; 22 | } 23 | 24 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 25 | export async function prepareTypescriptEnvironment ({ pkg, spawnOpts, rootDir }: PrepareTypescriptOptions): Promise { 26 | spawnOpts = { ...spawnOpts, env: { ...spawnOpts.env, NODE_PRESERVE_SYMLINKS: '1' } } 27 | 28 | if ((fs.existsSync('tsconfig.json'))) { 29 | let tsConfig: Record & { exclude: string[] } 30 | try { 31 | tsConfig = await readJSON('tsconfig.json') 32 | } catch (e) { 33 | throw new Error(`Can not read tsconfig.json from ${rootDir}`) 34 | } 35 | tsConfig.exclude = [...(tsConfig.exclude || []), 'node_modules_dev', 'node_modules_prod'] 36 | await fs.writeJSON('tsconfig.json', tsConfig) 37 | } 38 | 39 | // Edit dependencies 40 | if (pkg.dependencies && Object.keys(pkg.dependencies).includes('@nuxt/typescript-runtime')) { 41 | delete pkg.dependencies['@nuxt/typescript-runtime'] 42 | } 43 | } 44 | 45 | async function readAndMergeOptions (filename: string, rootDir: string, options: JsonOptions): Promise { 46 | let newOptions: JsonOptions = options 47 | if (fs.existsSync(filename)) { 48 | let tsConfig: { compilerOptions?: JsonOptions } 49 | try { 50 | tsConfig = await readJSON(filename) 51 | } catch (e) { 52 | throw new Error(`Can not read ${filename} from ${rootDir}`) 53 | } 54 | newOptions = { ...tsConfig.compilerOptions, ...options } 55 | } 56 | return newOptions 57 | } 58 | 59 | async function getTypescriptCompilerOptions (rootDir: string, options: JsonOptions = {}): Promise { 60 | let compilerOptions: string[] = [] 61 | 62 | const sources = [ 63 | 'tsconfig.json', 64 | 'tsconfig.vercel.json', 65 | 'tsconfig.now.json' 66 | ] 67 | 68 | for (const source of sources) { 69 | options = await readAndMergeOptions(source, rootDir, options) 70 | } 71 | 72 | compilerOptions = Object.keys(options).reduce((compilerOptions, option) => { 73 | if (compilerOptions && !['rootDirs', 'paths', 'outDir', 'rootDir', 'noEmit'].includes(option)) { 74 | compilerOptions.push(`--${option}`, String(options[option])) 75 | } 76 | return compilerOptions 77 | }, [] as string[]) 78 | return [...compilerOptions, '--noEmit', 'false', '--rootDir', rootDir, '--outDir', 'now_compiled'] 79 | } 80 | 81 | export async function compileTypescriptBuildFiles ({ rootDir, spawnOpts, tscOptions }: CompileTypescriptOptions): Promise<{ [filePath: string]: FileFsRef }> { 82 | const nuxtConfigName = getNuxtConfigName(rootDir) 83 | const compilerOptions = await getTypescriptCompilerOptions(rootDir, tscOptions) 84 | await fs.mkdirp('now_compiled') 85 | await exec('tsc', [...compilerOptions, nuxtConfigName], spawnOpts) 86 | const nuxtConfigFile = getNuxtConfig(rootDir, 'now_compiled/nuxt.config.js') 87 | const { serverMiddleware, modules } = nuxtConfigFile 88 | 89 | const filesToCompile: string[] = [ 90 | ...(serverMiddleware || []), 91 | ...(modules || []) 92 | ].reduce((filesToCompile, item) => { 93 | let itemPath = '' 94 | if (typeof item === 'string') { 95 | itemPath = item 96 | } else if (typeof item === 'object' && Array.isArray(item)) { 97 | if (typeof item[0] === 'string') { 98 | itemPath = item[0] 99 | } 100 | // We do not need to handle inline modules 101 | } else if (typeof item === 'object' && typeof item.handler === 'string') { 102 | itemPath = item.handler 103 | } 104 | if (itemPath) { 105 | const srcDir = nuxtConfigFile.srcDir ? (path.relative(rootDir, nuxtConfigFile.srcDir)).replace('now_compiled', '.') : '.' 106 | const resolvedPath = path.resolve(rootDir, itemPath.replace(/^[@~]\//, `${srcDir}/`).replace(/\.ts$/, '')) 107 | if (fs.existsSync(`${resolvedPath}.ts`)) { 108 | filesToCompile.push(resolvedPath) 109 | replaceInFile.sync({ 110 | files: path.resolve(rootDir, 'now_compiled/nuxt.config.js'), 111 | from: new RegExp(`(?<=['"\`])${itemPath}(?=['"\`])`, 'g'), 112 | to: itemPath.replace(/\.ts$/, '') 113 | }) 114 | } 115 | } 116 | return filesToCompile 117 | }, [] as string[]) 118 | await Promise.all( 119 | filesToCompile.map(file => exec('tsc', [...compilerOptions, file])) 120 | ) 121 | const files = await glob('**', path.join(rootDir, 'now_compiled')) 122 | Object.keys(files).forEach((filename) => { 123 | const compiledPath = files[filename].fsPath 124 | const newPath = compiledPath.replace('/now_compiled/', '/') 125 | fs.moveSync(compiledPath, newPath, { overwrite: true }) 126 | files[filename].fsPath = newPath 127 | }) 128 | return files 129 | } 130 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { SpawnOptions } from 'child_process' 3 | 4 | import { glob, Files, PackageJson } from '@vercel/build-utils' 5 | import consola from 'consola' 6 | import jiti from 'jiti' 7 | import execa, { ExecaReturnValue } from 'execa' 8 | import fs from 'fs-extra' 9 | 10 | import type { NuxtConfig as NuxtConfiguration } from '@nuxt/types' 11 | import type { IOptions } from 'glob' 12 | 13 | type Mutable = { 14 | -readonly [P in keyof T]: T[P] extends ReadonlyArray ? Mutable[] : Mutable 15 | } 16 | 17 | export type MutablePackageJson = Mutable 18 | 19 | export function exec (cmd: string, args: string[], { env, ...opts }: SpawnOptions = {}): Promise { 20 | args = args.filter(Boolean) 21 | 22 | consola.log('Running', cmd, ...args) 23 | 24 | return execa('npx', [cmd, ...args], { 25 | stdout: process.stdout, 26 | stderr: process.stderr, 27 | preferLocal: false, 28 | env: { 29 | MINIMAL: '1', 30 | NODE_OPTIONS: '--max_old_space_size=3000', 31 | ...env 32 | }, 33 | ...opts, 34 | stdio: Array.isArray(opts.stdio) ? opts.stdio.filter(Boolean) as Array ? Array> : never> : opts.stdio 35 | }) 36 | } 37 | 38 | /** 39 | * Read in a JSON file with support for UTF-16 fallback. 40 | */ 41 | 42 | export async function readJSON (filename: string): Promise { 43 | try { 44 | return await fs.readJSON(filename) 45 | } catch { 46 | return await fs.readJSON(filename, { encoding: 'utf16le' }) 47 | } 48 | } 49 | 50 | /** 51 | * Validate if the entrypoint is allowed to be used 52 | */ 53 | export function validateEntrypoint (entrypoint: string): void { 54 | const filename = path.basename(entrypoint) 55 | 56 | if (['package.json', 'nuxt.config.js', 'nuxt.config.ts'].includes(filename) === false) { 57 | throw new Error( 58 | 'Specified "src" for "@nuxtjs/vercel-builder" has to be "package.json", "nuxt.config.js" or "nuxt.config.ts"' 59 | ) 60 | } 61 | } 62 | 63 | // function filterFiles(files, filterFn) { 64 | // const newFiles = {} 65 | // for (const fileName in files) { 66 | // if (filterFn(files)) { 67 | // newFiles[fileName] = files[fileName] 68 | // } 69 | // } 70 | // return newFiles 71 | // } 72 | 73 | export function renameFiles (files: Files, renameFn: (fileName: string) => string): Files { 74 | const newFiles: Files = {} 75 | for (const fileName in files) { 76 | newFiles[renameFn(fileName)] = files[fileName] 77 | } 78 | return newFiles 79 | } 80 | 81 | export async function globAndRename (pattern: string, opts: IOptions | string, renameFn: (fileName: string) => string): Promise { 82 | const files = await glob(pattern, opts) 83 | return renameFiles(files, renameFn) 84 | } 85 | 86 | export function globAndPrefix (pattern: string, opts: IOptions | string, prefix: string): Promise { 87 | return globAndRename(pattern, opts, name => path.join(prefix, name)) 88 | } 89 | 90 | interface NuxtVersion { 91 | name: string 92 | version: string 93 | semver: string 94 | suffix: string 95 | section: string 96 | } 97 | 98 | export function findNuxtDep (pkg: MutablePackageJson): void | NuxtVersion { 99 | for (const section of ['dependencies', 'devDependencies'] as const) { 100 | const deps = pkg[section] 101 | if (deps) { 102 | for (const suffix of ['-edge', '']) { 103 | const name = 'nuxt' + suffix 104 | const version = deps[name] 105 | if (version) { 106 | const semver = version.replace(/^[\^~><=]{1,2}/, '') 107 | return { 108 | name, 109 | version, 110 | semver, 111 | suffix, 112 | section 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | export function preparePkgForProd (pkg: MutablePackageJson): NuxtVersion { 121 | // Ensure fields exist 122 | if (!pkg.dependencies) { 123 | pkg.dependencies = {} 124 | } 125 | if (!pkg.devDependencies) { 126 | pkg.devDependencies = {} 127 | } 128 | 129 | // Find nuxt dependency 130 | const nuxtDependency = findNuxtDep(pkg) 131 | if (!nuxtDependency) { 132 | throw new Error('No nuxt dependency found in package.json') 133 | } 134 | 135 | // Remove nuxt form dependencies 136 | for (const distro of ['nuxt', 'nuxt-start']) { 137 | for (const suffix of ['-edge', '']) { 138 | delete pkg.dependencies[distro + suffix] 139 | } 140 | } 141 | 142 | // Delete all devDependencies 143 | delete pkg.devDependencies 144 | 145 | // Add @nuxt/core to dependencies 146 | pkg.dependencies['@nuxt/core' + nuxtDependency.suffix] = nuxtDependency.version 147 | 148 | // Return nuxtDependency 149 | return nuxtDependency 150 | } 151 | 152 | let _step: string | undefined 153 | let _stepStartTime: [number, number] | undefined 154 | 155 | const dash = ' ----------------- ' 156 | 157 | export function hrToMs (hr: [number, number]): number { 158 | const hrTime = process.hrtime(hr) 159 | return ((hrTime[0] * 1e9) + hrTime[1]) / 1e6 160 | } 161 | 162 | export function endStep (): void { 163 | if (!_step) { 164 | return 165 | } 166 | if (_step && _stepStartTime) { 167 | consola.info(`${_step} took: ${hrToMs(_stepStartTime)} ms`) 168 | } 169 | _step = undefined 170 | _stepStartTime = undefined 171 | } 172 | 173 | export function startStep (step: string): void { 174 | endStep() 175 | consola.log(dash + step + dash) 176 | _step = step 177 | _stepStartTime = process.hrtime() 178 | } 179 | 180 | export function getNuxtConfig (rootDir: string, nuxtConfigName: string): NuxtConfiguration { 181 | const load = jiti() 182 | const nuxtConfigFile = load(path.resolve(rootDir, nuxtConfigName)) 183 | return nuxtConfigFile.default || nuxtConfigFile 184 | } 185 | 186 | export function getNuxtConfigName (rootDir: string): string { 187 | for (const filename of ['nuxt.config.ts', 'nuxt.config.js']) { 188 | if (fs.existsSync(path.resolve(rootDir, filename))) { 189 | return filename 190 | } 191 | } 192 | throw new Error(`Can not read nuxt.config from ${rootDir}`) 193 | } 194 | 195 | export async function prepareNodeModules (entrypointPath: string, namespaceDir: string): Promise { 196 | const modulesPath = path.join(entrypointPath, 'node_modules') 197 | 198 | try { 199 | const namespacedPath = path.join(entrypointPath, namespaceDir) 200 | try { 201 | if (fs.existsSync(modulesPath)) { 202 | await fs.unlink(modulesPath) 203 | } 204 | } catch {} 205 | if (fs.existsSync(namespacedPath)) { 206 | consola.log(`Using cached ${namespaceDir}`) 207 | fs.moveSync(namespaceDir, modulesPath) 208 | } 209 | } catch (e) { 210 | consola.log(`Error creating ${namespaceDir}.`, e) 211 | } 212 | } 213 | 214 | export async function backupNodeModules (entrypointPath: string, namespaceDir: string): Promise { 215 | const modulesPath = path.join(entrypointPath, 'node_modules') 216 | 217 | try { 218 | const namespacedPath = path.join(entrypointPath, namespaceDir) 219 | const stats = await fs.stat(modulesPath) 220 | if (stats.isDirectory()) { 221 | await fs.rm(namespacedPath, { force: true, recursive: true }) 222 | await fs.move(modulesPath, namespacedPath) 223 | } 224 | } catch (e) { 225 | consola.log(`Error backing up node_modules to ${namespaceDir}.`, e) 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /test/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | rules: 2 | "@typescript-eslint/explicit-function-return-type": 0 3 | -------------------------------------------------------------------------------- /test/build.test.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const rc9 = require('rc9') 4 | const runBuildLambda = require('./utils/run-build-lambda') 5 | 6 | const FOUR_MINUTES = 240000 7 | 8 | it('Should build the standard example', async () => { 9 | process.env.VERCEL_ANALYTICS_ID = 'test' 10 | const { buildResult, workPath } = await runBuildLambda( 11 | path.join(__dirname, 'fixture') 12 | ) 13 | delete process.env.VERCEL_ANALYTICS_ID 14 | 15 | const { output, routes } = buildResult 16 | 17 | // Lambda 18 | expect(output.index).toBeDefined() 19 | expect(routes).toBeDefined() 20 | 21 | // Build files 22 | const buildFiles = [ 23 | 'test.txt', 24 | '_nuxt/LICENSES', 25 | 'now-build' 26 | ] 27 | for (const file of buildFiles) { 28 | expect(output[file]).toBeDefined() 29 | } 30 | 31 | const nuxtrc = rc9.read({ name: '.nuxtrc', dir: path.resolve(workPath, 'www') }) 32 | expect(nuxtrc.buildModules).toMatchObject([/@nuxtjs\/web-vitals/]) 33 | expect(fs.existsSync(path.resolve(workPath, 'www/.nuxt/vitals.client.js'))) 34 | }, FOUR_MINUTES) 35 | 36 | it('Should build a Typescript example', async () => { 37 | const { buildResult } = await runBuildLambda( 38 | path.join(__dirname, 'fixture-ts') 39 | ) 40 | 41 | const { output, routes } = buildResult 42 | // Lambda 43 | expect(output.index).toBeDefined() 44 | expect(routes).toBeDefined() 45 | 46 | // Build files 47 | const buildFiles = [ 48 | 'test.txt', 49 | '_nuxt/LICENSES' 50 | ] 51 | for (const file of buildFiles) { 52 | expect(output[file]).toBeDefined() 53 | } 54 | }, FOUR_MINUTES) 55 | 56 | it('Should build the standard example with generated files', async () => { 57 | const { buildResult } = await runBuildLambda( 58 | path.join(__dirname, 'fixture-generated') 59 | ) 60 | 61 | const { output, routes } = buildResult 62 | // Lambda 63 | expect(output.index).toBeDefined() 64 | expect(routes).toBeDefined() 65 | 66 | // Generated files 67 | const generatedFiles = [ 68 | 'index.html', 69 | 'dynamic/1/index.html', 70 | 'dynamic/2/index.html' 71 | ] 72 | 73 | for (const file of generatedFiles) { 74 | expect(output[file]).toBeDefined() 75 | } 76 | }, FOUR_MINUTES) 77 | -------------------------------------------------------------------------------- /test/fixture-generated/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "www/nuxt.config.js", 6 | "use": "../..", 7 | "config": { 8 | "serverFiles": [ 9 | "custom/*.js" 10 | ], 11 | "generateStaticRoutes": true 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/fixture-generated/www/custom/foo.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/test/fixture-generated/www/custom/foo.js -------------------------------------------------------------------------------- /test/fixture-generated/www/nuxt.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | 3 | module.exports = { 4 | buildDir: resolve(__dirname, '.nuxt'), 5 | srcDir: __dirname, 6 | generate: { 7 | routes: [ 8 | '/', 9 | '/dynamic/1', 10 | '/dynamic/2' 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture-generated/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MIT", 4 | "scripts": { 5 | "now-build": "echo 1 > static/now-build" 6 | }, 7 | "devDependencies": { 8 | "nuxt": "^2.17.3" 9 | }, 10 | "engines": { 11 | "node": ">=16.x" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture-generated/www/pages/dynamic/_id.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /test/fixture-generated/www/pages/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /test/fixture-generated/www/static/test.txt: -------------------------------------------------------------------------------- 1 | test.txt 2 | -------------------------------------------------------------------------------- /test/fixture-ts/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "www/nuxt.config.ts", 6 | "use": "../..", 7 | "config": { 8 | "serverFiles": ["custom/*.js"], 9 | "tscOptions": { 10 | "sourceMap": false 11 | } 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/fixture-ts/www/another.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | const b: string = 'test' 3 | 4 | export default b 5 | -------------------------------------------------------------------------------- /test/fixture-ts/www/custom/foo.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/test/fixture-ts/www/custom/foo.js -------------------------------------------------------------------------------- /test/fixture-ts/www/modules/further.ts: -------------------------------------------------------------------------------- 1 | export const b = 'a' 2 | -------------------------------------------------------------------------------- /test/fixture-ts/www/modules/module.ts: -------------------------------------------------------------------------------- 1 | import { b } from './further' 2 | 3 | // eslint-disable-next-line 4 | const a: number = 3 5 | 6 | export default () => ({ a, b }) 7 | -------------------------------------------------------------------------------- /test/fixture-ts/www/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | import thing from './another' 3 | 4 | module.exports = { 5 | env: { 6 | thing 7 | }, 8 | rootDir: resolve(__dirname, '../..'), 9 | buildDir: resolve(__dirname, '.nuxt'), 10 | srcDir: __dirname, 11 | buildModules: ['@nuxt/typescript-build'], 12 | modules: ['~/modules/module.ts'], 13 | typescript: { 14 | typeCheck: { 15 | typescript: { 16 | configFile: resolve(__dirname, './tsconfig.json') 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/fixture-ts/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MIT", 4 | "devDependencies": { 5 | "@nuxt/typescript-build": "latest", 6 | "nuxt": "^2.17.3" 7 | }, 8 | "dependencies": { 9 | "@nuxt/typescript-runtime": "latest" 10 | }, 11 | "engines": { 12 | "node": ">=16.x" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/fixture-ts/www/pages/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /test/fixture-ts/www/static/test.txt: -------------------------------------------------------------------------------- 1 | test.txt 2 | -------------------------------------------------------------------------------- /test/fixture-ts/www/tsconfig.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/test/fixture-ts/www/tsconfig.json -------------------------------------------------------------------------------- /test/fixture/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "www/nuxt.config.js", 6 | "use": "../..", 7 | "config": { 8 | "serverFiles": [ 9 | "custom/*.js" 10 | ] 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test/fixture/www/custom/foo.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/vercel-builder/1fd500e73c613801437705d521a5b7be684e2cfe/test/fixture/www/custom/foo.js -------------------------------------------------------------------------------- /test/fixture/www/nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /test/fixture/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MIT", 4 | "scripts": { 5 | "now-build": "echo 1 > static/now-build" 6 | }, 7 | "devDependencies": { 8 | "nuxt": "^2.17.3" 9 | }, 10 | "engines": { 11 | "node": ">=16.x" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture/www/pages/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /test/fixture/www/static/test.txt: -------------------------------------------------------------------------------- 1 | test.txt 2 | -------------------------------------------------------------------------------- /test/utils/run-build-lambda.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { glob } = require('@vercel/build-utils') 3 | const fs = require('fs-extra') 4 | 5 | function runAnalyze (wrapper, context) { 6 | if (wrapper.analyze) { 7 | return wrapper.analyze(context) 8 | } 9 | 10 | return 'this-is-a-fake-analyze-result-from-default-analyze' 11 | } 12 | 13 | async function runBuildLambda (inputPath) { 14 | const inputFiles = await glob('**', inputPath) 15 | 16 | const nowJsonRef = inputFiles['now.json'] 17 | 18 | const nowJson = require(nowJsonRef.fsPath) 19 | 20 | const build = nowJson.builds[0] 21 | 22 | const entrypoint = build.src.replace(/^\//, '') // Strip leftmost slash 23 | 24 | inputFiles[entrypoint].digest = 'this-is-a-fake-digest-for-non-default-analyze' 25 | 26 | const wrapper = require(build.use) 27 | 28 | const analyzeResult = runAnalyze(wrapper, { 29 | files: inputFiles, 30 | entrypoint, 31 | config: build.config 32 | }) 33 | 34 | const workPath = path.join(__dirname, '../../.tmp', path.basename(inputPath)) 35 | await fs.remove(workPath) 36 | await fs.mkdirs(workPath) 37 | 38 | const buildResult = await wrapper.build({ 39 | files: inputFiles, 40 | entrypoint, 41 | workPath, 42 | config: build.config 43 | }) 44 | 45 | const cacheResult = await wrapper.prepareCache({ 46 | cachePath: path.join(workPath, '.cache'), 47 | workPath, 48 | entrypoint 49 | }) 50 | 51 | return { 52 | analyzeResult, 53 | buildResult, 54 | cacheResult, 55 | workPath 56 | } 57 | } 58 | 59 | module.exports = runBuildLambda 60 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src", 4 | "outDir": "lib", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "lib": ["esnext", "dom"], 8 | "target": "esnext", 9 | "module": "commonjs", 10 | "sourceMap": false, 11 | "declaration": true 12 | }, 13 | "exclude": ["node_modules", "lib", "test"] 14 | } 15 | --------------------------------------------------------------------------------