Compare commits

...

317 Commits

Author SHA1 Message Date
Steven
9d7dd3a713 Publish
- @now/python@0.1.1-canary.0
2019-05-02 11:32:59 -04:00
Steven
4f867b320d [now-python] Upgrade build env to python3.6 (#450)
* [now-python] Upgarde build env to python3.6

* Add support for isDev
2019-05-02 11:00:39 -04:00
Nathan Rajlich
c153690104 Publish
- @now/build-utils@0.5.2-canary.0
 - @now/go@0.4.3-canary.0
 - @now/next@0.2.1-canary.0
 - @now/static-build@0.5.3-canary.0
2019-05-01 23:34:18 -07:00
Nathan Rajlich
8c1b96edf7 [now-build-utils] Use cross-spawn for Windows support (#449) 2019-05-01 23:26:20 -07:00
Carlos Alexandro Becker
15c83a69f7 [now-go] Add support for config.includeFiles (#447) 2019-05-01 17:39:53 -04:00
Steven
0986de85ee [now-static-build] Warn when now-dev script is missing (#443)
* Warn when now-dev script is missing

* Add link to local dev docs

* Update packages/now-static-build/index.js

Co-Authored-By: styfle <steven@ceriously.com>

* Fix url
2019-05-01 17:32:50 -04:00
Leo Lamprecht
94c5d83ccc Copy the entire process environment to child (#448)
* Copy the entire process environment to child

* Removed lockfile

* Removed useless code

* Add comment
2019-05-01 22:26:35 +02:00
Steven
ff49b9d32d Publish
- @now/node-server@0.6.1-canary.0
 - @now/node@0.6.1-canary.0
2019-05-01 14:20:43 -04:00
Steven
ec5290dab1 [node-node] Bump ncc to 0.18.2 (#446) 2019-05-01 12:19:43 -04:00
Steven
4f758ec84e Create CODE_OF_CONDUCT.md (#444) 2019-05-01 09:04:34 -04:00
Steven
7951be156a Publish
- @now/build-utils@0.5.1
 - @now/go@0.4.2
 - @now/static-build@0.5.2
2019-04-30 10:33:52 -04:00
Nathan Rajlich
1bafc1d7b7 Publish
- @now/go@0.4.1
2019-04-29 18:03:38 -07:00
Sophearak Tha
1493101325 [now-go] Improve speed for now dev (#438)
* Improve speed for build() `@now/go` `now dev`

* Remove duplicate line

* Improve code flow

* Improve logging information

* no need to download Go if it available
2019-04-29 18:01:09 -07:00
Nathan Rajlich
824b044a96 Publish
- @now/static-build@0.5.1
2019-04-29 14:57:30 -07:00
Nathan Rajlich
0978be4c3d Publish
- @now/static-build@0.5.1-canary.0
2019-04-29 14:00:45 -07:00
Nathan Rajlich
dc832aa6c3 Regenerate yarn.lock file 2019-04-29 14:00:26 -07:00
Nathan Rajlich
8df77fe4fa [now-build-utils] Wait for the dev server to print the URL with correct port number (#439)
This is an alternative approach to detecting when the dev server is
ready to receive HTTP traffic, also bumps the timeout to 5 minutes.
2019-04-29 13:58:44 -07:00
Sophearak Tha
ff413b45fa Publish
- @now/go@0.4.1-canary.3
2019-04-29 16:46:58 +07:00
Sophearak Tha
e7befb5dc1 Make install @now/go faster (#435) 2019-04-29 16:46:08 +07:00
Steven
b898f82771 [now-go] Use optimized build flags (#428)
* Add optimized build output

* Add `config.ldsflags`
2019-04-29 09:41:50 +07:00
Leo Lamprecht
e6b22cb0df Publish
- @now/go@0.4.1-canary.2
2019-04-28 19:54:24 +00:00
Sophearak Tha
cbfe4a133d Using full path for checking against entrypoint (#433) 2019-04-29 00:44:31 +07:00
Sophearak Tha
823b78c626 Publish
- @now/go@0.4.1-canary.1
2019-04-28 21:22:30 +07:00
Sophearak Tha
03e1255043 Improve rebuild speed for now dev (#432) 2019-04-28 20:56:07 +07:00
Steven
3373cbca4e Publish
- @now/build-utils@0.5.1-canary.0
 - @now/go@0.4.1-canary.0
2019-04-25 17:11:31 -04:00
Sophearak Tha
4fba4b5f67 [now-go] Migrated to TypeScript and support Builder v2 API (#412)
* Migrated `@now/go` to TypeScript

* Add support for Builder v2 API

* Add now-go to .eslintignore

* Update go bin path

* Update packages/now-go/.gitignore

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* Update packages/now-go/go-helpers.ts

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* Update packages/now-go/go-helpers.ts

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* Using American English for consistency

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* Rename analyse.go to analyze.go

* Update packages/now-go/go-helpers.ts

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* Remove `mkdirp-promise` from now-go

* Ensure `watch` directory-aware given `entrypoint` in subdirectory

* Support export struct type declaration

* Improve log

* Add type to `analyzed`

* Update packages/now-go/index.ts

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* Migrate test fixtures from `add/now-go-tests`
2019-04-25 17:07:56 -04:00
Steven
9fcf6da3c1 Add comment to fsPath about absolute path (#427) 2019-04-25 11:27:36 -04:00
Steven
d8a5da6a3e Publish
- @now/bash@0.2.0
 - @now/build-utils@0.5.0
 - @now/cgi@0.1.0
 - @now/go@0.4.0
 - @now/html-minifier@1.1.0
 - @now/lambda@0.5.0
 - @now/md@0.5.0
 - @now/mdx-deck@0.5.0
 - @now/next@0.2.0
 - @now/node-bridge@1.1.0
 - @now/node-server@0.6.0
 - @now/node@0.6.0
 - @now/optipng@0.5.0
 - @now/php-bridge@0.5.0
 - @now/php@0.5.0
 - @now/python@0.1.0
 - @now/rust@0.2.0
 - @now/static-build@0.5.0
 - @now/wordpress@0.5.0
2019-04-25 09:19:19 -04:00
Leo Lamprecht
48f7b72bb2 Added missing workPath type property (#426) 2019-04-25 15:05:41 +02:00
Nathan Rajlich
8e2d5de446 [now-static-build] Add now-dev package.json script (#418)
* [static-build] Add `now-dev` package.json script

This will cause `now dev` to run the `now-dev` script as defined in the
`package.json` file.

`now dev` sets the `$PORT` env variable for use in the `now-dev` script,
which is expected to launch a file watching server (for example `hugo
dev`) on the specified `$PORT`. `now dev` wait for this port to be bound
before returning the `build()` function with the generated assets.

The `watch` array is just "watch everything in the
`path.dirname(entrypoint)`" so any file changed within that directory
will trigger a "re-sync" of the source files that `now dev` is working
with, which the user spawned static build watcher should pick up on and
return the updated assets.

* Use `routes` instead of globbing the output dir

Proxy passing to the development server is a cleaner approach, since it
removes the race condition about the builder not having knowledge of
when a dev watcher server rebuilds, so it's not possible to know how
long to wait to return outputs upon another call to `build()`.

* Make `watch` be an array

Co-Authored-By: TooTallNate <n@n8.io>

* Fix `promise-timeout` require()

* Inherit from `process.env` for `yarn run`

Otherwise ENOENT happens because there is no `PATH` env var set

* Only validate dist dir in production builds

* Fix `routes` and `watch` output

* Remove dev logging, add better timeout error message

* Populate `routes` upon rebuild

* Pass `meta` to the `download()` function

* Revert version change in `package.json`

* Save devport sooner
2019-04-25 08:41:38 -04:00
Steven
2b3efb06be Revert "[now-node] Add initial Builder v2 API (#424)" (#425)
This reverts commit dc3f112d4f.
2019-04-24 22:16:37 -04:00
Connor Davis
13e57bf68f [now/next] Set env variable directly 2019-04-24 16:57:00 -05:00
Nathan Rajlich
dc3f112d4f [now-node] Add initial Builder v2 API (#424)
This is a less complete version of #400, since it's still a WIP.
2019-04-24 14:46:02 -07:00
Connor Davis
da1c423033 Publish
- @now/next@0.2.0-canary.45
2019-04-24 14:57:51 -05:00
Connor Davis
5e1d58f0e8 Set target env variable (#423) 2019-04-25 04:16:08 +09:00
Leo Lamprecht
8940f7fa33 Publish
- @now/next@0.2.0-canary.44
2019-04-24 17:24:39 +00:00
Leo Lamprecht
0aeecd81d3 Pass meta to download() of @now/next (#422) 2019-04-24 19:23:57 +02:00
Joe Haddad
bd4cb3d2a0 Publish
- @now/next@0.2.0-canary.43
2019-04-24 10:50:02 -04:00
Joe Haddad
b3d14f536d Increase heap size for Next.js builds (#419)
* Increase heap size for Next.js builds

* Save some memory for the system
2019-04-24 23:10:27 +09:00
Leo Lamprecht
445d4d84cb Publish
- @now/build-utils@0.4.41-canary.8
 - @now/next@0.2.0-canary.42
2019-04-24 11:06:31 +00:00
Leo Lamprecht
30d4ec8cbd Make download() dedupe (#416)
* Make `download()` dedupe

* Made it work

* Fixed unused var
2019-04-24 13:05:31 +02:00
Nathan Rajlich
51355c263c [now-build-utils] Allow build options for runPackageJsonScript() (#417)
To allow, for example, setting `env` variable.
2019-04-24 10:23:46 +08:00
Steven
ec6d695f24 Add builder v2 tests to @now/next (#415)
* Add builder v2 tests to @now/next

* Add builder v2 tests

* wip

* Return childProcesses for tests
2019-04-23 18:41:26 -04:00
Leo Lamprecht
da910dc097 Publish
- @now/next@0.2.0-canary.41
2019-04-23 19:33:38 +00:00
Leo Lamprecht
00cb55f953 Make @now/next dev work on macOS (#414)
* Make `@now/next` dev work on macOS

* Fixed resolving

* Correctly use cwd

* Removed useless code
2019-04-23 21:32:56 +02:00
Leo Lamprecht
54ee557187 Publish
- @now/next@0.2.0-canary.40
 - @now/node-server@0.5.4-canary.5
 - @now/node@0.5.4-canary.6
2019-04-23 12:01:05 +00:00
Leo Lamprecht
e9a49f658b Develop Next.js app inside workPath (#410)
* Develop Next.js app inside workPath

* Made file watching work

* Define routes the first time

* Added index handling

* Fixed indexing

* Fixed indexing again

* Removed useless code

* Start server in child process
2019-04-23 13:58:50 +02:00
Steven
f3484bc7c9 [now-node] Bump ncc to 0.18.1 (#411)
* [now-node] Bump ncc to 0.18.1

* Fix unit test
2019-04-22 18:20:53 -04:00
Leo Lamprecht
f76d200fd2 Renamed property that has changed its name (#409)
* Define that initial build is needed

* Removed useless code
2019-04-21 20:30:02 +02:00
Leo Lamprecht
24ba90bfeb Wait for Next.js dev server to run before continuing (#408)
* Wait for server better

* Fixed version
2019-04-21 18:48:21 +02:00
Olli Vanhoja
9f0fdada18 Log HTTP status just after fetch returns in deploymentPost() (#406) 2019-04-20 21:40:38 +03:00
Leo Lamprecht
a987c4b298 Publish
- @now/build-utils@0.4.41-canary.7
 - @now/next@0.2.0-canary.39
 - @now/optipng@0.4.9-canary.1
2019-04-20 00:17:07 +00:00
Leo Lamprecht
a746adda93 Only match absolute paths in Next.js builder (#405) 2019-04-20 02:16:20 +02:00
Mark
6a00f02137 @now/optipng using typescript (#397)
Migration `@now/optipng` to typescript

Co-Authored-By: MAPESO <markdrew53@gmail.com>
2019-04-19 19:08:25 -04:00
Leo Lamprecht
65558f8197 Publish
- @now/build-utils@0.4.41-canary.6
 - @now/next@0.2.0-canary.38
2019-04-19 21:58:05 +00:00
Leo Lamprecht
a8bf77091f Made now-next invoke next dev (#398)
* Made `now-next` invoke `next dev`

* Even cleaner

* Fixed some types

* Fixed dirs

* Start server if initial build

* Added remaining types

* Added missing type

* Made property optional

* Fixed types

* Expose that the Builder is continuous

* Added more details

* Removed useless code

* Made it work

* Removed useless crap

* Added new version

* Synced up

* Refined logging

* Fixed routes

* Correctly parse request paths

* Fixed routes

* Fixed GET parameter issues
2019-04-19 23:54:47 +02:00
Steven
ee179b9b52 Add fetchTokenWithRetry for bad json response (#403)
This is a quick fix for our token problem by attempting to retry the fetch.

> FetchError: invalid json response body at NOW_TOKEN_FACTORY_URL reason: Unexpected token A in JSON at position 0

The root cause is in the token factory because it throws `Confirmation incomplete` but that will be harder to track down and fix.
2019-04-19 12:59:22 -04:00
Nathan Rajlich
b480b07cce [now-build-utils] Add meta object type definition for build() (#399)
* [now-build-utils] Add `meta` object type definition for `build()`

* Add `meta.requestPath`
2019-04-18 19:30:01 -07:00
Steven
fc8452abfd Publish
- @now/next@0.2.0-canary.1
 - @now/node-server@0.5.4-canary.4
 - @now/node@0.5.4-canary.5
2019-04-18 09:12:32 -04:00
Steven
48b6d0ebfc [now-node] Enable source maps so errors show proper stack trace (#391)
This will increase the lambda size a little bit but it will make for a much better user experience when debugging uncaught errors.

I also added a test so we don't regress.
2019-04-17 17:53:49 -04:00
Connor Davis
a3d6cea3c6 Migrate @now/next to Builders v2 + TS (#379) 2019-04-16 17:24:26 -05:00
Steven
8a61b1b513 Publish
- @now/build-utils@0.4.41-canary.5
 - @now/next@0.1.3-canary.15
 - @now/node@0.5.4-canary.4
2019-04-16 10:53:49 -04:00
Sophearak Tha
50e648d28a [@now/node] Make sure includeFiles have proper path inside lambda (#387)
Fixes #381 

Demo(public): https://now-node-fs.sophearak.now.sh/
2019-04-16 09:41:04 -04:00
Amio
52994bfe26 [build-utils] add default shouldServe and type ShouldServeOptions (#382)
Added the `ShouldServeParam` type declaration and default implementation for `shouldServe` for builder maker to import.
2019-04-16 19:51:51 +08:00
Connor Davis
1339f17585 Update index.js 2019-04-16 03:30:33 -05:00
Nathan Rajlich
9dd12cf1a7 Publish
- @now/build-utils@0.4.41-canary.4
2019-04-15 18:18:37 -07:00
Nathan Rajlich
6dab09f38e [now-build-utils] Create zip files with symlinks properly (#388)
After #359, creating zip files with symlinks would be corrupted because
the "target" of the symlink was actually the file contents.

This commit fixes the zip file construction logic to handle symlinks by
placing the target of the symlink as the "contents" of the file in the
zip. The unit test relies on GNU `unzip` and checks that it creates the
symlink from the produced zip file as expected.
2019-04-15 12:32:28 -07:00
Amio
c79d7be591 Mute stdio for runNpmInstall by default (#338)
Output logs if error occurs
2019-04-16 00:13:51 +08:00
Joe Haddad
9af3425d6d Publish
- @now/build-utils@0.4.41-canary.3
 - @now/next@0.1.3-canary.14
 - @now/node-bridge@1.0.2-canary.2
 - @now/node-server@0.5.4-canary.3
 - @now/node@0.5.4-canary.3
 - @now/php@0.4.17-canary.1
 - @now/python@0.1.0-canary.2
2019-04-15 08:31:34 +09:00
Igor Klopov
0700c16504 now/node: fix 07-content-length (#392) 2019-04-14 18:55:42 -04:00
Joe Haddad
4e55d9f709 Add a mutable flag to FileRef (#389)
* Add an immutable flag to FileRef

* `immutable` => `mutable`

* Add missing `assert` statement
2019-04-15 07:13:05 +09:00
Joe Haddad
945eb24bdc [now-next] Cache the new Next.js cache location (#386) 2019-04-11 16:10:45 -05:00
Connor Davis
c884102401 [now/next] cache everything in node_modules 2019-04-11 13:42:20 -05:00
Pete Nykänen
36e79efd7f Fix typo in Python builder (#383)
Simple typo fix
2019-04-11 11:29:20 -04:00
Steven
21ee0f3707 Publish
- @now/php@0.4.17-canary.0
2019-04-10 17:48:45 -04:00
Ties
ea5d3b8e80 [@now/php] Added includeFiles config (#326)
This is a retry of https://github.com/zeit/now-builders/pull/37, where the git history is a mess.
2019-04-10 17:44:18 -04:00
Connor Davis
301e0f216b Publish 2019-04-10 16:23:55 -05:00
Sophearak Tha
7a6fbd8c3d [now/python] Add django test (#378)
This PR added 08-django test.
2019-04-10 11:28:02 -04:00
Joe Haddad
77e7a0f502 [now-next] Cache Flying Shuttle directory (#380) 2019-04-09 22:53:41 -05:00
Nathan Rajlich
6bc42bbce9 Publish
- @now/build-utils@0.4.41-canary.2
2019-04-09 18:14:11 -07:00
Nathan Rajlich
de88969c46 [now-build-utils] Set the mode when creating the file, not afterwards (#375)
* [now-build-utils] Set the `mode` when creating the file, not afterwards

This is the more proper fix for #373.

* Use `parseInt()`

For some reason, `@types/node` allows string for `chmod()`
but not for `createWriteStream()`.

* Use a bitmask

See:

 - https://github.com/nodejs/node-v0.x-archive/issues/3045
 - https://www.martin-brennan.com/nodejs-file-permissions-fstat/
2019-04-09 11:17:23 -07:00
Igor Klopov
e86cd38787 Publish
- @now/node@0.5.4-canary.2
 - @now/node-server@0.5.4-canary.2
2019-04-09 20:41:20 +03:00
Igor Klopov
dc1badc931 [now-node] Move @zeit/ncc to npm dependencies (#370)
* now/node: move ncc to deps of package.json

* use require(ncc) instead of import (temporarily)

* do the same for now/node-server
2019-04-09 09:43:18 -07:00
Igor Klopov
ed3c176f5c Publish
- @now/node@0.5.4-canary.1
 - @now/node-server@0.5.4-canary.1
2019-04-09 18:09:37 +03:00
Igor Klopov
749ee5264c Publish
- @now/node-bridge@1.0.2-canary.1
2019-04-09 17:40:56 +03:00
Igor Klopov
9808ea1d8f Publish
- @now/build-utils@0.4.41-canary.1
2019-04-09 12:54:17 +03:00
Nathan Rajlich
a77e7109c7 [now-node-bridge] Exit the Node.js process upon unhandledRejection (#372)
If the `http.Server` handler function throws an error asynchronously,
then it ends up being an unhandled rejection which doesn't kill the node
process which causes the HTTP request to hang indefinitely. So print the
error here and force the process to exit so that the lambda invocation
returns an Unhandled error quickly.
2019-04-09 11:40:08 +03:00
Connor Davis
3b87c7ca83 Fix subscribe pattern 2019-04-08 21:43:28 -05:00
Connor Davis
1887df779a Handle static files in now dev (#369) 2019-04-08 21:41:34 -05:00
Steven
daccd0d8fc Publish
- @now/bash@0.1.5-canary.1
 - @now/build-utils@0.4.41-canary.0
 - @now/go@0.3.1-canary.3
 - @now/mdx-deck@0.4.19-canary.2
 - @now/next@0.1.3-canary.9
 - @now/node-bridge@1.0.2-canary.0
 - @now/node-server@0.5.4-canary.0
 - @now/node@0.5.4-canary.0
 - @now/python@0.1.0-canary.1
 - @now/rust@0.1.2-canary.2
 - @now/static-build@0.4.19-canary.2
2019-04-08 13:33:47 -04:00
Steven
fc9bbd2578 [now-static-build] fix link to docs (#368)
Fix link to documentation in static builder when the `dist` directory is not created.

I also changed the `build-utils` to use top-level imports like our docs specify.
2019-04-08 13:17:47 -04:00
Steven
f23f6ca643 Publish
- @now/python@0.1.0-canary.0
2019-04-08 09:30:06 -04:00
Sophearak Tha
c8d90fbcd1 [now/python] Update python tests (#367)
## What included?
- update python version to 3.6 in tests
- add `content-type` test
2019-04-08 08:45:51 -04:00
Nathan Rajlich
f4247da49a Publish
- @now/build-utils@0.4.40
2019-04-07 21:03:58 -07:00
Nathan Rajlich
9d781403ef [now-build-utils] Add support for symlinks to download() (#359)
* [now-build-utils] Add support for symlinks to `download()`

`now dev` is using this function to extract the `prepareCache()`
results, but upon extraction symlinks are lost and turned into
regular files, which breaks relative requires in Node.js modules.

This commit properly creates the symlinks from the files passed to
the `download()` function so that this problem no longer happens.

* Refactor `glob()` to return symlinks properly and add unit test

* Ensure the symlink target dir exists
2019-04-07 19:27:13 -07:00
Amio
ca188cf8e2 [now-built-utils] Add type declaration for glob (#360) 2019-04-07 19:17:06 -07:00
Connor Davis
207d895c0c Fix version check (#366) 2019-04-07 20:56:39 -05:00
Mike Engel
685821976d [now-rust] Make @now-rust's includeFiles match @now/node-server (#362)
This updates the `includeFiles` implementation to match how `now-node-server` does it, which is an array of globs rather than a single glob. To avoid making a new major version, this is done in a backwards compatible way to support existing projects using the current single glob API.

Added extra files to ensure that all would be included/processed even though the test just tests for one of them.

cc/ @styfle
2019-04-07 19:56:37 -04:00
Steven
fef5638cb9 [now-python] convert python builder to use typescript (#351)
* Add typescript to now-python

* Fix path

* Add gitignore

* Fix type errors

* Fix lint

* Move files back to root, add npmignore

* Final fixes
2019-04-07 11:13:02 -04:00
Nathan Rajlich
073ed247ad Update prepareCache() functions to use existing workPath (#356)
Considering that `prepareCache()` is executed directly after `build()`
is run, it seems that deleting the previous `workPath` and re-installing
the dependencies is just extra work, and it would be better to create
a cache from the `workPath` artifacts that `build()` just created.

From the perspective of `now dev`, this is ideal because:

 1. The `workPath` shouldn't be deleted since static file assets are
    served from the `workPath`, so `prepareCache()` deleting this
    directory is problematic.
 2. Creating the cache quickly becomes an important goal, because it
    reflects how quickly a developer can iterate on a file, and we don't
    want them waiting for `yarn` to finish installing dependencies all
    the time in order to see the change in their project.
2019-04-06 15:58:16 -07:00
Connor Davis
f071788ce6 [now/next] canary is updated 2019-04-05 18:18:11 -05:00
Igor Klopov
16f24bc3c8 [now-build-utils] decrease sema to fix deployments stuck at 'downloading' (#353)
[now-build-utils] decrease sema to fix deployments stuck at 'downloading'
2019-04-05 17:47:26 -04:00
Connor Davis
97fe3d489d [now-next] Strip Carat from version (#350)
* Strip Carat from version

* Get from next package.json

* Fix require

* Use resolveFrom

* Correctly fetch Next.js version

* Update index.js

* Update package.json
2019-04-05 02:12:27 -04:00
Joe Haddad
522d3a530c [now-next] Bump required version of Next.js (#352) 2019-04-05 01:49:19 -04:00
Steven
bafb49c464 Publish
- @now/python@0.0.42-canary.2
2019-04-04 15:21:34 -04:00
Sophearak Tha
7d5bd91e23 [@now/python] migrate @now/python-wsgi to @now/python` (#339)
* migrate `@now/python-wsgi to `@now/python`

* Refactor to use build-utils main

* Update install flag `--upgrade` in the right order.

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* using `cwd` in instead in `execa`

* add 01-cowsay test with `pip` and `pipenv`

* add 01-wsgi test

* Fix typo

* Add test script for now-python

* Add now_init.py to support dynamic imports

* Remove redundant files, no more config.wsgi

* replace all occurrences __NOW_HANDLER_FILENAME

* update tests for `@now/python`
2019-04-04 10:08:20 -04:00
Connor Davis
213614881c Delete .next if it exists (#347) 2019-04-03 23:20:15 -05:00
Nathan Rajlich
a225a4f855 Publish
- @now/build-utils@0.4.39
2019-04-03 17:23:18 -07:00
Connor Davis
ed2fd1dd29 Merge branch 'master' of github.com:zeit/now-builders 2019-04-03 19:00:21 -05:00
Connor Davis
bd33528fc7 Publish 2019-04-03 19:00:02 -05:00
Connor Davis
16969803f8 @now/next hotfixes for now dev optimizations (#345) 2019-04-03 18:58:13 -05:00
Nathan Rajlich
03cc4c0b01 Publish
- @now/build-utils@0.4.39-canary.0
2019-04-03 16:00:21 -07:00
Nathan Rajlich
0b9699da75 [now-build-utils] Remove yarn/npm cache clean after install (#346)
`cache clean` was being invoked for legacy purposes back when we were
dealing with Lambda's 500mb runtime filesystem limit. Now that builds
are not running on lambda, this can be removed. It also makes `now dev`
building unnecessarily slow.
2019-04-03 15:55:39 -07:00
Connor Davis
6737011a63 Publish 2019-04-03 14:55:32 -05:00
Joe Haddad
6d2b0e014c Make @now/next compatible with now dev (#340) 2019-04-03 14:53:22 -05:00
Sigurd Spieckermann
409359bfec Python WSGI builder without server (#95)
This PR adds a builder for Python WSGI web apps. It is heavily based on [`serverless-wsgi`](https://github.com/logandk/serverless-wsgi) and [`requests-wsgi-adapter`](https://github.com/seanbrant/requests-wsgi-adapter), uses [`Werkzeug`](https://github.com/pallets/werkzeug), and works without running an additional web server.

I've tested it manually with a simple Flask app that contains endpoints with `GET` and `POST` methods, nested paths, and JSON and binary response bodies.

Curiously, the `event` parameter in the `now_handler` function does not appear to have the expected structure of AWS Lambda functions. For instance, `event['isBase64Encoded']` does not exist. Instead, this information is encoded in `json.loads(event['body'])['encoding']`. The returned `dict` also expects `encoding='base64'` for a binary response body instead of `isBase64Encoded=True`. I've also noticed that no AWS request headers are included. I assume Now is responsible for these differences to plain AWS Lambda functions.

I'd appreciate code review and feedback to get robust official Python WSGI support into Now as soon as possible.
2019-04-02 10:35:32 -04:00
Steven
2151812596 Publish
- @now/node@0.5.3
 - @now/node-server@0.5.3
2019-04-01 15:38:49 -04:00
Steven
22860be6d0 Publish
- @now/node@0.5.3-canary.1
- @now/node-server@0.5.3-canary.1
2019-04-01 15:27:36 -04:00
Steven
78c3cbd7b4 [now-node] Bump ncc to 0.17.3 (#337)
* [now-node] Bump ncc to 0.17.3
2019-04-01 15:22:31 -04:00
Nathan Rajlich
a458a55e99 Publish
- @now/build-utils@0.4.38
2019-04-01 07:39:09 -07:00
Sophearak Tha
911d85be39 [now-go] fix subdirectory build fail for @now/go (#309)
* fix subdirectory build fail for `@now/go`

* using sep over platform specific forward slash

* Update packages/now-go/index.js

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* Update packages/now-go/index.js

Co-Authored-By: sophearak <t.sophearak@gmail.com>

* add 01-cowsay with subdirectory test case
2019-04-01 13:36:04 +07:00
Nathan Rajlich
98b5a4b0e9 Publish
- @now/node@0.5.2
 - @now/node-server@0.5.2
2019-03-31 15:01:28 -07:00
Nathan Rajlich
5f80e451b8 Update @now/node-bridge to v1.0.1 stable for @now/node and @now/node-server 2019-03-31 15:00:39 -07:00
Nathan Rajlich
0288f2d1a3 Publish
- @now/node-bridge@1.0.1
2019-03-31 14:47:13 -07:00
Sophearak Tha
e39a5eca04 Add Codecov report (#327)
* Add Codecov

* Using flag for coverage instead

* Revert "Using flag for coverage instead"

This reverts commit 9e14c8c8299267b2ff0431d822391e0538839ae7.

* Update codecov

* Add circle ci test coverage step

* try run jest without `node --expose-gc`

* Revert "try run jest without `node --expose-gc`"

This reverts commit 39fbd7995375ac62529d2d55519abc877b526071.

* run jest --coverage without `node --expose-gc`

* run jest with --runInBand flag

* Run tests and coverage at the same time
2019-03-29 09:11:14 -04:00
Steven
d4493f7d39 Fix rate limit for tests (#330)
This will generate a new token for every 10 API call so we stop getting rate limited.
2019-03-28 15:36:24 -04:00
Nathan Rajlich
145e5a10c2 [now-next] Set NODE_ENV to development in now dev (#325) 2019-03-26 12:52:50 -07:00
Nathan Rajlich
bd2d289252 [now-node] Set NODE_ENV to development in now dev (#324) 2019-03-26 12:52:26 -07:00
Nathan Rajlich
a673e5f752 [now-node-server] Set NODE_ENV to development in now dev (#323) 2019-03-26 12:52:04 -07:00
Nathan Rajlich
b2dc31a6b4 Revert "[now-node] Set NODE_ENV to development in now dev"
This reverts commit 62a308bed7.

Accidentally commited to `master` branch
2019-03-26 11:41:31 -07:00
Nathan Rajlich
62a308bed7 [now-node] Set NODE_ENV to development in now dev 2019-03-26 11:39:49 -07:00
Nathan Rajlich
ac08bfd26f [now-bash] Improvements for error handling (#318)
* Add shellcheck ignore comments to dynamic imports

* Add shebang to `runtime.sh`

So that vim applies proper syntax highlighting when bash-specific
features like `<<<` are used.

* Send error responses with `errorMessage` property

So that `now dev` can display the error message properly.
2019-03-26 10:51:59 -07:00
Mike Engel
d7f1371799 [now-rust] Allow extra static files on the lambda fs config.includeFiles (#300)
This add the ability to include other files into the filesystem of the lambda. This works by providing an `includeFiles` option in the builder config, and then moving those into the lambda when it's created. The `includeFiles` option is a glob matcher relative to the entrypoint.

**Note:** This is the first example I know of that allows a user to add more files to the filesystem through the official now builders. This API is something I came up with, and may not be the best or what y'all are thinking.

## Why?
This is helpful for my specific case, where I need extra binaries on the system when the lambda executes (`wget` and `now`). This could also be useful to include large files to parse, or other things one wouldn't want to include in the rust binary itself due to file size, memory allocation, whatever.

## Example
**Local file structure**
```
now_lambda/
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── static
    ├── now
    └── wget
```

**Now config**
```json
{
  "version": 2,
  "name": "now-lambda",
  "builds": [
    {
      "src": "Cargo.toml",
      "use": "@now/rust",
      "config": { "includeFiles": "static/*", "maxLambdaSize": "50mb" }
    }
  ]
}
```

**Lambda file structure**
```
/var/task/
├── bootstrap
└── static
    ├── now
    └── wget
```

## Testing
You can use `https://mike-now-rust-5shu849in.now.sh` as the builder's `"use"` key to test it out. It's a hosted tarball of this PR with a little extra debugging thrown in.
2019-03-26 11:19:25 -04:00
Nathan Rajlich
c97ad02aca [now-node] Enable TypeScript strict mode (#321) 2019-03-25 19:15:08 -07:00
Nathan Rajlich
c0460b734d Publish
- @now/node-server@0.5.2-canary.4
 - @now/node@0.5.2-canary.6
2019-03-25 19:03:21 -07:00
Steven
3b0ed55b57 [now-node] Bump ncc to 0.17.0 (#317)
* Bump ncc to 0.17.0

* Bump ncc to 0.17.0
2019-03-25 18:59:50 -07:00
Nathan Rajlich
402153f076 Publish
- @now/build-utils@0.4.38-canary.1
2019-03-25 18:22:30 -07:00
Nathan Rajlich
6ec823e292 [now-built-utils] Use constants for the type field (#320)
This is important for TypeScript usage with the combined `File` type
declaration. It helps the compiler understand the proper type in
consuming code via `if` checks to avoid casting.
2019-03-25 18:18:38 -07:00
Nathan Rajlich
a9af9ebb5a [now-build-utils] Pin "end-of-stream" dependency to v1.4.1 (#319)
All of the other deps are pinned versions, so this is for consistency.
2019-03-25 17:45:11 -07:00
Nathan Rajlich
ce88a64693 Publish
- @now/build-utils@0.4.38-canary.0
 - @now/python@0.0.42-canary.1
2019-03-22 17:31:09 -07:00
Nathan Rajlich
490cd8363e [now-python] Use workDir provided by builder (#294)
* [now-python] Use `workDir` provided by builder

Instead of generating another temp directory.

* [now-python] Add `--upgrade` flag to `pip install`

This is mainly for `now dev`.
2019-03-22 17:27:27 -07:00
Nathan Rajlich
71d1651797 Publish
- @now/build-utils@0.4.37
2019-03-22 15:56:18 -07:00
Nathan Rajlich
0da7197c3e Publish
- @now/build-utils@0.4.37-canary.3
 - @now/go@0.3.1-canary.2
 - @now/html-minifier@1.0.8-canary.1
 - @now/md@0.4.10-canary.2
 - @now/next@0.1.3-canary.2
 - @now/node-bridge@1.0.1-canary.1
 - @now/node-server@0.5.2-canary.3
 - @now/node@0.5.2-canary.5
 - @now/rust@0.1.2-canary.1
2019-03-22 14:44:44 -07:00
Nathan Rajlich
950a4e98e9 [now-node-server] Print a more helpful message when a module is missing (#313)
Similar to #312, but for `@now/node-server`.
2019-03-22 14:32:46 -07:00
Nathan Rajlich
8258ede23f [now-node] Print a more helpful message when a module is missing (#312) 2019-03-22 14:32:17 -07:00
Steven
77f84fe2aa Enhance TS declarations (#311)
This adds a few new exports to `@now/build-utils` with proper documentation 😄 

- AnalyzeOptions
- BuildOptions
- PrepareCacheOptions

I also updated TS is `now-node` and `now-node-bridge`.

### Screenshot

Here's an example of what the docs look like with TS Intellisense 📖 

![image](https://user-images.githubusercontent.com/229881/54851181-80825380-4cbf-11e9-9703-2ca75e8cdf47.png)
2019-03-22 17:31:43 -04:00
Igor Klopov
5c4b946864 Publish
- @now/build-utils@0.4.37-canary.2
2019-03-22 19:41:23 +03:00
Steven
dfc51ad97f Add tracing priority to tests (#310) 2019-03-22 10:09:45 -04:00
Steven
d32afc8332 [now-build-utils] Add typescript (#305)
* Add typescript to build utils

* Move file to file.ts

* Move to src directory

* Cast to readable

* Cast to stats

* Ignore js files

* Remove includes

* Run Linting after Building in CircleCI

* Move now-next tsconfig, add build script

* Fix default exports

* Attemp to fix default exports

* Change api to use index

* Add types to package.json

* Add missing fs

* Add shims

* Add missing end-of-stream dep

* Fix shims

* Ignore TS when linting

* Removed the unused top-level typescript
2019-03-21 18:23:05 -04:00
Sophearak Tha
9d1263ccc2 [now-go] better way to init go.mod (#304) 2019-03-21 09:33:24 +07:00
Matias Larsson
7bf2cfb3dc Remove collapseInlineTagWhitespace option (#281)
`collapseInlineTagWhitespace` removes white space between inline elements so `<p>Text with <a href="https://zeit.co/now">a link</a> is hard to read.</p>` renders as `Text witha linkis hard to read.`.

See example at https://runkit.com/5c8a1c7a0f950f0012252fbb/5c8a1c7a0f950f0012252fbc
2019-03-20 16:06:02 -04:00
Sophearak Tha
9b37460c4f [now-node] Add config.includeFiles (#277)
* add `includeFiles` support in `@now/node`

Co-Authored-By: sophearak <t.sophearak@gmail.com>
2019-03-20 14:07:06 -04:00
Sophearak Tha
b7f8b37ca6 [now-node-server] Add config.includeFiles (#302)
The main idea behind this is we want user be able to specific files in that should be include in the `handler` which later can be use in runtime.

For the most case, `ncc` be able to detect relevant files included in the `handler`. But the case that `ncc` not able to resolve, this option come in handy.

PR #277 does the same thing, but for `@now/node`.
2019-03-20 11:48:50 -04:00
Antonio Nuno Monteiro
13aa1b2d1c Publish now_lambda 0.1.3 (#287) 2019-03-20 10:56:12 -04:00
Nathan Rajlich
92437c075e [now-md] Remove console.log() call of Markdown file result (#303)
Not sure why this was here, but it's pretty noisey in the logs,
especially when executing this builder via `now dev`.
2019-03-20 09:09:16 -04:00
Steven
331c263587 Publish
- @now/bash@0.1.5-canary.0
 - @now/node-server@0.5.2-canary.2
 - @now/node@0.5.2-canary.4
 - @now/python@0.0.42-canary.0
2019-03-17 22:10:56 -04:00
Steven
7d4f6f636b [now-node] Bump ncc to 0.16.1 (#298)
[now-node] Bump `@zeit/ncc` to version 0.16.1 which includes fixes for webpack asset loader
2019-03-17 21:40:58 -04:00
Nathan Rajlich
5e90ef8e34 Publish
- @now/bash@0.1.4
 - @now/python@0.0.41
2019-03-17 17:01:43 -07:00
Nathan Rajlich
4885d680a7 Publish
- @now/next@0.1.3-canary.1
 - @now/python@0.0.41-canary.5
2019-03-17 13:58:36 -07:00
Nathan Rajlich
97cbe0b894 [now-python] Bind HTTP server to an ephemeral port (#296)
* [now-python] Bind HTTP server to an ephemeral port

Similar to #224, but for Python.

* Bind to port 0
2019-03-17 13:57:06 -07:00
Connor Davis
301eea90ee Add Terser Cache to Now Cache (#268)
* Add terser cache to Now cache

* Thought I removed this line

* Update to cache next dir

* Change to next-minifier
2019-03-17 14:28:51 -04:00
Nathan Rajlich
ea4f9dd930 Regenerate yarn.lock file 2019-03-16 21:00:31 -07:00
Nathan Rajlich
38928ab942 Publish
- @now/go@0.3.1-canary.1
 - @now/python@0.0.41-canary.4
2019-03-16 20:38:40 -07:00
Nathan Rajlich
bfb67d10ec [now-python] Run pip from within the installation directory (#295)
Otherwise the `setup.cfg` file is not respected (see #292).
2019-03-16 20:32:03 -07:00
Nathan Rajlich
616bad8a3d Fix packAndDeploy() for usage with @now/node (#293)
* Fix `packAndDeploy()` for usage with `@now/node`

Since the prepublish script for `@now/node` writes other stuff to
stdout, so relying on `npm pack` outputting the tarball name is not
reliable.

Instead, just `glob()` for a `*.tgz` file and expect that to be the
output package tarball.

Fixes error:

```
Error: ENOENT: no such file or directory, open '/home/circleci/repo/packages/now-node/> @now/node@0.5.2-canary.2 prepublish /home/circleci/repo/packages/now-node
> npm run build
> @now/node@0.5.2-canary.2 build /home/circleci/repo/packages/now-node
> ./build.sh
'/home/circleci/repo/packages/now-node-bridge/bridge.d.ts' -> 'src/bridge.d.ts'
now-node-0.5.2-canary.2.tgz'
```

* Make the `tgzPath` be absolute

* 'ignore' -> 'inherit'
2019-03-16 19:10:18 -07:00
Nathan Rajlich
e026ddf805 [now-go] Add missing await to getExportedFunctionName() (#285)
This fixes a subtle bug in the `getExportedFunctionName()` helper
function. It wasn't actually causing any issues in production, since the
`index.js` file also does `await` on the return value of this function
so it was still resolving the promise properly, but this is more correct
and fixes the associated `debug()` call.
2019-03-16 17:29:50 -07:00
Nathan Rajlich
bec9ea101f Publish
- @now/build-utils@0.4.37-canary.1
 - @now/node@0.5.2-canary.3
 - @now/python@0.0.41-canary.3
2019-03-16 16:44:26 -07:00
Nathan Rajlich
54f3f755fb [now-build-utils] Set npm_config_target env var when installing npm deps (#290) 2019-03-16 16:43:28 -07:00
Nathan Rajlich
5b03109ba7 [now-python] Add setup.cfg file before running pip install (#292)
* [now-python] Add `setup.cfg` file before running `pip install`

See: https://stackoverflow.com/a/44728772/376773

The `setup.cfg` is required for `now dev` on MacOS, where without
this file being present in the src dir then this error happens:

distutils.errors.DistutilsOptionError: must supply either home
or prefix/exec-prefix -- not both

* Only create the `seup.cfg` file once
2019-03-16 16:43:13 -07:00
Connor Davis
7ff9e810ff Add note about @now/next not supporting distDir (#289)
`now-builders` can't parse `next.config.js` because it can hold functions.

It expects `/.next` as the build output.

Closes: #288
2019-03-16 22:16:46 +01:00
Nathan Rajlich
3036aff45e [now-node] Add prepublish npm script
So that the TypeScript files are compiled before publishing.
2019-03-15 13:29:17 -07:00
Steven
c366aa69a4 Publish @now/node@0.5.2-canary.2 2019-03-15 16:18:46 -04:00
Steven
c8d225522d Publish
- @now/next@0.1.3-canary.0
 - @now/node-server@0.5.2-canary.1
 - @now/node@0.5.2-canary.1
2019-03-15 14:33:02 -04:00
Steven
8ee5063669 [now-node] Bump ncc to 0.16.0 (#283)
* [now-node] Bump ncc to 0.16.0

* [now-node] Bump ncc to 0.16.0
2019-03-15 11:14:33 -07:00
Nathan Rajlich
9372e70747 Publish
- @now/next@0.1.2
2019-03-14 20:59:14 -07:00
Nathan Rajlich
4a4bd550a1 Publish
- @now/bash@0.1.4-canary.0
 - @now/go@0.3.1-canary.0
 - @now/next@0.1.2-canary.0
 - @now/node-bridge@1.0.1-canary.0
 - @now/node-server@0.5.2-canary.0
 - @now/node@0.5.2-canary.0
 - @now/rust@0.1.2-canary.0
2019-03-14 20:39:34 -07:00
Connor Davis
f53343d547 Fix static files for latest now/next using target serverless (#282) 2019-03-14 21:52:06 -05:00
Nathan Rajlich
e4ed811b53 [now-bash] Only read the $event body once (#278)
In situations where the handler responds very quickly, the `$event` file
path gets deleted before the `_lambda_runtime_body` function can finish
its work. So instead, just read that file once and cache it to
a variable inside that function.

This fixes this issue when the `handler` responds very quickly, causing the
`$events` file to be deleted before the multiple `jq` invocations are able to
read it:

```
/var/task/.import-cache/runtime.sh: line 78: /tmp/T/tmp.DShzjeah: No such file or directory
/var/task/.import-cache/runtime.sh: line 82: /tmp/T/tmp.DShzjeah: No such file or directory
```
2019-03-14 19:27:39 -07:00
Nathan Rajlich
e9935dee31 [now-bash] Use the workPath directory (#279)
This is important for `now dev`, where the `workPath` is re-used and
thus leads to faster re-builds.
2019-03-14 14:13:16 -07:00
Nathan Rajlich
2e1e6bb131 Publish
- @now/next@0.1.1
2019-03-13 15:45:16 -07:00
Nathan Rajlich
4a01ac4bd0 [now-next] Update @now/node-bridge to v1.0.0 2019-03-13 15:44:32 -07:00
Nathan Rajlich
bd1a7c428f Publish
- @now/go@0.3.0
 - @now/next@0.1.0
2019-03-13 15:35:42 -07:00
Nathan Rajlich
9a4a3dac47 Publish
- @now/node@0.5.1
 - @now/node-server@0.5.1
2019-03-13 14:51:04 -07:00
Nathan Rajlich
4f2c35a0ee [now-node] Update @now/node-bridge to v1.0.0 2019-03-13 14:29:12 -07:00
Nathan Rajlich
672df5d026 [now-node-server] Update @now/node-bridge to v1.0.0 2019-03-13 14:28:37 -07:00
Nathan Rajlich
8cb648abc4 Publish
- @now/node@0.5.0
 - @now/node-server@0.5.0
2019-03-13 14:24:35 -07:00
Nathan Rajlich
74f658c634 Publish
- @now/node-bridge@1.0.0
2019-03-13 13:50:24 -07:00
Nathan Rajlich
efbb54a232 Publish
- @now/bash@0.1.3
2019-03-12 16:50:37 -07:00
Igor Klopov
3e2bd03e01 Publish
- @now/rust@0.1.1
2019-03-11 14:37:33 +03:00
Igor Klopov
8dc92b70b9 [now/rust] cleanup cache files a little + update def cache 2019-03-11 13:55:52 +03:00
Igor Klopov
4267be4e5a Publish
- @now/rust@0.1.0
2019-03-10 20:44:39 +03:00
Igor Klopov
43ba6459eb [now/rust] fixes 2019-03-10 20:40:38 +03:00
Igor Klopov
8c5638915d [now/rust] update default cache 2019-03-10 19:32:01 +03:00
Igor Klopov
3fab247c15 Publish
- @now/rust@0.0.9
2019-03-10 14:41:00 +03:00
Mike Engel
6ab0e2e9ab fix(now-rust): Fix how build.sh is detected and run (#267) 2019-03-10 14:34:10 +03:00
Igor Klopov
34369148d7 Publish
- @now/rust@0.0.8
2019-03-08 21:24:40 +03:00
Igor Klopov
662ad1ed3a [now/rust] remove gcc installation (#266)
* [now/rust] remove gcc

* update default cache to a blob, created without def.c.
2019-03-08 21:20:46 +03:00
Igor Klopov
890cd74ee5 Publish
- @now/rust@0.0.7
2019-03-08 20:03:18 +03:00
Igor Klopov
7ef616b31e lint fixes 2019-03-08 20:01:03 +03:00
Igor Klopov
bebcfa4bb5 Publish
- @now/rust@0.0.6
2019-03-08 19:57:34 +03:00
Igor Klopov
25100c53aa [now/rust] default cache (#265)
* [now/rust] default cache

* update default cache url
2019-03-08 19:55:04 +03:00
Sophearak Tha
fe20da87e7 add support go.mod (#258)
* add support go.mod

* add main__mod__.go

* add missing variable and fix import

* fix linter

* remove `gcc` and add `go mod tidy`

* let go build handle the getting the package
2019-03-08 05:02:44 +03:00
Igor Klopov
18cb147c86 Publish
- @now/rust@0.0.5
2019-03-07 20:25:25 +03:00
Mike Engel
9c9e18586f [now-rust]: Allow a build.sh to be run prior to compile (#253)
* fix(now-rust): Don't rely on encoding for parsing normal bodies

* feat(now-rust): Allow users to run a build.sh file
2019-03-07 20:11:20 +03:00
Igor Klopov
0cd7192740 Publish
- @now/rust@0.0.4
2019-03-07 19:40:17 +03:00
Juan Campa
a2d9c4fb4b Add headers to error when a deployment fails a probe (#257) 2019-03-07 09:35:56 -05:00
Nathan Rajlich
02fafd2ebc Publish
- @now/go@0.2.13-canary.2
 - @now/next@0.0.85-canary.10
 - @now/php-bridge@0.4.16-canary.0
 - @now/php@0.4.16-canary.0
 - @now/wordpress@0.4.16-canary.0
2019-03-04 15:53:34 -08:00
Daniel Rodríguez
42577c915c Update go to v1.12 (#247)
This might allow users to use `go.mod`, since on 1.12 they did the following:

> ... the go command now supports module-aware operations outside of a module directory, provided that those operations do not need to resolve import paths relative to the current directory or explicitly edit the go.mod file. ...

Perhaps after that, the users would be simply able to add this to their config files and make it work?

```
  "build": {    
    "env": {                                                              
      "GO111MODULE": "on"    
    }    
  },
```

Also related to #241.
2019-03-04 15:15:03 -08:00
Igor Klopov
73db9e11dd [now-php] improve routes test 2019-03-02 13:48:44 +03:00
Igor Klopov
3125125c16 Publish
- @now/php@0.4.15
 - @now/wordpress@0.4.15
2019-03-02 07:00:31 +03:00
Igor Klopov
5335291408 bump now-php to use new now-php-bridge 2019-03-02 06:01:33 +03:00
Igor Klopov
36620559f9 Publish
- @now/php-bridge@0.4.15
2019-03-02 03:53:48 +03:00
Igor Klopov
360ea3a609 [now-php] fix routing bug (#190) 2019-03-02 02:50:57 +03:00
Joe Haddad
1cd362126c Warn when .next is uploaded (#244)
* Add error document

* Check for a .next folder during deploy process

* Remove error and warn instead

* Check if path exists instead of globbing
2019-02-27 16:22:31 -05:00
Joe Haddad
ae19fe95f6 Publish
- @now/next@0.0.85-canary.9
2019-02-27 13:26:54 -05:00
Joe Haddad
3e34d402a2 Remove fs.promised dependency (#242)
* Remove fs.promised dependency

* Adjust import style per @dav-is

* Update names
2019-02-27 12:58:11 -05:00
Joe Haddad
cc7b97fbbb Dump next.config.js file on error (#243)
* Dump next.config.js file on error

* Relocate method to utilities

* Add tests for getNextConfig
2019-02-27 12:08:40 -05:00
Joe Haddad
c1049985af Publish
- @now/bash@0.1.3-canary.0
 - @now/build-utils@0.4.37-canary.0
 - @now/next@0.0.85-canary.8
2019-02-26 14:33:47 -05:00
Nathan Rajlich
214388ccf3 [now-bash] Use base64 --decode flag
Instead of `-d`. Seems the short flag BSD's `base64` is `-D`,
however the long option name is consistent between both versions.
2019-02-26 11:28:45 -08:00
Joe Haddad
b1d6b7bfc0 feat(@now/next): optional assets directory (#240)
* Make assets part of the build output

* Add console output for user
2019-02-26 14:18:03 -05:00
Nathan Rajlich
ece3564dfd [now-build-utils] Use os.tmpdir() for getWritableDirectory() (#238)
`os.tmpdir()` abstracts away platform differences related to retrieving
the writable temp directory, for example MacOS returns a user-specific
temp directory instead of `/tmp`.

On AWS Lambda, [it returns `/tmp`](https://nexec-v2.n8.io/api/node?arg=-p&arg=require(%27os%27).tmpdir()).

More importantly, it removes the `__dirname/tmp` special-case when not
running in prod. This was problematic for `now dev` because the module
directory is not always writable (i.e. for a `pkg` binary).
2019-02-25 16:56:41 -08:00
Igor Klopov
a88af1f077 Publish
- @now/build-utils@0.4.36
2019-02-24 23:26:14 +03:00
Igor Klopov
d92f7b26c0 lint fixes for build-utils 2019-02-24 23:08:09 +03:00
Igor Klopov
52198af750 Publish
- @now/build-utils@0.4.35-canary.3
2019-02-24 11:56:01 +03:00
Igor Klopov
d58bff2453 [build-utils] chmod +x before running user script 2019-02-24 11:50:07 +03:00
Joe Haddad
8c0a144ae4 Publish
- @now/next@0.0.85-canary.7
2019-02-22 17:13:46 -05:00
Joe Haddad
106e4d5f36 Cache Next.js node_modules for faster rebuilds (#235)
* Add `node_modules` caching for Next.js projects

* Use existing work directory instead of redownloading dependencies

* Reorder fns

* Add prettier configuration

* Move entry files to cache folder

* Correct glob base path

* Be sure to include .yarn-integrity

* Save `package.json` and `yarn.lock`, too

* Increase compatibility so Yarn can dedupe
2019-02-22 16:30:29 -05:00
Nathan Rajlich
66c28bd695 Publish
- @now/next@0.0.85-canary.6
 - @now/node-server@0.5.0-canary.3
 - @now/node@0.5.0-canary.5
2019-02-21 13:20:49 -08:00
Nathan Rajlich
55e75296ff [now-next] Update @now/node-bridge to v1.0.0-canary.2 (#232)
Update `@now/next` to use the latest `@now/node-bridge`, which removes the hard-coded port 3000 that was previously in-place, which was problematic for `now dev`. Now the `http.Server` instance listens on an ephemeral port which is detected by the Now node runtime.

(Similar to 6101ba9d95 and 8dc0c92c58)
2019-02-21 12:55:33 +01:00
Nathan Rajlich
36cbb36737 Add --tags flag when determining if commit it tagged
This fixes matching for "unannotated tags".

See: https://stackoverflow.com/questions/1474115/how-to-find-the-tag-associated-with-a-given-git-commit#comment9135284_1474161
2019-02-20 18:11:11 -08:00
Nathan Rajlich
978ca328ef Publish
- @now/node@0.5.0-canary.4
 - @now/node-server@0.5.0-canary.2
2019-02-20 16:49:58 -08:00
Nathan Rajlich
7b383e0f7c [now-node] Fix build.sh script 2019-02-20 16:40:52 -08:00
Nathan Rajlich
faa5ab36aa Publish
- @now/node@0.5.0-canary.3
2019-02-20 16:32:31 -08:00
Nathan Rajlich
c0a21969dd Regenerate yarn.lock file
Not really sure why this is happening, since `@now/node` and
`@now/node-server` both use this version, but without this commit, then
publish step on CircleCI fails due to a dirty working tree.

Go figure 🤷
2019-02-20 16:31:04 -08:00
Nathan Rajlich
73d0a1723f [now-node] Ensure that @now/node-bridge is installed for build 2019-02-20 14:59:24 -08:00
Nathan Rajlich
7c515544ae [now-node] Don't commit the bridge.d.ts file 2019-02-20 14:52:28 -08:00
Nathan Rajlich
b53c9a6299 Publish
- @now/node@0.5.0-canary.2
2019-02-20 14:51:02 -08:00
Nathan Rajlich
35ff11e6e4 Remove prepublish step for now
It's causing these kinds of failures:

https://circleci.com/gh/zeit/now-builders/839
2019-02-20 14:50:18 -08:00
Nathan Rajlich
64ee4905cd Publish
- @now/node-server@0.5.0-canary.1
 - @now/node@0.5.0-canary.1
2019-02-20 13:53:37 -08:00
Nathan Rajlich
e50dd7e50a Bump now/node and now/node-server to v0.5.0 canary 2019-02-20 13:53:00 -08:00
Nathan Rajlich
6101ba9d95 [now-node-server] Update @now/node-bridge to v1.0.0-canary.2 (#231)
Update `@now/node-server` to use the latest `@now/node-bridge`, which
removes the hard-coded port 3000 that was previously in-place,
which was problematic for `now dev`. Now the `http.Server`
instance listens on an ephemeral port which is detected by the
Now node runtime.
2019-02-20 13:47:17 -08:00
Nathan Rajlich
8dc0c92c58 [now-node] Migrate to TypeScript and use @now/node-bridge v1 (#212)
This moves `@now/node` to being implemented in TypeScript.

It also updates it to use the latest `@now/node-bridge`, which
removes the hard-coded port 3000 that was previously in-place,
which was problematic for `now dev`. Now the `http.Server`
instance listens on an ephemeral port which is detected by the
Now node runtime.
2019-02-20 12:33:21 -08:00
Nathan Rajlich
44c9f3765a Publish
- @now/node-bridge@1.0.0-canary.2
 - @now/node-server@0.4.27-canary.7
 - @now/node@0.4.29-canary.7
2019-02-19 20:49:58 -08:00
Nathan Rajlich
92c05ca338 Pin @now/node-bridge to v0.1.11-canary.0
To address: https://github.com/zeit/now-builders/pull/224#issuecomment-465410905
2019-02-19 20:26:38 -08:00
Nathan Rajlich
069b557906 Publish
- @now/node-bridge@1.0.0-canary.1
2019-02-19 17:26:04 -08:00
Nathan Rajlich
692a0df909 Publish
- @now/node-bridge@0.1.11-canary.1
 - @now/node-server@0.4.27-canary.6
 - @now/node@0.4.29-canary.6
2019-02-19 16:33:18 -08:00
Nathan Rajlich
aeafeb5441 [now-node-bridge] Refactor API to be http.Server focused (#224)
* [now-node-bridge] Refactor API to be `http.Server` focused

This commit refactors the `@now/node-bridge` helper module to work with
`http.Server` instances directly, instead of expecting the `port` to be
set.

The `Bridge` instance calls the `listen()` function on the server instance
which binds to an ephemeral port. This is especially important for
`now dev`, where using a hard-coded port will cause port conflicts for
multiple lambdas using the same builder.

Also converts to TypeScript and adds some basic unit tests.

Example usage:

```js
const server = new Server(() => {});
const bridge = new Bridge(server);
bridge.listen();

const info = await bridge.listening;
assert.equal(info.address, '127.0.0.1');
assert.equal(typeof info.port, 'number');
```

* Update `yarn.lock`

* Add `pretest` script

* Enable TypeScript `strict` mode

* Throw if a string is returned from `server.address()`

Defensive programming ftw

Co-Authored-By: TooTallNate <n@n8.io>

* Prettier

* Prettier

* Fixes

* Attempt to fix CI

* Add `files` array to package.json

* Check for the `Action` property to avoid type casting

Co-Authored-By: TooTallNate <n@n8.io>

* Split the normalizing functions into separate ones

Also adds additional unit tests.

* export the `NowProxyRequest` and `NowProxyResponse`

* Remove last `as` casting

* Move some up

* Debug CircleCI tests :/

* Fix "Invoke" check

* Attempt to fix CircleCI again

* Fix bad

* Convert tests to use `jest`
2019-02-19 16:17:58 -08:00
Nathan Rajlich
a09d5fb355 Add publish.sh script for CircleCI (#228)
* Add `publish.sh` script for CircleCI

Centralize the publishing logic for CircleCI to use.

There was a bug in the previous "Potentially publish stable release"
branch, such that it would be executed for non-tagged commits. The new
publish script checks if there are indeed any tags for the commit, and
bails if there are none.

As an additional bonus, there's now only one publish step in the
CircleCI config, because the publish script determines stable vs. canary
based on the tag name.

* Remove `echo`
2019-02-19 14:45:04 -08:00
Nathan Rajlich
d8017aa9aa Publish
- @now/python@0.0.41-canary.2
2019-02-19 12:14:49 -08:00
Honza Javorek
702f56b9b5 [now-python] Add missing base64 import and fix coding style (#226) 2019-02-19 12:11:12 -08:00
Nathan Rajlich
183b117152 Publish
- @now/python@0.0.41-canary.1
2019-02-19 11:29:26 -08:00
Nathaniel Hill
75b3fb4981 [now-python] Pass the request body to the HTTP request (#99)
* pass body to lambda

* reformat

* look for requirements.txt in lambda root

* single issue PR

* support base64 encoded request body

* support local requirements.txt

* formatting cleanup

* Update packages/now-python/now_handler.py

Co-Authored-By: NathanielHill <nata@goguna.com>

* Avoid error on empty POST body

Just realized I forgot to address the empty POST body error. This should do it

* quick formatting fix

remove double quotes for consistency
2019-02-19 11:28:21 -08:00
Steven
49e63de5fe Publish
- @now/next@0.0.85-canary.5
 - @now/node-server@0.4.27-canary.5
 - @now/node@0.4.29-canary.5
2019-02-17 19:09:27 -05:00
Steven
4742cd32f2 [now-node] add sourceMap flag to ncc (#222)
This adds a {sourceMap:true} flag to ncc builds
2019-02-17 19:08:19 -05:00
Steven
377b73105d [now-node] bump ncc to 0.15.2 (#221)
This has a couple fixes.

See release notes here https://github.com/zeit/ncc/releases/tag/0.15.2
2019-02-17 23:55:55 +01:00
Connor Davis
a5577efb3d Prepublish @now/next 2019-02-15 17:07:14 -06:00
Steven
2ec46dc5c9 Publish
- @now/node-server@0.4.27-canary.4
 - @now/node@0.4.29-canary.4
 - @now/rust@0.0.3-canary.2
2019-02-15 12:24:06 -05:00
Steven
42708ed93c [now-node] bump ncc to 0.15.1 (#219)
This fixes a regression introduced in ncc@0.13.2 and also prints the version of ncc used at build time.

See [release notes](https://github.com/zeit/ncc/releases/tag/0.15.1) for more info.
2019-02-15 17:48:58 +01:00
Antonio Nuno Monteiro
2fabe95f6e [now-rust] Build .rs entrypoints (#213) 2019-02-15 15:10:51 +00:00
Nathan Rajlich
ac1a3dab22 Publish
- @now/build-utils@0.4.35-canary.2
 - @now/go@0.2.13-canary.1
 - @now/rust@0.0.3-canary.1
2019-02-14 17:45:31 -08:00
Nathan Rajlich
ad4011512d [now-go] Refactor to be platform-aware for now dev (#216)
* [now-go] Refactor to be platform-aware for `now dev`

This is mostly to have `now dev` work, but also just a general cleanup
of the builder codebase.

Previously, the `get-exported-function-name` Go binary was being
compiled at publish-time for "linux x64" architecture, since that
is what AWS Lambda expects.

In order for `@now/go` to work on MacOS (or any other platform not
compatible with "linux x64") the helper program must be compiled by the
actual host machine, so that it builds a MacOS binary for example.
So building `get-exported-function-name` has been moved from the
"publish" step to the "postinstall" step.

Similarly, the `go` binary itself used to be downloaded explicitly by
the `build()` function during build-time. This step has been moved to
the "postinstall" script as well, so that said binary may be compiled.

This also makes sense because the `@now/go` builder will be downloaded
by `now dev` once, so this way the `go` binary only needs to be
downloaded once as well, rather than once per build.

Any assumptions about `GOOS` and `GOARCH` have also been removed.

* Don't lint the `go` tarball directory

* Use GitHub URL for the go bridge

* Tweak comments

* Use `glob()` instead of `FileFsRef`

`FileFsRef` appears to lose executable permissions on the file,
whereas `glob()` works as expected.
2019-02-14 17:36:33 -08:00
Nathan Rajlich
9ff1a25c8f [now-build-utils] Add FsFileRef.fromFsPath() helper function (#217)
* [now-build-utils] Add `FsFileRef.fromFile()` helper function

This helper function is very similar to the raw `FsFileRef` constructor,
however it stats the file to retrieve the proper `mode`, so that
settings like executable bits are not lost.

* Rename to `FileFsRef.fromFsPath()`
2019-02-14 17:09:01 -08:00
Antonio Nuno Monteiro
8039b3d377 [now-rust] Publish 0.1.2 to crates.io (#215) 2019-02-14 15:03:14 -08:00
Connor Davis
dd9017475c Publish
- @now/lambda@0.4.10-canary.1
 - @now/md@0.4.10-canary.1
 - @now/mdx-deck@0.4.19-canary.1
 - @now/next@0.0.85-canary.4
 - @now/node-server@0.4.27-canary.3
 - @now/node@0.4.29-canary.3
 - @now/php@0.4.14-canary.1
 - @now/static-build@0.4.19-canary.1
2019-02-12 21:41:52 -06:00
Connor Davis
031499014f Give tests 4 minutes instead of 2 2019-02-12 21:40:51 -06:00
Connor Davis
2a68d2a2ad Publish
- @now/build-utils@0.4.35-canary.1
 - @now/next@0.0.85-canary.3
 - @now/rust@0.0.3-canary.0
2019-02-12 21:00:19 -06:00
Connor Davis
31299fae6e Force canary bump in @now/next 2019-02-12 20:59:42 -06:00
Connor Davis
4bac0db379 fix @now/build-utils publishing tmp/ 2019-02-12 20:35:12 -06:00
Connor Davis
95e7d459d3 Increase timeout on now-build-utils 2019-02-12 19:39:15 -06:00
Antonio Nuno Monteiro
dd120b8d20 [now-rist] Implement simpler API for *.rs entrypoints (#210) 2019-02-12 13:21:03 -08:00
Mike Engel
b6975676e5 [now-rust] Import Deserialize traits behind de and ser (#203)
See https://github.com/serde-rs/serde/issues/1441
2019-02-12 13:16:14 -08:00
Igor Klopov
a7951dae81 [tests] produce public deployments 2019-02-12 22:07:10 +03:00
Connor Davis
b0c918f7fb Publish
- @now/next@0.0.85-canary.2
2019-02-11 20:28:52 -06:00
Connor Davis
df54dc7dc9 Download Entire Directory when Building now-next (#200) 2019-02-11 20:23:26 -06:00
paulogdm
0dd801ff6c Update now-next-legacy-mode.md (#208)
* Update now-next-legacy-mode.md

From `canary` to `latest`.

* Update now-next-no-serverless-pages-built.md

From `canary` to `latest`

* Apply suggestions from code review

Better words...

Co-Authored-By: paulogdm <paulogdemitri@gmail.com>
2019-02-11 14:41:42 +01:00
Connor Davis
398743ef95 Fix tests not completing 2019-02-08 20:03:32 -06:00
Nathan Rajlich
337c74b81b Publish
- @now/bash@0.1.2
2019-02-08 15:28:30 -08:00
Nathan Rajlich
680bb82ec3 [now-bash] Use mktemp dry run syntax that is compatible with BSD (#204)
For `now dev` running on a MacOS machine, some of our builders will need
to be adjusted to be "platform-aware".

In this case, `mktemp` is provided by BSD instead of GNU, which doesn't
support the long form `--dry-run` flag, however both support the `-u`
flag which does the same thing.
2019-02-08 15:25:51 -08:00
Nathan Rajlich
17ed5411e3 Publish
- @now/rust@0.0.2
2019-02-06 12:58:51 -08:00
Nathan Rajlich
d9bbcb6939 Publish
- @now/rust@0.0.2-canary.2
 - @now/static-build@0.4.19-canary.0
2019-02-05 12:06:03 -08:00
Igor Klopov
800e4de76f [tests] lint fixes 2019-02-05 22:35:18 +03:00
António Nuno Monteiro
864dd468d9 Place generated @now/rust lambdas in the location that matches their entrypoint (#198) 2019-02-05 11:31:41 -08:00
Igor Klopov
ba833871bb [tests] merge build.env from now.json 2019-02-05 22:15:02 +03:00
Arunoda Susiripal
e732bac78e @now/static-build@0.4.18 2019-02-05 02:27:26 +00:00
Arunoda Susiripal
28ea4015b4 Publish
- @now/static-build@0.4.18-canary.3
2019-02-05 02:12:15 +00:00
Arunoda Susiripal
a93d97cabd Use proper test code for now-static-build 2019-02-05 02:10:25 +00:00
Arunoda Susiripal
67f39f7c9b Publish
- @now/static-build@0.4.18-canary.2
2019-02-05 01:58:26 +00:00
Arunoda Susiripal
acd793b9e9 Fix a typo in a message of now-static-build 2019-02-05 01:57:27 +00:00
Arunoda Susiripal
f74d61279d Publish
- @now/static-build@0.4.18-canary.1
2019-02-05 01:50:45 +00:00
Arunoda Susiripala
fcb8eacec0 Check for distDir existence inside now-static-build (#197)
* Throw in case we don't see the distDir

* Add test cases.
2019-02-05 07:18:30 +05:30
Nathan Rajlich
c8fca2ba72 Publish
- @now/build-utils@0.4.35-canary.0
 - @now/rust@0.0.2-canary.1
2019-02-04 14:44:28 -08:00
António Nuno Monteiro
4feffa13eb Fix @now/rust for nested paths (#194)
* Fix `@now/rust` for nested paths

* fix bug

* fs-extra

* no need for mkdirp or rimraf anymore
2019-02-04 14:40:41 -08:00
Igor Klopov
3e330b25f4 Publish 2019-02-04 21:38:37 +03:00
Igor Klopov
9b2cae33af [build-utils] removed in-memory yarn cache 2019-02-04 19:45:46 +03:00
António Nuno Monteiro
4b6371530c Publish initial version to crates.io (#193) 2019-02-01 00:11:45 +01:00
Nathan Rajlich
9e1d577fc0 Publish
- @now/bash@0.1.2-canary.0
 - @now/build-utils@0.4.33-canary.2
 - @now/cgi@0.0.16-canary.0
 - @now/go@0.2.13-canary.0
 - @now/html-minifier@1.0.8-canary.0
 - @now/lambda@0.4.10-canary.0
 - @now/md@0.4.10-canary.0
 - @now/mdx-deck@0.4.19-canary.0
 - @now/next@0.0.85-canary.1
 - @now/node-bridge@0.1.11-canary.0
 - @now/node-server@0.4.27-canary.2
 - @now/node@0.4.29-canary.2
 - @now/optipng@0.4.9-canary.0
 - @now/php-bridge@0.4.14-canary.0
 - @now/php@0.4.14-canary.0
 - @now/python@0.0.41-canary.0
 - @now/rust@0.0.2-canary.0
 - @now/static-build@0.4.18-canary.0
 - @now/wordpress@0.4.15-canary.0
2019-01-31 14:32:48 -08:00
António Nuno Monteiro
cf2f542c71 Initial import of @now/rust (#191)
* Fix docstrings, remove unused rust dev dependency

* Add support for multiple binaries, prepareCache too

* Remove context from the handler signature, abstract over the AWS error

* Fix Rust tarball URL, caching and lint

* GCC URL
2019-01-31 22:29:50 +00:00
Igor Klopov
e608861e4e [now-node] bump ncc 2019-01-31 02:52:37 +03:00
Steven
a99b999209 Remove @now/build-utils peer dependency (#187)
* Remove peer dependencies

* Suppress eslint warning
2019-01-29 15:18:36 -05:00
Steven
fd9c6e7847 Add license to satisify warning (#186)
This solves the warning seen in `/_logs` all the time:

```
warning package.json: No license field
```
2019-01-29 12:59:25 -05:00
Steven
b2ad3a6147 Add repository key to package.json (#185) 2019-01-29 17:52:01 +01:00
Steven
997d3c2a30 Add link to builder docs (#184) 2019-01-29 17:24:54 +01:00
Igor Klopov
ca575bf0a6 Publish
- @now/next@0.0.85-canary.0
 - @now/node-server@0.4.27-canary.1
 - @now/node@0.4.29-canary.1
2019-01-29 19:09:01 +03:00
Igor Klopov
4c2e93ccef [now-node] bump ncc 2019-01-29 18:28:13 +03:00
paulogdm
4d6437d235 Fix broken error link (#175)
* Fix broken error link

* Added legacy mode error

Standard "Next.js" for all citations of Next.
Add "Legacy Mode" error.

* Update no serverless pages built doc

* Update doc on now/next legacy mode

* Add link after the error message
2019-01-28 19:05:02 +01:00
Igor Klopov
0d8058d062 Publish
- @now/node-server@0.4.27-canary.0
 - @now/node@0.4.29-canary.0
2019-01-27 21:58:47 +03:00
Igor Klopov
2b5cdfc0a7 [now-node] bump ncc 2019-01-27 15:27:00 +03:00
Igor Klopov
69a41f78fb Publish
- @now/build-utils@0.4.33-canary.1
2019-01-26 00:59:33 +03:00
Igor Klopov
a013d59d62 [build-utils] yarn in linux calls chown. mock it as well 2019-01-26 00:11:09 +03:00
Igor Klopov
173a29cfdb Publish
- @now/build-utils@0.4.33-canary.0
2019-01-25 02:51:46 +03:00
Igor Klopov
3f73451311 [build-utils] lint fixes 2019-01-25 00:55:32 +03:00
Igor Klopov
2fc706be43 [build-utils] make in-memory cache support git deps 2019-01-25 00:41:33 +03:00
Igor Klopov
0fb7eb6093 [build-utils] don't print download time of individual files 2019-01-24 18:44:10 +03:00
Igor Klopov
aa43c0bc87 [tests] consider now's 404 a case to retry 2019-01-23 18:43:55 +03:00
Igor Klopov
3c5925a6e3 [tests] print what failed page contains 2019-01-23 01:14:01 +03:00
Igor Klopov
9fc7b047f5 [tests] buildDelegate argument for testDeployment 2019-01-23 00:14:43 +03:00
Igor Klopov
ecae29457f Revert "[lerna] trying to set the order of package to publish"
This reverts commit 79251ad180.
2019-01-20 03:35:23 +03:00
270 changed files with 10638 additions and 3813 deletions

12
.circleci/build.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
set -euo pipefail
circleci_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
needs_build="$(grep -rn '"build"' packages/*/package.json | cut -d: -f1)"
for pkg in $needs_build; do
dir="$(dirname "$pkg")"
cd "$circleci_dir/../$dir"
echo "Building \`$dir\`"
yarn build
done

View File

@@ -20,21 +20,21 @@ jobs:
- run:
name: Bootstrapping
command: yarn bootstrap
- run:
name: Building
command: ./.circleci/build.sh
- run:
name: Linting
command: yarn lint
- run:
name: Tests
command: yarn test
name: Tests and Coverage
command: yarn test-coverage
- run:
name: Potentially save npm token
command: "([[ ! -z $NPM_TOKEN ]] && echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" >> ~/.npmrc) || echo \"Did not write npm token\""
- run:
name: Potentially publish canary release
command: "if ls ~/.npmrc >/dev/null 2>&1 && [[ $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --npm-tag canary --yes; else echo \"Did not publish\"; fi"
- run:
name: Potentially publish stable release
command: "if ls ~/.npmrc >/dev/null 2>&1 && [[ ! $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --yes; else echo \"Did not publish\"; fi"
name: Potentially publish releases to npm
command: ./.circleci/publish.sh
workflows:
version: 2
build-and-deploy:

24
.circleci/publish.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
set -euo pipefail
if [ ! -e ~/.npmrc ]; then
echo "~/.npmrc file does not exist, skipping publish"
exit 0
fi
npm_tag=""
tag="$(git describe --tags --exact-match 2> /dev/null || :)"
if [ -z "$tag" ]; then
echo "Not a tagged commit, skipping publish"
exit 0
fi
if [[ "$tag" =~ -canary ]]; then
echo "Publishing canary release"
npm_tag="--npm-tag canary"
else
echo "Publishing stable release"
fi
yarn run lerna publish from-git $npm_tag --yes

View File

@@ -1,3 +1,11 @@
/tmp/*
/node_modules/*
/**/node_modules/*
/packages/now-go/go/*
/packages/now-build-utils/dist/*
/packages/now-node/dist/*
/packages/now-next/dist/*
/packages/now-node-bridge/*
/packages/now-python/*
/packages/now-optipng/dist/*
/packages/now-go/*

View File

@@ -8,7 +8,7 @@
},
"overrides": [
{
"files": ["test/**"],
"files": ["**/test/**"],
"rules": {
"import/no-extraneous-dependencies": 0
},

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
node_modules
tmp
target/
.next

4
.prettierrc.json Normal file
View File

@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "es5"
}

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"eslint.enable": false
}

74
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,74 @@
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [abuse@zeit.co](mailto:abuse@zeit.co). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -2,7 +2,7 @@
This is the full list of official Builders provided by the ZEIT team.
More details here: http://zeit.co/docs
More details here: https://zeit.co/docs/v2/deployments/builders/overview/
### Publishing to npm

View File

@@ -0,0 +1,73 @@
# `@now/next` Legacy Mode
#### Why This Warning Occurred
`@now/next` has two modes: `legacy` and `serverless`. You will always want to use the `serverless` mode. `legacy` is to provide backwards compatibility with previous `@now/next` versions.
The differences:
Legacy:
- Minimal lambda size of `2.2Mb` (approximately)
- Forces `next@v7.0.2-canary.49` and `next-server@v7.0.2-canary.49`
- Forces all `dependencies` to be `devDependencies`
- Loads `next.config.js` on bootup, breaking sometimes when users didn't use `phases` to load files
- Used `next-server` which is the full Next.js server with routing etc.
- Runs `npm install`
- Runs `npm run now-build`
- Runs `npm install --production` after build
Serverless:
- Minimal lambda size of `49Kb` (approximately)
- Uses Next.js build targets (`target: 'serverless'`) in `next.config.js`. [documentation](https://github.com/zeit/next.js#summary)
- Does not make changes to your application dependencies
- Does not load `next.config.js` ([as per the serverless target documentation](https://github.com/zeit/next.js#summary))
- Runs `npm install`
- Runs `npm run now-build`
- Does not run `npm install --production` as the output from the build is all that's needed to bundle lambdas.
- No runtime dependencies, meaning smaller lambda functions
- Optimized for fast [cold start](https://zeit.co/blog/serverless-ssr#cold-start)
#### Possible Ways to Fix It
In order to create the smallest possible lambdas Next.js has to be configured to build for the `serverless` target.
1. Serverless Next.js requires Next.js 8 or later, to upgrade you can install the `latest` version:
```
npm install next --save
```
2. Add the `now-build` script to your `package.json`
```json
{
"scripts": {
"now-build": "next build"
},
}
```
3. Add `target: 'serverless'` to `next.config.js`
```js
module.exports = {
target: 'serverless'
// Other options are still valid
}
```
4. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
```js
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }]
}
```
### Useful Links
- [Serverless target implementation](https://github.com/zeit/now-builders/pull/150)

View File

@@ -0,0 +1,45 @@
# `@now/next` No Serverless Pages Built
#### Why This Error Occurred
This error occurs when you have your application is not configured for Serverless Next.js build output.
#### Possible Ways to Fix It
In order to create the smallest possible lambdas Next.js has to be configured to build for the `serverless` target.
1. Serverless Next.js requires Next.js 8 or later, to upgrade you can install the `latest` version:
```
npm install next --save
```
2. Add the `now-build` script to your `package.json`
```json
{
"scripts": {
"now-build": "next build"
},
}
```
3. Add `target: 'serverless'` to `next.config.js`
```js
module.exports = {
target: 'serverless'
// Other options
}
```
4. Remove `distDir` from `next.config.js` as `@now/next` can't parse this file and expects your build output at `/.next`
5. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
```js
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }]
}
```

View File

@@ -1,4 +1,8 @@
module.exports = {
testEnvironment: 'node',
collectCoverageFrom: ['packages/**/*.{js,jsx}', '!**/node_modules/**'],
collectCoverageFrom: [
'packages/(!test)/**/*.{js,jsx}',
'!**/node_modules/**',
'!**/test/**',
],
};

View File

@@ -2,24 +2,7 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/now-build-utils",
"packages/now-node-bridge",
"packages/now-php-bridge",
"packages/now-bash",
"packages/now-cgi",
"packages/now-go",
"packages/now-html-minifier",
"packages/now-lambda",
"packages/now-md",
"packages/now-mdx-deck",
"packages/now-next",
"packages/now-node",
"packages/now-node-server",
"packages/now-optipng",
"packages/now-php",
"packages/now-python",
"packages/now-static-build",
"packages/now-wordpress"
"packages/*"
],
"command": {
"publish": {

View File

@@ -14,16 +14,23 @@
"bootstrap": "lerna bootstrap",
"publish-stable": "lerna version",
"publish-canary": "lerna version prerelease --preid canary",
"lint": "tsc && eslint .",
"test": "jest --runInBand",
"build": "./.circleci/build.sh",
"lint": "eslint .",
"codecov": "codecov",
"test": "jest --runInBand --verbose",
"test-coverage": "jest --runInBand --verbose --coverage --globals \"{\\\"coverage\\\":true}\" && codecov",
"lint-staged": "lint-staged"
},
"pre-commit": "lint-staged",
"lint-staged": {
"*.js": [
"prettier --write --single-quote",
"prettier --write",
"eslint --fix",
"git add"
],
"*.ts": [
"prettier --write",
"git add"
]
},
"devDependencies": {
@@ -33,6 +40,7 @@
"@types/node": "^10.12.8",
"async-retry": "1.2.3",
"buffer-replace": "^1.0.0",
"codecov": "^3.2.0",
"eslint": "^5.9.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-config-prettier": "^3.1.0",
@@ -43,7 +51,6 @@
"lint-staged": "^8.0.4",
"node-fetch": "^2.3.0",
"pre-commit": "^1.2.2",
"prettier": "^1.15.2",
"typescript": "^3.1.6"
"prettier": "^1.15.2"
}
}

View File

@@ -7,7 +7,9 @@ export IMPORT_CACHE="$LAMBDA_TASK_ROOT/.import-cache"
export PATH="$IMPORT_CACHE/bin:$PATH"
# Load `import` and runtime
# shellcheck disable=SC1090
. "$(which import)"
# shellcheck disable=SC1090
. "$IMPORT_CACHE/runtime.sh"
# Load user code and process events in a loop forever

View File

@@ -1,10 +1,10 @@
const execa = require('execa');
const { join } = require('path');
const snakeCase = require('snake-case');
const glob = require('@now/build-utils/fs/glob');
const download = require('@now/build-utils/fs/download');
const { createLambda } = require('@now/build-utils/lambda');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory');
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
exports.config = {
maxLambdaSize: '10mb',
@@ -12,9 +12,10 @@ exports.config = {
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({ files, entrypoint, config }) => {
exports.build = async ({
workPath, files, entrypoint, config,
}) => {
const srcDir = await getWritableDirectory();
const workDir = await getWritableDirectory();
console.log('downloading files...');
await download(files, srcDir);
@@ -24,7 +25,7 @@ exports.build = async ({ files, entrypoint, config }) => {
return o;
}, {});
const IMPORT_CACHE = `${workDir}/.import-cache`;
const IMPORT_CACHE = `${workPath}/.import-cache`;
const env = Object.assign({}, process.env, configEnv, {
PATH: `${IMPORT_CACHE}/bin:${process.env.PATH}`,
IMPORT_CACHE,
@@ -37,12 +38,12 @@ exports.build = async ({ files, entrypoint, config }) => {
await execa(builderPath, [entrypoint], {
env,
cwd: workDir,
cwd: workPath,
stdio: 'inherit',
});
const lambda = await createLambda({
files: await glob('**', workDir),
files: await glob('**', workPath),
handler: entrypoint, // not actually used in `bootstrap`
runtime: 'provided',
environment: Object.assign({}, configEnv, {

View File

@@ -1,10 +1,15 @@
{
"name": "@now/bash",
"version": "0.1.1",
"version": "0.2.0",
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
"main": "index.js",
"author": "Nathan Rajlich <nate@zeit.co>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-bash"
},
"files": [
"builder.sh",
"runtime.sh",
@@ -15,8 +20,5 @@
"dependencies": {
"execa": "^1.0.0",
"snake-case": "^2.1.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,3 +1,4 @@
#!/bin/bash
import "static-binaries@1.0.0"
static_binaries jq
@@ -13,12 +14,14 @@ _lambda_runtime_api() {
_lambda_runtime_init() {
# Initialize user code
# shellcheck disable=SC1090
. "$SCRIPT_FILENAME" || {
local exit_code="$?"
local error
error='{"exitCode":'"$exit_code"'}'
local error_message="Initialization failed for '$SCRIPT_FILENAME' (exit code $exit_code)"
echo "$error_message" >&2
local error='{"errorMessage":"'"$error_message"'"}'
_lambda_runtime_api "init/error" -X POST -d "$error"
exit "$EXIT_CODE"
exit "$exit_code"
}
# Process events
@@ -45,18 +48,17 @@ _lambda_runtime_next() {
local body
body="$(mktemp)"
local exit_code=0
REQUEST="$event"
# Stdin of the `handler` function is the HTTP request body.
# Need to use a fifo here instead of bash <() because Lambda
# errors with "/dev/fd/63 not found" for some reason :/
local stdin
stdin="$(mktemp --dry-run)"
stdin="$(mktemp -u)"
mkfifo "$stdin"
_lambda_runtime_body "$event" > "$stdin" &
_lambda_runtime_body < "$event" > "$stdin" &
local exit_code=0
handler "$event" < "$stdin" > "$body" || exit_code="$?"
rm -f "$event" "$stdin"
if [ "$exit_code" -eq 0 ]; then
@@ -68,18 +70,21 @@ _lambda_runtime_next() {
| _lambda_runtime_api "invocation/$request_id/response" -X POST -d @- > /dev/null
rm -f "$body" "$_HEADERS"
else
echo "\`handler\` function return code: $exit_code"
_lambda_runtime_api "invocation/$request_id/error" -X POST -d @- > /dev/null <<< '{"exitCode":'"$exit_code"'}'
local error_message="Invocation failed for 'handler' function in '$SCRIPT_FILENAME' (exit code $exit_code)"
echo "$error_message" >&2
_lambda_runtime_api "invocation/$request_id/error" -X POST -d '{"errorMessage":"'"$error_message"'"}' > /dev/null
fi
}
_lambda_runtime_body() {
if [ "$(jq --raw-output '.body | type' < "$1")" = "string" ]; then
if [ "$(jq --raw-output '.encoding' < "$1")" = "base64" ]; then
jq --raw-output '.body' < "$1" | base64 -d
local event
event="$(cat)"
if [ "$(jq --raw-output '.body | type' <<< "$event")" = "string" ]; then
if [ "$(jq --raw-output '.encoding' <<< "$event")" = "base64" ]; then
jq --raw-output '.body' <<< "$event" | base64 --decode
else
# assume plain-text body
jq --raw-output '.body' < "$1"
jq --raw-output '.body' <<< "$event"
fi
fi
}
@@ -97,7 +102,10 @@ http_response_header() {
local value="$2"
local tmp
tmp="$(mktemp)"
jq --arg name "$name" --arg value "$value" '.[$name] = $value' < "$_HEADERS" > "$tmp"
jq \
--arg name "$name" \
--arg value "$value" \
'.[$name] = $value' < "$_HEADERS" > "$tmp"
mv -f "$tmp" "$_HEADERS"
}

3
packages/now-build-utils/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
dist
test/symlinks-out
test/symlinks.zip

View File

@@ -1 +1,3 @@
/src
/test
tmp

View File

@@ -1,33 +1 @@
const assert = require('assert');
const intoStream = require('into-stream');
class FileBlob {
constructor({ mode = 0o100644, data }) {
assert(typeof mode === 'number');
assert(typeof data === 'string' || Buffer.isBuffer(data));
this.type = 'FileBlob';
this.mode = mode;
this.data = data;
}
static async fromStream({ mode = 0o100644, stream }) {
assert(typeof mode === 'number');
assert(typeof stream.pipe === 'function'); // is-stream
const chunks = [];
await new Promise((resolve, reject) => {
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
stream.on('error', error => reject(error));
stream.on('end', () => resolve());
});
const data = Buffer.concat(chunks);
return new FileBlob({ mode, data });
}
toStream() {
return intoStream(this.data);
}
}
module.exports = FileBlob;
module.exports = require('./dist/index').FileBlob;

View File

@@ -1,88 +1 @@
const assert = require('assert');
const fs = require('fs-extra');
const multiStream = require('multistream');
const path = require('path');
const Sema = require('async-sema');
/** @typedef {{[filePath: string]: FileFsRef}} FsFiles */
const semaToPreventEMFILE = new Sema(30);
/**
* @constructor
* @argument {Object} options
* @argument {number} [options.mode=0o100644]
* @argument {string} options.fsPath
*/
class FileFsRef {
constructor({ mode = 0o100644, fsPath }) {
assert(typeof mode === 'number');
assert(typeof fsPath === 'string');
/** @type {string} */
this.type = 'FileFsRef';
/** @type {number} */
this.mode = mode;
/** @type {string} */
this.fsPath = fsPath;
}
/**
* @argument {Object} options
* @argument {number} [options.mode=0o100644]
* @argument {NodeJS.ReadableStream} options.stream
* @argument {string} options.fsPath
* @returns {Promise<FileFsRef>}
*/
static async fromStream({ mode = 0o100644, stream, fsPath }) {
assert(typeof mode === 'number');
assert(typeof stream.pipe === 'function'); // is-stream
assert(typeof fsPath === 'string');
await fs.mkdirp(path.dirname(fsPath));
await new Promise((resolve, reject) => {
const dest = fs.createWriteStream(fsPath);
stream.pipe(dest);
stream.on('error', reject);
dest.on('finish', resolve);
dest.on('error', reject);
});
await fs.chmod(fsPath, mode.toString(8).slice(-3));
return new FileFsRef({ mode, fsPath });
}
/**
* @returns {Promise<NodeJS.ReadableStream>}
*/
async toStreamAsync() {
await semaToPreventEMFILE.acquire();
const release = () => semaToPreventEMFILE.release();
const stream = fs.createReadStream(this.fsPath);
stream.on('close', release);
stream.on('error', release);
return stream;
}
/**
* @returns {NodeJS.ReadableStream}
*/
toStream() {
let flag;
// eslint-disable-next-line consistent-return
return multiStream((cb) => {
if (flag) return cb(null, null);
flag = true;
this.toStreamAsync()
.then((stream) => {
cb(null, stream);
})
.catch((error) => {
cb(error, null);
});
});
}
}
module.exports = FileFsRef;
module.exports = require('./dist/index').FileFsRef;

View File

@@ -1,96 +1 @@
const assert = require('assert');
const fetch = require('node-fetch');
const multiStream = require('multistream');
const retry = require('async-retry');
const Sema = require('async-sema');
/** @typedef {{[filePath: string]: FileRef}} Files */
const semaToDownloadFromS3 = new Sema(10);
class BailableError extends Error {
constructor(...args) {
super(...args);
/** @type {boolean} */
this.bail = false;
}
}
/**
* @constructor
* @argument {Object} options
* @argument {number} [options.mode=0o100644]
* @argument {string} options.digest
*/
class FileRef {
constructor({ mode = 0o100644, digest }) {
assert(typeof mode === 'number');
assert(typeof digest === 'string');
/** @type {string} */
this.type = 'FileRef';
/** @type {number} */
this.mode = mode;
/** @type {string} */
this.digest = digest;
}
/**
* @returns {Promise<NodeJS.ReadableStream>}
*/
async toStreamAsync() {
let url;
// sha:24be087eef9fac01d61b30a725c1a10d7b45a256
const digestParts = this.digest.split(':');
if (digestParts[0] === 'sha') {
// url = `https://s3.amazonaws.com/now-files/${digestParts[1]}`;
url = `https://dmmcy0pwk6bqi.cloudfront.net/${digestParts[1]}`;
}
assert(url);
await semaToDownloadFromS3.acquire();
console.time(`downloading ${url}`);
try {
return await retry(
async () => {
const resp = await fetch(url);
if (!resp.ok) {
const error = new BailableError(
`download: ${resp.status} ${resp.statusText} for ${url}`,
);
if (resp.status === 403) error.bail = true;
throw error;
}
return resp.body;
},
{ factor: 1, retries: 3 },
);
} finally {
console.timeEnd(`downloading ${url}`);
semaToDownloadFromS3.release();
}
}
/**
* @returns {NodeJS.ReadableStream}
*/
toStream() {
let flag;
// eslint-disable-next-line consistent-return
return multiStream((cb) => {
if (flag) return cb(null, null);
flag = true;
this.toStreamAsync()
.then((stream) => {
cb(null, stream);
})
.catch((error) => {
cb(error, null);
});
});
}
}
module.exports = FileRef;
module.exports = require('./dist/index').FileRef;

View File

@@ -1,94 +0,0 @@
const MemoryFileSystem = require('memory-fs');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const yarnPath = spawnSync('which', ['yarn'])
.stdout.toString()
.trim();
const cachePath = spawnSync(yarnPath, ['cache', 'dir'])
.stdout.toString()
.trim();
spawnSync(yarnPath, ['cache', 'clean']);
const vfs = new MemoryFileSystem();
function isInsideCachePath(filename) {
const relative = path.relative(cachePath, filename);
return !relative.startsWith('..');
}
const saveCreateWriteStream = fs.createWriteStream;
fs.createWriteStream = (...args) => {
const filename = args[0];
if (!isInsideCachePath(filename)) {
return saveCreateWriteStream.call(fs, ...args);
}
vfs.mkdirpSync(path.dirname(filename));
fs.writeFileSync(filename, Buffer.alloc(0));
const stream = vfs.createWriteStream(...args);
stream.on('finish', () => {
setTimeout(() => {
stream.emit('close');
});
});
return stream;
};
const saveReadFile = fs.readFile;
fs.readFile = (...args) => {
const filename = args[0];
if (!isInsideCachePath(filename)) {
return saveReadFile.call(fs, ...args);
}
const callback = args[args.length - 1];
return vfs.readFile(...args.slice(0, -1), (error, result) => {
if (error) {
saveReadFile.call(fs, ...args);
return;
}
callback(error, result);
});
};
const saveCopyFile = fs.copyFile;
fs.copyFile = (...args) => {
const src = args[0];
const dest = args[1];
const callback = args[args.length - 1];
if (isInsideCachePath(src) && !isInsideCachePath(dest)) {
const buffer = vfs.readFileSync(src);
return fs.writeFile(dest, buffer, callback);
}
if (!isInsideCachePath(src) && isInsideCachePath(dest)) {
const buffer = fs.readFileSync(src);
vfs.mkdirpSync(path.dirname(dest));
fs.writeFileSync(dest, Buffer.alloc(0));
return vfs.writeFile(dest, buffer, callback);
}
return saveCopyFile.call(fs, ...args);
};
const saveWriteFile = fs.writeFile;
fs.writeFile = (...args) => {
const filename = args[0];
if (!isInsideCachePath(filename)) {
return saveWriteFile.call(fs, ...args);
}
vfs.mkdirpSync(path.dirname(filename));
fs.writeFileSync(filename, Buffer.alloc(0));
return vfs.writeFile(...args);
};
require(yarnPath);

View File

@@ -1,38 +1 @@
const path = require('path');
const FileFsRef = require('../file-fs-ref.js');
/** @typedef {import('../file-ref')} FileRef */
/** @typedef {import('../file-fs-ref')} FileFsRef */
/** @typedef {{[filePath: string]: FileRef|FileFsRef}} Files */
/** @typedef {{[filePath: string]: FileFsRef}|{}} DownloadedFiles */
/**
* @param {FileRef|FileFsRef} file
* @param {string} fsPath
* @returns {Promise<FileFsRef>}
*/
async function downloadFile(file, fsPath) {
const { mode } = file;
const stream = file.toStream();
return FileFsRef.fromStream({ mode, stream, fsPath });
}
/**
* Download files to disk
* @argument {Files} files
* @argument {string} basePath
* @returns {Promise<DownloadedFiles>}
*/
module.exports = async function download(files, basePath) {
const files2 = {};
await Promise.all(
Object.keys(files).map(async (name) => {
const file = files[name];
const fsPath = path.join(basePath, name);
files2[name] = await downloadFile(file, fsPath);
}),
);
return files2;
};
module.exports = require('../dist/fs/download').default;

View File

@@ -1,12 +1 @@
const path = require('path');
const fs = require('fs-extra');
const prod = process.env.AWS_EXECUTION_ENV || process.env.X_GOOGLE_CODE_LOCATION;
const TMP_PATH = prod ? '/tmp' : path.join(__dirname, 'tmp');
module.exports = async function getWritableDirectory() {
const name = Math.floor(Math.random() * 0x7fffffff).toString(16);
const directory = path.join(TMP_PATH, name);
await fs.mkdirp(directory);
return directory;
};
module.exports = require('../dist/fs/get-writable-directory').default;

View File

@@ -1,67 +1 @@
const assert = require('assert');
const path = require('path');
const vanillaGlob = require('glob');
const FileFsRef = require('../file-fs-ref.js');
/** @typedef {import('fs').Stats} Stats */
/** @typedef {import('glob').IOptions} GlobOptions */
/** @typedef {import('../file-fs-ref').FsFiles|{}} GlobFiles */
/**
* @argument {string} pattern
* @argument {GlobOptions|string} opts
* @argument {string} [mountpoint]
* @returns {Promise<GlobFiles>}
*/
module.exports = function glob(pattern, opts = {}, mountpoint) {
return new Promise((resolve, reject) => {
/** @type {GlobOptions} */
let options;
if (typeof opts === 'string') {
options = { cwd: opts };
} else {
options = opts;
}
if (!options.cwd) {
throw new Error(
'Second argument (basePath) must be specified for names of resulting files',
);
}
if (!path.isAbsolute(options.cwd)) {
throw new Error(`basePath/cwd must be an absolute path (${options.cwd})`);
}
options.statCache = {};
options.stat = true;
options.dot = true;
// eslint-disable-next-line consistent-return
vanillaGlob(pattern, options, (error, files) => {
if (error) return reject(error);
resolve(
files.reduce((files2, relativePath) => {
const fsPath = path.join(options.cwd, relativePath);
/** @type {Stats|any} */
const stat = options.statCache[fsPath];
assert(
stat,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`,
);
if (stat && stat.isFile()) {
let finalPath = relativePath;
if (mountpoint) finalPath = path.join(mountpoint, finalPath);
return {
...files2,
[finalPath]: new FileFsRef({ mode: stat.mode, fsPath }),
};
}
return files2;
}, {}),
);
});
});
};
module.exports = require('../dist/fs/glob').default;

View File

@@ -1,25 +1 @@
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef { import('@now/build-utils/file-fs-ref') } FileFsRef */
/** @typedef {{[filePath: string]: FileRef|FileFsRef}} Files */
/**
* @callback delegate
* @argument {string} name
* @returns {string}
*/
/**
* Rename files using delegate function
* @argument {Files} files
* @argument {delegate} delegate
* @returns {Files}
*/
module.exports = function rename(files, delegate) {
return Object.keys(files).reduce(
(newFiles, name) => ({
...newFiles,
[delegate(name)]: files[name],
}),
{},
);
};
module.exports = require('../dist/fs/rename').default;

View File

@@ -1,108 +1 @@
const assert = require('assert');
const fs = require('fs-extra');
const path = require('path');
const { spawn } = require('child_process');
const prod = process.env.AWS_EXECUTION_ENV
|| process.env.X_GOOGLE_CODE_LOCATION;
function spawnAsync(command, args, cwd) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: 'inherit', cwd });
child.on('error', reject);
child.on('close', (code, signal) => (code !== 0
? reject(new Error(`Exited with ${code || signal}`))
: resolve()));
});
}
async function runShellScript(fsPath) {
assert(path.isAbsolute(fsPath));
const destPath = path.dirname(fsPath);
await spawnAsync(`./${path.basename(fsPath)}`, [], destPath);
return true;
}
async function scanParentDirs(destPath, scriptName) {
assert(path.isAbsolute(destPath));
let hasScript = false;
let hasPackageLockJson = false;
let currentDestPath = destPath;
// eslint-disable-next-line no-constant-condition
while (true) {
const packageJsonPath = path.join(currentDestPath, 'package.json');
// eslint-disable-next-line no-await-in-loop
if (await fs.exists(packageJsonPath)) {
// eslint-disable-next-line no-await-in-loop
const packageJson = JSON.parse(await fs.readFile(packageJsonPath));
hasScript = Boolean(
packageJson.scripts && scriptName && packageJson.scripts[scriptName],
);
// eslint-disable-next-line no-await-in-loop
hasPackageLockJson = await fs.exists(
path.join(currentDestPath, 'package-lock.json'),
);
break;
}
const newDestPath = path.dirname(currentDestPath);
if (currentDestPath === newDestPath) break;
currentDestPath = newDestPath;
}
return { hasScript, hasPackageLockJson };
}
async function installDependencies(destPath, args = []) {
assert(path.isAbsolute(destPath));
let commandArgs = args;
console.log(`installing to ${destPath}`);
const { hasPackageLockJson } = await scanParentDirs(destPath);
if (hasPackageLockJson) {
commandArgs = args.filter(a => a !== '--prefer-offline');
await spawnAsync('npm', ['install'].concat(commandArgs), destPath);
await spawnAsync('npm', ['cache', 'clean', '--force'], destPath);
} else if (prod) {
console.log('using memory-fs for yarn cache');
await spawnAsync(
'node',
[path.join(__dirname, 'bootstrap-yarn.js'), '--cwd', destPath].concat(
commandArgs,
),
destPath,
);
} else {
await spawnAsync('yarn', ['--cwd', destPath].concat(commandArgs), destPath);
await spawnAsync('yarn', ['cache', 'clean'], destPath);
}
}
async function runPackageJsonScript(destPath, scriptName) {
assert(path.isAbsolute(destPath));
const { hasScript, hasPackageLockJson } = await scanParentDirs(
destPath,
scriptName,
);
if (!hasScript) return false;
if (hasPackageLockJson) {
console.log(`running "npm run ${scriptName}"`);
await spawnAsync('npm', ['run', scriptName], destPath);
} else {
console.log(`running "yarn run ${scriptName}"`);
await spawnAsync('yarn', ['--cwd', destPath, 'run', scriptName], destPath);
}
return true;
}
module.exports = {
runShellScript,
installDependencies,
runNpmInstall: installDependencies,
runPackageJsonScript,
};
module.exports = require('../dist/fs/run-user-scripts');

View File

@@ -1,4 +1 @@
const fastStreamToBuffer = require('fast-stream-to-buffer');
const { promisify } = require('util');
module.exports = promisify(fastStreamToBuffer);
module.exports = require('../dist/fs/stream-to-buffer').default;

View File

@@ -1,60 +1 @@
const assert = require('assert');
const Sema = require('async-sema');
const { ZipFile } = require('yazl');
const streamToBuffer = require('./fs/stream-to-buffer.js');
class Lambda {
constructor({
zipBuffer, handler, runtime, environment,
}) {
this.type = 'Lambda';
this.zipBuffer = zipBuffer;
this.handler = handler;
this.runtime = runtime;
this.environment = environment;
}
}
const sema = new Sema(10);
const mtime = new Date(1540000000000);
async function createLambda({
files, handler, runtime, environment = {},
}) {
assert(typeof files === 'object', '"files" must be an object');
assert(typeof handler === 'string', '"handler" is not a string');
assert(typeof runtime === 'string', '"runtime" is not a string');
assert(typeof environment === 'object', '"environment" is not an object');
await sema.acquire();
try {
const zipFile = new ZipFile();
const zipBuffer = await new Promise((resolve, reject) => {
Object.keys(files)
.sort()
.forEach((name) => {
const file = files[name];
const stream = file.toStream();
stream.on('error', reject);
zipFile.addReadStream(stream, name, { mode: file.mode, mtime });
});
zipFile.end();
streamToBuffer(zipFile.outputStream).then(resolve).catch(reject);
});
return new Lambda({
zipBuffer,
handler,
runtime,
environment,
});
} finally {
sema.release();
}
}
module.exports = {
Lambda,
createLambda,
};
module.exports = require('./dist/index');

View File

@@ -1,20 +1,41 @@
{
"name": "@now/build-utils",
"version": "0.4.32",
"version": "0.5.2-canary.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-build-utils"
},
"dependencies": {
"@types/cross-spawn": "6.0.0",
"async-retry": "1.2.3",
"async-sema": "2.1.4",
"fast-stream-to-buffer": "1.0.0",
"cross-spawn": "6.0.5",
"end-of-stream": "1.4.1",
"fs-extra": "7.0.0",
"glob": "7.1.3",
"into-stream": "4.0.0",
"into-stream": "5.0.0",
"memory-fs": "0.4.1",
"multistream": "2.1.1",
"node-fetch": "2.2.0",
"yazl": "2.4.3"
},
"scripts": {
"test": "jest"
"build": "tsc",
"test": "tsc && jest",
"prepublish": "tsc"
},
"devDependencies": {
"@types/async-retry": "^1.2.1",
"@types/end-of-stream": "^1.4.0",
"@types/fs-extra": "^5.0.5",
"@types/glob": "^7.1.1",
"@types/node-fetch": "^2.1.6",
"@types/yazl": "^2.4.1",
"execa": "^1.0.0",
"typescript": "3.3.4000"
}
}

View File

@@ -0,0 +1,46 @@
import assert from 'assert';
import intoStream from 'into-stream';
import { File } from './types';
interface FileBlobOptions {
mode?: number;
data: string | Buffer;
}
interface FromStreamOptions {
mode?: number;
stream: NodeJS.ReadableStream;
}
export default class FileBlob implements File {
public type: 'FileBlob';
public mode: number;
public data: string | Buffer;
constructor({ mode = 0o100644, data }: FileBlobOptions) {
assert(typeof mode === 'number');
assert(typeof data === 'string' || Buffer.isBuffer(data));
this.type = 'FileBlob';
this.mode = mode;
this.data = data;
}
static async fromStream({ mode = 0o100644, stream }: FromStreamOptions) {
assert(typeof mode === 'number');
assert(typeof stream.pipe === 'function'); // is-stream
const chunks: Buffer[] = [];
await new Promise<void>((resolve, reject) => {
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
stream.on('error', error => reject(error));
stream.on('end', () => resolve());
});
const data = Buffer.concat(chunks);
return new FileBlob({ mode, data });
}
toStream(): NodeJS.ReadableStream {
return intoStream(this.data);
}
}

View File

@@ -0,0 +1,90 @@
import assert from 'assert';
import fs from 'fs-extra';
import multiStream from 'multistream';
import path from 'path';
import Sema from 'async-sema';
import { File } from './types';
const semaToPreventEMFILE = new Sema(20);
interface FileFsRefOptions {
mode?: number;
fsPath: string;
}
interface FromStreamOptions {
mode: number;
stream: NodeJS.ReadableStream;
fsPath: string;
}
class FileFsRef implements File {
public type: 'FileFsRef';
public mode: number;
public fsPath: string;
constructor({ mode = 0o100644, fsPath }: FileFsRefOptions) {
assert(typeof mode === 'number');
assert(typeof fsPath === 'string');
this.type = 'FileFsRef';
this.mode = mode;
this.fsPath = fsPath;
}
static async fromFsPath({ mode, fsPath }: FileFsRefOptions): Promise<FileFsRef> {
let m = mode;
if (!m) {
const stat = await fs.lstat(fsPath);
m = stat.mode;
}
return new FileFsRef({ mode: m, fsPath });
}
static async fromStream({ mode = 0o100644, stream, fsPath }: FromStreamOptions): Promise<FileFsRef> {
assert(typeof mode === 'number');
assert(typeof stream.pipe === 'function'); // is-stream
assert(typeof fsPath === 'string');
await fs.mkdirp(path.dirname(fsPath));
await new Promise<void>((resolve, reject) => {
const dest = fs.createWriteStream(fsPath, {
mode: mode & 0o777
});
stream.pipe(dest);
stream.on('error', reject);
dest.on('finish', resolve);
dest.on('error', reject);
});
return new FileFsRef({ mode, fsPath });
}
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
await semaToPreventEMFILE.acquire();
const release = () => semaToPreventEMFILE.release();
const stream = fs.createReadStream(this.fsPath);
stream.on('close', release);
stream.on('error', release);
return stream;
}
toStream(): NodeJS.ReadableStream {
let flag = false;
// eslint-disable-next-line consistent-return
return multiStream((cb) => {
if (flag) return cb(null, null);
flag = true;
this.toStreamAsync()
.then((stream) => {
cb(null, stream);
})
.catch((error) => {
cb(error, null);
});
});
}
}
export = FileFsRef;

View File

@@ -0,0 +1,93 @@
import assert from 'assert';
import fetch from 'node-fetch';
import multiStream from 'multistream';
import retry from 'async-retry';
import Sema from 'async-sema';
import { File } from './types';
interface FileRefOptions {
mode?: number;
digest: string;
mutable?: boolean;
}
const semaToDownloadFromS3 = new Sema(5);
class BailableError extends Error {
public bail: boolean;
constructor(...args: string[]) {
super(...args);
this.bail = false;
}
}
export default class FileRef implements File {
public type: 'FileRef';
public mode: number;
public digest: string;
public mutable: boolean;
constructor({ mode = 0o100644, digest, mutable = false }: FileRefOptions) {
assert(typeof mode === 'number');
assert(typeof digest === 'string');
assert(typeof mutable === 'boolean');
this.type = 'FileRef';
this.mode = mode;
this.digest = digest;
this.mutable = mutable;
}
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
let url = '';
// sha:24be087eef9fac01d61b30a725c1a10d7b45a256
const digestParts = this.digest.split(':');
if (digestParts[0] === 'sha') {
url = this.mutable
? `https://s3.amazonaws.com/now-files/${digestParts[1]}`
: `https://dmmcy0pwk6bqi.cloudfront.net/${digestParts[1]}`;
} else {
throw new Error('Expected digest to be sha');
}
await semaToDownloadFromS3.acquire();
// console.time(`downloading ${url}`);
try {
return await retry(
async () => {
const resp = await fetch(url);
if (!resp.ok) {
const error = new BailableError(
`download: ${resp.status} ${resp.statusText} for ${url}`,
);
if (resp.status === 403) error.bail = true;
throw error;
}
return resp.body;
},
{ factor: 1, retries: 3 },
);
} finally {
// console.timeEnd(`downloading ${url}`);
semaToDownloadFromS3.release();
}
}
toStream(): NodeJS.ReadableStream {
let flag = false;
// eslint-disable-next-line consistent-return
return multiStream((cb) => {
if (flag) return cb(null, null);
flag = true;
this.toStreamAsync()
.then((stream) => {
cb(null, stream);
})
.catch((error) => {
cb(error, null);
});
});
}
}

View File

@@ -0,0 +1,62 @@
import path from 'path';
import FileFsRef from '../file-fs-ref';
import { File, Files, Meta } from '../types';
import { remove, mkdirp, readlink, symlink } from 'fs-extra';
export interface DownloadedFiles {
[filePath: string]: FileFsRef
}
const S_IFMT = 61440; /* 0170000 type of file */
const S_IFLNK = 40960; /* 0120000 symbolic link */
export function isSymbolicLink(mode: number): boolean {
return (mode & S_IFMT) === S_IFLNK;
}
async function downloadFile(file: File, fsPath: string): Promise<FileFsRef> {
const { mode } = file;
if (mode && isSymbolicLink(mode) && file.type === 'FileFsRef') {
const [ target ] = await Promise.all([
readlink((file as FileFsRef).fsPath),
mkdirp(path.dirname(fsPath))
]);
await symlink(target, fsPath);
return FileFsRef.fromFsPath({ mode, fsPath });
} else {
const stream = file.toStream();
return FileFsRef.fromStream({ mode, stream, fsPath });
}
}
async function removeFile(basePath: string, fileMatched: string) {
const file = path.join(basePath, fileMatched);
await remove(file);
}
export default async function download(files: Files, basePath: string, meta?: Meta): Promise<DownloadedFiles> {
const files2: DownloadedFiles = {};
const { filesChanged = null, filesRemoved = null } = meta || {};
await Promise.all(
Object.keys(files).map(async (name) => {
// If the file does not exist anymore, remove it.
if (Array.isArray(filesRemoved) && filesRemoved.includes(name)) {
await removeFile(basePath, name);
return;
}
// If a file didn't change, do not re-download it.
if (Array.isArray(filesChanged) && !filesChanged.includes(name)) {
return;
}
const file = files[name];
const fsPath = path.join(basePath, name);
files2[name] = await downloadFile(file, fsPath);
}),
);
return files2;
}

View File

@@ -0,0 +1,10 @@
import { join } from 'path';
import { tmpdir } from 'os';
import { mkdirp } from 'fs-extra';
export default async function getWritableDirectory() {
const name = Math.floor(Math.random() * 0x7fffffff).toString(16);
const directory = join(tmpdir(), name);
await mkdirp(directory);
return directory;
}

View File

@@ -0,0 +1,66 @@
import path from 'path';
import assert from 'assert';
import vanillaGlob_ from 'glob';
import { promisify } from 'util';
import { lstat, Stats } from 'fs-extra';
import FileFsRef from '../file-fs-ref';
type GlobOptions = vanillaGlob_.IOptions;
interface FsFiles {
[filePath: string]: FileFsRef
}
const vanillaGlob = promisify(vanillaGlob_);
export default async function glob(pattern: string, opts: GlobOptions | string, mountpoint?: string): Promise<FsFiles> {
let options: GlobOptions;
if (typeof opts === 'string') {
options = { cwd: opts };
} else {
options = opts;
}
if (!options.cwd) {
throw new Error(
'Second argument (basePath) must be specified for names of resulting files',
);
}
if (!path.isAbsolute(options.cwd)) {
throw new Error(`basePath/cwd must be an absolute path (${options.cwd})`);
}
const results: FsFiles = {};
options.symlinks = {};
options.statCache = {};
options.stat = true;
options.dot = true;
const files = await vanillaGlob(pattern, options);
for (const relativePath of files) {
const fsPath = path.join(options.cwd!, relativePath);
let stat: Stats = options.statCache![fsPath] as Stats;
assert(
stat,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`,
);
if (stat.isFile()) {
const isSymlink = options.symlinks![fsPath];
if (isSymlink) {
stat = await lstat(fsPath);
}
let finalPath = relativePath;
if (mountpoint) {
finalPath = path.join(mountpoint, finalPath);
}
results[finalPath] = new FileFsRef({ mode: stat.mode, fsPath });
}
}
return results;
}

View File

@@ -0,0 +1,12 @@
import { Files } from '../types';
type Delegate = (name: string) => string;
export default function rename(files: Files, delegate: Delegate): Files {
return Object.keys(files).reduce(
(newFiles, name) => ({
...newFiles,
[delegate(name)]: files[name],
}),
{},
);
}

View File

@@ -0,0 +1,154 @@
import assert from 'assert';
import fs from 'fs-extra';
import path from 'path';
import spawn from 'cross-spawn';
import { SpawnOptions } from 'child_process';
function spawnAsync(
command: string,
args: string[],
cwd: string,
opts: SpawnOptions = {}
) {
return new Promise<void>((resolve, reject) => {
const stderrLogs: Buffer[] = [];
opts = { stdio: 'inherit', cwd, ...opts };
const child = spawn(command, args, opts);
if (opts.stdio === 'pipe') {
child.stderr.on('data', data => stderrLogs.push(data));
}
child.on('error', reject);
child.on('close', (code, signal) => {
if (code === 0) {
return resolve();
}
const errorLogs = stderrLogs.map(line => line.toString()).join('');
if (opts.stdio !== 'inherit') {
reject(new Error(`Exited with ${code || signal}\n${errorLogs}`));
} else {
reject(new Error(`Exited with ${code || signal}`));
}
});
});
}
async function chmodPlusX(fsPath: string) {
const s = await fs.stat(fsPath);
const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise
if (s.mode === newMode) return;
const base8 = newMode.toString(8).slice(-3);
await fs.chmod(fsPath, base8);
}
export async function runShellScript(fsPath: string) {
assert(path.isAbsolute(fsPath));
const destPath = path.dirname(fsPath);
await chmodPlusX(fsPath);
await spawnAsync(`./${path.basename(fsPath)}`, [], destPath);
return true;
}
async function scanParentDirs(destPath: string, scriptName?: string) {
assert(path.isAbsolute(destPath));
let hasScript = false;
let hasPackageLockJson = false;
let currentDestPath = destPath;
// eslint-disable-next-line no-constant-condition
while (true) {
const packageJsonPath = path.join(currentDestPath, 'package.json');
// eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(packageJsonPath)) {
// eslint-disable-next-line no-await-in-loop
const packageJson = JSON.parse(
await fs.readFile(packageJsonPath, 'utf8')
);
hasScript = Boolean(
packageJson.scripts && scriptName && packageJson.scripts[scriptName]
);
// eslint-disable-next-line no-await-in-loop
hasPackageLockJson = await fs.pathExists(
path.join(currentDestPath, 'package-lock.json')
);
break;
}
const newDestPath = path.dirname(currentDestPath);
if (currentDestPath === newDestPath) break;
currentDestPath = newDestPath;
}
return { hasScript, hasPackageLockJson };
}
export async function installDependencies(
destPath: string,
args: string[] = []
) {
assert(path.isAbsolute(destPath));
let commandArgs = args;
console.log(`installing to ${destPath}`);
const { hasPackageLockJson } = await scanParentDirs(destPath);
const opts = {
env: {
...process.env,
// This is a little hack to force `node-gyp` to build for the
// Node.js version that `@now/node` and `@now/node-server` use
npm_config_target: '8.10.0',
},
stdio: 'pipe',
};
if (hasPackageLockJson) {
commandArgs = args.filter(a => a !== '--prefer-offline');
await spawnAsync(
'npm',
['install'].concat(commandArgs),
destPath,
opts as SpawnOptions
);
} else {
await spawnAsync(
'yarn',
['--cwd', destPath].concat(commandArgs),
destPath,
opts as SpawnOptions
);
}
}
export async function runPackageJsonScript(
destPath: string,
scriptName: string,
opts?: SpawnOptions
) {
assert(path.isAbsolute(destPath));
const { hasScript, hasPackageLockJson } = await scanParentDirs(
destPath,
scriptName
);
if (!hasScript) return false;
if (hasPackageLockJson) {
console.log(`running "npm run ${scriptName}"`);
await spawnAsync('npm', ['run', scriptName], destPath, opts);
} else {
console.log(`running "yarn run ${scriptName}"`);
await spawnAsync(
'yarn',
['--cwd', destPath, 'run', scriptName],
destPath,
opts
);
}
return true;
}
export const runNpmInstall = installDependencies;

View File

@@ -0,0 +1,26 @@
import eos from 'end-of-stream';
export default function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> {
return new Promise<Buffer>((resolve, reject) => {
const buffers: Buffer[] = [];
stream.on('data', buffers.push.bind(buffers))
eos(stream, (err) => {
if (err) {
reject(err);
return;
}
switch (buffers.length) {
case 0:
resolve(Buffer.allocUnsafe(0));
break;
case 1:
resolve(buffers[0]);
break;
default:
resolve(Buffer.concat(buffers));
}
});
});
}

View File

@@ -0,0 +1,34 @@
import FileBlob from './file-blob';
import FileFsRef from './file-fs-ref';
import FileRef from './file-ref';
import { File, Files, AnalyzeOptions, BuildOptions, PrepareCacheOptions, ShouldServeOptions, Meta } from './types';
import { Lambda, createLambda } from './lambda';
import download from './fs/download';
import getWriteableDirectory from './fs/get-writable-directory'
import glob from './fs/glob';
import rename from './fs/rename';
import { installDependencies, runPackageJsonScript, runNpmInstall, runShellScript } from './fs/run-user-scripts';
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
export {
FileBlob,
FileFsRef,
FileRef,
Files,
File,
Meta,
Lambda,
createLambda,
download,
getWriteableDirectory,
glob,
rename,
installDependencies, runPackageJsonScript, runNpmInstall, runShellScript,
streamToBuffer,
AnalyzeOptions,
BuildOptions,
PrepareCacheOptions,
ShouldServeOptions,
shouldServe,
};

View File

@@ -0,0 +1,104 @@
import assert from 'assert';
import Sema from 'async-sema';
import { ZipFile } from 'yazl';
import { readlink } from 'fs-extra';
import { Files } from './types';
import FileFsRef from './file-fs-ref';
import { isSymbolicLink } from './fs/download';
import streamToBuffer from './fs/stream-to-buffer';
interface Environment {
[key: string]: string;
}
interface LambdaOptions {
zipBuffer: Buffer;
handler: string;
runtime: string;
environment: Environment;
}
interface CreateLambdaOptions {
files: Files;
handler: string;
runtime: string;
environment?: Environment;
}
export class Lambda {
public type: 'Lambda';
public zipBuffer: Buffer;
public handler: string;
public runtime: string;
public environment: Environment;
constructor({
zipBuffer, handler, runtime, environment,
}: LambdaOptions) {
this.type = 'Lambda';
this.zipBuffer = zipBuffer;
this.handler = handler;
this.runtime = runtime;
this.environment = environment;
}
}
const sema = new Sema(10);
const mtime = new Date(1540000000000);
export async function createLambda({
files, handler, runtime, environment = {},
}: CreateLambdaOptions): Promise<Lambda> {
assert(typeof files === 'object', '"files" must be an object');
assert(typeof handler === 'string', '"handler" is not a string');
assert(typeof runtime === 'string', '"runtime" is not a string');
assert(typeof environment === 'object', '"environment" is not an object');
await sema.acquire();
try {
const zipBuffer = await createZip(files);
return new Lambda({
zipBuffer,
handler,
runtime,
environment,
});
} finally {
sema.release();
}
}
export async function createZip(files: Files): Promise<Buffer> {
const names = Object.keys(files).sort();
const symlinkTargets = new Map<string, string>();
for (const name of names) {
const file = files[name];
if (file.mode && isSymbolicLink(file.mode) && file.type === 'FileFsRef') {
const symlinkTarget = await readlink((file as FileFsRef).fsPath);
symlinkTargets.set(name, symlinkTarget);
}
}
const zipFile = new ZipFile();
const zipBuffer = await new Promise<Buffer>((resolve, reject) => {
for (const name of names) {
const file = files[name];
const opts = { mode: file.mode, mtime };
const symlinkTarget = symlinkTargets.get(name);
if (typeof symlinkTarget === 'string') {
zipFile.addBuffer(Buffer.from(symlinkTarget, 'utf8'), name, opts);
} else {
const stream = file.toStream() as import('stream').Readable;
stream.on('error', reject);
zipFile.addReadStream(stream, name, opts);
}
}
zipFile.end();
streamToBuffer(zipFile.outputStream).then(resolve).catch(reject);
});
return zipBuffer;
}

View File

@@ -0,0 +1,27 @@
import { parse } from 'path';
import { ShouldServeOptions } from './types';
import FileFsRef from './file-fs-ref';
export default function shouldServe({
entrypoint,
files,
requestPath
}: ShouldServeOptions): boolean {
requestPath = requestPath.replace(/\/$/, ''); // sanitize trailing '/'
entrypoint = entrypoint.replace(/\\/, '/'); // windows compatibility
if (entrypoint === requestPath && hasProp(files, entrypoint)) {
return true;
}
const { dir, name } = parse(entrypoint);
if (name === 'index' && dir === requestPath && hasProp(files, entrypoint)) {
return true;
}
return false;
}
function hasProp(obj: { [path: string]: FileFsRef }, key: string): boolean {
return Object.hasOwnProperty.call(obj, key)
}

View File

@@ -0,0 +1,157 @@
import FileRef from './file-ref';
import FileFsRef from './file-fs-ref';
export interface File {
type: string;
mode: number;
toStream: () => NodeJS.ReadableStream;
/**
* The absolute path to the file in the filesystem
*/
fsPath?: string;
}
export interface Files {
[filePath: string]: File;
}
export interface Config {
[key: string]: string;
}
export interface Meta {
isDev?: boolean;
requestPath?: string;
filesChanged?: string[];
filesRemoved?: string[];
}
export interface AnalyzeOptions {
/**
* All source files of the project
*/
files: {
[filePath: string]: FileRef;
};
/**
* Name of entrypoint file for this particular build job. Value
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
* `entrypoint` is always a discrete file and never a glob, since globs are
* expanded into separate builds at deployment time.
*/
entrypoint: string;
/**
* A writable temporary directory where you are encouraged to perform your
* build process. This directory will be populated with the restored cache.
*/
workPath: string;
/**
* An arbitrary object passed by the user in the build definition defined
* in `now.json`.
*/
config: Config;
}
export interface BuildOptions {
/**
* All source files of the project
*/
files: Files;
/**
* Name of entrypoint file for this particular build job. Value
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
* `entrypoint` is always a discrete file and never a glob, since globs are
* expanded into separate builds at deployment time.
*/
entrypoint: string;
/**
* A writable temporary directory where you are encouraged to perform your
* build process. This directory will be populated with the restored cache.
*/
workPath: string;
/**
* An arbitrary object passed by the user in the build definition defined
* in `now.json`.
*/
config: Config;
/**
* Metadata related to the invoker of the builder, used by `now dev`.
* Builders may use the properties on this object to change behavior based
* on the build environment.
*/
meta?: Meta;
}
export interface PrepareCacheOptions {
/**
* All source files of the project
*/
files: Files;
/**
* Name of entrypoint file for this particular build job. Value
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
* `entrypoint` is always a discrete file and never a glob, since globs are
* expanded into separate builds at deployment time.
*/
entrypoint: string;
/**
* A writable temporary directory where you are encouraged to perform your
* build process.
*/
workPath: string;
/**
* A writable temporary directory where you can build a cache to use for
* the next run.
*/
cachePath: string;
/**
* An arbitrary object passed by the user in the build definition defined
* in `now.json`.
*/
config: Config;
}
export interface ShouldServeOptions {
/**
* A path string from a request.
*/
requestPath: string;
/**
* Name of entrypoint file for this particular build job. Value
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
* `entrypoint` is always a discrete file and never a glob, since globs are
* expanded into separate builds at deployment time.
*/
entrypoint: string;
/**
* All source files of the project
*/
files: {
[path: string]: FileFsRef;
};
/**
* A writable temporary directory where you are encouraged to perform your
* build process. This directory will be populated with the restored cache.
*/
workPath: string;
/**
* An arbitrary object passed by the user in the build definition defined
* in `now.json`.
*/
config: Config;
}

View File

@@ -0,0 +1 @@
contents

View File

@@ -0,0 +1 @@
./a.txt

View File

@@ -1,13 +1,18 @@
/* global beforeAll, expect, it, jest */
const fs = require('fs');
const path = require('path');
const fs = require('fs-extra');
// eslint-disable-next-line import/no-extraneous-dependencies
const execa = require('execa');
const assert = require('assert');
const { glob, download } = require('../');
const { createZip } = require('../dist/lambda');
const {
packAndDeploy,
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const builderUrl = '@canary';
let buildUtilsUrl;
@@ -17,6 +22,48 @@ beforeAll(async () => {
console.log('buildUtilsUrl', buildUtilsUrl);
});
// unit tests
it('should re-create symlinks properly', async () => {
const files = await glob('**', path.join(__dirname, 'symlinks'));
assert.equal(Object.keys(files).length, 2);
const outDir = path.join(__dirname, 'symlinks-out');
await fs.remove(outDir);
const files2 = await download(files, outDir);
assert.equal(Object.keys(files2).length, 2);
const [linkStat, aStat] = await Promise.all([
fs.lstat(path.join(outDir, 'link.txt')),
fs.lstat(path.join(outDir, 'a.txt')),
]);
assert(linkStat.isSymbolicLink());
assert(aStat.isFile());
});
it('should create zip files with symlinks properly', async () => {
const files = await glob('**', path.join(__dirname, 'symlinks'));
assert.equal(Object.keys(files).length, 2);
const outFile = path.join(__dirname, 'symlinks.zip');
await fs.remove(outFile);
const outDir = path.join(__dirname, 'symlinks-out');
await fs.remove(outDir);
await fs.mkdirp(outDir);
await fs.writeFile(outFile, await createZip(files));
await execa('unzip', [outFile], { cwd: outDir });
const [linkStat, aStat] = await Promise.all([
fs.lstat(path.join(outDir, 'link.txt')),
fs.lstat(path.join(outDir, 'a.txt')),
]);
assert(linkStat.isSymbolicLink());
assert(aStat.isFile());
});
// own fixtures
const fixturesPath = path.resolve(__dirname, 'fixtures');

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"declaration": true,
"esModuleInterop": true,
"lib": ["esnext"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "./dist",
"types": ["node"],
"strict": true,
"target": "esnext"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,10 +1,10 @@
const path = require('path');
const { mkdirp, copyFile } = require('fs-extra');
const glob = require('@now/build-utils/fs/glob');
const download = require('@now/build-utils/fs/download');
const { createLambda } = require('@now/build-utils/lambda');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory');
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;

View File

@@ -1,7 +1,12 @@
{
"name": "@now/cgi",
"version": "0.0.15",
"version": "0.1.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-cgi"
},
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"
@@ -16,8 +21,5 @@
"devDependencies": {
"@zeit/best": "0.4.3",
"rmfr": "2.0.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,4 +1,6 @@
node_modules
*.log
launcher
bin
/go
/analyze
*.js
!util/install.js

View File

@@ -0,0 +1,5 @@
*.ts
test
tsconfig.json
package-lock.json
yarn.lock

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
mkdir -p bin
cd util
GOOS=linux GOARCH=amd64 go build get-exported-function-name.go
mv get-exported-function-name ../bin/

View File

@@ -1,23 +0,0 @@
const path = require('path');
const fetch = require('node-fetch');
const tar = require('tar');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const url = 'https://dl.google.com/go/go1.11.1.linux-amd64.tar.gz';
module.exports = async () => {
const res = await fetch(url);
const dir = await getWritableDirectory();
if (!res.ok) {
throw new Error(`Failed to download: ${url}`);
}
return new Promise((resolve, reject) => {
res.body
.on('error', reject)
.pipe(tar.extract({ cwd: dir, strip: 1 }))
.on('finish', () => resolve(path.join(dir, 'bin', 'go')));
});
};

View File

@@ -0,0 +1,151 @@
import tar from 'tar';
import execa from 'execa';
import fetch from 'node-fetch';
import { mkdirp, pathExists } from 'fs-extra';
import { dirname, join } from 'path';
import Debug from 'debug';
const debug = Debug('@now/go:go-helpers');
const archMap = new Map([['x64', 'amd64'], ['x86', '386']]);
const platformMap = new Map([['win32', 'windows']]);
// Location where the `go` binary will be installed after `postinstall`
const GO_DIR = join(__dirname, 'go');
const GO_BIN = join(GO_DIR, 'bin/go');
const getPlatform = (p: string) => platformMap.get(p) || p;
const getArch = (a: string) => archMap.get(a) || a;
const getGoUrl = (version: string, platform: string, arch: string) => {
const goArch = getArch(arch);
const goPlatform = getPlatform(platform);
const ext = platform === 'win32' ? 'zip' : 'tar.gz';
return `https://dl.google.com/go/go${version}.${goPlatform}-${goArch}.${ext}`;
};
export async function getAnalyzedEntrypoint(filePath: string) {
debug('Analyzing entrypoint %o', filePath);
const bin = join(__dirname, 'analyze');
const isAnalyzeExist = await pathExists(bin);
if (!isAnalyzeExist) {
const src = join(__dirname, 'util', 'analyze.go');
const dest = join(__dirname, 'analyze');
const go = await downloadGo();
await go.build(src, dest);
}
const args = [filePath];
const analyzed = await execa.stdout(bin, args);
debug('Analyzed entrypoint %o', analyzed);
return analyzed;
}
// Creates a `$GOPATH` directory tree, as per `go help gopath` instructions.
// Without this, `go` won't recognize the `$GOPATH`.
function createGoPathTree(goPath: string, platform: string, arch: string) {
const tuple = `${getPlatform(platform)}_${getArch(arch)}`;
debug('Creating GOPATH directory structure for %o (%s)', goPath, tuple);
return Promise.all([
mkdirp(join(goPath, 'bin')),
mkdirp(join(goPath, 'pkg', tuple)),
]);
}
class GoWrapper {
private env: { [key: string]: string };
private opts: execa.Options;
constructor(env: { [key: string]: string }, opts: execa.Options = {}) {
if (!opts.cwd) {
opts.cwd = process.cwd();
}
this.env = env;
this.opts = opts;
}
private execute(...args: string[]) {
const { opts, env } = this;
debug('Exec %o', `go ${args.join(' ')}`);
return execa('go', args, { stdio: 'inherit', ...opts, env });
}
mod() {
return this.execute('mod', 'tidy');
}
get(src?: string) {
const args = ['get'];
if (src) {
debug('Fetching `go` dependencies for file %o', src);
args.push(src);
} else {
debug('Fetching `go` dependencies for cwd %o', this.opts.cwd);
}
return this.execute(...args);
}
build(src: string | string[], dest: string, ldsflags = '-s -w') {
debug('Building optimized `go` binary %o -> %o', src, dest);
const sources = Array.isArray(src) ? src : [src];
return this.execute('build', '-ldflags', ldsflags, '-o', dest, ...sources);
}
}
export async function createGo(
goPath: string,
platform = process.platform,
arch = process.arch,
opts: execa.Options = {},
goMod = false
) {
const path = `${dirname(GO_BIN)}:${process.env.PATH}`;
const env: { [key: string]: string } = {
...process.env,
PATH: path,
GOPATH: goPath,
...opts.env,
};
if (goMod) {
env.GO111MODULE = 'on';
}
await createGoPathTree(goPath, platform, arch);
return new GoWrapper(env, opts);
}
export async function downloadGo(
dir = GO_DIR,
version = '1.12',
platform = process.platform,
arch = process.arch
) {
debug('Installing `go` v%s to %o for %s %s', version, dir, platform, arch);
const url = getGoUrl(version, platform, arch);
// if we found GOPATH in ENV, use it
if (process.env.GOPATH !== undefined) {
return createGo(dir, platform, arch);
} else {
const isGoExist = await pathExists(join(dir, 'bin'));
if (!isGoExist) {
debug('Downloading `go` URL: %o', url);
console.log('Downloading Go ...');
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to download: ${url} (${res.status})`);
}
// TODO: use a zip extractor when `ext === "zip"`
await mkdirp(dir);
await new Promise((resolve, reject) => {
res.body
.on('error', reject)
.pipe(tar.extract({ cwd: dir, strip: 1 }))
.on('error', reject)
.on('finish', resolve);
});
}
return createGo(dir, platform, arch);
}
}

View File

@@ -1,136 +0,0 @@
const path = require('path');
const { mkdirp, readFile, writeFile } = require('fs-extra');
const execa = require('execa');
const { createLambda } = require('@now/build-utils/lambda.js');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const download = require('@now/build-utils/fs/download.js');
const downloadGit = require('lambda-git');
const glob = require('@now/build-utils/fs/glob.js');
const downloadGoBin = require('./download-go-bin');
// creates a `$GOPATH` directory tree, as per
// `go help gopath`'s instructions.
// without this, Go won't recognize the `$GOPATH`
async function createGoPathTree(goPath) {
await mkdirp(path.join(goPath, 'bin'));
await mkdirp(path.join(goPath, 'pkg', 'linux_amd64'));
}
exports.config = {
maxLambdaSize: '10mb',
};
exports.build = async ({ files, entrypoint }) => {
console.log('downloading files...');
const gitPath = await getWritableDirectory();
const goPath = await getWritableDirectory();
const srcPath = path.join(goPath, 'src', 'lambda');
const outDir = await getWritableDirectory();
await createGoPathTree(goPath);
const downloadedFiles = await download(files, srcPath);
console.log('downloading go binary...');
const goBin = await downloadGoBin();
console.log('downloading git binary...');
// downloads a git binary that works on Amazon Linux and sets
// `process.env.GIT_EXEC_PATH` so `go(1)` can see it
await downloadGit({ targetDirectory: gitPath });
const goEnv = {
...process.env,
GOOS: 'linux',
GOARCH: 'amd64',
GOPATH: goPath,
};
console.log(`parsing AST for "${entrypoint}"`);
let handlerFunctionName = '';
try {
handlerFunctionName = await execa.stdout(
path.join(__dirname, 'bin', 'get-exported-function-name'),
[downloadedFiles[entrypoint].fsPath],
);
} catch (err) {
console.log(`failed to parse AST for "${entrypoint}"`);
throw err;
}
if (handlerFunctionName === '') {
const e = new Error(
`Could not find an exported function on "${entrypoint}"`,
);
console.log(e.message);
throw e;
}
console.log(
`Found exported function "${handlerFunctionName}" on "${entrypoint}"`,
);
const origianlMainGoContents = await readFile(
path.join(__dirname, 'main.go'),
'utf8',
);
const mainGoContents = origianlMainGoContents.replace(
'__NOW_HANDLER_FUNC_NAME',
handlerFunctionName,
);
// in order to allow the user to have `main.go`, we need our `main.go` to be called something else
const mainGoFileName = 'main__now__go__.go';
// we need `main.go` in the same dir as the entrypoint,
// otherwise `go build` will refuse to build
const entrypointDirname = path.dirname(downloadedFiles[entrypoint].fsPath);
// Go doesn't like to build files in different directories,
// so now we place `main.go` together with the user code
await writeFile(path.join(entrypointDirname, mainGoFileName), mainGoContents);
console.log('installing dependencies');
// `go get` will look at `*.go` (note we set `cwd`), parse
// the `import`s and download any packages that aren't part of the stdlib
try {
await execa(goBin, ['get'], {
env: goEnv,
cwd: entrypointDirname,
stdio: 'inherit',
});
} catch (err) {
console.log('failed to `go get`');
throw err;
}
console.log('running go build...');
try {
await execa(
goBin,
[
'build',
'-o',
path.join(outDir, 'handler'),
path.join(entrypointDirname, mainGoFileName),
downloadedFiles[entrypoint].fsPath,
],
{ env: goEnv, cwd: entrypointDirname, stdio: 'inherit' },
);
} catch (err) {
console.log('failed to `go build`');
throw err;
}
const lambda = await createLambda({
files: await glob('**', outDir),
handler: 'handler',
runtime: 'go1.x',
environment: {},
});
return {
[entrypoint]: lambda,
};
};

302
packages/now-go/index.ts Normal file
View File

@@ -0,0 +1,302 @@
import { join, sep, dirname } from 'path';
import { readFile, writeFile, pathExists, move } from 'fs-extra';
import {
glob,
download,
createLambda,
getWriteableDirectory,
BuildOptions,
shouldServe,
Files,
} from '@now/build-utils';
import { createGo, getAnalyzedEntrypoint } from './go-helpers';
interface Analyzed {
packageName: string;
functionName: string;
watch: string[];
}
interface BuildParamsMeta {
isDev: boolean | undefined;
}
interface BuildParamsType extends BuildOptions {
files: Files;
entrypoint: string;
workPath: string;
meta: BuildParamsMeta;
}
export const version = 2;
export const config = {
maxLambdaSize: '10mb',
};
export async function build({
files,
entrypoint,
config,
meta = {} as BuildParamsMeta,
}: BuildParamsType) {
console.log('Downloading user files...');
const entrypointArr = entrypoint.split(sep);
let [goPath, outDir] = await Promise.all([
getWriteableDirectory(),
getWriteableDirectory(),
]);
if (meta.isDev) {
const devGoPath = `dev${entrypointArr[entrypointArr.length - 1]}`;
const goPathArr = goPath.split(sep);
goPathArr.pop();
goPathArr.push(devGoPath);
goPath = goPathArr.join(sep);
}
const srcPath = join(goPath, 'src', 'lambda');
const downloadedFiles = await download(files, srcPath);
const input = dirname(downloadedFiles[entrypoint].fsPath);
var includedFiles: Files = {};
if (config && config.includeFiles) {
for (const pattern of config.includeFiles) {
const files = await glob(pattern, input);
for (const assetName of Object.keys(files)) {
includedFiles[assetName] = files[assetName];
}
}
}
console.log(`Parsing AST for "${entrypoint}"`);
let analyzed: string;
try {
analyzed = await getAnalyzedEntrypoint(downloadedFiles[entrypoint].fsPath);
} catch (err) {
console.log(`Failed to parse AST for "${entrypoint}"`);
throw err;
}
if (!analyzed) {
const err = new Error(
`Could not find an exported function in "${entrypoint}"`
);
console.log(err.message);
throw err;
}
const parsedAnalyzed = JSON.parse(analyzed) as Analyzed;
const handlerFunctionName = parsedAnalyzed.functionName;
console.log(
`Found exported function "${handlerFunctionName}" in "${entrypoint}"`
);
// we need `main.go` in the same dir as the entrypoint,
// otherwise `go build` will refuse to build
const entrypointDirname = dirname(downloadedFiles[entrypoint].fsPath);
// check if package name other than main
const packageName = parsedAnalyzed.packageName;
const isGoModExist = await pathExists(join(entrypointDirname, 'go.mod'));
if (packageName !== 'main') {
const go = await createGo(
goPath,
process.platform,
process.arch,
{
cwd: entrypointDirname,
},
true
);
if (!isGoModExist) {
try {
const defaultGoModContent = `module ${packageName}`;
await writeFile(join(entrypointDirname, 'go.mod'), defaultGoModContent);
} catch (err) {
console.log(`failed to create default go.mod for ${packageName}`);
throw err;
}
}
const mainModGoFileName = 'main__mod__.go';
const modMainGoContents = await readFile(
join(__dirname, mainModGoFileName),
'utf8'
);
let goPackageName = `${packageName}/${packageName}`;
const goFuncName = `${packageName}.${handlerFunctionName}`;
if (isGoModExist) {
const goModContents = await readFile(
join(entrypointDirname, 'go.mod'),
'utf8'
);
const usrModName = goModContents.split('\n')[0].split(' ')[1];
goPackageName = `${usrModName}/${packageName}`;
}
const mainModGoContents = modMainGoContents
.replace('__NOW_HANDLER_PACKAGE_NAME', goPackageName)
.replace('__NOW_HANDLER_FUNC_NAME', goFuncName);
// write main__mod__.go
await writeFile(
join(entrypointDirname, mainModGoFileName),
mainModGoContents
);
// move user go file to folder
try {
// default path
let finalDestination = join(entrypointDirname, packageName, entrypoint);
let forceMove = false;
if (meta.isDev) {
forceMove = true;
}
// if `entrypoint` include folder, only use filename
if (entrypointArr.length > 1) {
finalDestination = join(
entrypointDirname,
packageName,
entrypointArr[entrypointArr.length - 1]
);
}
await move(downloadedFiles[entrypoint].fsPath, finalDestination, {
overwrite: forceMove,
});
} catch (err) {
console.log('failed to move entry to package folder');
throw err;
}
if (meta.isDev) {
const isGoModBk = await pathExists(join(entrypointDirname, 'go.mod.bk'));
if (isGoModBk) {
await move(
join(entrypointDirname, 'go.mod.bk'),
join(entrypointDirname, 'go.mod'),
{ overwrite: true }
);
await move(
join(entrypointDirname, 'go.sum.bk'),
join(entrypointDirname, 'go.sum'),
{ overwrite: true }
);
}
}
console.log('Tidy `go.mod` file...');
try {
// ensure go.mod up-to-date
await go.mod();
} catch (err) {
console.log('failed to `go mod tidy`');
throw err;
}
console.log('Running `go build`...');
const destPath = join(outDir, 'handler');
try {
const src = [join(entrypointDirname, mainModGoFileName)];
await go.build(src, destPath, config.ldsflags);
} catch (err) {
console.log('failed to `go build`');
throw err;
}
if (meta.isDev) {
// caching for `now dev`
await move(
join(entrypointDirname, 'go.mod'),
join(entrypointDirname, 'go.mod.bk'),
{ overwrite: true }
);
await move(
join(entrypointDirname, 'go.sum'),
join(entrypointDirname, 'go.sum.bk'),
{ overwrite: true }
);
}
} else {
const go = await createGo(
goPath,
process.platform,
process.arch,
{
cwd: entrypointDirname,
},
false
);
const origianlMainGoContents = await readFile(
join(__dirname, 'main.go'),
'utf8'
);
const mainGoContents = origianlMainGoContents.replace(
'__NOW_HANDLER_FUNC_NAME',
handlerFunctionName
);
// in order to allow the user to have `main.go`,
// we need our `main.go` to be called something else
const mainGoFileName = 'main__now__go__.go';
// Go doesn't like to build files in different directories,
// so now we place `main.go` together with the user code
await writeFile(join(entrypointDirname, mainGoFileName), mainGoContents);
// `go get` will look at `*.go` (note we set `cwd`), parse the `import`s
// and download any packages that aren't part of the stdlib
console.log('Running `go get`...');
try {
await go.get();
} catch (err) {
console.log('failed to `go get`');
throw err;
}
console.log('Running `go build`...');
const destPath = join(outDir, 'handler');
try {
const src = [
join(entrypointDirname, mainGoFileName),
downloadedFiles[entrypoint].fsPath,
];
await go.build(src, destPath);
} catch (err) {
console.log('failed to `go build`');
throw err;
}
}
const lambda = await createLambda({
files: { ...(await glob('**', outDir)), ...includedFiles },
handler: 'handler',
runtime: 'go1.x',
environment: {},
});
const output = {
[entrypoint]: lambda,
};
let watch = parsedAnalyzed.watch;
// if `entrypoint` located in subdirectory
// we will need to concat it with return watch array
if (entrypointArr.length > 1) {
entrypointArr.pop();
watch = parsedAnalyzed.watch.map(file => join(...entrypointArr, file));
}
return {
output,
watch,
};
}
export { shouldServe };

View File

@@ -1,8 +1,8 @@
package main
import (
now "../../utils/go/bridge"
"net/http"
now "github.com/zeit/now-builders/utils/go/bridge"
)
func main() {

View File

@@ -0,0 +1,12 @@
package main
import (
"net/http"
"__NOW_HANDLER_PACKAGE_NAME"
now "github.com/zeit/now-builders/utils/go/bridge"
)
func main() {
now.Start(http.HandlerFunc(__NOW_HANDLER_FUNC_NAME))
}

View File

@@ -1,30 +1,36 @@
{
"name": "@now/go",
"version": "0.2.12",
"version": "0.4.3-canary.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-go"
},
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"
"build": "tsc",
"test": "tsc && jest",
"prepublish": "tsc"
},
"files": [
"bin",
"download-go-bin.js",
"index.js",
"main.go"
"*.js",
"main.go",
"main__mod__.go",
"util"
],
"dependencies": {
"debug": "^4.1.1",
"execa": "^1.0.0",
"fs-extra": "^7.0.0",
"lambda-git": "^0.1.2",
"mkdirp-promise": "5.0.1",
"node-fetch": "^2.2.1",
"tar": "4.4.6"
},
"devDependencies": {
"@zeit/best": "0.4.3",
"rmfr": "2.0.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"@types/debug": "^4.1.3",
"@types/execa": "^0.9.0",
"@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"typescript": "^3.4.2"
}
}

View File

@@ -0,0 +1,13 @@
package cowsay
import (
"fmt"
"net/http"
say "github.com/dhruvbird/go-cowsay"
)
// Handler function
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, say.Format("cow:RANDOMNESS_PLACEHOLDER"))
}

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "index.go", "use": "@now/go" },
{ "src": "subdirectory/index.go", "use": "@now/go" }
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory", "mustContain": "subcow:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,13 @@
package subcow
import (
"fmt"
"net/http"
say "github.com/dhruvbird/go-cowsay"
)
// Handler function
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, say.Format("subcow:RANDOMNESS_PLACEHOLDER"))
}

View File

@@ -0,0 +1 @@
module build-env

View File

@@ -0,0 +1,17 @@
package buildenv
import (
"fmt"
"net/http"
"os"
)
// Handler function
func Handler(w http.ResponseWriter, r *http.Request) {
rdm := os.Getenv("RANDOMNESS_BUILD_ENV")
if rdm == "" {
fmt.Println("No build env received")
}
fmt.Fprintf(w, rdm+":build-env")
}

View File

@@ -0,0 +1 @@
module env

View File

@@ -0,0 +1,17 @@
package env
import (
"fmt"
"net/http"
"os"
)
// Handler function
func Handler(w http.ResponseWriter, r *http.Request) {
rdm := os.Getenv("RANDOMNESS_ENV")
if rdm == "" {
fmt.Println("No env received")
}
fmt.Fprintf(w, rdm)
}

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"builds": [
{
"src": "env/index.go",
"use": "@now/go"
}
],
"env": {
"RANDOMNESS_ENV": "RANDOMNESS_PLACEHOLDER"
},
"probes": [
{
"path": "/env",
"mustContain": "RANDOMNESS_PLACEHOLDER"
}
]
}

View File

@@ -0,0 +1,11 @@
package function
import (
"fmt"
"net/http"
)
// Handler function
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "RANDOMNESS_PLACEHOLDER")
}

View File

@@ -0,0 +1,6 @@
{
"version": 2,
"builds": [
{ "src": "index.go", "use": "@now/go" }
]
}

View File

@@ -0,0 +1,9 @@
{
"version": 2,
"builds": [
{ "src": "*.go", "use": "@now/go" }
],
"env": {
"RANDOMNESS_ENV_VAR": "RANDOMNESS_PLACEHOLDER"
}
}

View File

@@ -0,0 +1,16 @@
package function
import (
"net/http"
"os"
"strconv"
)
// HandlerTest1 function
func HandlerTest1(w http.ResponseWriter, r *http.Request) {
rdm := os.Getenv("RANDOMNESS_ENV_VAR")
w.WriteHeader(401)
w.Header().Set("content-length", strconv.Itoa(len(rdm+":content-length")))
w.Write([]byte(rdm + ":content-length"))
}

View File

@@ -0,0 +1,12 @@
package function
import (
"net/http"
)
// HandlerTest2 function
func HandlerTest2(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", "2")
w.WriteHeader(401)
w.Write([]byte(""))
}

View File

@@ -0,0 +1,13 @@
package function
import (
"net/http"
"os"
)
// HandlerTest3 function
func HandlerTest3(w http.ResponseWriter, r *http.Request) {
rev := os.Getenv("RANDOMNESS_ENV_VAR")
w.WriteHeader(401)
w.Write([]byte(rev + ":content-length"))
}

View File

@@ -0,0 +1,16 @@
package cowsay
import (
"fmt"
"io/ioutil"
"net/http"
)
// Handler function
func Handler(w http.ResponseWriter, r *http.Request) {
bts, err := ioutil.ReadFile("templates/foo.txt")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
fmt.Fprintf(w, string(bts))
}

View File

@@ -0,0 +1,20 @@
{
"version": 2,
"builds": [
{
"src": "index.go",
"use": "@now/go",
"config": {
"includeFiles": [
"templates/**"
]
}
}
],
"probes": [
{
"path": "/",
"mustContain": "foobar from file"
}
]
}

View File

@@ -0,0 +1 @@
foobar from file

View File

@@ -0,0 +1,33 @@
/* global beforeAll, expect, it, jest */
const fs = require('fs');
const path = require('path');
const {
packAndDeploy,
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
const builderPath = path.resolve(__dirname, '..');
builderUrl = await packAndDeploy(builderPath);
console.log('builderUrl', builderUrl);
});
const fixturesPath = path.resolve(__dirname, 'fixtures');
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"declaration": false,
"esModuleInterop": true,
"lib": ["esnext"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitThis": false,
"types": ["node"],
"strict": true,
"target": "esnext"
}
}

View File

@@ -0,0 +1,161 @@
package main
import (
"encoding/json"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
)
type analyze struct {
PackageName string `json:"packageName"`
FuncName string `json:"functionName"`
Watch []string `json:"watch"`
}
// parse go file
func parse(fileName string) *ast.File {
fset := token.NewFileSet()
parsed, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err != nil {
log.Fatalf("Could not parse Go file \"%s\"\n", fileName)
os.Exit(1)
}
return parsed
}
// ensure we only working with interest go file(s)
func visit(files *[]string) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
itf, err := filepath.Match("*test.go", path)
if err != nil {
log.Fatal(err)
}
// we don't need Dirs, or test files
// we only want `.go` files
if info.IsDir() || itf || filepath.Ext(path) != ".go" {
return nil
}
*files = append(*files, path)
return nil
}
}
// return unique file
func unique(files []string) []string {
encountered := map[string]bool{}
for v := range files {
encountered[files[v]] = true
}
result := []string{}
for key := range encountered {
result = append(result, key)
}
return result
}
func main() {
if len(os.Args) != 2 {
// Args should have the program name on `0`
// and the file name on `1`
fmt.Println("Wrong number of args; Usage is:\n ./go-analyze file_name.go")
os.Exit(1)
}
fileName := os.Args[1]
rf, err := ioutil.ReadFile(fileName)
if err != nil {
log.Fatal(err)
}
se := string(rf)
var files []string
var relatedFiles []string
// Add entrypoint to watchlist
relFileName, err := filepath.Rel(filepath.Dir(fileName), fileName)
if err != nil {
log.Fatal(err)
}
relatedFiles = append(relatedFiles, relFileName)
// looking for all go files that have export func
// using in entrypoint
err = filepath.Walk(filepath.Dir(fileName), visit(&files))
if err != nil {
log.Fatal(err)
}
for _, file := range files {
absFileName, _ := filepath.Abs(fileName)
absFile, _ := filepath.Abs(file)
// if it isn't entrypoint
if absFileName != absFile {
// find all export structs and functions
pf := parse(file)
var exportedDecl []string
ast.Inspect(pf, func(n ast.Node) bool {
switch t := n.(type) {
case *ast.FuncDecl:
if t.Name.IsExported() {
exportedDecl = append(exportedDecl, t.Name.Name)
}
// find variable declarations
case *ast.TypeSpec:
// which are public
if t.Name.IsExported() {
switch t.Type.(type) {
// and are interfaces
case *ast.StructType:
exportedDecl = append(exportedDecl, t.Name.Name)
}
}
}
return true
})
for _, ed := range exportedDecl {
if strings.Contains(se, ed) {
// find relative path of related file
rel, err := filepath.Rel(filepath.Dir(fileName), file)
if err != nil {
log.Fatal(err)
}
relatedFiles = append(relatedFiles, rel)
}
}
}
}
parsed := parse(fileName)
for _, decl := range parsed.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok {
// this declaraction is not a function
// so we're not interested
continue
}
if fn.Name.IsExported() == true {
// we found the first exported function
// we're done!
analyzed := analyze{
PackageName: parsed.Name.Name,
FuncName: fn.Name.Name,
Watch: unique(relatedFiles),
}
json, _ := json.Marshal(analyzed)
fmt.Print(string(json))
os.Exit(0)
}
}
}

View File

@@ -1,41 +0,0 @@
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
)
func main() {
if len(os.Args) != 2 {
// Args should have the program name on `0`
// and the file name on `1`
fmt.Println("Wrong number of args; Usage is:\n ./get-exported-function-name file_name.go")
os.Exit(1)
}
fileName := os.Args[1]
fset := token.NewFileSet()
parsed, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err != nil {
fmt.Printf("Could not parse Go file \"%s\"\n", fileName)
os.Exit(1)
}
for _, decl := range parsed.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok {
// this declaraction is not a function
// so we're not interested
continue
}
if fn.Name.IsExported() == true {
// we found the first exported function
// we're done!
fmt.Print(fn.Name.Name)
os.Exit(0)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
const FileBlob = require('@now/build-utils/file-blob.js');
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const { minify } = require('html-minifier');
const defaultOptions = {
@@ -11,7 +11,6 @@ const defaultOptions = {
removeRedundantAttributes: true,
useShortDoctype: true,
collapseWhitespace: true,
collapseInlineTagWhitespace: true,
collapseBooleanAttributes: true,
caseSensitive: true,
};

View File

@@ -1,11 +1,13 @@
{
"name": "@now/html-minifier",
"version": "1.0.7",
"version": "1.1.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-html-minifier"
},
"dependencies": {
"html-minifier": "3.5.21"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,5 +1,5 @@
const { Lambda } = require('@now/build-utils/lambda.js');
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js');
const { Lambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js'); // eslint-disable-line import/no-extraneous-dependencies
exports.build = async ({ files, entrypoint, config }) => {
if (!files[entrypoint]) throw new Error('Entrypoint not found in files');

View File

@@ -1,9 +1,11 @@
{
"name": "@now/lambda",
"version": "0.4.9",
"version": "0.5.0",
"license": "MIT",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-lambda"
},
"scripts": {
"test": "jest"

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

View File

@@ -1,4 +1,4 @@
const FileBlob = require('@now/build-utils/file-blob.js');
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const unified = require('unified');
const unifiedStream = require('unified-stream');
const markdown = require('remark-parse');
@@ -34,8 +34,6 @@ exports.build = async ({ files, entrypoint, config }) => {
stream: stream.pipe(unifiedStream(processor)),
});
console.log(result.data.toString());
const replacedEntrypoint = entrypoint.replace(/\.[^.]+$/, '.html');
return { [replacedEntrypoint]: result };

View File

@@ -1,7 +1,12 @@
{
"name": "@now/md",
"version": "0.4.9",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-md"
},
"dependencies": {
"rehype-document": "^2.2.0",
"rehype-format": "^2.3.0",
@@ -11,9 +16,6 @@
"unified": "^7.0.0",
"unified-stream": "^1.0.2"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},
"scripts": {
"test": "jest"
}

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

View File

@@ -1,10 +1,10 @@
const download = require('@now/build-utils/fs/download.js');
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const fs = require('fs');
const { promisify } = require('util');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const glob = require('@now/build-utils/fs/glob.js');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const { runNpmInstall } = require('@now/build-utils/fs/run-user-scripts.js');
const { runNpmInstall } = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
const writeFile = promisify(fs.writeFile);
@@ -43,17 +43,8 @@ exports.build = async ({ files, entrypoint, workPath }) => {
return glob('**', outDir, mountpoint);
};
exports.prepareCache = async ({ cachePath }) => {
console.log('writing package.json...');
const packageJson = { dependencies: { 'mdx-deck': '1.7.15' } };
const packageJsonPath = path.join(cachePath, 'package.json');
await writeFile(packageJsonPath, JSON.stringify(packageJson));
console.log('running npm install...');
await runNpmInstall(path.dirname(packageJsonPath), ['--prod']);
return {
...(await glob('node_modules/**', cachePath)),
...(await glob('package-lock.json', cachePath)),
...(await glob('yarn.lock', cachePath)),
};
};
exports.prepareCache = async ({ workPath }) => ({
...(await glob('node_modules/**', workPath)),
...(await glob('package-lock.json', workPath)),
...(await glob('yarn.lock', workPath)),
});

View File

@@ -1,9 +1,11 @@
{
"name": "@now/mdx-deck",
"version": "0.4.18",
"version": "0.5.0",
"license": "MIT",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-mdx-deck"
},
"scripts": {
"test": "jest"

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

2
packages/now-next/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/dist
/src/now__bridge.d.ts

View File

@@ -0,0 +1,11 @@
#!/bin/bash
set -euo pipefail
bridge_entrypoint="$(node -p 'require.resolve("@now/node-bridge")')"
bridge_defs="$(dirname "$bridge_entrypoint")/bridge.d.ts"
if [ ! -e "$bridge_defs" ]; then
yarn install --cwd "$bridge_entrypoint"
fi
cp -v "$bridge_defs" src/now__bridge.d.ts

View File

@@ -1,335 +0,0 @@
const { createLambda } = require('@now/build-utils/lambda.js');
const download = require('@now/build-utils/fs/download.js');
const FileFsRef = require('@now/build-utils/file-fs-ref.js');
const FileBlob = require('@now/build-utils/file-blob');
const path = require('path');
const { readFile, writeFile, unlink } = require('fs.promised');
const {
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts.js');
const glob = require('@now/build-utils/fs/glob.js');
const semver = require('semver');
const nextLegacyVersions = require('./legacy-versions');
const {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
moveEntryDirectoryToRoot,
normalizePackageJson,
excludeStaticDirectory,
onlyStaticDirectory,
} = require('./utils');
/** @typedef { import('@now/build-utils/file-ref').Files } Files */
/** @typedef { import('@now/build-utils/fs/download').DownloadedFiles } DownloadedFiles */
/**
* @typedef {Object} BuildParamsType
* @property {Files} files - Files object
* @property {string} entrypoint - Entrypoint specified for the builder
* @property {string} workPath - Working directory for this build
*/
/**
* Read package.json from files
* @param {DownloadedFiles} files
*/
async function readPackageJson(files) {
if (!files['package.json']) {
return {};
}
const packageJsonPath = files['package.json'].fsPath;
return JSON.parse(await readFile(packageJsonPath, 'utf8'));
}
/**
* Write package.json
* @param {string} workPath
* @param {Object} packageJson
*/
async function writePackageJson(workPath, packageJson) {
await writeFile(
path.join(workPath, 'package.json'),
JSON.stringify(packageJson, null, 2),
);
}
/**
* Write .npmrc with npm auth token
* @param {string} workPath
* @param {string} token
*/
async function writeNpmRc(workPath, token) {
await writeFile(
path.join(workPath, '.npmrc'),
`//registry.npmjs.org/:_authToken=${token}`,
);
}
exports.config = {
maxLambdaSize: '5mb',
};
/**
* @param {BuildParamsType} buildParams
* @returns {Promise<Files>}
*/
exports.build = async ({ files, workPath, entrypoint }) => {
validateEntrypoint(entrypoint);
console.log('downloading user files...');
const entryDirectory = path.dirname(entrypoint);
const filesOnlyEntryDirectory = includeOnlyEntryDirectory(
files,
entryDirectory,
);
const filesWithEntryDirectoryRoot = moveEntryDirectoryToRoot(
filesOnlyEntryDirectory,
entryDirectory,
);
const filesWithoutStaticDirectory = excludeStaticDirectory(
filesWithEntryDirectoryRoot,
);
const downloadedFiles = await download(filesWithoutStaticDirectory, workPath);
const pkg = await readPackageJson(downloadedFiles);
let nextVersion;
if (pkg.dependencies && pkg.dependencies.next) {
nextVersion = pkg.dependencies.next;
} else if (pkg.devDependencies && pkg.devDependencies.next) {
nextVersion = pkg.devDependencies.next;
}
if (!nextVersion) {
throw new Error(
'No Next.js version could be detected in "package.json". Make sure `"next"` is installed in "dependencies" or "devDependencies"',
);
}
const isLegacy = (() => {
// If version is using the dist-tag instead of a version range
if (nextVersion === 'canary' || nextVersion === 'latest') {
return false;
}
// If the version is an exact match with the legacy versions
if (nextLegacyVersions.indexOf(nextVersion) !== -1) {
return true;
}
const maxSatisfying = semver.maxSatisfying(nextLegacyVersions, nextVersion);
// When the version can't be matched with legacy versions, so it must be a newer version
if (maxSatisfying === null) {
return false;
}
return true;
})();
console.log(`MODE: ${isLegacy ? 'legacy' : 'serverless'}`);
if (isLegacy) {
try {
await unlink(path.join(workPath, 'yarn.lock'));
} catch (err) {
console.log('no yarn.lock removed');
}
try {
await unlink(path.join(workPath, 'package-lock.json'));
} catch (err) {
console.log('no package-lock.json removed');
}
console.warn(
"WARNING: your application is being deployed in @now/next's legacy mode.",
);
console.log('normalizing package.json');
const packageJson = normalizePackageJson(pkg);
console.log('normalized package.json result: ', packageJson);
await writePackageJson(workPath, packageJson);
} else if (!pkg.scripts || !pkg.scripts['now-build']) {
console.warn(
'WARNING: "now-build" script not found. Adding \'"now-build": "next build"\' to "package.json" automatically',
);
pkg.scripts = {
'now-build': 'next build',
...(pkg.scripts || {}),
};
console.log('normalized package.json result: ', pkg);
await writePackageJson(workPath, pkg);
}
if (process.env.NPM_AUTH_TOKEN) {
console.log('found NPM_AUTH_TOKEN in environment, creating .npmrc');
await writeNpmRc(workPath, process.env.NPM_AUTH_TOKEN);
}
console.log('installing dependencies...');
await runNpmInstall(workPath, ['--prefer-offline']);
console.log('running user script...');
await runPackageJsonScript(workPath, 'now-build');
if (isLegacy) {
console.log('running npm install --production...');
await runNpmInstall(workPath, ['--prefer-offline', '--production']);
}
if (process.env.NPM_AUTH_TOKEN) {
await unlink(path.join(workPath, '.npmrc'));
}
const lambdas = {};
if (isLegacy) {
const filesAfterBuild = await glob('**', workPath);
console.log('preparing lambda files...');
let buildId;
try {
buildId = await readFile(
path.join(workPath, '.next', 'BUILD_ID'),
'utf8',
);
} catch (err) {
console.error(
'BUILD_ID not found in ".next". The "package.json" "build" script did not run "next build"',
);
throw new Error('Missing BUILD_ID');
}
const dotNextRootFiles = await glob('.next/*', workPath);
const dotNextServerRootFiles = await glob('.next/server/*', workPath);
const nodeModules = excludeFiles(
await glob('node_modules/**', workPath),
file => file.startsWith('node_modules/.cache'),
);
const launcherFiles = {
'now__bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
};
const nextFiles = {
...nodeModules,
...dotNextRootFiles,
...dotNextServerRootFiles,
...launcherFiles,
};
if (filesAfterBuild['next.config.js']) {
nextFiles['next.config.js'] = filesAfterBuild['next.config.js'];
}
const pages = await glob(
'**/*.js',
path.join(workPath, '.next', 'server', 'static', buildId, 'pages'),
);
const launcherPath = path.join(__dirname, 'legacy-launcher.js');
const launcherData = await readFile(launcherPath, 'utf8');
await Promise.all(
Object.keys(pages).map(async (page) => {
// These default pages don't have to be handled as they'd always 404
if (['_app.js', '_error.js', '_document.js'].includes(page)) {
return;
}
const pathname = page.replace(/\.js$/, '');
const launcher = launcherData.replace(
'PATHNAME_PLACEHOLDER',
`/${pathname.replace(/(^|\/)index$/, '')}`,
);
const pageFiles = {
[`.next/server/static/${buildId}/pages/_document.js`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/_document.js`
],
[`.next/server/static/${buildId}/pages/_app.js`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/_app.js`
],
[`.next/server/static/${buildId}/pages/_error.js`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/_error.js`
],
[`.next/server/static/${buildId}/pages/${page}`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/${page}`
],
};
console.log(`Creating lambda for page: "${page}"...`);
lambdas[path.join(entryDirectory, pathname)] = await createLambda({
files: {
...nextFiles,
...pageFiles,
'now__launcher.js': new FileBlob({ data: launcher }),
},
handler: 'now__launcher.launcher',
runtime: 'nodejs8.10',
});
console.log(`Created lambda for page: "${page}"`);
}),
);
} else {
console.log('preparing lambda files...');
const launcherFiles = {
'now__bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
'now__launcher.js': new FileFsRef({
fsPath: path.join(__dirname, 'launcher.js'),
}),
};
const pages = await glob(
'**/*.js',
path.join(workPath, '.next', 'serverless', 'pages'),
);
const pageKeys = Object.keys(pages);
if (pageKeys.length === 0) {
throw new Error(
'No serverless pages were built. https://err.sh/zeit/now-builders/now-next-no-serverless-pages-built',
);
}
await Promise.all(
pageKeys.map(async (page) => {
// These default pages don't have to be handled as they'd always 404
if (['_app.js', '_error.js', '_document.js'].includes(page)) {
return;
}
const pathname = page.replace(/\.js$/, '');
console.log(`Creating lambda for page: "${page}"...`);
lambdas[path.join(entryDirectory, pathname)] = await createLambda({
files: {
...launcherFiles,
'page.js': pages[page],
},
handler: 'now__launcher.launcher',
runtime: 'nodejs8.10',
});
console.log(`Created lambda for page: "${page}"`);
}),
);
}
const nextStaticFiles = await glob(
'**',
path.join(workPath, '.next', 'static'),
);
const staticFiles = Object.keys(nextStaticFiles).reduce(
(mappedFiles, file) => ({
...mappedFiles,
[path.join(entryDirectory, `_next/static/${file}`)]: nextStaticFiles[file],
}),
{},
);
const nextStaticDirectory = onlyStaticDirectory(filesWithEntryDirectoryRoot);
const staticDirectoryFiles = Object.keys(nextStaticDirectory).reduce(
(mappedFiles, file) => ({
...mappedFiles,
[path.join(entryDirectory, file)]: nextStaticDirectory[file],
}),
{},
);
return { ...lambdas, ...staticFiles, ...staticDirectoryFiles };
};

View File

@@ -1,13 +0,0 @@
process.env.NODE_ENV = 'production';
const { Server } = require('http');
const { Bridge } = require('./now__bridge.js');
const page = require('./page.js');
const bridge = new Bridge();
bridge.port = 3000;
const server = new Server(page.render);
server.listen(bridge.port);
exports.launcher = bridge.launcher;

View File

@@ -1,19 +0,0 @@
const { Server } = require('http');
const next = require('next-server');
const url = require('url');
const { Bridge } = require('./now__bridge.js');
const bridge = new Bridge();
bridge.port = 3000;
process.env.NODE_ENV = 'production';
const app = next({});
const server = new Server((req, res) => {
const parsedUrl = url.parse(req.url, true);
app.render(req, res, 'PATHNAME_PLACEHOLDER', parsedUrl.query, parsedUrl);
});
server.listen(bridge.port);
exports.launcher = bridge.launcher;

Some files were not shown because too many files have changed in this diff Show More