Compare commits

...

126 Commits

Author SHA1 Message Date
luc
3108332043 Publish
- @now/build-utils@0.5.8
 - @now/md@0.5.4
 - @now/next@0.4.2
 - @now/node-bridge@1.2.0
 - @now/node-server@0.7.5
 - @now/node@0.9.0
 - @now/python@0.2.7
 - @now/static-build@0.5.9
2019-06-17 18:19:38 +02:00
Steven
7509c82c32 [tests] Fix yarn.lock (#628) 2019-06-17 16:40:33 +02:00
Steven
c4f5a5b48d [now-node] Remove layers (#617) 2019-06-17 16:05:31 +02:00
Luc
05314da810 [now-node] Fix faulty behavior when request's body is empty and content-type is application/json (#615)
* add test for empty body and `application/json`

* fix test
2019-06-17 16:01:16 +02:00
Steven
5f1cf714c1 [tests] Run against all commits in the branch (#611) 2019-06-17 16:01:09 +02:00
Steven
2623e2e799 [now-node] Bump layer versions (#610) 2019-06-17 16:01:00 +02:00
Steven
bac1da09d4 [now-build-utils] Add types for Layers (#604)
* [now-build-utils] Add PrepareLayersOptions

* [now-build-utils] Add types for layers

* Add layer types to lambda

* Add any values to a layer function

* Change props to optional

* Update layers per call

* Add missing getFiles function

* Fix types

* Improve error message

* Change createLambda() api

* Remove node-gyp hack

* [now-layer-node] Add bootstrap

* Add fallback to empty object

* Add config.useLayers

* Remove config, change to layers check

* Fix typo

* Add deprecation message
2019-06-17 16:00:47 +02:00
Luc
5b57f1a3ac [now-node] Improve @now/node helpers (#609)
* lazy load everything 

* do not read charset in content-type to set encoding

* add tests

* add tests for express compat

* add test for `res.status().send()`

* update after PR comments
2019-06-17 16:00:39 +02:00
Nathan Rajlich
2e95dd5329 [now-md] Fix shouldServe() logic (#608)
* [now-md] Fix `shouldServe()` logic

Since this builder is not a 1-1 mapping of the input -> output names,
the default `shouldServe()` function needs to be augmented such that
the `.html` requestPath is "considered like" a `.md` file.

* Remove "ends with .html" optimization

It breaks index files, i.e. `GET /`
2019-06-17 16:00:30 +02:00
Joe Haddad
215f6367d6 [now-next] Humanize message about now-build (#607)
Some users read this message as a warning that required action (the captain caps lock WARNING didn't help).

Users should never need to define a `now-build` script unless they grow out of running `next build`.
2019-06-17 16:00:24 +02:00
Joe Haddad
e8cd348a79 [now-next] Fix erroneous .next folder message (#606)
We now store the `.next/cache` folder so we need to check for the `static` folder to know if the user uploaded something they should've excluded.
2019-06-17 16:00:17 +02:00
Luc
168f373641 [now-node] Assign type for req.headers.cookie (#603) 2019-06-17 16:00:00 +02:00
Luc
8c3174be29 [now-node] Move @types/node to dependencies (#602)
* move @types/node to dependencies

* fix types
2019-06-17 15:59:44 +02:00
Luc
898de78b63 [now-node] Make helpers opt-out instead of opt-in (#601)
* set helpers to true by default

* update tests
2019-06-17 15:58:00 +02:00
Luc
26e33c1c4b Pin now-node-bridge dependencies in now-next and now-node-server (#600)
* pin dependencies in now-next

* pin dependencies in now-node-server

* regenerate yarn.lock
2019-06-17 15:57:33 +02:00
Luc
c2f95de3ec [now-node] Helpers compatibility with express, micro, etc (#594)
* copy bridge into now-node

* pass body buffer to listener

* only send addon when helpers are added

* ship bridge.js to deployment

* remove raw-body deps

* fix not waiting for server `listening` event

* add test for express compat

* add test for micro compat

* update now-node-bridge

* remove unnecessary yarn.lock

* fix wrong replacement in launcher.ts

* fix listener not defined

* fix unit tests

* add "test" for exports

* add console.log

* add test in node-bridge

* log error before throwing

* revert now-node-bridge to canary state

* remove unused code

* refactor consumeProxyRequests -> consumeEvent

* remove ts-jest

* update tests

* do not transform body to string if not necessary

* fix tests

* remove jest from deps in now-node

* x-bridge-reqid -> x-now-bridge-request-id

* add test for consumeEvent

* do not expose request id header to the client

* pin node-bridge version

* update node-bridge deps to 1.2.0-canary.1

* update yarn.lock

* add await for user's listener

* refactor

* pass async function to `Server`
2019-06-17 15:56:09 +02:00
Luc
6a7de860db [now-node-bridge] Make normalized events consumable by server (#598)
* add `consumeProxyRequest` to bridge

* refactor

* fix tests

* Update packages/now-node-bridge/test/bridge.test.js

Co-Authored-By: Leo Lamprecht <mindrun@icloud.com>

* Update packages/now-node-bridge/src/bridge.ts

Co-Authored-By: Leo Lamprecht <mindrun@icloud.com>
2019-06-17 15:28:05 +02:00
Steven
acb8cadafe [now-static-build] Should fail build when distDir is empty (#597) 2019-06-17 15:27:58 +02:00
Kai Richard König
1a8df7080d [now-python] Do not append query string to path (#580)
* Do not append query string to path - fixes \#545

* Avoid creating multiple parsed urls

* Fix missing whitespace

* Fix handler

* Remove unused imports
2019-06-17 15:27:50 +02:00
JJ Kasper
5a92826eb0 [now-next] Update appending .html to exported routes (#592) 2019-06-17 15:27:24 +02:00
Steven
e083aa3750 Publish
- @now/node@0.8.1
2019-06-08 14:56:27 -04:00
Steven
941f675657 [now-node] Change helpers to opt-in (#591)
* [now-node] Change helpers to opt-in

* Fix tests

* Fix tests
2019-06-08 14:55:56 -04:00
Steven
6fad726abb Publish
- @now/build-utils@0.5.7
 - @now/node@0.8.0
2019-06-08 10:38:13 -04:00
Luc
dd22051d6b [now-node] Add tests for types exports (#590)
* helpers.test.js -> helpers.test.ts

* import NowRequest and NowResponse in test

* fix addHelpers return type

* add comments
2019-06-08 10:36:05 -04:00
Luc
7e86cb403f [now-node] Add Express-like API (#577)
* first iteration of express-like api for @now/node

* fix error when config is undefined

* add integration test for helpers

* add `res.status()` to helpers integration test

* fix error caused by config values being strings

* add helpers opt-out integration test

* add `helpers.js` to deployed files

* add boolean and number to config values types

* update config.helpers to work with boolean

* add unit tests

* fix type error in config type

* add unit test for req.body

* ignore errors not generated in helpers

* Update packages/now-node/test/fixtures/15-helpers/no-helpers/index.js

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

* update config type

* use ternary instead of filtering with `Boolean`

* add probe in 15-helpers

* add probe for ts function

* ncc `helpers.js`

* fix Config type

* fix @now/rust type issue

* add comment in build.sh

* test that content-type header is correctly added

* add missing tsconfig.json in fixtures

* add `body` to fix `Invalid JSON` errors

* Revert "add `body` to fix `Invalid JSON` errors"

This reverts commit 9b2ff55409501140f0d7411d121fc3a4dfd34ccc.

* make `helpers` false by default

* add method POST to probe for helpers

* Revert "make `helpers` false by default"

This reverts commit d029a432a0bf2463e1613e6cfd76929ce6e45073.

* replace POST requests by GET in probes

* remove unnecessary comments

* destructure in parseQuery

* keep @now/rust unchanged

* Request -> NowRequest and Response -> NowResponse

* improve config types

* add NowListener type

* export NowRequest and NowResponse

* generate `.d.ts` files

* add types to helpers/ts fixtures

* Update packages/now-build-utils/src/types.ts

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

* Update packages/now-node/build.sh

Co-Authored-By: Steven <steven@ceriously.com>
2019-06-08 10:35:45 -04:00
Nathan Rajlich
d19d557738 [now-node] Add watch array for Builder v2 API (#582)
* [now-node] Add `watch` array

For `now dev`

* Use ncc-watcher from npm registry

* Update `yarn.lock`

* Fix integration tests

* Apply @styfle's suggestions

* Update `@zeit/ncc-watcher` to v1.0.3
2019-06-08 10:35:23 -04:00
Steven
e4281f698c Publish
- @now/rust@0.2.6
2019-06-07 12:56:50 -04:00
Steven
86ff681c6d Use prettier on .json files (#588) 2019-06-07 12:56:21 -04:00
Steven
ba97a7cf19 [now-rust] Move ts files into /src (#585) 2019-06-07 12:56:14 -04:00
Luc
0a94397700 [now-rust] config.includeFiles can be string[] (#584) 2019-06-07 12:56:04 -04:00
Luc
5c8e2f2ccc Fix content-type: application/json header being added even if body is undefined (#583) 2019-06-07 12:55:47 -04:00
Steven
35d56a34cb Publish
- @now/python@0.2.6
2019-06-06 17:19:25 -04:00
Steven
9dfd37e135 Update publish instructions in the README (#579)
* Update README.md

* Fix typo

* Add prettier to markdown files

* Fix table

* Another cherry pick
2019-06-06 16:58:57 -04:00
Steven
6f815f2645 Fix python (#581)
This change never made it to master
2019-06-06 16:58:28 -04:00
Steven
e186f89cfd Publish
- @now/build-utils@0.5.6
 - @now/go@0.5.1
 - @now/lambda@0.5.4
 - @now/layer-node@0.0.2
 - @now/layer-npm@0.0.2
 - @now/layer-yarn@0.0.2
 - @now/next@0.4.1
 - @now/node-bridge@1.1.4
 - @now/node-server@0.7.4
 - @now/node@0.7.4
 - @now/optipng@0.6.2
 - @now/php-bridge@0.5.3
 - @now/php@0.5.5
 - @now/python@0.2.5
 - @now/rust@0.2.5
 - @now/static-build@0.5.8
 - @now/wordpress@0.5.3
2019-06-06 15:15:47 -04:00
Steven
50cade8bba [now-layer] Add entrypoint to BuildLayerResult (#578)
* [now-layer] Add meta to BuildLayerResult

* Fix tests

* Fix test for expect entrypoint

* Return entrypoint

* Remove meta

* Remove meta

* Remove npmVerison meta check from layer-node
2019-06-06 15:13:06 -04:00
dependabot[bot]
13866e61f6 Bump js-yaml from 3.12.0 to 3.13.1 (#576)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.12.0 to 3.13.1.
- [Release notes](https://github.com/nodeca/js-yaml/releases)
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.12.0...3.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2019-06-06 17:49:33 +00:00
Joe Haddad
b72f902271 [now-next] Route 404 to custom Next.js error page (#569) 2019-06-06 17:49:28 +00:00
JJ Kasper
159cfe99dd Revert "Fix missing slash from prepending entryDirectory (#573)" (#575)
This reverts commit 36ac7605762639968a52d66a1bcb6d3cab68da3f.
2019-06-06 17:49:24 +00:00
JJ Kasper
1d9a96d104 Fix missing slash from prepending entryDirectory (#573)
* Fix missing slash for staticRoute

* Add util for prepending entryDirectory correctly

* Apply suggestions from code review

Co-Authored-By: Connor Davis <mail@connordav.is>

* Don't add export route for dynamic pages

* Fix type error from review commit

* Don't use scopeToEntry on lambdas
2019-06-06 17:49:20 +00:00
JJ Kasper
245f846d3e [now-next] Make sure .html is appended to dynamic auto exported routes (#572) 2019-06-06 17:49:14 +00:00
JJ Kasper
c5ef7f3f35 [now-next] Add generating of dynamic routes for auto exported pages (#570) 2019-06-06 17:49:10 +00:00
Steven
ccba15a5aa [now-next] Add timer to codeowners (#571) 2019-06-06 17:49:06 +00:00
dependabot[bot]
f49aefa8e4 Bump handlebars from 4.0.12 to 4.1.2 (#568)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.0.12 to 4.1.2.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.0.12...v4.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-06-06 17:48:57 +00:00
Sophearak Tha
d6b36df4ce [now-go] Fix subdirectory parse fail (#565) 2019-06-06 17:48:52 +00:00
JJ Kasper
3e4dd10a79 [now-next] Re-add generating of dynamic routes (#559)
* Re-add generating of dynamic routes

* Run prettier

* Update to use startsWith
2019-06-06 17:48:47 +00:00
Connor Davis
73956706bd Prettier everything (#563)
* Prettier everything

* md files should use spaces too

* Move prettier config to package.json

* Retry tests
2019-06-06 17:48:40 +00:00
Steven
bd8da5360d Add layers for npm and yarn (#558)
* Add `now-layer-npm`

* Change version to 0.0.1-canary.0

* Add now-layer-yarn

* Update version

* Add now-metadata.json

* Read package.json to detect npm version

* Fix tests for windows zip
2019-06-06 17:48:27 +00:00
Steven
6d5a2a4438 [now-layer-node] Add node runtime layer (#552)
* [now-node-runtime] Add node runtime layer

* Add now-node-runtime to eslintignore

* Add test

* Rename variables, add comment

* Remove createGunzip()

* Add .yarnrc

* Rename to `now-layer-node`

* Pin dependencies

* Remove npm and npx from the layer

* Ignore uncessary files for node installer

* Use single glob string
2019-06-06 17:48:18 +00:00
Timothy
c88dc78e33 Fix static-build local development docs link (#556) 2019-06-06 17:48:10 +00:00
Steven
63ac11e9f7 [now-python] Publish from dist directory (#550)
* [now-python] Publish from dist directory

* Fix path to `now_init.py`

* Add now-init.py to npm publish

* Fix typo
2019-06-06 17:48:02 +00:00
Steven
1840632729 [tests] Fix duplicate GH Actions (#551)
* [tests] Fix duplicate GH Actions

* Fix typo

* Use linear to avoid cancelled checks
2019-06-06 17:46:48 +00:00
Steven
00d8eb0f65 [now-python] Improve error message (#548)
* [now-python] Improve error message

* Check if BaseHTTPRequestHandler

* Add a test for uppercase Handler
2019-06-06 17:46:43 +00:00
Steven
3db58ac373 [now-python] Disable pip upgrade notice (#547) 2019-06-06 17:46:39 +00:00
Steven
92a1720eea [tests] Only publish to npm from master/canary branch (#546)
* Change circleci workflow to build-and-test

* Add branch filter

* Separate into two workflows

* Move both workflows into single file

* Rename to main.workflow
2019-06-06 17:46:34 +00:00
Sophearak Tha
9abbfbe3f3 [now-go] Add support for go.mod in different entrypoint directory (#540)
* Add support for `go.mod` in different `entrypoint` directory

* Add tests cover Go Modules

* Workaround solution to make `now dev` support `go.mod` in different folder than `entrypoint`

* Ensure to backup `go.mod` and `go.sum` only in isDev

* Update packages/now-go/index.ts

* Update packages/now-go/index.ts

* Update watcher to work for different local packages under the same `go.mod`

* Improve code readability
2019-06-06 17:46:30 +00:00
Joe Haddad
11ef8aa816 [now-next] Define Next.js generated routes (#536)
* Match static files before checking dynamic routes

* Add Next.js lambda matching

* Apply suggestions from code review

Co-Authored-By: Connor Davis <mail@connordav.is>

* Correct index logic

* Add builder support for handle filesystem

* Check html rewrites first
2019-06-06 17:46:24 +00:00
Mike Engel
3a122ea950 [now-rust] Install rust with rustup & convert now-rust to typescript (#533)
* fix: Don't try and install rust and openssl during development

* feat: Convert now-rust to typescript

* Fix some typescript errors

* feat: Fix types for now-rust

* fix: Fix typo in type name

* fix: Small cleanups and tweaks from the PR review

* fix: Increase test timeout duration

- Add built JS files from now-build-utils to the eslintignore file

* fix: Compile to dist directory and don't lint now-rust

* fix: Only ignore linting for now-rust's dist dir
2019-06-06 17:46:20 +00:00
Steven
737e50630a Enable tests for all (#544)
* Enable circleci tests for all

* Update readme to mention GH Actions

* Move variable to the top
2019-06-06 17:46:11 +00:00
Lukáš Huvar
fb27b7b9be Fix servless error page fixes#234 (#541) 2019-06-06 17:46:00 +00:00
Steven
d1a4aecd2f Use GitHub Actions for publishing to npm (#539)
* Use GitHub Actions for publishing to npm

* Move publish from CircleCI to GH Actions

* Add npm install

* Fix names

* Use yarn instead of npm

* Fix typo

* Remove publish from circleci

* Fix typo

* Add check for NPM_TOKEN

* Add tag filter

* Remove tag filter
2019-06-06 17:45:55 +00:00
Steven
5ef7014ed8 Update codeowners for python & rust (#538) 2019-06-06 17:45:43 +00:00
Sophearak Tha
0ff2c9950e Show error when using package main with go.mod #528 (#537) 2019-06-06 17:45:31 +00:00
Steven
ddcdcdf3e2 Merge branch 'canary' into master 2019-05-27 17:21:55 -04:00
Mike Engel
bfc99f19d2 [now-rust] Don't try and install rust and openssl during development (#532) 2019-05-27 16:32:09 -04:00
Steven
de2c08cfe8 Publish
- @now/build-utils@0.5.6-canary.0
2019-05-25 14:24:52 -04:00
Steven
9679f07124 [build-utils] add --unsafe-perm to npm install, so that postinstall runs (#530) 2019-05-25 14:24:01 -04:00
Steven
6ce24d6a4e Switch branch during publish (#529) 2019-05-24 17:28:29 -04:00
Steven
e3e029f5f6 Update README.md 2019-05-24 13:37:35 -04:00
Steven
89172a6e89 Publish
- @now/go@0.5.0
 - @now/next@0.4.0
 - @now/node-bridge@1.1.3
 - @now/node-server@0.7.3
 - @now/node@0.7.3
 - @now/python@0.2.4
 - @now/static-build@0.5.7
2019-05-24 12:29:57 -04:00
Steven
e8f1dbaa46 Empty commit to satisfy lerna 2019-05-24 12:27:37 -04:00
Sophearak Tha
16b5b6fdf3 Publish
- @now/python@0.2.4-canary.2
2019-05-24 20:59:19 +07:00
Sophearak Tha
3bab29ff76 Add custom path and proper check (#527)
* Add custom path and proper check

* Change route tests to use another name
2019-05-24 20:55:28 +07:00
Sophearak Tha
d675d2e668 Use unquote() for http handler (#525)
* Use unquote() for http handler

* Add tests for url params http handler
2019-05-24 20:27:44 +07:00
Joe Haddad
2dda88e676 Publish
- @now/next@0.3.4-canary.4
2019-05-23 15:32:28 -07:00
Joe Haddad
5a0090eb1f [now-next] Ensure route begins with a slash (#524)
* Ensure route begins with a slash

* Simplift

* Apply suggestions from code review

Co-Authored-By: Connor Davis <mail@connordav.is>

* Adjust spacing
2019-05-23 15:31:13 -07:00
Sophearak Tha
d438b4ec4e Publish
- @now/go@0.4.8-canary.1
 - @now/python@0.2.4-canary.1
2019-05-23 20:34:53 +07:00
Sophearak Tha
f8810fd7e6 [now-python] Make sure to pass decode url params (#496)
* Make sure to pass decode url params

* Add tests cover default and custom routes behaviour on url param

* Removed `unquote` since `urlparse` already return expected value

* Using unquote in both PATH_INFO and QUERY_STRING

* Better code structure now_init.py

* Better test
2019-05-23 09:27:44 -04:00
Sophearak Tha
a642cfea96 [now-go] Add option to use private Git for go get (#513)
* Add option to use private Git

* Update packages/now-go/index.ts

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

* Fix import error

* Using `GIT_CREDENTIALS` over multiple env vars

* Ignore initialize Git credentials in `meta.isDev`
2019-05-23 11:39:36 +07:00
Joe Haddad
2daa20a9f2 Publish
- @now/next@0.3.4-canary.3
2019-05-22 18:36:15 -07:00
JJ Kasper
4d5c0c40f0 [now-next] Update to use routes for static pages (#521) 2019-05-22 20:25:10 -05:00
Joe Haddad
29051681df Publish
- @now/next@0.3.4-canary.2
2019-05-22 16:18:59 -07:00
JJ Kasper
96d5e81538 [now-next] Handle statically exported pages (#520)
* Add support for auto exported pages

* Add entryDirectory to staticPages mapping

* Map to FsFileRef instead of path
2019-05-22 16:14:24 -07:00
Steven
9ba9dd6949 Publish
- @now/go@0.4.8-canary.0
 - @now/next@0.3.4-canary.1
 - @now/node-bridge@1.1.3-canary.0
 - @now/node-server@0.7.3-canary.0
 - @now/node@0.7.3-canary.0
 - @now/python@0.2.4-canary.0
2019-05-22 15:11:27 -04:00
Steven
b362d57270 [now-node-bridge] Disable callbackWaitsForEmptyEventLoop (#505)
* [now-node] Disable callbackWaitsForEmptyEventLoop

* Fix unit tests
2019-05-22 15:06:02 -04:00
Nathan Rajlich
4ff95e1718 Publish
- @now/go@0.4.7
 - @now/python@0.2.3
2019-05-20 12:40:51 -07:00
Sophearak Tha
ef02bedd4d Publish
- @now/python@0.2.3-canary.0
2019-05-20 23:54:04 +07:00
Sophearak Tha
ed68a09c3e Make sure cwd user clean by using cwd/.now (#516) 2019-05-20 23:53:03 +07:00
Leo Lamprecht
ac7ae5fc5d Run only tests for packages that changed (#515) 2019-05-20 18:10:42 +02:00
Sophearak Tha
9727b1f020 Publish
- @now/go@0.4.7-canary.1
2019-05-20 21:30:41 +07:00
Sophearak Tha
2dc454f15f [now-go] Give user descriptive error message when Go Modules not available (#514)
* Give user descriptive error message when Go Modules not available

* Include go1.11 onward
2019-05-20 21:29:36 +07:00
Sophearak Tha
4463af5c7a Publish
- @now/go@0.4.7-canary.0
 - @now/next@0.3.4-canary.0
2019-05-20 18:56:19 +07:00
Sophearak Tha
c00fb37cf6 [now-go] Use meta in download() (#495)
* Use meta in `download()`

* better handling cwd

* Ignore .now in during parsing entrypoint

* Make re-built faster
2019-05-20 18:52:50 +07:00
Marcel Haupenthal
4deb426f9c [now-go] Ignore folders in analyze.go (#503)(#504) (#506)
* [now-go] Ignore folders in `analyze.go` (#503)(#504)

This commit adds some changes to the way the AST for the source is
built.

The `analyze.go` program now ignores every `vendor`, `.git` and
`testdata` folder. This improves performance, since `vendor` and `.git`
are usually large folders (#504).

By ignoring `testdata`, we mimick the behaviour of `go build` and avoid
failing the parsing because of invalid Go code inside of `testdata` (#503)

* [now-go] Don't ignore `.git` in analyze (#506)

If the user wants to ignore `.git`, he should put it into `.nowignore`
2019-05-17 13:02:20 +07:00
Nathan Rajlich
008b04413a Publish
- @now/next@0.3.3
2019-05-16 11:49:42 -07:00
Nathan Rajlich
f177ba46e9 Publish
- @now/next@0.3.3-canary.0
 - @now/static-build@0.5.7-canary.0
2019-05-16 10:46:18 -07:00
Nathan Rajlich
c030fce589 [now-next] Default NODE_ENV to "development" for dev server (#493) 2019-05-15 18:46:26 -07:00
Nathan Rajlich
50a5150bb5 Publish
- @now/static-build@0.5.6
2019-05-14 10:40:21 -07:00
Nathan Rajlich
0578ccf47e Publish
- @now/static-build@0.5.6-canary.1
2019-05-13 15:58:26 -07:00
Nathan Rajlich
e32cd36ded [now-static-build] Remove srcBase from the proxy pass destination (#499)
The `srcBase` (directory where the entrypoint is located) should not be
in the `dest` proxy pass URL.

Consider an entrypoint like `www/package.json`. The development server
will be running within the `www` directory. A request for `GET
/static/foo.js` comes in, so we want to proxy pass to
`http://localhost:12345/static/foo.js` rather than
`http://localhost:12345/www/static/foo.js` which would lead to a 404.
2019-05-13 15:58:01 -07:00
Nathan Rajlich
6ac0ab121c Publish
- @now/static-build@0.5.6-canary.0
2019-05-13 13:12:35 -07:00
Nathan Rajlich
05db2e6a73 [now-static-build] Add err.sh link when dev server detection fails (#498)
* [now-static-build] Add `err.sh` link when dev server detection fails

The error message alone doesn't explain how to fix it, so adding this
`err.sh` link to guide the user to instructions on how to fix it.

For example: https://github.com/zeit/now-cli/issues/2339

* Shorter title

* should -> must
2019-05-13 13:11:03 -07:00
Nathan Rajlich
0b89d30d6c Publish
- @now/build-utils@0.5.5
 - @now/cgi@0.1.4
 - @now/go@0.4.6
 - @now/mdx-deck@0.5.4
 - @now/next@0.3.2
 - @now/node-server@0.7.2
 - @now/node@0.7.2
 - @now/php@0.5.4
 - @now/python@0.2.2
 - @now/rust@0.2.4
2019-05-11 08:13:57 -07:00
Tim Neutkens
8a021c9417 [now-next] Add support for /api routes (#494) 2019-05-11 16:18:23 +02:00
Nathan Rajlich
f218771382 Publish
- @now/cgi@0.1.4-canary.0
 - @now/go@0.4.6-canary.0
 - @now/mdx-deck@0.5.4-canary.0
 - @now/node-server@0.7.2-canary.1
 - @now/node@0.7.2-canary.2
 - @now/php@0.5.4-canary.0
 - @now/python@0.2.2-canary.0
 - @now/rust@0.2.4-canary.1
2019-05-10 19:19:15 -07:00
Nathan Rajlich
17309291ed [now-node-server] Pass meta to download() function (#489) 2019-05-10 19:18:31 -07:00
Nathan Rajlich
86300577ae [now-rust] Pass meta to download() function 2019-05-10 17:14:22 -07:00
Nathan Rajlich
f9594e0d61 [now-python] Pass meta to download() function (#491) 2019-05-11 01:13:48 +02:00
Nathan Rajlich
20fd4b2e12 [now-php] Download files to workPath and pass meta (#490)
It's not clear to me why we were installing to `userfiles` directory,
so let me know if this breaks something.
2019-05-11 01:13:14 +02:00
Nathan Rajlich
718e4d0e0c [now-mdx-deck] Pass meta to download() function (#488) 2019-05-11 01:12:46 +02:00
Nathan Rajlich
dc3584cd08 [now-cgi] Download files to workPath and pass meta (#487) 2019-05-11 01:12:12 +02:00
Nathan Rajlich
b41788b241 Update yarn.lock 2019-05-10 13:17:05 -07:00
Nathan Rajlich
af9a2f9792 [now-node-server] Update @zeit/ncc to v0.18.5 2019-05-10 13:17:05 -07:00
Nathan Rajlich
f8b8e760de [now-node] Update @zeit/ncc to v0.18.5 2019-05-10 13:17:05 -07:00
Sophearak Tha
93d6ec8024 [now-go] Only use valid exported function with net/http interface (#477)
* Only use valid exported function with `net/http` interface

* Improve log, show link to docs if we coudn't parsed the entrypoint
2019-05-10 21:13:44 +07:00
Nathan Rajlich
7ed6b84056 Remove packages/now-rust/now-rust-0.2.3.tgz (#480)
It appears to have been accidentally committed in
bd2d05344e.
2019-05-08 14:50:57 -07:00
Nathan Rajlich
31da488365 Publish
- @now/build-utils@0.5.5-canary.1
 - @now/next@0.3.2-canary.1
 - @now/node@0.7.2-canary.1
2019-05-08 12:22:17 -07:00
Nathan Rajlich
8eaf05f782 Add initial CODEOWNERS file (#479)
* Add initial `CODEOWNERS` file

* Add `@now/go` to `CODEOWNERS` file
2019-05-08 12:07:32 -07:00
Nathan Rajlich
9311e90f27 [now-next] Fix failing unit test when isDev: true (#478) 2019-05-08 12:00:35 -07:00
Steven
c0de970de2 Add coverage to .gitignore (#473)
* Add coverage to `.gitignore`

* Add .tgz to `.gitignore`

* *.tgz
2019-05-07 17:57:09 -07:00
Nathan Rajlich
465ac2093d [now-node] Pass in the meta object to download() (#475)
Depends on https://github.com/zeit/now-builders/pull/474.
2019-05-07 17:56:39 -07:00
Nathan Rajlich
19ab0e8698 [now-build-utils] Make download() a no-op in now dev (#474)
This will be necessary for the update in `now dev` to have the builder
`workPath` be equal to the `cwd` source directory of the dev server.

Otherwise, unnecessary file modifications are made (copying the source
file to itself) and file corruption often occurs.
2019-05-07 17:56:16 -07:00
Steven
02fa98e5e3 Publish
- @now/build-utils@0.5.5-canary.0
 - @now/next@0.3.2-canary.0
 - @now/node-server@0.7.2-canary.0
 - @now/node@0.7.2-canary.0
 - @now/rust@0.2.4-canary.0
2019-05-07 17:08:55 -04:00
Steven
4aef9d48b0 [now-node] Bump ncc to 0.18.3 (#472) 2019-05-07 15:40:14 -04:00
Luis Fernando Alvarez D
bd2d05344e [now-next] Add public files to the output (#468) 2019-05-07 14:06:05 -05:00
Steven
edc7696623 Add newline to .gitignore 2019-05-07 09:28:59 -04:00
193 changed files with 2819 additions and 907 deletions

View File

@@ -29,14 +29,8 @@ jobs:
- run:
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 releases to npm
command: ./.circleci/publish.sh
workflows:
version: 2
build-and-deploy:
build-and-test:
jobs:
- build

View File

@@ -1,6 +1,13 @@
#!/bin/bash
set -euo pipefail
if [ -z "$NPM_TOKEN" ]; then
echo "NPM_TOKEN not found. Did you forget to assign the GitHub Action secret?"
exit 1
fi
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
if [ ! -e ~/.npmrc ]; then
echo "~/.npmrc file does not exist, skipping publish"
exit 0

43
.editorconfig Normal file
View File

@@ -0,0 +1,43 @@
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{*.json,*.json.example,*.gyp,*.yml,*.yaml,*.workflow}]
indent_style = space
indent_size = 2
[{*.py,*.asm}]
indent_style = space
[*.py]
indent_size = 4
[*.asm]
indent_size = 8
[*.md]
trim_trailing_whitespace = false
indent_style = space
indent_size = 2
# Ideal settings - some plugins might support these
[*.js,*.jsx,*.ts,*.tsx]
quote_type = single
indent_style = space
indent_size = 2
[{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.java,*.go,*.rs,*.php,*.ng,*.d,*.cs,*.swift}]
indent_style = tab
indent_size = 4
tab_width = 4
[{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.js,*.java,*.go,*.rs,*.php,*.ng,*.jsx,*.ts,*.tsx,*.d,*.cs,*.swift}]
curly_bracket_next_line = false
spaces_around_operators = true
spaces_around_brackets = outside
# close enough to 1TB
indent_brace_style = K&R

View File

@@ -3,9 +3,12 @@
/**/node_modules/*
/packages/now-go/go/*
/packages/now-build-utils/dist/*
/packages/now-build-utils/src/*.js
/packages/now-build-utils/src/fs/*.js
/packages/now-node/dist/*
/packages/now-next/dist/*
/packages/now-node-bridge/*
/packages/now-python/*
/packages/now-python/dist/*
/packages/now-optipng/dist/*
/packages/now-go/*
/packages/now-rust/dist/*

9
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,9 @@
# Documentation
# https://help.github.com/en/articles/about-code-owners
* @styfle
/packages/now-node @styfle @tootallnate
/packages/now-next @timer @dav-is
/packages/now-go @styfle @sophearak
/packages/now-python @styfle @sophearak
/packages/now-rust @styfle @mike-engel @anmonteiro

76
.github/main.workflow vendored Normal file
View File

@@ -0,0 +1,76 @@
workflow "Canary publish" {
on = "push"
resolves = ["3. Canary yarn run publish"]
}
action "0. Canary filter" {
uses = "actions/bin/filter@3c0b4f0e63ea54ea5df2914b4fabf383368cd0da"
args = "branch canary"
}
action "0. Canary PR not deleted" {
uses = "actions/bin/filter@3c0b4f0e63ea54ea5df2914b4fabf383368cd0da"
needs = ["0. Canary filter"]
args = "not deleted"
}
action "1. Canary yarn install" {
uses = "actions/npm@59b64a598378f31e49cb76f27d6f3312b582f680"
needs = ["0. Canary PR not deleted"]
runs = "yarn"
args = "install"
}
action "2. Canary yarn run build" {
uses = "actions/npm@59b64a598378f31e49cb76f27d6f3312b582f680"
needs = ["1. Canary yarn install"]
runs = "yarn"
args = "run build"
}
action "3. Canary yarn run publish" {
uses = "actions/npm@59b64a598378f31e49cb76f27d6f3312b582f680"
needs = ["2. Canary yarn run build"]
runs = "yarn"
args = "run publish-from-github"
secrets = ["NPM_TOKEN"]
}
workflow "Master publish" {
on = "push"
resolves = ["3. Master yarn run publish"]
}
action "0. Master filter" {
uses = "actions/bin/filter@3c0b4f0e63ea54ea5df2914b4fabf383368cd0da"
args = "branch master"
}
action "0. Master PR not deleted" {
uses = "actions/bin/filter@3c0b4f0e63ea54ea5df2914b4fabf383368cd0da"
needs = ["0. Master filter"]
args = "not deleted"
}
action "1. Master yarn install" {
uses = "actions/npm@59b64a598378f31e49cb76f27d6f3312b582f680"
needs = ["0. Master PR not deleted"]
runs = "yarn"
args = "install"
}
action "2. Master yarn run build" {
uses = "actions/npm@59b64a598378f31e49cb76f27d6f3312b582f680"
needs = ["1. Master yarn install"]
runs = "yarn"
args = "run build"
}
action "3. Master yarn run publish" {
uses = "actions/npm@59b64a598378f31e49cb76f27d6f3312b582f680"
needs = ["2. Master yarn run build"]
runs = "yarn"
args = "run publish-from-github"
secrets = ["NPM_TOKEN"]
}

4
.gitignore vendored
View File

@@ -1,4 +1,6 @@
node_modules
tmp
target/
.next
.next
coverage
*.tgz

View File

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

View File

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

1
.yarnrc Normal file
View File

@@ -0,0 +1 @@
save-prefix ""

View File

@@ -1,27 +1,43 @@
# now-builders
This is the full list of official Builders provided by the ZEIT team.
This is a monorepo containing the [Official Builders](https://zeit.co/docs/v2/deployments/builders/overview) provided by the ZEIT team.
More details here: https://zeit.co/docs/v2/deployments/builders/overview/
## Channels
There are two Channels:
| Channel | Git Branch | npm dist-tag | use example |
| ------- | ---------- | ------------ | ------------------ |
| Canary | `canary` | `@canary` | `@now/node@canary` |
| Stable | `master` | `@latest` | `@now/node@latest` |
All PRs should be submitted to the `canary` branch.
Once a PR is merged into the `canary` branch, it should be published to npm immediately using the Canary Channel.
### Publishing to npm
Run the following command to publish modified builders to npm:
For the stable channel use:
```
yarn publish-stable
```
For the canary channel use:
For the Canary Channel, publish the modified Builders to npm with the following:
```
yarn publish-canary
```
CircleCI will take care of publishing the updated packages to npm from there.
For the Stable Channel, you must cherry pick each commit from canary to master and then deploy the modified Builders:
If for some reason CircleCI fails to publish the npm package, you may do so
```
git checkout master
git pull # make sure you're up to date
git cherry-pick <PR501_COMMIT_SHA>
git cherry-pick <PR502_COMMIT_SHA>
git cherry-pick <PR503_COMMIT_SHA>
git cherry-pick <PR504_COMMIT_SHA>
# ... etc ...
yarn publish-stable
```
After running this publish step, GitHub Actions will take care of publishing the modified Builder packages to npm.
If for some reason GitHub Actions fails to publish the npm package, you may do so
manually by running `npm publish` from the package directory. Make sure to
include the `--tag canary` parameter if you are publishing a canary release!
use `npm publish --tag canary` if you are publishing a canary release!

View File

@@ -29,12 +29,11 @@ Serverless:
- 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:
1. Serverless Next.js requires Next.js 8 or later, to upgrade you can install the `latest` version:
```
npm install next --save
@@ -46,7 +45,7 @@ npm install next --save
{
"scripts": {
"now-build": "next build"
},
}
}
```
@@ -54,9 +53,9 @@ npm install next --save
```js
module.exports = {
target: 'serverless'
target: 'serverless',
// Other options are still valid
}
};
```
4. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
@@ -70,4 +69,4 @@ module.exports = {
### Useful Links
- [Serverless target implementation](https://github.com/zeit/now-builders/pull/150)
- [Serverless target implementation](https://github.com/zeit/now-builders/pull/150)

View File

@@ -20,7 +20,7 @@ npm install next --save
{
"scripts": {
"now-build": "next build"
},
}
}
```
@@ -28,9 +28,9 @@ npm install next --save
```js
module.exports = {
target: 'serverless'
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`

View File

@@ -0,0 +1,38 @@
# `@now/static-build` Failed to detect a server running
#### Why This Warning Occurred
When running `now dev`, the `@now/static-build` builder proxies relevant HTTP
requests to the server that is created by the `now-dev` script in the
`package.json` file.
In order for `now dev` to know which port the server is running on, the builder
is provided a `$PORT` environment variable that the server _must_ bind to. The
error "Failed to detect a server running on port" is printed if the builder fails
to detect a server listening on that specific port within five minutes.
#### Possible Ways to Fix It
Please ensure that your `now-dev` script binds the spawned development server on
the provided `$PORT` that the builder expects the server to bind to.
For example, if you are using Gatsby, your `now-dev` script must use the `-p`
(port) option to bind to the `$PORT` specified from the builder:
```
{
...
"scripts": {
...
"now-dev": "gatsby develop -p $PORT"
}
}
```
Consult your static builder program's `--help` or documentation to figure out what
the command line flag to bind to a specific port is (in many cases, it is one of:
`-p` / `-P` / `--port`).
### Useful Links
- [`@now/static-build` Local Development Documentation](https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build#local-development)

View File

@@ -1,5 +1,39 @@
const { execSync } = require('child_process');
const { relative } = require('path');
const branch = execSync('git branch | grep "*" | cut -d " " -f2').toString();
console.log(`Running tests on branch "${branch}"`);
const base = branch === 'master' ? 'HEAD~1' : 'origin/canary';
const diff = execSync(`git diff ${base} --name-only`).toString();
const changed = diff
.split('\n')
.filter(item => Boolean(item) && item.includes('packages/'))
.map(item => relative('packages', item).split('/')[0]);
const matches = [];
if (changed.length > 0) {
console.log('The following packages have changed:');
changed.map((item) => {
matches.push(item);
console.log(item);
return null;
});
} else {
matches.push('now-node');
console.log(`No packages changed, defaulting to ${matches[0]}`);
}
const testMatch = Array.from(new Set(matches)).map(
item => `**/${item}/**/?(*.)+(spec|test).[jt]s?(x)`,
);
module.exports = {
testEnvironment: 'node',
testMatch,
collectCoverageFrom: [
'packages/(!test)/**/*.{js,jsx}',
'!**/node_modules/**',

View File

@@ -1,9 +1,7 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/*"
],
"packages": ["packages/*"],
"command": {
"publish": {
"npmClient": "npm",

View File

@@ -12,8 +12,9 @@
"scripts": {
"lerna": "lerna",
"bootstrap": "lerna bootstrap",
"publish-stable": "lerna version",
"publish-canary": "lerna version prerelease --preid canary",
"publish-stable": "git checkout master && git pull && lerna version",
"publish-canary": "git checkout canary && git pull && lerna version prerelease --preid canary",
"publish-from-github": "./.circleci/publish.sh",
"build": "./.circleci/build.sh",
"lint": "eslint .",
"codecov": "codecov",
@@ -31,6 +32,14 @@
"*.ts": [
"prettier --write",
"git add"
],
"*.json": [
"prettier --write",
"git add"
],
"*.md": [
"prettier --write",
"git add"
]
},
"devDependencies": {
@@ -51,6 +60,10 @@
"lint-staged": "^8.0.4",
"node-fetch": "^2.3.0",
"pre-commit": "^1.2.2",
"prettier": "^1.15.2"
"prettier": "1.17.1"
},
"prettier": {
"singleQuote": true,
"trailingComma": "es5"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "0.5.4",
"version": "0.5.8",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -31,7 +31,10 @@ class FileFsRef implements File {
this.fsPath = fsPath;
}
static async fromFsPath({ mode, fsPath }: FileFsRefOptions): Promise<FileFsRef> {
static async fromFsPath({
mode,
fsPath,
}: FileFsRefOptions): Promise<FileFsRef> {
let m = mode;
if (!m) {
const stat = await fs.lstat(fsPath);
@@ -40,7 +43,11 @@ class FileFsRef implements File {
return new FileFsRef({ mode: m, fsPath });
}
static async fromStream({ mode = 0o100644, stream, fsPath }: FromStreamOptions): Promise<FileFsRef> {
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');
@@ -48,7 +55,7 @@ class FileFsRef implements File {
await new Promise<void>((resolve, reject) => {
const dest = fs.createWriteStream(fsPath, {
mode: mode & 0o777
mode: mode & 0o777,
});
stream.pipe(dest);
stream.on('error', reject);
@@ -72,15 +79,15 @@ class FileFsRef implements File {
let flag = false;
// eslint-disable-next-line consistent-return
return multiStream((cb) => {
return multiStream(cb => {
if (flag) return cb(null, null);
flag = true;
this.toStreamAsync()
.then((stream) => {
.then(stream => {
cb(null, stream);
})
.catch((error) => {
.catch(error => {
cb(error, null);
});
});

View File

@@ -4,11 +4,11 @@ import { File, Files, Meta } from '../types';
import { remove, mkdirp, readlink, symlink } from 'fs-extra';
export interface DownloadedFiles {
[filePath: string]: FileFsRef
[filePath: string]: FileFsRef;
}
const S_IFMT = 61440; /* 0170000 type of file */
const S_IFLNK = 40960; /* 0120000 symbolic link */
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;
@@ -17,9 +17,9 @@ export function isSymbolicLink(mode: number): boolean {
async function downloadFile(file: File, fsPath: string): Promise<FileFsRef> {
const { mode } = file;
if (mode && isSymbolicLink(mode) && file.type === 'FileFsRef') {
const [ target ] = await Promise.all([
const [target] = await Promise.all([
readlink((file as FileFsRef).fsPath),
mkdirp(path.dirname(fsPath))
mkdirp(path.dirname(fsPath)),
]);
await symlink(target, fsPath);
return FileFsRef.fromFsPath({ mode, fsPath });
@@ -34,12 +34,25 @@ async function removeFile(basePath: string, fileMatched: string) {
await remove(file);
}
export default async function download(files: Files, basePath: string, meta?: Meta): Promise<DownloadedFiles> {
export default async function download(
files: Files,
basePath: string,
meta?: Meta
): Promise<DownloadedFiles> {
const { isDev = false, filesChanged = null, filesRemoved = null } =
meta || {};
if (isDev) {
// In `now dev`, the `download()` function is a no-op because
// the `basePath` matches the `cwd` of the dev server, so the
// source files are already available.
return files as DownloadedFiles;
}
const files2: DownloadedFiles = {};
const { filesChanged = null, filesRemoved = null } = meta || {};
await Promise.all(
Object.keys(files).map(async (name) => {
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);
@@ -55,7 +68,7 @@ export default async function download(files: Files, basePath: string, meta?: Me
const fsPath = path.join(basePath, name);
files2[name] = await downloadFile(file, fsPath);
}),
})
);
return files2;

View File

@@ -8,12 +8,16 @@ import FileFsRef from '../file-fs-ref';
type GlobOptions = vanillaGlob_.IOptions;
interface FsFiles {
[filePath: string]: FileFsRef
[filePath: string]: FileFsRef;
}
const vanillaGlob = promisify(vanillaGlob_);
export default async function glob(pattern: string, opts: GlobOptions | string, mountpoint?: string): Promise<FsFiles> {
export default async function glob(
pattern: string,
opts: GlobOptions | string,
mountpoint?: string
): Promise<FsFiles> {
let options: GlobOptions;
if (typeof opts === 'string') {
options = { cwd: opts };
@@ -23,7 +27,7 @@ export default async function glob(pattern: string, opts: GlobOptions | string,
if (!options.cwd) {
throw new Error(
'Second argument (basePath) must be specified for names of resulting files',
'Second argument (basePath) must be specified for names of resulting files'
);
}
@@ -41,11 +45,11 @@ export default async function glob(pattern: string, opts: GlobOptions | string,
const files = await vanillaGlob(pattern, options);
for (const relativePath of files) {
const fsPath = path.join(options.cwd!, relativePath);
const fsPath = path.join(options.cwd!, relativePath).replace(/\\/g, '/');
let stat: Stats = options.statCache![fsPath] as Stats;
assert(
stat,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`
);
if (stat.isFile()) {
const isSymlink = options.symlinks![fsPath];

View File

@@ -7,6 +7,6 @@ export default function rename(files: Files, delegate: Delegate): Files {
...newFiles,
[delegate(name)]: files[name],
}),
{},
{}
);
}

View File

@@ -3,6 +3,7 @@ import fs from 'fs-extra';
import path from 'path';
import spawn from 'cross-spawn';
import { SpawnOptions } from 'child_process';
import { deprecate } from 'util';
function spawnAsync(
command: string,
@@ -64,12 +65,14 @@ async function scanParentDirs(destPath: string, scriptName?: string) {
// 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]
);
if (scriptName) {
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')
@@ -85,9 +88,10 @@ async function scanParentDirs(destPath: string, scriptName?: string) {
return { hasScript, hasPackageLockJson };
}
export async function installDependencies(
export async function runNpmInstall(
destPath: string,
args: string[] = []
args: string[] = [],
cmd?: string
) {
assert(path.isAbsolute(destPath));
@@ -95,30 +99,26 @@ export async function installDependencies(
console.log(`installing to ${destPath}`);
const { hasPackageLockJson } = await scanParentDirs(destPath);
const opts = {
const opts: SpawnOptions = {
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),
cmd || 'npm',
commandArgs.concat(['install', '--unsafe-perm']),
destPath,
opts as SpawnOptions
opts
);
} else {
await spawnAsync(
'yarn',
['--ignore-engines', '--cwd', destPath].concat(commandArgs),
cmd || 'yarn',
commandArgs.concat(['--ignore-engines', '--cwd', destPath]),
destPath,
opts as SpawnOptions
opts
);
}
}
@@ -151,4 +151,11 @@ export async function runPackageJsonScript(
return true;
}
export const runNpmInstall = installDependencies;
/**
* installDependencies() is deprecated.
* Please use runNpmInstall() instead.
*/
export const installDependencies = deprecate(
runNpmInstall,
'installDependencies() is deprecated. Please use runNpmInstall() instead.'
);

View File

@@ -1,26 +1,28 @@
import eos from 'end-of-stream';
export default function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> {
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))
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));
}
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

@@ -1,34 +1,51 @@
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 {
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 download, { DownloadedFiles } 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 {
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,
FileBlob,
FileFsRef,
FileRef,
Files,
File,
Meta,
Lambda,
createLambda,
download,
DownloadedFiles,
getWriteableDirectory,
glob,
rename,
installDependencies,
runPackageJsonScript,
runNpmInstall,
runShellScript,
streamToBuffer,
AnalyzeOptions,
BuildOptions,
PrepareCacheOptions,
ShouldServeOptions,
shouldServe,
};

View File

@@ -32,9 +32,7 @@ export class Lambda {
public runtime: string;
public environment: Environment;
constructor({
zipBuffer, handler, runtime, environment,
}: LambdaOptions) {
constructor({ zipBuffer, handler, runtime, environment }: LambdaOptions) {
this.type = 'Lambda';
this.zipBuffer = zipBuffer;
this.handler = handler;
@@ -47,7 +45,10 @@ const sema = new Sema(10);
const mtime = new Date(1540000000000);
export async function createLambda({
files, handler, runtime, environment = {},
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');
@@ -97,7 +98,9 @@ export async function createZip(files: Files): Promise<Buffer> {
}
zipFile.end();
streamToBuffer(zipFile.outputStream).then(resolve).catch(reject);
streamToBuffer(zipFile.outputStream)
.then(resolve)
.catch(reject);
});
return zipBuffer;

View File

@@ -5,7 +5,7 @@ import FileFsRef from './file-fs-ref';
export default function shouldServe({
entrypoint,
files,
requestPath
requestPath,
}: ShouldServeOptions): boolean {
requestPath = requestPath.replace(/\/$/, ''); // sanitize trailing '/'
entrypoint = entrypoint.replace(/\\/, '/'); // windows compatibility
@@ -23,5 +23,5 @@ export default function shouldServe({
}
function hasProp(obj: { [path: string]: FileFsRef }, key: string): boolean {
return Object.hasOwnProperty.call(obj, key)
return Object.hasOwnProperty.call(obj, key);
}

View File

@@ -16,7 +16,14 @@ export interface Files {
}
export interface Config {
[key: string]: string;
[key: string]: string | string[] | boolean | number | undefined;
maxLambdaSize?: string;
includeFiles?: string | string[];
bundle?: boolean;
ldsflags?: string;
helpers?: boolean;
rust?: string;
debug?: boolean;
}
export interface Meta {

View File

@@ -1,9 +1,10 @@
{
"version": 2,
"builds": [
{ "src": "api/index.js", "use": "@now/node" }
],
"builds": [{ "src": "api/index.js", "use": "@now/node" }],
"probes": [
{ "path": "/api/index.js", "mustContain": "cross-cow:RANDOMNESS_PLACEHOLDER" }
{
"path": "/api/index.js",
"mustContain": "cross-cow:RANDOMNESS_PLACEHOLDER"
}
]
}

View File

@@ -1,10 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "index.js", "use": "@now/node", "config": { "maxLambdaSize": "15mb" } }
],
"probes": [
{ "path": "/", "mustContain": "found:RANDOMNESS_PLACEHOLDER" }
]
}
"version": 2,
"builds": [
{
"src": "index.js",
"use": "@now/node",
"config": { "maxLambdaSize": "15mb" }
}
],
"probes": [{ "path": "/", "mustContain": "found:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -15,10 +15,6 @@
"strict": true,
"target": "esnext"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -9,12 +9,13 @@ const { shouldServe } = require('@now/build-utils'); // eslint-disable-line impo
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({ files, entrypoint }) => {
exports.build = async ({
workPath, files, entrypoint, meta,
}) => {
console.log('downloading files...');
const srcDir = await getWritableDirectory();
const outDir = await getWritableDirectory();
await download(files, srcDir);
await download(files, workPath, meta);
const handlerPath = path.join(__dirname, 'handler');
await copyFile(handlerPath, path.join(outDir, 'handler'));
@@ -24,7 +25,7 @@ exports.build = async ({ files, entrypoint }) => {
// For now only the entrypoint file is copied into the lambda
await copyFile(
path.join(srcDir, entrypoint),
path.join(workPath, entrypoint),
path.join(outDir, entrypoint),
);

View File

@@ -1,6 +1,6 @@
{
"name": "@now/cgi",
"version": "0.1.3",
"version": "0.1.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -23,7 +23,7 @@ const getGoUrl = (version: string, platform: string, arch: string) => {
return `https://dl.google.com/go/go${version}.${goPlatform}-${goArch}.${ext}`;
};
export async function getAnalyzedEntrypoint(filePath: string) {
export async function getAnalyzedEntrypoint(filePath: string, modulePath = '') {
debug('Analyzing entrypoint %o', filePath);
const bin = join(__dirname, 'analyze');
@@ -35,7 +35,8 @@ export async function getAnalyzedEntrypoint(filePath: string) {
await go.build(src, dest);
}
const args = [filePath];
const args = [`-modpath=${modulePath}`, filePath];
const analyzed = await execa.stdout(bin, args);
debug('Analyzed entrypoint %o', analyzed);
return analyzed;
@@ -125,7 +126,15 @@ export async function downloadGo(
// If we found GOPATH in ENV, or default `Go` path exists
// asssume that user have `Go` installed
if (isUserGo || process.env.GOPATH !== undefined) {
return createGo(dir, platform, arch);
const { stdout } = await execa('go', ['version']);
if (parseInt(stdout.split('.')[1]) >= 11) {
return createGo(dir, platform, arch);
}
throw new Error(
`Your current ${stdout} doesn't support Go Modules. Please update.`
);
} else {
// Check `Go` bin in builder CWD
const isGoExist = await pathExists(join(dir, 'bin'));

View File

@@ -1,5 +1,7 @@
import { join, sep, dirname } from 'path';
import { join, sep, dirname, basename } from 'path';
import { readFile, writeFile, pathExists, move } from 'fs-extra';
import { homedir } from 'os';
import execa from 'execa';
import {
glob,
@@ -14,6 +16,7 @@ import {
import { createGo, getAnalyzedEntrypoint } from './go-helpers';
interface Analyzed {
found?: boolean;
packageName: string;
functionName: string;
watch: string[];
@@ -28,6 +31,18 @@ interface BuildParamsType extends BuildOptions {
meta: BuildParamsMeta;
}
// Initialize private git repo for Go Modules
async function initPrivateGit(credentials: string) {
await execa('git', [
'config',
'--global',
'credential.helper',
`store --file ${join(homedir(), '.git-credentials')}`,
]);
await writeFile(join(homedir(), '.git-credentials'), credentials);
}
export const version = 2;
export const config = {
@@ -38,8 +53,14 @@ export async function build({
files,
entrypoint,
config,
workPath,
meta = {} as BuildParamsMeta,
}: BuildParamsType) {
if (process.env.GIT_CREDENTIALS && !meta.isDev) {
console.log('Initialize Git credentials...');
await initPrivateGit(process.env.GIT_CREDENTIALS);
}
console.log('Downloading user files...');
const entrypointArr = entrypoint.split(sep);
@@ -48,17 +69,82 @@ export async function build({
getWriteableDirectory(),
]);
const srcPath = join(goPath, 'src', 'lambda');
let downloadedFiles;
if (meta.isDev) {
const devGoPath = `dev${entrypointArr[entrypointArr.length - 1]}`;
const goPathArr = goPath.split(sep);
goPathArr.pop();
goPathArr.push(devGoPath);
goPath = goPathArr.join(sep);
downloadedFiles = await download(files, workPath, meta);
} else {
downloadedFiles = await download(files, srcPath);
}
const srcPath = join(goPath, 'src', 'lambda');
const downloadedFiles = await download(files, srcPath);
const input = dirname(downloadedFiles[entrypoint].fsPath);
console.log(`Parsing AST for "${entrypoint}"`);
let analyzed: string;
try {
let goModAbsPathDir = '';
for (const file of Object.keys(downloadedFiles)) {
if (file === 'go.mod') {
goModAbsPathDir = dirname(downloadedFiles[file].fsPath);
}
}
analyzed = await getAnalyzedEntrypoint(
downloadedFiles[entrypoint].fsPath,
goModAbsPathDir
);
} 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}"
Learn more: https://zeit.co/docs/v2/deployments/official-builders/go-now-go/#entrypoint
`
);
console.log(err.message);
throw err;
}
const parsedAnalyzed = JSON.parse(analyzed) as Analyzed;
if (meta.isDev) {
const base = dirname(downloadedFiles['now.json'].fsPath);
const destNow = join(
base,
'.now',
'cache',
basename(entrypoint, '.go'),
'src',
'lambda'
);
// this will ensure Go rebuilt fast
goPath = join(base, '.now', 'cache', basename(entrypoint, '.go'));
await download(downloadedFiles, destNow);
downloadedFiles = await glob('**', destNow);
}
// find `go.mod` in downloadedFiles
const entrypointDirname = dirname(downloadedFiles[entrypoint].fsPath);
let isGoModExist = false;
let goModPath = '';
let goModPathArr: string[] = [];
for (const file of Object.keys(downloadedFiles)) {
const fileDirname = dirname(downloadedFiles[file].fsPath);
if (file === 'go.mod') {
isGoModExist = true;
goModPath = fileDirname;
goModPathArr = goModPath.split(sep);
} else if (file.includes('go.mod')) {
isGoModExist = true;
if (entrypointDirname === fileDirname) {
goModPath = fileDirname;
goModPathArr = goModPath.split(sep);
}
}
}
const input = entrypointDirname;
var includedFiles: Files = {};
if (config && config.includeFiles) {
@@ -70,37 +156,19 @@ export async function build({
}
}
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
// using `go.mod` way building the handler
const packageName = parsedAnalyzed.packageName;
const isGoModExist = await pathExists(join(entrypointDirname, 'go.mod'));
if (isGoModExist && packageName === 'main') {
throw new Error('Please change `package main` to `package handler`');
}
if (packageName !== 'main') {
const go = await createGo(
goPath,
@@ -132,10 +200,7 @@ export async function build({
const goFuncName = `${packageName}.${handlerFunctionName}`;
if (isGoModExist) {
const goModContents = await readFile(
join(entrypointDirname, 'go.mod'),
'utf8'
);
const goModContents = await readFile(join(goModPath, 'go.mod'), 'utf8');
const usrModName = goModContents.split('\n')[0].split(' ')[1];
goPackageName = `${usrModName}/${packageName}`;
}
@@ -144,11 +209,16 @@ export async function build({
.replace('__NOW_HANDLER_PACKAGE_NAME', goPackageName)
.replace('__NOW_HANDLER_FUNC_NAME', goFuncName);
// write main__mod__.go
await writeFile(
join(entrypointDirname, mainModGoFileName),
mainModGoContents
);
if (goModPathArr.length > 1) {
// using `go.mod` path to write main__mod__.go
await writeFile(join(goModPath, mainModGoFileName), mainModGoContents);
} else {
// using `entrypointDirname` to write main__mod__.go
await writeFile(
join(entrypointDirname, mainModGoFileName),
mainModGoContents
);
}
// move user go file to folder
try {
@@ -169,25 +239,34 @@ export async function build({
);
}
await move(downloadedFiles[entrypoint].fsPath, finalDestination, {
overwrite: forceMove,
});
if (
dirname(downloadedFiles[entrypoint].fsPath) === goModPath ||
!isGoModExist
) {
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'));
let entrypointDir = entrypointDirname;
if (goModPathArr.length > 1) {
entrypointDir = goModPath;
}
const isGoModBk = await pathExists(join(entrypointDir, 'go.mod.bk'));
if (isGoModBk) {
await move(
join(entrypointDirname, 'go.mod.bk'),
join(entrypointDirname, 'go.mod'),
join(entrypointDir, 'go.mod.bk'),
join(entrypointDir, 'go.mod'),
{ overwrite: true }
);
await move(
join(entrypointDirname, 'go.sum.bk'),
join(entrypointDirname, 'go.sum'),
join(entrypointDir, 'go.sum.bk'),
join(entrypointDir, 'go.sum'),
{ overwrite: true }
);
}
@@ -204,8 +283,11 @@ export async function build({
console.log('Running `go build`...');
const destPath = join(outDir, 'handler');
const isGoModInRootDir = goModPathArr.length === 1;
const baseGoModPath = isGoModInRootDir ? entrypointDirname : goModPath;
try {
const src = [join(entrypointDirname, mainModGoFileName)];
let src = [join(baseGoModPath, mainModGoFileName)];
await go.build(src, destPath, config.ldsflags);
} catch (err) {
console.log('failed to `go build`');
@@ -214,17 +296,20 @@ export async function build({
if (meta.isDev) {
// caching for `now dev`
await move(
join(entrypointDirname, 'go.mod'),
join(entrypointDirname, 'go.mod.bk'),
join(baseGoModPath, 'go.mod'),
join(baseGoModPath, 'go.mod.bk'),
{ overwrite: true }
);
await move(
join(entrypointDirname, 'go.sum'),
join(entrypointDirname, 'go.sum.bk'),
join(baseGoModPath, 'go.sum'),
join(baseGoModPath, 'go.sum.bk'),
{ overwrite: true }
);
}
} else {
// legacy mode
// we need `main.go` in the same dir as the entrypoint,
// otherwise `go build` will refuse to build
const go = await createGo(
goPath,
process.platform,
@@ -286,16 +371,17 @@ export async function build({
};
let watch = parsedAnalyzed.watch;
let watchSub: string[] = [];
// 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));
watchSub = parsedAnalyzed.watch.map(file => join(...entrypointArr, file));
}
return {
output,
watch,
watch: watch.concat(watchSub),
};
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "0.4.5",
"version": "0.5.1",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -8,4 +8,4 @@
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory", "mustContain": "subcow:RANDOMNESS_PLACEHOLDER" }
]
}
}

View File

@@ -15,4 +15,4 @@
"mustContain": "RANDOMNESS_PLACEHOLDER"
}
]
}
}

View File

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

View File

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

View File

@@ -5,9 +5,7 @@
"src": "index.go",
"use": "@now/go",
"config": {
"includeFiles": [
"templates/**"
]
"includeFiles": ["templates/**"]
}
}
],

View File

@@ -0,0 +1,22 @@
package function
import (
"fmt"
"net/http"
)
// Person struct
type Person struct {
name string
age int
}
// NewPerson struct method
func NewPerson(name string, age int) *Person {
return &Person{name: name, age: age}
}
// H func
func H(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "RANDOMNESS_PLACEHOLDER")
}

View File

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

View File

@@ -0,0 +1,3 @@
module go-mod
go 1.12

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
package api
import (
"fmt"
"net/http"
"with-shared/shared"
)
// Handler func
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, shared.Say("RANDOMNESS_PLACEHOLDER"))
}

View File

@@ -0,0 +1,3 @@
module with-shared
go 1.12

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "api/*.go", "use": "@now/go" }],
"probes": [{ "path": "/api", "mustContain": "RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,6 @@
package shared
// Say func
func Say(text string) string {
return text
}

View File

@@ -0,0 +1,8 @@
{
"version": 2,
"builds": [{ "src": "**/*.go", "use": "@now/go" }],
"probes": [
{ "path": "/sub-1", "mustContain": "RANDOMNESS_PLACEHOLDER" },
{ "path": "/sub-2", "mustContain": "RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,3 @@
module sub-1
go 1.12

View File

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

View File

@@ -0,0 +1,3 @@
module sub-2
go 1.12

View File

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

View File

@@ -26,8 +26,8 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
await expect(
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
path.join(fixturesPath, fixture)
)
).resolves.toBeDefined();
});
}

View File

@@ -2,6 +2,7 @@ package main
import (
"encoding/json"
"flag"
"fmt"
"go/ast"
"go/parser"
@@ -10,9 +11,22 @@ import (
"log"
"os"
"path/filepath"
"regexp"
"strings"
)
var ignoredFoldersRegex []*regexp.Regexp
func init() {
ignoredFolders := []string{"vendor", "testdata", ".now"}
// Build the regex that matches if a path contains the respective ignored folder
// The pattern will look like: (.*/)?vendor/.*, which matches every path that contains a vendor folder
for _, folder := range ignoredFolders {
ignoredFoldersRegex = append(ignoredFoldersRegex, regexp.MustCompile("(.*/)?"+folder+"/.*"))
}
}
type analyze struct {
PackageName string `json:"packageName"`
FuncName string `json:"functionName"`
@@ -40,8 +54,9 @@ func visit(files *[]string) filepath.WalkFunc {
}
// we don't need Dirs, or test files
// we only want `.go` files
if info.IsDir() || itf || filepath.Ext(path) != ".go" {
// we only want `.go` files. Further, we ignore
// every file that is in one of the ignored folders.
if info.IsDir() || itf || filepath.Ext(path) != ".go" || isInIgnoredFolder(path) {
return nil
}
@@ -50,6 +65,19 @@ func visit(files *[]string) filepath.WalkFunc {
}
}
// isInIgnoredFolder checks if the given path is in one of the ignored folders.
func isInIgnoredFolder(path string) bool {
// Make sure the regex works for Windows paths
path = filepath.ToSlash(path)
for _, pattern := range ignoredFoldersRegex {
if pattern.MatchString(path) {
return true
}
}
return false
}
// return unique file
func unique(files []string) []string {
encountered := map[string]bool{}
@@ -65,13 +93,13 @@ func unique(files []string) []string {
}
func main() {
if len(os.Args) != 2 {
if len(os.Args) != 3 {
// 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")
fmt.Println("Wrong number of args; Usage is:\n ./go-analyze -modpath=module-path file_name.go")
os.Exit(1)
}
fileName := os.Args[1]
fileName := os.Args[2]
rf, err := ioutil.ReadFile(fileName)
if err != nil {
log.Fatal(err)
@@ -95,6 +123,17 @@ func main() {
log.Fatal(err)
}
// looking related packages
var modPath string
flag.StringVar(&modPath, "modpath", "", "module path")
flag.Parse()
if len(modPath) > 1 {
err = filepath.Walk(modPath, visit(&files))
if err != nil {
log.Fatal(err)
}
}
for _, file := range files {
absFileName, _ := filepath.Abs(fileName)
absFile, _ := filepath.Abs(file)
@@ -127,7 +166,14 @@ func main() {
for _, ed := range exportedDecl {
if strings.Contains(se, ed) {
// find relative path of related file
rel, err := filepath.Rel(filepath.Dir(fileName), file)
var basePath string
if modPath == "" {
basePath = filepath.Dir(fileName)
} else {
basePath = modPath
}
rel, err := filepath.Rel(basePath, file)
if err != nil {
log.Fatal(err)
}
@@ -138,24 +184,32 @@ func main() {
}
parsed := parse(fileName)
offset := parsed.Pos()
reqRep := "*http.Request http.ResponseWriter"
for _, decl := range parsed.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok {
// this declaraction is not a function
// this declaration 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),
// find a valid `net/http` handler function
for _, param := range fn.Type.Params.List {
if strings.Contains(reqRep, string(rf[param.Type.Pos()-offset:param.Type.End()-offset])) {
// we found the first exported function with `net/http`
// we're done!
analyzed := analyze{
PackageName: parsed.Name.Name,
FuncName: fn.Name.Name,
Watch: unique(relatedFiles),
}
analyzedJSON, _ := json.Marshal(analyzed)
fmt.Print(string(analyzedJSON))
os.Exit(0)
}
}
json, _ := json.Marshal(analyzed)
fmt.Print(string(json))
os.Exit(0)
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/lambda",
"version": "0.5.3",
"version": "0.5.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,11 +1,22 @@
{
"version": 2,
"builds": [
{ "src": "index.zip", "use": "@now/lambda", "config": { "handler": "index.handler", "runtime": "nodejs8.10" } },
{ "src": "subdirectory/index.zip", "use": "@now/lambda", "config": { "handler": "index.handler", "runtime": "nodejs8.10" } }
{
"src": "index.zip",
"use": "@now/lambda",
"config": { "handler": "index.handler", "runtime": "nodejs8.10" }
},
{
"src": "subdirectory/index.zip",
"use": "@now/lambda",
"config": { "handler": "index.handler", "runtime": "nodejs8.10" }
}
],
"probes": [
{ "path": "/", "mustContain": "cow:NO_REPLACE_TO_AVOID_CRC_MISMATCH" },
{ "path": "/subdirectory/", "mustContain": "yoda:NO_REPLACE_TO_AVOID_CRC_MISMATCH" }
{
"path": "/subdirectory/",
"mustContain": "yoda:NO_REPLACE_TO_AVOID_CRC_MISMATCH"
}
]
}

View File

@@ -39,4 +39,7 @@ exports.build = async ({ files, entrypoint, config }) => {
return { [replacedEntrypoint]: result };
};
exports.shouldServe = shouldServe;
exports.shouldServe = (options) => {
const requestPath = options.requestPath.replace(/\.html$/, '.md');
return shouldServe({ ...options, requestPath });
};

View File

@@ -1,6 +1,6 @@
{
"name": "@now/md",
"version": "0.5.3",
"version": "0.5.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -8,9 +8,11 @@ const { runNpmInstall } = require('@now/build-utils/fs/run-user-scripts.js'); //
const writeFile = promisify(fs.writeFile);
exports.build = async ({ files, entrypoint, workPath }) => {
exports.build = async ({
files, entrypoint, workPath, meta,
}) => {
console.log('downloading user files...');
const downloadedFiles = await download(files, workPath);
const downloadedFiles = await download(files, workPath, meta);
console.log('writing package.json...');
const packageJson = { dependencies: { 'mdx-deck': '1.7.15' } };
const packageJsonPath = path.join(workPath, 'package.json');

View File

@@ -1,6 +1,6 @@
{
"name": "@now/mdx-deck",
"version": "0.5.3",
"version": "0.5.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "0.3.1",
"version": "0.4.2",
"license": "MIT",
"main": "./dist/index",
"scripts": {
@@ -14,7 +14,7 @@
"directory": "packages/now-next"
},
"dependencies": {
"@now/node-bridge": "^1.1.2",
"@now/node-bridge": "^1.2.0",
"fs-extra": "^7.0.0",
"get-port": "^5.0.0",
"resolve-from": "^5.0.0",

View File

@@ -32,10 +32,12 @@ import {
getRoutes,
includeOnlyEntryDirectory,
normalizePackageJson,
onlyStaticDirectory,
filesFromDirectory,
stringMap,
syncEnvVars,
validateEntrypoint,
normalizePage,
getDynamicRoutes,
} from './utils';
interface BuildParamsMeta {
@@ -155,17 +157,16 @@ export const build = async ({
entrypoint,
meta = {} as BuildParamsMeta,
}: BuildParamsType): Promise<{
routes?: any[];
routes?: ({ src?: string; dest?: string } | { handle: string })[];
output: Files;
watch?: string[];
childProcesses: ChildProcess[];
}> => {
validateEntrypoint(entrypoint);
const routes: any[] = [];
const entryDirectory = path.dirname(entrypoint);
const entryPath = path.join(workPath, entryDirectory);
const dotNext = path.join(entryPath, '.next');
const dotNextStatic = path.join(entryPath, '.next/static');
console.log(`${name} Downloading user files...`);
await download(files, workPath, meta);
@@ -191,6 +192,10 @@ export const build = async ({
console.log(`${name} Installing dependencies...`);
await runNpmInstall(entryPath, ['--prefer-offline']);
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'development';
}
// The runtime env vars consist of the base `process.env` vars, but with the
// build env vars removed, and the runtime env vars mixed in afterwards
const runtimeEnv: EnvConfig = Object.assign({}, process.env);
@@ -210,13 +215,19 @@ export const build = async ({
return {
output: {},
routes: getRoutes(entryDirectory, pathsInside, files, urls[entrypoint]),
routes: getRoutes(
entryPath,
entryDirectory,
pathsInside,
files,
urls[entrypoint]
),
watch: pathsInside,
childProcesses: childProcess ? [childProcess] : [],
};
}
if (await pathExists(dotNext)) {
if (await pathExists(dotNextStatic)) {
console.warn(
'WARNING: You should not upload the `.next` directory. See https://zeit.co/docs/v2/deployments/official-builders/next-js-now-next/ for more details.'
);
@@ -248,14 +259,15 @@ export const build = async ({
console.log('normalized package.json result: ', packageJson);
await writePackageJson(entryPath, 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'
console.log(
'Your application is being built using `next build`. ' +
'If you need to define a different build step, please create a `now-build` script in your `package.json` ' +
'(e.g. `{ "scripts": { "now-build": "npm run prepare && next build" } }`).'
);
pkg.scripts = {
'now-build': 'next build',
...(pkg.scripts || {}),
};
console.log('normalized package.json result: ', pkg);
await writePackageJson(entryPath, pkg);
}
@@ -285,7 +297,10 @@ export const build = async ({
await unlinkFile(path.join(entryPath, '.npmrc'));
}
const exportedPageRoutes: { src: string; dest: string }[] = [];
const lambdas: { [key: string]: Lambda } = {};
const staticPages: { [key: string]: FileFsRef } = {};
const dynamicPages: string[] = [];
if (isLegacy) {
const filesAfterBuild = await glob('**', entryPath);
@@ -310,7 +325,9 @@ export const build = async ({
file => file.startsWith('node_modules/.cache')
);
const launcherFiles = {
'now__bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
'now__bridge.js': new FileFsRef({
fsPath: require('@now/node-bridge'),
}),
};
const nextFiles: { [key: string]: FileFsRef } = {
...nodeModules,
@@ -372,15 +389,33 @@ export const build = async ({
} else {
console.log('preparing lambda files...');
const launcherFiles = {
'now__bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
'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(entryPath, '.next', 'serverless', 'pages')
);
const pagesDir = path.join(entryPath, '.next', 'serverless', 'pages');
const pages = await glob('**/*.js', pagesDir);
const staticPageFiles = await glob('**/*.html', pagesDir);
Object.keys(staticPageFiles).forEach((page: string) => {
const staticRoute = path.join(entryDirectory, page);
staticPages[staticRoute] = staticPageFiles[page];
const pathname = page.replace(/\.html$/, '');
if (pathname.startsWith('$') || pathname.includes('/$')) {
dynamicPages.push(pathname);
}
exportedPageRoutes.push({
src: `^${path.join('/', entryDirectory, pathname)}$`,
dest: path.join('/', staticRoute),
});
});
const pageKeys = Object.keys(pages);
@@ -413,12 +448,16 @@ export const build = async ({
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)) {
if (['_app.js', '_document.js'].includes(page)) {
return;
}
const pathname = page.replace(/\.js$/, '');
if (pathname.startsWith('$') || pathname.includes('/$')) {
dynamicPages.push(normalizePage(pathname));
}
console.log(`Creating lambda for page: "${page}"...`);
lambdas[path.join(entryDirectory, pathname)] = await createLambda({
files: {
@@ -448,14 +487,61 @@ export const build = async ({
{}
);
const staticDirectoryFiles = onlyStaticDirectory(
includeOnlyEntryDirectory(files, entryDirectory),
entryDirectory
const entryDirectoryFiles = includeOnlyEntryDirectory(files, entryDirectory);
const staticDirectoryFiles = filesFromDirectory(
entryDirectoryFiles,
path.join(entryDirectory, 'static')
);
const publicDirectoryFiles = filesFromDirectory(
entryDirectoryFiles,
path.join(entryDirectory, 'public')
);
const publicFiles = Object.keys(publicDirectoryFiles).reduce(
(mappedFiles, file) => ({
...mappedFiles,
[file.replace(/public[/\\]+/, '')]: publicDirectoryFiles[file],
}),
{}
);
let dynamicRoutes = getDynamicRoutes(
entryPath,
entryDirectory,
dynamicPages
).map(route => {
// make sure .html is added to dest for now until
// outputting static files to clean routes is available
if (staticPages[`${route.dest}.html`.substr(1)]) {
route.dest = `${route.dest}.html`;
}
return route;
});
return {
output: { ...lambdas, ...staticFiles, ...staticDirectoryFiles },
routes: [],
output: {
...publicFiles,
...lambdas,
...staticPages,
...staticFiles,
...staticDirectoryFiles,
},
routes: [
// Static exported pages (.html rewrites)
...exportedPageRoutes,
// Next.js page lambdas, `static/` folder, reserved assets, and `public/`
// folder
{ handle: 'filesystem' },
// Dynamic routes
...dynamicRoutes,
...(isLegacy
? []
: [
{
src: path.join('/', entryDirectory, '.*'),
dest: path.join('/', entryDirectory, '_error'),
},
]),
],
watch: [],
childProcesses: [],
};

View File

@@ -1,12 +1,16 @@
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
process.env.NODE_ENV =
process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
}
const { Server } = require('http');
const { Bridge } = require('./now__bridge');
const page = require('./page');
const server = new Server(page.render);
// page.render is for React rendering
// page.default is for /api rendering
// page is for module.exports in /api
const server = new Server(page.render || page.default || page);
const bridge = new Bridge(server);
bridge.listen();

View File

@@ -4,7 +4,8 @@ import url from 'url';
import { Bridge } from './now__bridge';
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
process.env.NODE_ENV =
process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
}
const app = next({});

View File

@@ -1,5 +1,6 @@
import fs from 'fs-extra';
import path from 'path';
import resolveFrom from 'resolve-from';
import { Files } from '@now/build-utils';
type stringMap = { [key: string]: string };
@@ -73,11 +74,11 @@ function excludeLockFiles(files: Files): Files {
}
/**
* Include the static directory from files
* Include only the files from a selected directory
*/
function onlyStaticDirectory(files: Files, entryDir: string): Files {
function filesFromDirectory(files: Files, dir: string): Files {
function matcher(filePath: string) {
return !filePath.startsWith(path.join(entryDir, 'static'));
return !filePath.startsWith(dir.replace(/\\/g, '/'));
}
return excludeFiles(files, matcher);
@@ -168,7 +169,18 @@ function getPathsInside(entryDirectory: string, files: Files) {
return watch;
}
function normalizePage(page: string): string {
// remove '/index' from the end
page = page.replace(/\/index$/, '/');
// Resolve on anything that doesn't start with `/`
if (!page.startsWith('/')) {
page = `/${page}`;
}
return page;
}
function getRoutes(
entryPath: string,
entryDirectory: string,
pathsInside: string[],
files: Files,
@@ -195,8 +207,10 @@ function getRoutes(
dest: `${url}/static/$1`,
},
];
const filePaths = Object.keys(filesInside);
const dynamicPages = [];
for (const file of Object.keys(filesInside)) {
for (const file of filePaths) {
const relativePath = path.relative(entryDirectory, file);
const isPage = pathIsInside('pages', relativePath);
@@ -212,6 +226,10 @@ function getRoutes(
continue;
}
if (pageName.startsWith('$') || pageName.includes('/$')) {
dynamicPages.push(normalizePage(pageName));
}
routes.push({
src: `${prefix}${pageName}`,
dest: `${url}/${pageName}`,
@@ -227,6 +245,82 @@ function getRoutes(
}
}
routes.push(
...getDynamicRoutes(entryPath, entryDirectory, dynamicPages).map(
(route: { src: string; dest: string }) => {
// convert to make entire RegExp match as one group
route.src = route.src.replace('^', '^(').replace('$', ')$');
route.dest = `${url}/$1`;
return route;
}
)
);
// Add public folder routes
for (const file of filePaths) {
const relativePath = path.relative(entryDirectory, file);
const isPublic = pathIsInside('public', relativePath);
if (!isPublic) continue;
const fileName = path.relative('public', relativePath);
const route = {
src: `${prefix}${fileName}`,
dest: `${url}/${fileName}`,
};
// Only add the route if a page is not already using it
if (!routes.some(r => r.src === route.src)) {
routes.push(route);
}
}
return routes;
}
export function getDynamicRoutes(
entryPath: string,
entryDirectory: string,
dynamicPages: string[]
): { src: string; dest: string }[] {
if (!dynamicPages.length) {
return [];
}
let getRouteRegex:
| ((pageName: string) => { re: RegExp })
| undefined = undefined;
let getSortedRoutes: ((normalizedPages: string[]) => string[]) | undefined;
try {
({ getRouteRegex, getSortedRoutes } = require(resolveFrom(
entryPath,
'next-server/dist/lib/router/utils'
)));
if (typeof getRouteRegex !== 'function') {
getRouteRegex = undefined;
}
} catch (_) {}
if (!getRouteRegex || !getSortedRoutes) {
throw new Error(
'Found usage of dynamic routes but not on a new enough version of Next.js.'
);
}
const pageMatchers = getSortedRoutes(dynamicPages).map(pageName => ({
pageName,
matcher: getRouteRegex!(pageName).re,
}));
const routes: { src: string; dest: string }[] = [];
pageMatchers.forEach(pageMatcher => {
routes.push({
src: pageMatcher.matcher.source,
dest: path.join('/', entryDirectory, pageMatcher.pageName),
});
});
return routes;
}
@@ -250,10 +344,11 @@ export {
includeOnlyEntryDirectory,
excludeLockFiles,
normalizePackageJson,
onlyStaticDirectory,
filesFromDirectory,
getNextConfig,
getPathsInside,
getRoutes,
stringMap,
syncEnvVars,
normalizePage,
};

View File

@@ -12,10 +12,12 @@ it(
} = await runBuildLambda(path.join(__dirname, 'standard'));
expect(output.index).toBeDefined();
const filePaths = Object.keys(output);
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
const hasUnderScoreAppStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_app\.js$/));
const hasUnderScoreErrorStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_error\.js$/));
expect(hasUnderScoreAppStaticFile).toBeTruthy();
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
expect(serverlessError).toBeTruthy();
},
FOUR_MINUTES,
);
@@ -28,6 +30,7 @@ it(
} = await runBuildLambda(path.join(__dirname, 'monorepo'));
expect(output['www/index']).toBeDefined();
expect(output['www/static/test.txt']).toBeDefined();
expect(output['www/data.txt']).toBeDefined();
const filePaths = Object.keys(output);
const hasUnderScoreAppStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_app\.js$/));
const hasUnderScoreErrorStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_error\.js$/));
@@ -96,3 +99,14 @@ it(
},
FOUR_MINUTES,
);
it(
'Should build the public-files test',
async () => {
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'public-files'));
expect(output['robots.txt']).toBeDefined();
},
FOUR_MINUTES,
);

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{"src": "package.json", "use": "@now/next"}
]
"builds": [{ "src": "package.json", "use": "@now/next" }]
}

View File

@@ -3,4 +3,4 @@
"isomorphic-unfetch": "latest",
"next": "7.0.0"
}
}
}

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{"src": "next.config.js", "use": "@now/next"}
]
"builds": [{ "src": "next.config.js", "use": "@now/next" }]
}

View File

@@ -2,4 +2,4 @@
"dependencies": {
"next": "^7.0.2"
}
}
}

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{"src": "next.config.js", "use": "@now/next"}
]
"builds": [{ "src": "next.config.js", "use": "@now/next" }]
}

View File

@@ -2,4 +2,4 @@
"dependencies": {
"next": "7.0.2"
}
}
}

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{ "src": "www/package.json", "use": "@now/next" }
]
"version": 2,
"builds": [{ "src": "www/package.json", "use": "@now/next" }]
}

View File

@@ -0,0 +1 @@
data

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{"src": "pages/index.js", "use": "@now/next"}
]
"builds": [{ "src": "pages/index.js", "use": "@now/next" }]
}

View File

@@ -0,0 +1,3 @@
module.exports = {
target: 'serverless',
};

View File

@@ -0,0 +1,4 @@
{
"version": 2,
"builds": [{ "src": "next.config.js", "use": "@now/next" }]
}

View File

@@ -0,0 +1,10 @@
{
"scripts": {
"now-build": "next build"
},
"dependencies": {
"next": "8",
"react": "16",
"react-dom": "16"
}
}

View File

@@ -0,0 +1 @@
export default () => 'Index page';

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{"src": "next.config.js", "use": "@now/next"}
]
"builds": [{ "src": "next.config.js", "use": "@now/next" }]
}

View File

@@ -4,4 +4,4 @@
"react": "latest",
"react-dom": "latest"
}
}
}

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{"src": "next.config.js", "use": "@now/next"}
]
"builds": [{ "src": "next.config.js", "use": "@now/next" }]
}

View File

@@ -7,4 +7,4 @@
"react": "16",
"react-dom": "16"
}
}
}

View File

@@ -2,7 +2,7 @@
const path = require('path');
const os = require('os');
const { build } = require('@now/next');
const { FileBlob } = require('@now/build-utils');
const { download, FileBlob } = require('@now/build-utils');
jest.setTimeout(45000);
@@ -22,6 +22,15 @@ describe('build meta dev', () => {
export default () => 'Index page'
`,
}),
// This file should be omitted because `pages/index.js` will use the same route
'public/index': new FileBlob({
mode: 0o777,
data: 'text',
}),
'public/data.txt': new FileBlob({
mode: 0o777,
data: 'data',
}),
'package.json': new FileBlob({
mode: 0o777,
data: `
@@ -46,24 +55,12 @@ describe('build meta dev', () => {
.slice(3),
);
console.log('workPath directory: ', workPath);
/*
it('should have builder v2 response isDev=false', async () => {
const meta = { isDev: false, requestPath: null };
const { output, routes, watch } = await build({
files,
workPath,
entrypoint,
meta,
});
//console.log('output: ', Object.keys(output));
expect(Object.keys(output).length).toBe(7);
expect(output.index.type).toBe('Lambda');
expect(routes.length).toBe(0);
expect(watch.length).toBe(0);
});
*/
it('should have builder v2 response isDev=true', async () => {
// Since `download()` is a no-op when `isDev=true`, the assumption is that the
// source files are already present, so manually download them here first.
await download(files, workPath);
const meta = { isDev: true, requestPath: null };
const {
output, routes, watch, childProcesses,
@@ -83,8 +80,15 @@ describe('build meta dev', () => {
{ src: '/static/(.*)', dest: 'http://localhost:5000/static/$1' },
{ src: '/index', dest: 'http://localhost:5000/index' },
{ src: '/', dest: 'http://localhost:5000/' },
{ src: '/data.txt', dest: 'http://localhost:5000/data.txt' },
]);
expect(watch).toEqual([
'next.config.js',
'pages/index.js',
'public/index',
'public/data.txt',
'package.json',
]);
expect(watch).toEqual(['next.config.js', 'pages/index.js', 'package.json']);
childProcesses.forEach(cp => cp.kill());
});
});

View File

@@ -9,10 +9,6 @@
"sourceMap": false,
"declaration": false
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-bridge",
"version": "1.1.2",
"version": "1.2.0",
"license": "MIT",
"main": "./index.js",
"repository": {

View File

@@ -1,10 +1,10 @@
import { AddressInfo } from 'net';
import { APIGatewayProxyEvent } from 'aws-lambda';
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
import {
Server,
IncomingHttpHeaders,
OutgoingHttpHeaders,
request
request,
} from 'http';
interface NowProxyEvent {
@@ -95,9 +95,13 @@ export class Bridge {
private server: Server | null;
private listening: Promise<AddressInfo>;
private resolveListening: (info: AddressInfo) => void;
private events: { [key: string]: NowProxyRequest } = {};
private reqIdSeed: number = 1;
private shouldStoreEvents: boolean = false;
constructor(server?: Server) {
constructor(server?: Server, shouldStoreEvents: boolean = false) {
this.server = null;
this.shouldStoreEvents = shouldStoreEvents;
if (server) {
this.setServer(server);
}
@@ -133,26 +137,27 @@ export class Bridge {
return this.server.listen({
host: '127.0.0.1',
port: 0
port: 0,
});
}
async launcher(
event: NowProxyEvent | APIGatewayProxyEvent
event: NowProxyEvent | APIGatewayProxyEvent,
context: Context
): Promise<NowProxyResponse> {
context.callbackWaitsForEmptyEventLoop = false;
const { port } = await this.listening;
const { isApiGateway, method, path, headers, body } = normalizeEvent(
event
);
const normalizedEvent = normalizeEvent(event);
const { isApiGateway, method, path, headers, body } = normalizedEvent;
const opts = {
hostname: '127.0.0.1',
port,
path,
method,
headers
};
if (this.shouldStoreEvents) {
const reqId = `${this.reqIdSeed++}`;
this.events[reqId] = normalizedEvent;
headers['x-now-bridge-request-id'] = reqId;
}
const opts = { hostname: '127.0.0.1', port, path, method, headers };
// eslint-disable-next-line consistent-return
return new Promise((resolve, reject) => {
@@ -175,7 +180,7 @@ export class Bridge {
statusCode: response.statusCode || 200,
headers: response.headers,
body: bodyBuffer.toString('base64'),
encoding: 'base64'
encoding: 'base64',
});
});
});
@@ -192,4 +197,10 @@ export class Bridge {
req.end();
});
}
consumeEvent(reqId: string) {
const event = this.events[reqId];
delete this.events[reqId];
return event;
}
}

View File

@@ -16,56 +16,108 @@ test('port binding', async () => {
});
test('`APIGatewayProxyEvent` normalizing', async () => {
const server = new Server((req, res) => res.end(
JSON.stringify({
method: req.method,
path: req.url,
headers: req.headers,
}),
));
const server = new Server((req, res) =>
res.end(
JSON.stringify({
method: req.method,
path: req.url,
headers: req.headers,
})
)
);
const bridge = new Bridge(server);
bridge.listen();
const result = await bridge.launcher({
httpMethod: 'GET',
headers: { foo: 'bar' },
path: '/apigateway',
body: null,
});
const context = {};
const result = await bridge.launcher(
{
httpMethod: 'GET',
headers: { foo: 'bar' },
path: '/apigateway',
body: null,
},
context
);
assert.equal(result.encoding, 'base64');
assert.equal(result.statusCode, 200);
const body = JSON.parse(Buffer.from(result.body, 'base64').toString());
assert.equal(body.method, 'GET');
assert.equal(body.path, '/apigateway');
assert.equal(body.headers.foo, 'bar');
assert.equal(context.callbackWaitsForEmptyEventLoop, false);
server.close();
});
test('`NowProxyEvent` normalizing', async () => {
const server = new Server((req, res) => res.end(
JSON.stringify({
method: req.method,
path: req.url,
headers: req.headers,
}),
));
const server = new Server((req, res) =>
res.end(
JSON.stringify({
method: req.method,
path: req.url,
headers: req.headers,
})
)
);
const bridge = new Bridge(server);
bridge.listen();
const result = await bridge.launcher({
Action: 'Invoke',
body: JSON.stringify({
method: 'POST',
headers: { foo: 'baz' },
path: '/nowproxy',
body: 'body=1',
}),
});
const context = { callbackWaitsForEmptyEventLoop: true };
const result = await bridge.launcher(
{
Action: 'Invoke',
body: JSON.stringify({
method: 'POST',
headers: { foo: 'baz' },
path: '/nowproxy',
body: 'body=1',
}),
},
context
);
assert.equal(result.encoding, 'base64');
assert.equal(result.statusCode, 200);
const body = JSON.parse(Buffer.from(result.body, 'base64').toString());
assert.equal(body.method, 'POST');
assert.equal(body.path, '/nowproxy');
assert.equal(body.headers.foo, 'baz');
assert.equal(context.callbackWaitsForEmptyEventLoop, false);
server.close();
});
test('consumeEvent', async () => {
const mockListener = jest.fn((req, res) => {
res.end('hello');
});
const server = new Server(mockListener);
const bridge = new Bridge(server, true);
bridge.listen();
const context = { callbackWaitsForEmptyEventLoop: true };
await bridge.launcher(
{
Action: 'Invoke',
body: JSON.stringify({
method: 'POST',
headers: { foo: 'baz' },
path: '/nowproxy',
body: 'body=1',
}),
},
context
);
const headers = mockListener.mock.calls[0][0].headers;
const reqId = headers['x-now-bridge-request-id'];
expect(reqId).toBeTruthy();
const event = bridge.consumeEvent(reqId);
expect(event.body.toString()).toBe('body=1');
// an event can't be consumed multiple times
// to avoid memory leaks
expect(bridge.consumeEvent(reqId)).toBeUndefined();
server.close();
});

View File

@@ -9,10 +9,6 @@
"sourceMap": true,
"declaration": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -28,11 +28,13 @@ const { shouldServe } = require('@now/build-utils'); // eslint-disable-line impo
* @param {string[]} [options.npmArguments]
*/
async function downloadInstallAndBundle(
{ files, entrypoint, workPath },
{
files, entrypoint, workPath, meta,
},
{ npmArguments = [] } = {},
) {
console.log('downloading user files...');
const downloadedFiles = await download(files, workPath);
const downloadedFiles = await download(files, workPath, meta);
console.log("installing dependencies for user's code...");
const entrypointFsDirname = path.join(workPath, path.dirname(entrypoint));
@@ -99,10 +101,15 @@ exports.config = {
* @returns {Promise<Files>}
*/
exports.build = async ({
files, entrypoint, config, workPath,
files, entrypoint, config, workPath, meta,
}) => {
const [downloadedFiles, entrypointFsDirname] = await downloadInstallAndBundle(
{ files, entrypoint, workPath },
{
files,
entrypoint,
workPath,
meta,
},
{ npmArguments: ['--prefer-offline'] },
);

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-server",
"version": "0.7.1",
"version": "0.7.5",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,8 +8,8 @@
"directory": "packages/now-node-server"
},
"dependencies": {
"@now/node-bridge": "^1.1.2",
"@zeit/ncc": "0.18.2",
"@now/node-bridge": "^1.2.0",
"@zeit/ncc": "0.18.5",
"fs-extra": "7.0.1"
},
"scripts": {

View File

@@ -1,6 +1,4 @@
{
"version": 2,
"builds": [
{ "src": "index.js", "use": "@now/node-server" }
]
"builds": [{ "src": "index.js", "use": "@now/node-server" }]
}

View File

@@ -1,9 +1,10 @@
{
"version": 2,
"builds": [
{ "src": "index.js", "use": "@now/node-server" }
],
"builds": [{ "src": "index.js", "use": "@now/node-server" }],
"probes": [
{ "path": "/", "mustContain": "asset1:RANDOMNESS_PLACEHOLDER,asset2:RANDOMNESS_PLACEHOLDER" }
{
"path": "/",
"mustContain": "asset1:RANDOMNESS_PLACEHOLDER,asset2:RANDOMNESS_PLACEHOLDER"
}
]
}

View File

@@ -2,10 +2,20 @@
"version": 2,
"builds": [
{ "src": "with-bundle/index.js", "use": "@now/node-server" },
{ "src": "without-bundle/index.js", "use": "@now/node-server", "config": { "bundle": false } }
{
"src": "without-bundle/index.js",
"use": "@now/node-server",
"config": { "bundle": false }
}
],
"probes": [
{ "path": "/with-bundle", "mustContain": "RANDOMNESS_PLACEHOLDER:with-bundle" },
{ "path": "/without-bundle", "mustContain": "RANDOMNESS_PLACEHOLDER:without-bundle" }
{
"path": "/with-bundle",
"mustContain": "RANDOMNESS_PLACEHOLDER:with-bundle"
},
{
"path": "/without-bundle",
"mustContain": "RANDOMNESS_PLACEHOLDER:without-bundle"
}
]
}

View File

@@ -2,7 +2,11 @@ const http = require('http');
const isBundled = require('./is-bundled.js');
const server = http.createServer((req, resp) => {
resp.end(isBundled() ? 'RANDOMNESS_PLACEHOLDER:with-bundle' : 'WITHOUT-BUNDLE-THAT-IS-WRONG');
resp.end(
isBundled()
? 'RANDOMNESS_PLACEHOLDER:with-bundle'
: 'WITHOUT-BUNDLE-THAT-IS-WRONG',
);
});
server.listen();

View File

@@ -2,7 +2,11 @@ const http = require('http');
const isBundled = require('./is-bundled.js');
const server = http.createServer((req, resp) => {
resp.end(isBundled() ? 'WITH-BUNDLE-THAT-IS-WRONG' : 'RANDOMNESS_PLACEHOLDER:without-bundle');
resp.end(
isBundled()
? 'WITH-BUNDLE-THAT-IS-WRONG'
: 'RANDOMNESS_PLACEHOLDER:without-bundle',
);
});
server.listen();

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