mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-24 11:49:13 +00:00
Compare commits
180 Commits
@now/node@
...
now-cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e1b099285 | ||
|
|
87b1f5a5fb | ||
|
|
ae8cbdce0a | ||
|
|
95de48c03f | ||
|
|
5ea4fd9a5e | ||
|
|
1f3d9e7e0f | ||
|
|
05e4528b1c | ||
|
|
40789776bc | ||
|
|
75759edd26 | ||
|
|
2a5dc1dba6 | ||
|
|
0cbf50b7ff | ||
|
|
a3f5b805bb | ||
|
|
c337877501 | ||
|
|
a6e9dd850a | ||
|
|
a14d4057de | ||
|
|
210240ce66 | ||
|
|
d21f7fe75c | ||
|
|
54805cd2a0 | ||
|
|
c7da2e732a | ||
|
|
4b4beaa892 | ||
|
|
e13596acae | ||
|
|
8eb065181f | ||
|
|
1d97219fef | ||
|
|
d312f94825 | ||
|
|
478a6f7369 | ||
|
|
e309123296 | ||
|
|
777646cb0e | ||
|
|
39e23f7144 | ||
|
|
b68b5c0ea3 | ||
|
|
46dfeb8ca9 | ||
|
|
6771ad43af | ||
|
|
1881b9c6cb | ||
|
|
dae6b7a980 | ||
|
|
aed6209f6a | ||
|
|
b44a590aa0 | ||
|
|
936b5fc983 | ||
|
|
473d6617f2 | ||
|
|
fcf50b5aeb | ||
|
|
a3835d2e8a | ||
|
|
d89481966e | ||
|
|
3148ce0f31 | ||
|
|
f65363856a | ||
|
|
e205b57352 | ||
|
|
f44a1dbf21 | ||
|
|
b7fe2f606c | ||
|
|
6e81c1795c | ||
|
|
141d8e4467 | ||
|
|
edbfeab75f | ||
|
|
e48ea10ebb | ||
|
|
8565af5526 | ||
|
|
511d3bae8f | ||
|
|
8df233fef1 | ||
|
|
4b97410cc0 | ||
|
|
aadf8097d1 | ||
|
|
40d8b74b1b | ||
|
|
8c4d42891c | ||
|
|
3672374d23 | ||
|
|
9ff9d3f174 | ||
|
|
86540fa7fd | ||
|
|
1538c80a7d | ||
|
|
0225fcfe51 | ||
|
|
57f3861de6 | ||
|
|
04c365a251 | ||
|
|
7dfe4690ce | ||
|
|
92bcf1b7c9 | ||
|
|
8a15d5c65a | ||
|
|
f64374225d | ||
|
|
7537eac6a7 | ||
|
|
e2880d2434 | ||
|
|
8296de16ef | ||
|
|
5cff5e9dfd | ||
|
|
cb07a748c2 | ||
|
|
c7b985bdc6 | ||
|
|
bf32ca0e4a | ||
|
|
a4e52de0e3 | ||
|
|
e43e9b11a0 | ||
|
|
847b9e97c4 | ||
|
|
baad689286 | ||
|
|
5e7afc4385 | ||
|
|
d724b7a631 | ||
|
|
5e17fe5ad6 | ||
|
|
b216adadc0 | ||
|
|
5078c95667 | ||
|
|
7612d77647 | ||
|
|
0d76041c10 | ||
|
|
59be596d24 | ||
|
|
63e51a3c98 | ||
|
|
7f3128b3e5 | ||
|
|
c235813ae7 | ||
|
|
3ee18e7051 | ||
|
|
14fc5d8796 | ||
|
|
9fb0077385 | ||
|
|
2e9c7265b6 | ||
|
|
d9e77b784a | ||
|
|
b5b296ad7f | ||
|
|
acdfde5aa2 | ||
|
|
0eddfbd28c | ||
|
|
037f0610bc | ||
|
|
dd45f8f2ab | ||
|
|
d454c84f61 | ||
|
|
a4c98e07a5 | ||
|
|
85715630bd | ||
|
|
45d8d4a84f | ||
|
|
690882c97a | ||
|
|
e03836e4a1 | ||
|
|
d4ec54135a | ||
|
|
b29785d851 | ||
|
|
148870d706 | ||
|
|
ed1f7e335d | ||
|
|
eaf4695194 | ||
|
|
c312d42302 | ||
|
|
a8a2a6c066 | ||
|
|
8b3512cb07 | ||
|
|
945facdd2c | ||
|
|
8676ed4cff | ||
|
|
e4d4afa840 | ||
|
|
81cf286ea4 | ||
|
|
e834625728 | ||
|
|
a7c22eb08c | ||
|
|
837c358371 | ||
|
|
9743db27e7 | ||
|
|
ce725143e6 | ||
|
|
677805c33a | ||
|
|
3d3f1fe39b | ||
|
|
123c68ad2b | ||
|
|
86861c58af | ||
|
|
d4ddb6b3f9 | ||
|
|
0fc7de40d4 | ||
|
|
3cb4a9c8dd | ||
|
|
d681289457 | ||
|
|
bc1c3c3f5b | ||
|
|
5ae4287c0f | ||
|
|
cde9f56886 | ||
|
|
2b6b006bbd | ||
|
|
54e1bcafc0 | ||
|
|
7e98d0a22b | ||
|
|
c27b4a6aaf | ||
|
|
8f17ffd817 | ||
|
|
0a7d688d32 | ||
|
|
8cb2fe1284 | ||
|
|
e6e375232e | ||
|
|
08c4ab8a0c | ||
|
|
f86647fc26 | ||
|
|
f310f6a86f | ||
|
|
58c6acd265 | ||
|
|
c7d97e3866 | ||
|
|
0890144c61 | ||
|
|
69a7d91b57 | ||
|
|
a9016c88f6 | ||
|
|
e8990742cf | ||
|
|
f51400a3a1 | ||
|
|
e763ee5301 | ||
|
|
fd978699e8 | ||
|
|
bbd9585829 | ||
|
|
54cf1ebb31 | ||
|
|
6fc77c6cfa | ||
|
|
f8372e3bb9 | ||
|
|
6728be7b1a | ||
|
|
65e1a1e731 | ||
|
|
cd9478e853 | ||
|
|
b9364ed4fc | ||
|
|
2715e8e9d8 | ||
|
|
f987c93cf0 | ||
|
|
5c254a7151 | ||
|
|
f253e29f33 | ||
|
|
f37fa13eab | ||
|
|
64765b393a | ||
|
|
54c84b4ce0 | ||
|
|
146bcba794 | ||
|
|
d3dd1b731d | ||
|
|
d608ee7390 | ||
|
|
4e2e0950c7 | ||
|
|
ddc7e97ab6 | ||
|
|
a21759ee42 | ||
|
|
cc4beb94cf | ||
|
|
0bfafa9311 | ||
|
|
4eefc34629 | ||
|
|
4d3f882dc0 | ||
|
|
3a802fbb70 | ||
|
|
6f00b03d24 |
@@ -1,135 +1,72 @@
|
||||
version: 2
|
||||
jobs:
|
||||
|
||||
install:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
- image: circleci/node:9.10.0
|
||||
working_directory: ~/repo
|
||||
environment:
|
||||
GOPATH: $HOME/go
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "yarn.lock" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- v1-dependencies-
|
||||
- run:
|
||||
name: Updating apt packages
|
||||
command: sudo apt-get update
|
||||
- run:
|
||||
name: Installing the latest version of Go
|
||||
command: sudo apt-get install golang-go
|
||||
- v1-dependencies-{{ checksum "yarn.lock" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- v1-dependencies-
|
||||
- run:
|
||||
name: Installing Dependencies
|
||||
command: yarn install --check-files --frozen-lockfile
|
||||
command: yarn
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
- packages/gatsby-plugin-now/node_modules
|
||||
- packages/now-build-utils/node_modules
|
||||
- packages/now-cgi/node_modules
|
||||
- packages/now-cli/node_modules
|
||||
- packages/now-client/node_modules
|
||||
- packages/now-go/node_modules
|
||||
- packages/now-next/node_modules
|
||||
- packages/now-node/node_modules
|
||||
- packages/now-node-bridge/node_modules
|
||||
- packages/now-python/node_modules
|
||||
- packages/now-routing-utils/node_modules
|
||||
- packages/now-ruby/node_modules
|
||||
- packages/now-static-build/node_modules
|
||||
key: v1-dependencies-{{ checksum "yarn.lock" }}
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- node_modules
|
||||
- packages/gatsby-plugin-now/node_modules
|
||||
- packages/now-build-utils/node_modules
|
||||
- packages/now-cgi/node_modules
|
||||
- packages/now-cli/node_modules
|
||||
- packages/now-client/node_modules
|
||||
- packages/now-go/node_modules
|
||||
- packages/now-next/node_modules
|
||||
- packages/now-node/node_modules
|
||||
- packages/now-node-bridge/node_modules
|
||||
- packages/now-python/node_modules
|
||||
- packages/now-routing-utils/node_modules
|
||||
- packages/now-ruby/node_modules
|
||||
- packages/now-static-build/node_modules
|
||||
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
- image: circleci/node:9.10.0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Installing apt dependencies
|
||||
command: sudo apt install -y rsync
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Building
|
||||
command: yarn build
|
||||
name: Compiling Binaries
|
||||
command: yarn run pack
|
||||
- store_artifacts:
|
||||
path: packages/now-cli/dist
|
||||
path: packed
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- packages/gatsby-plugin-now/test/fixtures
|
||||
- packages/now-build-utils/dist
|
||||
- packages/now-cgi/dist
|
||||
- packages/now-cli/dist
|
||||
- packages/now-cli/assets
|
||||
- packages/now-client/dist
|
||||
- packages/now-go/dist
|
||||
- packages/now-next/dist
|
||||
- packages/now-node/dist
|
||||
- packages/now-node/test/fixtures/15-helpers/ts/types.d.ts
|
||||
- packages/now-node/test/fixtures/11-symlinks/symlink
|
||||
- packages/now-node-bridge/index.js
|
||||
- packages/now-node-bridge/bridge.js
|
||||
- packages/now-python/dist
|
||||
- packages/now-routing-utils/dist
|
||||
- packages/now-ruby/dist
|
||||
- packages/now-static-build/dist
|
||||
- packages/now-static-build/test/fixtures/10a-gatsby-redirects/plugins
|
||||
- packed
|
||||
|
||||
test-lint:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
- image: circleci/node:9.10.0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Compiling `now dev` HTML error templates
|
||||
command: node packages/now-cli/scripts/compile-templates.js
|
||||
- run:
|
||||
name: Linting Code
|
||||
command: yarn test-lint
|
||||
|
||||
# test-unit:
|
||||
# docker:
|
||||
# - image: circleci/node:10
|
||||
# working_directory: ~/repo
|
||||
# steps:
|
||||
# - checkout
|
||||
# - attach_workspace:
|
||||
# at: .
|
||||
# - run:
|
||||
# name: Compiling `now dev` HTML error templates
|
||||
# command: node packages/now-cli/scripts/compile-templates.js
|
||||
# - run:
|
||||
# name: Running Unit Tests
|
||||
# command: yarn test-unit --clean false
|
||||
# - persist_to_workspace:
|
||||
# root: .
|
||||
# paths:
|
||||
# - packages/now-cli/.nyc_output
|
||||
test-unit:
|
||||
docker:
|
||||
- image: circleci/node:9.10.0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Running Unit Tests
|
||||
command: yarn test-unit
|
||||
|
||||
test-integration-macos-node-8:
|
||||
test-integration:
|
||||
macos:
|
||||
xcode: '9.2.0'
|
||||
working_directory: ~/repo
|
||||
@@ -137,259 +74,59 @@ jobs:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Update Node.js
|
||||
command: curl -sfLS install-node.now.sh/8.11 | sh -s -- --yes
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Running Integration Tests
|
||||
command: yarn test-integration --clean false
|
||||
command: yarn test-integration
|
||||
|
||||
test-integration-macos-node-10:
|
||||
macos:
|
||||
xcode: '10.0.0'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Running Integration Tests
|
||||
command: yarn test-integration --clean false
|
||||
|
||||
test-integration-macos-node-12:
|
||||
macos:
|
||||
xcode: '10.3.0'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Running Integration Tests
|
||||
command: yarn test-integration --clean false
|
||||
|
||||
test-integration-linux-node-8:
|
||||
compress:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
- image: circleci/node:9.10.0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Running Integration Tests
|
||||
command: yarn test-integration --clean false
|
||||
name: Compressing Binaries
|
||||
command: yarn gzip
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- packed
|
||||
|
||||
test-integration-linux-node-10:
|
||||
upload:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
- image: circleci/golang:1.10
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-pkg-cache
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
name: Installing Uploader
|
||||
command: go get github.com/aktau/github-release
|
||||
- run:
|
||||
name: Running Integration Tests
|
||||
command: yarn test-integration --clean false
|
||||
|
||||
test-integration-linux-node-12:
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Running Integration Tests
|
||||
command: yarn test-integration --clean false
|
||||
|
||||
test-integration-macos-now-dev-node-8:
|
||||
macos:
|
||||
xcode: '9.2.0'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Update Node.js
|
||||
command: curl -sfLS install-node.now.sh/8.11 | sh -s -- --yes
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Downloading Hugo
|
||||
command: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run:
|
||||
name: Running Integration Tests for `now dev`
|
||||
command: yarn test-integration-now-dev --clean false
|
||||
|
||||
test-integration-macos-now-dev-node-10:
|
||||
macos:
|
||||
xcode: '10.0.0'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Downloading Hugo
|
||||
command: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run:
|
||||
name: Running Integration Tests for `now dev`
|
||||
command: yarn test-integration-now-dev --clean false
|
||||
|
||||
test-integration-macos-now-dev-node-12:
|
||||
macos:
|
||||
xcode: '10.3.0'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Downloading Hugo
|
||||
command: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run:
|
||||
name: Running Integration Tests for `now dev`
|
||||
command: yarn test-integration-now-dev --clean false
|
||||
|
||||
test-integration-linux-now-dev-node-8:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Downloading Hugo
|
||||
command: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.55.6/hugo_0.55.6_Linux-64bit.tar.gz && tar -xzf hugo_0.55.6_Linux-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run:
|
||||
name: Running Integration Tests for `now dev`
|
||||
command: yarn test-integration-now-dev --clean false
|
||||
|
||||
test-integration-linux-now-dev-node-10:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Downloading Hugo
|
||||
command: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.55.6/hugo_0.55.6_Linux-64bit.tar.gz && tar -xzf hugo_0.55.6_Linux-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run:
|
||||
name: Running Integration Tests for `now dev`
|
||||
command: yarn test-integration-now-dev --clean false
|
||||
|
||||
test-integration-linux-now-dev-node-12:
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Downloading Hugo
|
||||
command: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.55.6/hugo_0.55.6_Linux-64bit.tar.gz && tar -xzf hugo_0.55.6_Linux-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run:
|
||||
name: Running Integration Tests for `now dev`
|
||||
command: yarn test-integration-now-dev --clean false
|
||||
|
||||
test-integration-once:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Output version
|
||||
command: node --version
|
||||
- run:
|
||||
name: Running Integration Tests Once
|
||||
command: yarn test-integration-once --clean false
|
||||
|
||||
coverage:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Compiling `now dev` HTML error templates
|
||||
command: node packages/now-cli/scripts/compile-templates.js
|
||||
- run:
|
||||
name: Run unit tests
|
||||
command: yarn workspace now run test-unit
|
||||
- run:
|
||||
name: Run coverage report
|
||||
command: yarn workspace now run coverage
|
||||
|
||||
source-maps:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Installing Sentry CLI
|
||||
command: npm install -g @sentry/cli
|
||||
- run:
|
||||
name: Creating a New Sentry Release
|
||||
command: sentry-cli releases new now-cli@`git describe --tags`
|
||||
- run:
|
||||
name: Upload Sourcemap Files
|
||||
command: sentry-cli releases files now-cli@`git describe --tags` upload-sourcemaps ./dist
|
||||
- run:
|
||||
name: Finalize Sentry Release
|
||||
command: sentry-cli releases finalize now-cli@`git describe --tags`
|
||||
name: Uploading Binaries
|
||||
command:
|
||||
>-
|
||||
for file in ./packed/*.gz;
|
||||
do
|
||||
github-release upload -R -t `git describe --tags` -n ${file##*/} -f $file
|
||||
done
|
||||
- save_cache:
|
||||
key: v1-pkg-cache
|
||||
paths:
|
||||
- "/go/pkg"
|
||||
|
||||
publish-stable:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
- image: circleci/node:9.10.0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
@@ -404,7 +141,7 @@ jobs:
|
||||
|
||||
publish-canary:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
- image: circleci/node:9.10.0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
@@ -437,123 +174,43 @@ workflows:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
# - test-unit:
|
||||
# requires:
|
||||
# - build
|
||||
# filters:
|
||||
# tags:
|
||||
# only: /.*/
|
||||
- test-integration-macos-node-8:
|
||||
- test-unit:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-macos-node-10:
|
||||
- test-integration:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-macos-node-12:
|
||||
- compress:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-linux-node-8:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-linux-node-10:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-linux-node-12:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-macos-now-dev-node-8:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-macos-now-dev-node-10:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-macos-now-dev-node-12:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-linux-now-dev-node-8:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-linux-now-dev-node-10:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-linux-now-dev-node-12:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- test-integration-once:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- coverage:
|
||||
requires:
|
||||
#- test-unit
|
||||
- test-integration-macos-node-8
|
||||
- test-integration-macos-node-10
|
||||
- test-integration-macos-node-12
|
||||
- test-integration-linux-node-8
|
||||
- test-integration-linux-node-10
|
||||
- test-integration-linux-node-12
|
||||
- test-integration-macos-now-dev-node-8
|
||||
- test-integration-macos-now-dev-node-10
|
||||
- test-integration-macos-now-dev-node-12
|
||||
- test-integration-linux-now-dev-node-8
|
||||
- test-integration-linux-now-dev-node-10
|
||||
- test-integration-linux-now-dev-node-12
|
||||
- test-integration-once
|
||||
- test-lint
|
||||
- test-integration
|
||||
- test-unit
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- upload:
|
||||
requires:
|
||||
- compress
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- publish-canary:
|
||||
requires:
|
||||
- coverage
|
||||
- upload
|
||||
filters:
|
||||
tags:
|
||||
only: /^.*canary.*($|\b)/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- publish-stable:
|
||||
requires:
|
||||
- coverage
|
||||
- upload
|
||||
filters:
|
||||
tags:
|
||||
only: /^(\d+\.)?(\d+\.)?(\*|\d+)$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/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
|
||||
fi
|
||||
|
||||
npm_tag=""
|
||||
tag="$(git describe --tags --exact-match 2> /dev/null || :)"
|
||||
|
||||
if [ -z "$tag" ]; then
|
||||
echo "Not a tagged commit, skipping publish"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$tag" =~ -canary ]]; then
|
||||
echo "Publishing canary release"
|
||||
npm_tag="--npm-tag canary"
|
||||
else
|
||||
echo "Publishing stable release"
|
||||
fi
|
||||
|
||||
# Sometimes this is a false alarm and blocks publish
|
||||
git checkout yarn.lock
|
||||
|
||||
yarn run lerna publish from-git $npm_tag --yes
|
||||
@@ -1,32 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
|
||||
# gatsby-plugin-now
|
||||
packages/gatsby-plugin-now/test/fixtures
|
||||
|
||||
# now-cli
|
||||
packages/now-cli/@types
|
||||
packages/now-cli/download
|
||||
packages/now-cli/dist
|
||||
packages/now-cli/test/fixtures
|
||||
packages/now-cli/test/dev/fixtures
|
||||
packages/now-cli/bin
|
||||
packages/now-cli/link
|
||||
packages/now-cli/src/util/dev/templates/*.ts
|
||||
|
||||
# now-client
|
||||
packages/now-client/tests/fixtures
|
||||
packages/now-client/lib
|
||||
|
||||
# now-next
|
||||
packages/now-next/test/fixtures
|
||||
|
||||
# now-node
|
||||
packages/now-node/src/bridge.ts
|
||||
packages/now-node/test/fixtures
|
||||
|
||||
# now-node-bridge
|
||||
packages/now-node-bridge/bridge.*
|
||||
|
||||
# now-static-build
|
||||
packages/now-static-build/test/fixtures
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"modules": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
"jest": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"require-atomic-updates": 0,
|
||||
"@typescript-eslint/ban-ts-ignore": 0,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-use-before-define": 0
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.js"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-var-requires": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/now-cli/**/*"],
|
||||
"rules": {
|
||||
"lines-between-class-members": 0,
|
||||
"no-async-promise-executor": 0,
|
||||
"no-control-regex": 0,
|
||||
"no-empty": 0,
|
||||
"prefer-const": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"@typescript-eslint/ban-types": 0,
|
||||
"@typescript-eslint/consistent-type-assertions": 0,
|
||||
"@typescript-eslint/member-delimiter-style": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-inferrable-types": 0,
|
||||
"@typescript-eslint/no-var-requires": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/now-client/**/*"],
|
||||
"rules": {
|
||||
"prefer-const": 0,
|
||||
"require-atomic-updates": 0,
|
||||
"@typescript-eslint/ban-ts-ignore": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
2
.flowconfig
Normal file
2
.flowconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
[ignore]
|
||||
.*/node_modules/.*/\(test\).*\.json$
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,3 +0,0 @@
|
||||
# Ignore test fixtures in GitHub Languages
|
||||
# See https://github.com/github/linguist#vendored-code
|
||||
packages/*/test/* linguist-vendored
|
||||
18
.github/CODEOWNERS
vendored
18
.github/CODEOWNERS
vendored
@@ -1,18 +0,0 @@
|
||||
# Documentation
|
||||
# https://help.github.com/en/articles/about-code-owners
|
||||
|
||||
* @tootallnate @leo
|
||||
/packages/now-cli/src/commands/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||
/packages/now-cli/src/util/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||
/packages/now-cli/src/commands/domains/ @javivelasco @mglagola @anatrajkovska
|
||||
/packages/now-cli/src/commands/certs/ @javivelasco @mglagola @anatrajkovska
|
||||
/packages/now-client @leo @rdev
|
||||
/packages/now-build-utils @styfle @AndyBitz
|
||||
/packages/now-node @styfle @tootallnate @lucleray
|
||||
/packages/now-node-bridge @styfle @tootallnate @lucleray
|
||||
/packages/now-next @Timer
|
||||
/packages/now-go @styfle @sophearak
|
||||
/packages/now-python @styfle @sophearak
|
||||
/packages/now-ruby @styfle @coetry @nathancahill
|
||||
/packages/now-static-build @styfle @AndyBitz
|
||||
/packages/now-routing-utils @dav-is
|
||||
33
.github/workflows/publish.yml
vendored
33
.github/workflows/publish.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- canary
|
||||
tags:
|
||||
- '!*'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Checkout
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install
|
||||
run: yarn install --check-files --frozen-lockfile
|
||||
- name: Build
|
||||
run: yarn build
|
||||
- name: Publish
|
||||
run: yarn publish-from-github
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -1,20 +1,12 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
|
||||
# build output
|
||||
dist
|
||||
packed
|
||||
test/fixtures/integration
|
||||
|
||||
# logs
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
.nyc_output
|
||||
coverage
|
||||
*.swp
|
||||
*.bak
|
||||
*.tgz
|
||||
packages/now-cli/.builders
|
||||
packages/now-cli/assets
|
||||
packages/now-cli/src/util/dev/templates/*.ts
|
||||
packages/now-cli/test/**/yarn.lock
|
||||
!packages/now-cli/test/dev/**/yarn.lock
|
||||
packages/now-cli/test/**/node_modules
|
||||
packages/now-cli/test/dev/fixtures/08-hugo/hugo
|
||||
packages/now-cli/test/dev/fixtures/**/dist
|
||||
packages/now-cli/test/dev/fixtures/**/public
|
||||
packages/now-cli/test/fixtures/integration
|
||||
|
||||
16
.kodiak.toml
16
.kodiak.toml
@@ -1,16 +0,0 @@
|
||||
version = 1
|
||||
|
||||
[merge]
|
||||
automerge_label = "automerge"
|
||||
blacklist_title_regex = "^WIP.*"
|
||||
blacklist_labels = ["work in progress"]
|
||||
method = "squash"
|
||||
delete_branch_on_merge = true
|
||||
block_on_reviews_requested = false
|
||||
notify_on_conflict = true
|
||||
optimistic_updates = true
|
||||
|
||||
[merge.message]
|
||||
title = "pull_request_title"
|
||||
include_pr_number = true
|
||||
body_type = "markdown"
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
## Code of Conduct
|
||||
|
||||
### Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
### Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
### Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
### Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
### Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [abuse@zeit.co](mailto:abuse@zeit.co). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
### Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -1,96 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
When contributing to this repository, please first discuss the change you wish to make via [GitHub Issue](https://github.com/zeit/now/issues/new) or [Spectrum](https://spectrum.chat/zeit) with the owners of this repository before submitting a Pull Request.
|
||||
|
||||
Please read our [code of conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
|
||||
|
||||
## Local development
|
||||
|
||||
This project is configured in a monorepo pattern where one repo contains multiple npm packages. Dependencies are installed and managed with `yarn`, not `npm` CLI.
|
||||
|
||||
To get started, execute the following:
|
||||
|
||||
```
|
||||
git clone https://github.com/zeit/now
|
||||
yarn install
|
||||
yarn bootstrap
|
||||
yarn build
|
||||
yarn lint
|
||||
yarn test
|
||||
```
|
||||
|
||||
Make sure all the tests pass before making changes.
|
||||
|
||||
## Verifying your change
|
||||
|
||||
Once you are done with your changes (we even suggest doing it along the way ), make sure all the test still run by running
|
||||
|
||||
```
|
||||
yarn build && yarn test
|
||||
```
|
||||
|
||||
from the root of the project.
|
||||
|
||||
If any test fails, make sure to fix it along with your changes. See [Interpreting test errors](#Interpreting-test-errors) for more information about how the tests are executed, especially the integration tests.
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
Once you are confident that your changes work properly, open a pull request on the main repository.
|
||||
|
||||
The pull request will be reviewed by the maintainers and the tests will be checked by our continuous integration platform.
|
||||
|
||||
## Interpreting test errors
|
||||
|
||||
There are 2 kinds of tests in this repository – Unit tests and Integration tests.
|
||||
|
||||
Unit tests are run locally with `jest` and execute quickly because they are testing the smallest units of code.
|
||||
|
||||
### Integration tests
|
||||
|
||||
Integration tests create deployments to your ZEIT account using the `test` project name. After each test is deployed, the `probes` key is used to check if the response is the expected value. If the value doesn't match, you'll see a message explaining the difference. If the deployment failed to build, you'll see a more generic message like the following:
|
||||
|
||||
```
|
||||
[Error: Fetched page https://test-8ashcdlew.now.sh/root.js does not contain hello Root!. Instead it contains An error occurred with this application.
|
||||
|
||||
NO_STATUS_CODE_FRO Response headers:
|
||||
cache-control=s-maxage=0
|
||||
connection=close
|
||||
content-type=text/plain; charset=utf-8
|
||||
date=Wed, 19 Jun 2019 18:01:37 GMT
|
||||
server=now
|
||||
strict-transport-security=max-age=63072000
|
||||
transfer-encoding=chunked
|
||||
x-now-id=iad1:hgtzj-1560967297876-44ae12559f95
|
||||
x-now-trace=iad1]
|
||||
```
|
||||
|
||||
In such cases you can visit the URL of the failed deployment and append `/_logs` so see the build error. In the case above, that would be https://test-8ashcdlew.now.sh/_logs
|
||||
|
||||
The logs of this deployment will contain the actual error which may help you to understand what went wrong.
|
||||
|
||||
### @zeit/node-file-trace
|
||||
|
||||
Some of the Builders use `@zeit/node-file-trace` to tree-shake files before deployment. If you suspect an error with this tree-shaking mechanism, you can create the following script in your project:
|
||||
|
||||
```js
|
||||
const trace = require('@zeit/node-file-trace');
|
||||
trace(['path/to/entrypoint.js'], {
|
||||
ts: true,
|
||||
mixedModules: true,
|
||||
})
|
||||
.then(o => console.log(o.fileList))
|
||||
.then(e => console.error(e));
|
||||
```
|
||||
|
||||
When you run this script, you'll see all imported files. If anything file is missing, the bug is in [@zeit/node-file-trace](https://github.com/zeit/node-file-trace) and not the Builder.
|
||||
|
||||
## Deploy a Builder with existing project
|
||||
|
||||
Sometimes you want to test changes to a Builder against an existing project, maybe with `now dev` or an actual deployment. You can avoid publishing every Builder change to npm by uploading the Builder as a tarball.
|
||||
|
||||
1. Change directory to the desired Builder `cd ./packages/now-node`
|
||||
2. Run `yarn build` to compile typescript and other build steps
|
||||
3. Run `npm pack` to create a tarball file
|
||||
4. Run `now *.tgz` to upload the tarball file and get a URL
|
||||
5. Edit any existing `now.json` project and replace `use` with the URL
|
||||
6. Run `now` or `now dev` to deploy with the experimental Builder
|
||||
@@ -1,447 +0,0 @@
|
||||
# Builders Developer Reference
|
||||
|
||||
The following page is a reference for how to create a Builder using the available Builder's API.
|
||||
|
||||
A Builder is an npm module that exposes a `build` function and optionally an `analyze` function and `prepareCache` function.
|
||||
Official Builders are published to [npmjs.com](https://npmjs.com) as a package and referenced in the `use` property of the `now.json` configuration file.
|
||||
However, the `use` property will work with any [npm install argument](https://docs.npmjs.com/cli/install) such as a git repo url which is useful for testing your Builder.
|
||||
|
||||
See the [Builders Documentation](https://zeit.co/docs/v2/advanced/builders) to view example usage.
|
||||
|
||||
## Builder Exports
|
||||
|
||||
### `version`
|
||||
|
||||
A **required** exported constant that decides which version of the Builder API to use.
|
||||
|
||||
The latest and suggested version is `2`.
|
||||
|
||||
### `analyze`
|
||||
|
||||
An **optional** exported function that returns a unique fingerprint used for the purpose of [build de-duplication](https://zeit.co/docs/v2/advanced/concepts/immutability#deduplication-algorithm). If the `analyze` function is not supplied, a random fingerprint is assigned to each build.
|
||||
|
||||
```js
|
||||
export analyze({
|
||||
files: Files,
|
||||
entrypoint: String,
|
||||
workPath: String,
|
||||
config: Object
|
||||
}) : String fingerprint
|
||||
```
|
||||
|
||||
If you are using TypeScript, you should use the following types:
|
||||
|
||||
```ts
|
||||
import { AnalyzeOptions } from '@now/build-utils'
|
||||
|
||||
export analyze(options: AnalyzeOptions) {
|
||||
return 'fingerprint goes here'
|
||||
}
|
||||
```
|
||||
|
||||
### `build`
|
||||
|
||||
A **required** exported function that returns a [Files](#files) data structure that contains the Build outputs, which can be a [Static File](#file) or a [Serverless Function](#serverless-function).
|
||||
|
||||
What's a Serverless Function? Read about [Serverless Function concepts](https://zeit.co/docs/v2/deployments/concepts/lambdas) to learn more.
|
||||
|
||||
```js
|
||||
build({
|
||||
files: Files,
|
||||
entrypoint: String,
|
||||
workPath: String,
|
||||
config: Object,
|
||||
meta?: {
|
||||
isDev?: Boolean,
|
||||
requestPath?: String,
|
||||
filesChanged?: Array<String>,
|
||||
filesRemoved?: Array<String>
|
||||
}
|
||||
}) : {
|
||||
watch: Array<String>,
|
||||
output: Files output,
|
||||
routes: Object
|
||||
}
|
||||
```
|
||||
|
||||
If you are using TypeScript, you should use the following types:
|
||||
|
||||
```ts
|
||||
import { BuildOptions } from '@now/build-utils'
|
||||
|
||||
export build(options: BuildOptions) {
|
||||
// Build the code here
|
||||
|
||||
return {
|
||||
output: {
|
||||
'path-to-file': File,
|
||||
'path-to-lambda': Lambda
|
||||
},
|
||||
watch: [],
|
||||
routes: {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `prepareCache`
|
||||
|
||||
An **optional** exported function that is equivalent to [`build`](#build), but it executes the instructions necessary to prepare a cache for the next run.
|
||||
|
||||
```js
|
||||
prepareCache({
|
||||
files: Files,
|
||||
entrypoint: String,
|
||||
workPath: String,
|
||||
cachePath: String,
|
||||
config: Object
|
||||
}) : Files cacheOutput
|
||||
```
|
||||
|
||||
If you are using TypeScript, you can import the types for each of these functions by using the following:
|
||||
|
||||
```ts
|
||||
import { PrepareCacheOptions } from '@now/build-utils'
|
||||
|
||||
export prepareCache(options: PrepareCacheOptions) {
|
||||
return { 'path-to-file': File }
|
||||
}
|
||||
```
|
||||
|
||||
### `shouldServe`
|
||||
|
||||
An **optional** exported function that is only used by `now dev` in [Now CLI](https:///download) and indicates whether a [Builder](https://zeit.co/docs/v2/advanced/builders) wants to be responsible for building a certain request path.
|
||||
|
||||
```js
|
||||
shouldServe({
|
||||
entrypoint: String,
|
||||
files: Files,
|
||||
config: Object,
|
||||
requestPath: String,
|
||||
workPath: String
|
||||
}) : Boolean
|
||||
```
|
||||
|
||||
If you are using TypeScript, you can import the types for each of these functions by using the following:
|
||||
|
||||
```ts
|
||||
import { ShouldServeOptions } from '@now/build-utils'
|
||||
|
||||
export shouldServe(options: ShouldServeOptions) {
|
||||
return Boolean
|
||||
}
|
||||
```
|
||||
|
||||
If this method is not defined, Now CLI will default to [this function](https://github.com/zeit/now/blob/52994bfe26c5f4f179bdb49783ee57ce19334631/packages/now-build-utils/src/should-serve.ts).
|
||||
|
||||
### Builder Options
|
||||
|
||||
The exported functions [`analyze`](#analyze), [`build`](#build), and [`prepareCache`](#preparecache) receive one argument with the following properties.
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `files`: All source files of the project as a [Files](#files) data structure.
|
||||
- `entrypoint`: Name of entrypoint file for this particular build job. Value `files[entrypoint]` is guaranteed to exist and be a valid [File](#files) reference. `entrypoint` is always a discrete file and never a glob, since globs are expanded into separate builds at deployment time.
|
||||
- `workPath`: A writable temporary directory where you are encouraged to perform your build process. This directory will be populated with the restored cache from the previous run (if any) for [`analyze`](#analyze) and [`build`](#build).
|
||||
- `cachePath`: A writable temporary directory where you can build a cache for the next run. This is only passed to `prepareCache`.
|
||||
- `config`: An arbitrary object passed from by the user in the [Build definition](#defining-the-build-step) in `now.json`.
|
||||
|
||||
## Example: html-minifier
|
||||
|
||||
Let's walk through what it takes to create a simple builder that takes in a HTML source file and yields a minified HTML static file as its build output.
|
||||
|
||||
While this is a very simple builder, the approach demonstrated here can be used to return anything: one or more static files and/or one or more lambdas.
|
||||
|
||||
## Setting up the module
|
||||
|
||||
### Defining the analyze step
|
||||
|
||||
The `analyze` hook is optional. Its goal is to give the developer a tool to avoid wasting time _re-computing a build_ that has already occurred.
|
||||
|
||||
The return value of `analyze` is a _fingerprint_: a simple string that uniquely identifies the build process.
|
||||
|
||||
If `analyze` is not specified, its behavior is to use as the fingerprint the combined checksums of **all the files in the same directory level as the entrypoint**. This is a default that errs on making sure that we re-execute builds when files _other than the entrypoint_ (like dependencies, manifest files, etc) have changed.
|
||||
|
||||
For our `html-minify` example, we know that HTML files don't have dependencies. Therefore, our analyze step can just return the `digest` of the entrypoint.
|
||||
|
||||
Our `index.js` file looks as follows:
|
||||
|
||||
```js
|
||||
exports.analyze = function({ files, entrypoint }) {
|
||||
return files[entrypoint].digest
|
||||
}
|
||||
```
|
||||
|
||||
This means that we will only re-minify and re-create the build output _only if the file contents (and therefore its digest) change._
|
||||
|
||||
### Defining the build step
|
||||
|
||||
Your module will need some utilities to manipulate the data structures we pass you, create new ones and alter the filesystem.
|
||||
|
||||
To that end, we expose our API as part of a `@now/build-utils` package. This package is always loaded on your behalf, so make sure it's only included as `peerDependencies` in your `package.json`.
|
||||
|
||||
Builders can include dependencies of their liking:
|
||||
|
||||
```js
|
||||
const htmlMinifier = require('html-minifier')
|
||||
|
||||
exports.version = 2
|
||||
|
||||
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest
|
||||
|
||||
exports.build = async ({ files, entrypoint, config }) => {
|
||||
const stream = files[entrypoint].toStream()
|
||||
const options = Object.assign({}, config || {})
|
||||
const { data } = await FileBlob.fromStream({ stream })
|
||||
const content = data.toString()
|
||||
const minified = htmlMinifier(content, options)
|
||||
const result = new FileBlob({ data: minified })
|
||||
|
||||
return {
|
||||
output: {
|
||||
[entrypoint]: result
|
||||
},
|
||||
watch: [],
|
||||
routes: {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Defining a `prepareCache` step
|
||||
|
||||
If our builder had performed work that could be re-used in the next build invocation, we could define a `prepareCache` step.
|
||||
|
||||
In this case, there are not intermediate artifacts that we can cache, and our `analyze` step already takes care of caching the full output based on the fingerprint of the input.
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Execution Context
|
||||
|
||||
A [Serverless Function](https://zeit.co/docs/v2/advanced/concepts/lambdas) is created where the builder logic is executed. The lambda is run using the Node.js 8 runtime. A brand new sandbox is created for each deployment, for security reasons. The sandbox is cleaned up between executions to ensure no lingering temporary files are shared from build to build.
|
||||
|
||||
All the APIs you export ([`analyze`](#analyze), [`build`](#build) and [`prepareCache`](#preparecache)) are not guaranteed to be run in the same process, but the filesystem we expose (e.g.: `workPath` and the results of calling [`getWriteableDirectory`](#getWriteableDirectory) ) is retained.
|
||||
|
||||
If you need to share state between those steps, use the filesystem.
|
||||
|
||||
### Directory and Cache Lifecycle
|
||||
|
||||
When a new build is created, we pre-populate the `workPath` supplied to `analyze` with the results of the `prepareCache` step of the previous build.
|
||||
|
||||
The `analyze` step can modify that directory, and it will not be re-created when it's supplied to `build` and `prepareCache`.
|
||||
|
||||
To learn how the cache key is computed and invalidated, refer to the [overview](https://zeit.co/docs/v2/advanced/builders#technical-details).
|
||||
|
||||
### Accessing Environment and Secrets
|
||||
|
||||
The env and secrets specified by the user as `build.env` are passed to the builder process. This means you can access user env via `process.env` in Node.js.
|
||||
|
||||
### Utilities as peerDependencies
|
||||
|
||||
When you publish your builder to npm, make sure to not specify `@now/build-utils` (as seen below in the API definitions) as a dependency, but rather as part of `peerDependencies`.
|
||||
|
||||
## Types
|
||||
|
||||
### `Files`
|
||||
|
||||
```ts
|
||||
import { File } from '@now/build-utils'
|
||||
type Files = { [filePath: string]: File }
|
||||
```
|
||||
|
||||
This is an abstract type that is implemented as a plain [JavaScript Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object). It's helpful to think of it as a virtual filesystem representation.
|
||||
|
||||
When used as an input, the `Files` object will only contain `FileRefs`. When `Files` is an output, it may consist of `Lambda` (Serverless Functions) types as well as `FileRefs`.
|
||||
|
||||
An example of a valid output `Files` object is:
|
||||
|
||||
```json
|
||||
{
|
||||
"index.html": FileRef,
|
||||
"api/index.js": Lambda
|
||||
}
|
||||
```
|
||||
|
||||
### `File`
|
||||
|
||||
This is an abstract type that can be imported if you are using TypeScript.
|
||||
|
||||
```ts
|
||||
import { File } from '@now/build-utils'
|
||||
```
|
||||
|
||||
Valid `File` types include:
|
||||
|
||||
- [`FileRef`](#fileref)
|
||||
- [`FileFsRef`](#filefsref)
|
||||
- [`FileBlob`](#fileblob)
|
||||
|
||||
### `FileRef`
|
||||
|
||||
```ts
|
||||
import { FileRef } from '@now/build-utils'
|
||||
```
|
||||
|
||||
This is a [JavaScript class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) that represents an abstract file instance stored in our platform, based on the file identifier string (its checksum). When a `Files` object is passed as an input to `analyze` or `build`, all its values will be instances of `FileRef`.
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `mode : Number` file mode
|
||||
- `digest : String` a checksum that represents the file
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `toStream() : Stream` creates a [Stream](https://nodejs.org/api/stream.html) of the file body
|
||||
|
||||
### `FileFsRef`
|
||||
|
||||
```ts
|
||||
import { FileFsRef } from '@now/build-utils'
|
||||
```
|
||||
|
||||
This is a [JavaScript class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) that represents an abstract instance of a file present in the filesystem that the build process is executing in.
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `mode : Number` file mode
|
||||
- `fsPath : String` the absolute path of the file in file system
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `static async fromStream({ mode : Number, stream : Stream, fsPath : String }) : FileFsRef` creates an instance of a [FileFsRef](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) from `Stream`, placing file at `fsPath` with `mode`
|
||||
- `toStream() : Stream` creates a [Stream](https://nodejs.org/api/stream.html) of the file body
|
||||
|
||||
### `FileBlob`
|
||||
|
||||
```ts
|
||||
import { FileBlob } from '@now/build-utils'
|
||||
```
|
||||
|
||||
This is a [JavaScript class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) that represents an abstract instance of a file present in memory.
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `mode : Number` file mode
|
||||
- `data : String | Buffer` the body of the file
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `static async fromStream({ mode : Number, stream : Stream }) :FileBlob` creates an instance of a [FileBlob](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) from [`Stream`](https://nodejs.org/api/stream.html) with `mode`
|
||||
- `toStream() : Stream` creates a [Stream](https://nodejs.org/api/stream.html) of the file body
|
||||
|
||||
### `Lambda`
|
||||
|
||||
```ts
|
||||
import { Lambda } from '@now/build-utils'
|
||||
```
|
||||
|
||||
This is a [JavaScript class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes), called a Serverless Function, that can be created by supplying `files`, `handler`, `runtime`, and `environment` as an object to the [`createLambda`](#createlambda) helper. The instances of this class should not be created directly. Instead use a call to [`createLambda`](#createlambda).
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `files : Files` the internal filesystem of the lambda
|
||||
- `handler : String` path to handler file and (optionally) a function name it exports
|
||||
- `runtime : LambdaRuntime` the name of the lambda runtime
|
||||
- `environment : Object` key-value map of handler-related (aside of those passed by user) environment variables
|
||||
|
||||
### `LambdaRuntime`
|
||||
|
||||
This is an abstract enumeration type that is implemented by one of the following possible `String` values:
|
||||
|
||||
- `nodejs10.x`
|
||||
- `nodejs8.10`
|
||||
- `go1.x`
|
||||
- `java-1.8.0-openjdk`
|
||||
- `python3.6`
|
||||
- `python2.7`
|
||||
- `dotnetcore2.1`
|
||||
- `dotnetcore2.0`
|
||||
- `dotnetcore1.0`
|
||||
|
||||
## JavaScript API
|
||||
|
||||
The following is exposed by `@now/build-utils` to simplify the process of writing Builders, manipulating the file system, using the above types, etc.
|
||||
|
||||
### `createLambda`
|
||||
|
||||
Signature: `createLambda(Object spec) : Lambda`
|
||||
|
||||
```ts
|
||||
import { createLambda } from '@now/build-utils'
|
||||
```
|
||||
|
||||
Constructor for the [`Lambda`](#lambda) type.
|
||||
|
||||
```js
|
||||
const { createLambda, FileBlob } = require('@now/build-utils')
|
||||
await createLambda({
|
||||
runtime: 'nodejs8.10',
|
||||
handler: 'index.main',
|
||||
files: {
|
||||
'index.js': new FileBlob({ data: 'exports.main = () => {}' })
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `download`
|
||||
|
||||
Signature: `download() : Files`
|
||||
|
||||
```ts
|
||||
import { download } from '@now/build-utils'
|
||||
```
|
||||
|
||||
This utility allows you to download the contents of a [`Files`](#files) data structure, therefore creating the filesystem represented in it.
|
||||
|
||||
Since `Files` is an abstract way of representing files, you can think of `download` as a way of making that virtual filesystem _real_.
|
||||
|
||||
If the **optional** `meta` property is passed (the argument for [build](#build)), only the files that have changed are downloaded. This is decided using `filesRemoved` and `filesChanged` inside that object.
|
||||
|
||||
```js
|
||||
await download(files, workPath, meta)
|
||||
```
|
||||
|
||||
### `glob`
|
||||
|
||||
Signature: `glob() : Files`
|
||||
|
||||
```ts
|
||||
import { glob } from '@now/build-utils'
|
||||
```
|
||||
|
||||
This utility allows you to _scan_ the filesystem and return a [`Files`](#files) representation of the matched glob search string. It can be thought of as the reverse of [`download`](#download).
|
||||
|
||||
The following trivial example downloads everything to the filesystem, only to return it back (therefore just re-creating the passed-in [`Files`](#files)):
|
||||
|
||||
```js
|
||||
const { glob, download } = require('@now/build-utils')
|
||||
|
||||
exports.build = ({ files, workPath }) => {
|
||||
await download(files, workPath)
|
||||
return glob('**', workPath)
|
||||
}
|
||||
```
|
||||
|
||||
### `getWriteableDirectory`
|
||||
|
||||
Signature: `getWriteableDirectory() : String`
|
||||
|
||||
```ts
|
||||
import { getWriteableDirectory } from '@now/build-utils'
|
||||
```
|
||||
|
||||
In some occasions, you might want to write to a temporary directory.
|
||||
|
||||
### `rename`
|
||||
|
||||
Signature: `rename(Files) : Files`
|
||||
|
||||
```ts
|
||||
import { rename } from '@now/build-utils'
|
||||
```
|
||||
|
||||
Renames the keys of the [`Files`](#files) object, which represent the paths. For example, to remove the `*.go` suffix you can use:
|
||||
|
||||
```js
|
||||
const rename = require('@now/build-utils')
|
||||
const originalFiles = { 'one.go': fileFsRef1, 'two.go': fileFsRef2 }
|
||||
const renamedFiles = rename(originalFiles, path => path.replace(/\.go$/, '')
|
||||
```
|
||||
@@ -1,40 +0,0 @@
|
||||
# Publishing to npm
|
||||
|
||||
Always publish to the Canary Channel as soon as a PR is merged into the `canary` branch.
|
||||
|
||||
```
|
||||
yarn publish-canary
|
||||
```
|
||||
|
||||
Publish the Stable Channel weekly.
|
||||
|
||||
- Cherry pick each commit from `canary` to `master` branch
|
||||
- Verify that you are _in-sync_ with canary (with the exception of the `version` line in `package.json`)
|
||||
- Deploy the modified Builders
|
||||
|
||||
```
|
||||
# View differences excluding "Publish" commits
|
||||
git checkout canary && git pull
|
||||
git log --pretty=format:"$ad- %s [%an]" | grep -v Publish > ~/Desktop/canary.txt
|
||||
git checkout master && git pull
|
||||
git log --pretty=format:"$ad- %s [%an]" | grep -v Publish > ~/Desktop/master.txt
|
||||
diff ~/Desktop/canary.txt ~/Desktop/master.txt
|
||||
|
||||
# Cherry pick all PRs from canary into master ...
|
||||
git cherry-pick <PR501_COMMIT_SHA>
|
||||
git cherry-pick <PR502_COMMIT_SHA>
|
||||
git cherry-pick <PR503_COMMIT_SHA>
|
||||
git cherry-pick <PR504_COMMIT_SHA>
|
||||
|
||||
# Verify the only difference is "version" in package.json
|
||||
git diff origin/canary
|
||||
|
||||
# Ship it
|
||||
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
|
||||
use `npm publish --tag canary` if you are publishing a canary release!
|
||||
30
README.md
30
README.md
@@ -1,26 +1,18 @@
|
||||

|
||||

|
||||
|
||||
[](https://circleci.com/gh/zeit/workflows/now)
|
||||
[](https://spectrum.chat/zeit)
|
||||
[](https://circleci.com/gh/zeit/workflows/now-cli) [](https://spectrum.chat/zeit)
|
||||
|
||||
**Note**: The [canary](https://github.com/zeit/now/tree/canary) branch is under heavy development – the stable release branch is [master](https://github.com/zeit/now/tree/master).
|
||||
**Note**: The [canary](https://github.com/zeit/now-cli/tree/canary) branch is under heavy development – the stable release branch is [master](https://github.com/zeit/now-cli/tree/master).
|
||||
|
||||
## Usage
|
||||
|
||||
To install the latest version of Now CLI, visit [zeit.co/download](https://zeit.co/download) or run this command:
|
||||
To install the latest version of Now CLI, run this command:
|
||||
|
||||
```
|
||||
npm i -g now
|
||||
npm install -g now
|
||||
```
|
||||
|
||||
To quickly start a new project, run the following commands:
|
||||
|
||||
```
|
||||
now init # Pick an example project to clone
|
||||
cd <PROJECT> # Change directory to the newly created project
|
||||
now dev # Run locally during development
|
||||
now # Deploy to the cloud
|
||||
```
|
||||
There are also [other installation methods](https://zeit.co/download) available.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -29,13 +21,11 @@ For details on how to use Now CLI, check out our [documentation](https://zeit.co
|
||||
## Caught a Bug?
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
|
||||
2. Install dependencies with `yarn install`
|
||||
3. Compile the code: `yarn build`
|
||||
4. Link the package to the global module directory: `yarn link`
|
||||
5. You can now start using `now` anywhere inside the command line
|
||||
2. Link the package to the global module directory: `yarn run link` (not `yarn link`)
|
||||
3. You can now start using `now` from the command line!
|
||||
|
||||
As always, you should use `yarn test-unit` to run the tests and see if your changes have broken anything.
|
||||
As always, you can use `yarn test` to run the tests and see if your changes have broken anything.
|
||||
|
||||
## How to Create a Release
|
||||
|
||||
If you have write access to this repository, you can read more about how to publish a release [here](https://github.com/zeit/now/wiki/Creating-a-Release).
|
||||
If you have write access to this repository, you can read more about how to publish a release [here](https://github.com/zeit/zeit/blob/master/guides/now-cli-release.md).
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# Versioning
|
||||
|
||||
Builders are released to two different channels.
|
||||
|
||||
## Channels
|
||||
|
||||
| Channel | Git Branch | npm dist-tag | use example |
|
||||
| ------- | ------------------------------------------------------------- | ------------ | ------------------ |
|
||||
| Canary | [canary](https://github.com/zeit/now/commits/canary) | `@canary` | `@now/node@canary` |
|
||||
| Stable | [master](https://github.com/zeit/now/commits/master) | `@latest` | `@now/node@latest` |
|
||||
|
||||
All PRs are 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.
|
||||
|
||||
## Version Selection
|
||||
|
||||
Since Builders are published to [npmjs.com](https://npmjs.com), this makes versioning works the same for Builders as it does for any npm package. The `use` statement in [now.json](https://zeit.co/docs/v2/advanced/configuration#builds) has a similar syntax to `npm install`.
|
||||
|
||||
The following are valid examples [@now/node](https://www.npmjs.com/package/@now/node?activeTab=versions):
|
||||
|
||||
- `@now/node`
|
||||
- `@now/node@0.7.3`
|
||||
- `@now/node@canary`
|
||||
- `@now/node@0.7.2-canary.2`
|
||||
|
||||
We always recommend using the latest version by leaving off the dist-tag suffix, `@now/node` for example.
|
||||
16
download/install.js
Normal file
16
download/install.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* eslint-disable no-var */
|
||||
|
||||
// Native
|
||||
var path = require('path')
|
||||
var fs = require('fs')
|
||||
|
||||
var dist = path.join(__dirname, 'dist')
|
||||
var src = path.join(__dirname, 'src')
|
||||
|
||||
// Don't install when developing locally
|
||||
if (fs.existsSync(src)) {
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
require(path.join(dist, 'download.js'))
|
||||
10
download/src/chmod.js
Normal file
10
download/src/chmod.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// Native
|
||||
import fs from 'fs'
|
||||
|
||||
export default function (file) {
|
||||
const s = fs.statSync(file)
|
||||
const newMode = s.mode | 64 | 8 | 1
|
||||
if (s.mode === newMode) return
|
||||
const base8 = newMode.toString(8).slice(-3)
|
||||
fs.chmodSync(file, base8)
|
||||
}
|
||||
191
download/src/index.js
Normal file
191
download/src/index.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/* eslint-disable unicorn/no-process-exit */
|
||||
|
||||
// Native
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { spawnSync } from 'child_process'
|
||||
import zlib from 'zlib'
|
||||
|
||||
// Packages
|
||||
import onDeath from 'death'
|
||||
import fetch from 'node-fetch'
|
||||
import retry from 'async-retry'
|
||||
import which from 'which-promise'
|
||||
import readPkg from 'read-pkg'
|
||||
|
||||
// Utilities
|
||||
import plusxSync from './chmod'
|
||||
import {
|
||||
disableProgress,
|
||||
enableProgress,
|
||||
info,
|
||||
showProgress,
|
||||
warn
|
||||
} from './log'
|
||||
|
||||
fetch.Promise = Promise
|
||||
global.Promise = Promise
|
||||
let { platform } = process
|
||||
if (detectAlpine()) platform = 'alpine'
|
||||
|
||||
const packageDir = path.join(__dirname, '..', '..')
|
||||
const packageJSON = readPkg.sync(packageDir)
|
||||
|
||||
const now = path.join(__dirname, 'now')
|
||||
const targetWin32 = path.join(__dirname, 'now.exe')
|
||||
const target = platform === 'win32' ? targetWin32 : now
|
||||
const partial = target + '.partial'
|
||||
const backup = target + '.' + packageJSON.version + '.backup'
|
||||
|
||||
const platformToName = {
|
||||
alpine: 'now-alpine',
|
||||
darwin: 'now-macos',
|
||||
linux: 'now-linux',
|
||||
win32: 'now-win.exe'
|
||||
}
|
||||
|
||||
function detectAlpine () {
|
||||
if (platform !== 'linux') return false
|
||||
// https://github.com/sass/node-sass/issues/1589#issuecomment-265292579
|
||||
const ldd = spawnSync('ldd', [ process.execPath ]).stdout.toString()
|
||||
return /\bmusl\b/.test(ldd)
|
||||
}
|
||||
|
||||
async function download() {
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
now,
|
||||
'#!/usr/bin/env node\n' +
|
||||
'console.log("Please wait until the \'now\' installation completes!")\n'
|
||||
)
|
||||
} catch (err) {
|
||||
if (err.code === 'EACCES') {
|
||||
warn(
|
||||
'Please try installing Now CLI again with the `--unsafe-perm` option.'
|
||||
)
|
||||
info('Example: `npm i -g --unsafe-perm now`')
|
||||
|
||||
process.exit()
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
|
||||
onDeath(() => {
|
||||
fs.writeFileSync(
|
||||
now,
|
||||
'#!/usr/bin/env node\n' +
|
||||
'console.log("The \'now\' installation did not complete successfully.")\n' +
|
||||
'console.log("Please run \'npm i -g now\' to reinstall!")\n'
|
||||
)
|
||||
process.exit()
|
||||
})
|
||||
|
||||
info('For the source code, check out: https://github.com/zeit/now-cli')
|
||||
|
||||
// Print an empty line
|
||||
console.log('')
|
||||
|
||||
await retry(async () => {
|
||||
enableProgress('Downloading Now CLI ' + packageJSON.version)
|
||||
showProgress(0)
|
||||
|
||||
try {
|
||||
const name = platformToName[platform]
|
||||
const url = `https://github.com/zeit/now-cli/releases/download/${packageJSON.version}/${name}.gz`
|
||||
const resp = await fetch(url, { compress: false })
|
||||
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(resp.statusText + ' ' + url)
|
||||
}
|
||||
|
||||
const size = resp.headers.get('content-length')
|
||||
const ws = fs.createWriteStream(partial)
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
let bytesRead = 0
|
||||
|
||||
resp.body
|
||||
.on('error', reject)
|
||||
.on('data', chunk => {
|
||||
bytesRead += chunk.length
|
||||
|
||||
if (size) {
|
||||
showProgress(100 * bytesRead / size)
|
||||
}
|
||||
})
|
||||
|
||||
const gunzip = zlib.createGunzip()
|
||||
|
||||
gunzip
|
||||
.on('error', reject)
|
||||
|
||||
resp.body.pipe(gunzip).pipe(ws)
|
||||
|
||||
ws
|
||||
.on('error', reject)
|
||||
.on('close', () => {
|
||||
showProgress(100)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
} finally {
|
||||
disableProgress()
|
||||
}
|
||||
}, {
|
||||
retries: 500,
|
||||
onRetry: (err) => console.error(err)
|
||||
})
|
||||
|
||||
fs.renameSync(partial, target)
|
||||
fs.writeFileSync(backup, fs.readFileSync(target))
|
||||
}
|
||||
|
||||
function modifyGitBashFile (content) {
|
||||
return (
|
||||
'#!/bin/sh\n' +
|
||||
'basedir=$(dirname "$(echo "$0" | sed -e \'s,\\\\,/,g\')")\n' +
|
||||
'\n' +
|
||||
'case `uname` in\n' +
|
||||
' *CYGWIN*) basedir=`cygpath -w "$basedir"`;;\n' +
|
||||
'esac\n' +
|
||||
'\n' +
|
||||
content.replace(
|
||||
'download/dist/now"', 'download/dist/now.exe"'));
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (fs.existsSync(backup)) {
|
||||
fs.writeFileSync(target, fs.readFileSync(backup))
|
||||
} else {
|
||||
await download()
|
||||
}
|
||||
|
||||
if (platform === 'win32') {
|
||||
try {
|
||||
fs.writeFileSync(now, '')
|
||||
// Workaround for https://github.com/npm/cmd-shim/pull/25
|
||||
const globalPath = path.dirname(await which('npm'))
|
||||
let gitBashFile = path.join(globalPath, 'now')
|
||||
if (!fs.existsSync(gitBashFile)) {
|
||||
gitBashFile = path.join(process.env.APPDATA, 'npm/now');
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
gitBashFile, modifyGitBashFile(fs.readFileSync(gitBashFile, 'utf8'))
|
||||
)
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
// Not a problem. only git cmd will not work
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
plusxSync(now)
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err)
|
||||
process.exit(2)
|
||||
})
|
||||
43
download/src/log.js
Normal file
43
download/src/log.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// Packages
|
||||
import assert from 'assert'
|
||||
import chalk from 'chalk'
|
||||
import Progress from 'progress'
|
||||
|
||||
let bar
|
||||
|
||||
export function enableProgress(text) {
|
||||
assert(!bar)
|
||||
|
||||
bar = new Progress(`> ${text} [:bar] :percent`, {
|
||||
stream: process.stdout,
|
||||
width: 20,
|
||||
complete: '=',
|
||||
incomplete: ' ',
|
||||
total: 100
|
||||
})
|
||||
}
|
||||
|
||||
export function info(text) {
|
||||
console.log(`> ${text}`)
|
||||
}
|
||||
|
||||
export function warn(text) {
|
||||
console.log(chalk.red('> Warning!'), text)
|
||||
}
|
||||
|
||||
export function showProgress(percentage) {
|
||||
assert(bar)
|
||||
bar.update(percentage / 100)
|
||||
}
|
||||
|
||||
export function disableProgress() {
|
||||
assert(bar)
|
||||
|
||||
// It is auto-completed once it updates to 100
|
||||
// otherwise it outputs a blank line
|
||||
if (!bar.complete) {
|
||||
bar.terminate()
|
||||
}
|
||||
|
||||
bar = undefined
|
||||
}
|
||||
34
download/webpack.config.js
Normal file
34
download/webpack.config.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// Native
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
target: 'node',
|
||||
node: {
|
||||
__dirname: false,
|
||||
__filename: false,
|
||||
process: false
|
||||
},
|
||||
entry: [
|
||||
'./src/index.js'
|
||||
],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'download.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [ {
|
||||
test: /.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
query: {
|
||||
plugins: [
|
||||
'@babel/transform-async-to-generator',
|
||||
'@babel/transform-runtime'
|
||||
],
|
||||
presets: [
|
||||
'@babel/preset-env'
|
||||
]
|
||||
}
|
||||
} ]
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ it could be that:
|
||||
- The domain was acquired recently, and it might not be ready for use yet.
|
||||
- Required DNS records have not propagated yet.
|
||||
|
||||
When running into this, ensure that your nameservers are configured correctly. Also, if you bought the domain recently or have made changes, please be patient,
|
||||
When running into this, ensure that your nameservers have configuration is correct. Also, if you bought the domain recently or have made changes, please be patient,
|
||||
it might take a while for these to be ready.
|
||||
|
||||
If your domain is _not_ pointing to ZEIT World DNS and you’re getting this
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# Domain Verification
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
The domain you supplied cannot be verified using either the intended set of nameservers of the given verification TXT record.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
Apply the intended set of nameservers to your domain or add the given TXT verification record through your domain provider.
|
||||
|
||||
You can retrieve both the intended nameservers and TXT verification record for the domain you wish to verify by running `now domains inspect <domain>`.
|
||||
|
||||
When you have added either verification method to your domain, you can run `now domains verify <domain>` again to complete verification for your domain.
|
||||
|
||||
ZEIT will also automatically check periodically that your domain has been verified and automatically mark it as such if we detect either verification method on the domain.
|
||||
|
||||
If you would not like to verify your domain, you can remove it from your account using `now domains rm <domain>`.
|
||||
|
||||
#### Resources
|
||||
- [ZEIT Domains Documentation](https://zeit.co/docs/v2/domains-and-aliases/adding-a-domain/)
|
||||
- [Zero-Downtime Domain Migration Guide](https://zeit.co/docs/v2/domains-and-aliases/zero-downtime-domain-migration/)
|
||||
@@ -14,4 +14,4 @@ now -e VARIABLE_NAME=@secret-name
|
||||
|
||||
In addition, ensure that the secret (`@secret-name` in the example above) exists in the current scope (the team or user account that you're using).
|
||||
|
||||
You can run `now switch` or `--scope` to switch to a different team or user.
|
||||
You can run `now switch` or `--team` to switch to a different team or user.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Missing Environment Variables While Developing
|
||||
|
||||
#### Why This Error Occurred
|
||||
You ran `now dev` inside a project that contains a `now.json` file with `env` or `build.env` properties that use [Now Secrets](https://zeit.co/docs/v2/deployments/environment-variables-and-secrets).
|
||||
|
||||
In order to use environment variables in your project locally that have values defined using the Now Secrets format (e.g. `@my-secret-value`), you will need to provide the value as an environment variable using a `.env` or `.env.build` file.
|
||||
|
||||
We require this to ensure your app works as you intend it to, in the Now Dev environment, and to provide you with a way to mirror or separate private environment variables within your applications, for example when connecting to a database.
|
||||
|
||||
Read below for how to address this error.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
The error message will list environment variables that are required and which file they are required to be included in (either `.env` or `.env.build`).
|
||||
|
||||
If the file does not exist yet, please create the file that the error message mentions and insert the missing environment variable into it.
|
||||
|
||||
For example, if the error message shows that the environment variable `TEST` is missing from `.env`, then the `.env` file should look like this:
|
||||
|
||||
```
|
||||
TEST=value
|
||||
```
|
||||
|
||||
In the above example, `TEST` represents the name of the environment variable and `value` its value.
|
||||
|
||||
For more information on Environment Variables in development, [see the documentation](https://zeit.co/docs/v2/development/environment-variables/).
|
||||
@@ -1,11 +0,0 @@
|
||||
# Missing Scope Value
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
The `--scope` flag was specified, but there's no value for it available.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
In order to make it work, you need to specify a value for the `--scope` flag. This needs to be the slug or ID of the team as which you'd like to act or the username or ID of a user you'd like to act as.
|
||||
|
||||
As an example, if your team URL is `https://zeit.co/teams/zeit`, you would set `--scope` to `zeit`.
|
||||
11
errors/missing-team-value.md
Normal file
11
errors/missing-team-value.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Missing Team Value
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
The `--team` flag was specified, but there's no value for it available.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
In order to make it work, you need to specify a value for the `--team` flag. This needs to be the slug of the team as which you'd like to act.
|
||||
|
||||
As an example, if your team URL is `https://zeit.co/teams/zeit`, the slug of the team is `zeit`.
|
||||
@@ -8,4 +8,4 @@ In turn, they would have to take the value of the `--token` flag into considerat
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
Specify a value for the `--scope` flag. This needs to be the slug or ID of the team as which you'd like to act (as an example, if your team URL is `https://zeit.co/teams/zeit`, the value can be `zeit`) or the username or ID of a user you'd like to act as.
|
||||
Specify a value for the `--team` flag. This needs to be the slug of the team as which you'd like to act. As an example, if your team URL is `https://zeit.co/teams/zeit`, the slug is `zeit`.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# `now dev` as `dev` script
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
The `package.json` file of the used project invokes `now dev` as `dev` script. This would cause `now dev` to recursively invoke itself.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
Adjust the `dev` script inside the `package.json` file to match what your framework uses to begin development mode, e.g. `next` for Next.js or `gatsby develop` for Gatsby.
|
||||
@@ -1,72 +0,0 @@
|
||||
# `@now/next` Legacy Mode
|
||||
|
||||
#### Why This Warning Occurred
|
||||
|
||||
`@now/next` has two modes: `legacy` and `serverless`. You will always want to use the `serverless` mode. `legacy` is to provide backwards compatibility with previous `@now/next` versions.
|
||||
|
||||
The differences:
|
||||
|
||||
Legacy:
|
||||
|
||||
- Minimal lambda size of `2.2Mb` (approximately)
|
||||
- Forces `next@v7.0.2-canary.49` and `next-server@v7.0.2-canary.49`
|
||||
- Forces all `dependencies` to be `devDependencies`
|
||||
- Loads `next.config.js` on bootup, breaking sometimes when users didn't use `phases` to load files
|
||||
- Used `next-server` which is the full Next.js server with routing etc.
|
||||
- Runs `npm install`
|
||||
- Runs `npm run now-build`
|
||||
- Runs `npm install --production` after build
|
||||
|
||||
Serverless:
|
||||
|
||||
- Minimal lambda size of `49Kb` (approximately)
|
||||
- Uses Next.js build targets (`target: 'serverless'`) in `next.config.js`. [documentation](https://github.com/zeit/next.js#summary)
|
||||
- Does not make changes to your application dependencies
|
||||
- Does not load `next.config.js` ([as per the serverless target documentation](https://github.com/zeit/next.js#summary))
|
||||
- Runs `npm install`
|
||||
- Runs `npm run now-build`
|
||||
- Does not run `npm install --production` as the output from the build is all that's needed to bundle lambdas.
|
||||
- No runtime dependencies, meaning smaller lambda functions
|
||||
- Optimized for fast [cold start](https://zeit.co/blog/serverless-ssr#cold-start)
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
In order to create the smallest possible lambdas Next.js has to be configured to build for the `serverless` target.
|
||||
|
||||
1. Serverless Next.js requires Next.js 8 or later, to upgrade you can install the `latest` version:
|
||||
|
||||
```
|
||||
npm install next --save
|
||||
```
|
||||
|
||||
2. Add the `now-build` script to your `package.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"now-build": "next build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Add `target: 'serverless'` to `next.config.js`
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
target: 'serverless',
|
||||
// Other options are still valid
|
||||
};
|
||||
```
|
||||
|
||||
4. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
|
||||
|
||||
```js
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@now/next" }]
|
||||
}
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Serverless target implementation](https://github.com/zeit/now-builders/pull/150)
|
||||
@@ -1,45 +0,0 @@
|
||||
# `@now/next` No Serverless Pages Built
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
This error occurs when you have your application is not configured for Serverless Next.js build output.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
In order to create the smallest possible lambdas Next.js has to be configured to build for the `serverless` target.
|
||||
|
||||
1. Serverless Next.js requires Next.js 8 or later, to upgrade you can install the `latest` version:
|
||||
|
||||
```
|
||||
npm install next --save
|
||||
```
|
||||
|
||||
2. Add the `now-build` script to your `package.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"now-build": "next build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Add `target: 'serverless'` to `next.config.js`
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
target: 'serverless',
|
||||
// Other options
|
||||
};
|
||||
```
|
||||
|
||||
4. Remove `distDir` from `next.config.js` as `@now/next` can't parse this file and expects your build output at `/.next`
|
||||
|
||||
5. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
|
||||
|
||||
```js
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@now/next" }]
|
||||
}
|
||||
```
|
||||
@@ -1,38 +0,0 @@
|
||||
# `@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)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Scope Not Accessible
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
You specified the `--scope` flag and specified the ID or slug of a team that you're not a part of or a user whose account you don't own. This problem could also occur if your user credentials aren't valid anymore.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
- Make sure commands like `now ls` work just fine. This will ensure that your user credentials are valid. If it's not working correctly, please log in again using `now login`.
|
||||
- Ensure that the scope you specified using `--scope` shows up in the output of `now switch`. If it doesn't, you're either not part of the team (if you specified a team) or you logged into the wrong user account.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Scope Not Existent
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
You specified the `--scope` flag and specified the ID or slug of a team that does not exist or that you're not a part of. Similarly you might have specified the ID or username of user whose account you don't own.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
If you're sure the specified team exists, please make sure that you're a part of it (ask an owner of the team to invite you). If you specified the identifier of a user, make sure you are actually the owner of this account.
|
||||
|
||||
Otherwise, either create a team with the specified slug or ensure that the identifier is correct if you're sure that the scope exists.
|
||||
10
errors/team-not-accessible.md
Normal file
10
errors/team-not-accessible.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Team Not Accessible
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
You specified the `--team` flag and specified the slug of a team that you're not a part of. This problem could also occur if your user credentials aren't valid anymore.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
- Make sure commands like `now ls` work just fine. This will ensure that your user credentials are valid. If it's not working correctly, please log in again using `now login`.
|
||||
- Ensure that the team you specified using `--team` shows up in the output of `now switch`. If it doesn't, you're not part of it. In that case, please ask an owner of the team to invite you.
|
||||
11
errors/team-not-existent.md
Normal file
11
errors/team-not-existent.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Team Not Existent
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
You specified the `--team` flag and specified the slug of a team that does not exist or that you're not a part of.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
If you're sure the team exists, please make sure that you're a part of it (ask an owner of the team to invite you). After that, the command will work.
|
||||
|
||||
Otherwise, either create a team with the specified slug or ensure that the slug is correct if you're sure that the team exists.
|
||||
@@ -6,7 +6,7 @@ After the deployment build completed and the deployment state was set to `READY`
|
||||
instances failed to initialize properly.
|
||||
|
||||
The CLI attempted to verify that the scale settings of your instances matched,
|
||||
but it couldn't do so within the allotted time (defaults to 2 minutes).
|
||||
but it couldn't do so within the alloted time (defaults to 2 minutes).
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
|
||||
14
lerna.json
14
lerna.json
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": ["packages/*"],
|
||||
"command": {
|
||||
"publish": {
|
||||
"npmClient": "npm",
|
||||
"allowBranch": ["master", "canary"],
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
}
|
||||
},
|
||||
"version": "independent"
|
||||
}
|
||||
|
||||
11
link/link.js
Executable file
11
link/link.js
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
require('../dist/now.js')
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT' && err.syscall === 'uv_cwd') {
|
||||
console.error(`Current path doesn't exist!`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
9
link/package.json
Normal file
9
link/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "now",
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command line interface for Now",
|
||||
"repository": "zeit/now-cli",
|
||||
"bin": {
|
||||
"now": "link.js"
|
||||
}
|
||||
}
|
||||
227
package.json
227
package.json
@@ -1,60 +1,187 @@
|
||||
{
|
||||
"name": "now-builders",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"nohoist": [
|
||||
"**/@types/**"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"lerna": "3.16.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "2.0.0",
|
||||
"@typescript-eslint/parser": "2.0.0",
|
||||
"@zeit/ncc": "0.20.4",
|
||||
"async-retry": "1.2.3",
|
||||
"buffer-replace": "1.0.0",
|
||||
"eslint": "6.2.2",
|
||||
"eslint-config-prettier": "6.1.0",
|
||||
"husky": "3.0.4",
|
||||
"lint-staged": "9.2.5",
|
||||
"node-fetch": "2.6.0",
|
||||
"prettier": "1.18.2"
|
||||
},
|
||||
"name": "now",
|
||||
"version": "12.0.0-canary.84",
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Now",
|
||||
"repository": "zeit/now-cli",
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"publish-stable": "git checkout master && git pull && lerna version --exact",
|
||||
"publish-canary": "git checkout canary && git pull && lerna version prerelease --preid canary --exact",
|
||||
"publish-from-github": "./.circleci/publish.sh",
|
||||
"build": "node run.js build all",
|
||||
"test-lint": "node run.js test-lint",
|
||||
"test-unit": "node run.js test-unit",
|
||||
"test-integration": "node run.js test-integration",
|
||||
"test-integration-once": "node run.js test-integration-once",
|
||||
"test-integration-now-dev": "node run.js test-integration-now-dev",
|
||||
"lint": "eslint . --ext .ts,.js"
|
||||
"test": "yarn test-lint",
|
||||
"test-unit": "ava test/unit.js --fail-fast",
|
||||
"test-integration": "ava test/integration.js --serial --fail-fast",
|
||||
"test-lint": "eslint .",
|
||||
"prepublishOnly": "yarn build-download && cp /dev/null download/dist/now",
|
||||
"dev": "webpack -w",
|
||||
"precommit": "lint-staged",
|
||||
"postinstall": "node download/install.js",
|
||||
"gzip": "ls packed/now* | xargs gzip",
|
||||
"bundle": "pkg dist/now.js -c package.json -o packed/now --options no-warnings",
|
||||
"pack": "rm -rf packed && webpack && yarn bundle",
|
||||
"build-download": "webpack --context download --config download/webpack.config.js",
|
||||
"link": "webpack && cd link && npm link",
|
||||
"build": "webpack"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts}": [
|
||||
"prettier --write",
|
||||
"eslint",
|
||||
"git add"
|
||||
],
|
||||
"*.{json,md}": [
|
||||
"prettier --write",
|
||||
"*.js": [
|
||||
"yarn test-lint",
|
||||
"prettier --write --single-quote --no-semi",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
"bin": {
|
||||
"now": "download/dist/now"
|
||||
},
|
||||
"files": [
|
||||
"download/dist",
|
||||
"download/install.js"
|
||||
],
|
||||
"pkg": {
|
||||
"scripts": [
|
||||
"dist/*.js"
|
||||
],
|
||||
"targets": [
|
||||
"node9-alpine-x64",
|
||||
"node9-linux-x64",
|
||||
"node9-macos-x64",
|
||||
"node9-win-x64"
|
||||
]
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"download",
|
||||
"dist",
|
||||
"test/fixtures"
|
||||
],
|
||||
"eslintConfig": {
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:flowtype/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"flowtype"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"func-names": 0,
|
||||
"no-console": 0,
|
||||
"no-shadow": 0,
|
||||
"no-extra-semi": 0,
|
||||
"no-empty": 0
|
||||
}
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-flow-comments",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/plugin-proposal-async-generator-functions"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-async-generator-functions": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.0.0-beta.44",
|
||||
"@babel/plugin-transform-flow-comments": "7.0.0-beta.44",
|
||||
"@babel/plugin-transform-runtime": "7.0.0-beta.44",
|
||||
"@babel/preset-env": "7.0.0-beta.44",
|
||||
"@babel/runtime": "7.0.0-beta.44",
|
||||
"@zeit/dockerignore": "0.0.3",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.4.0",
|
||||
"alpha-sort": "2.0.1",
|
||||
"ansi-escapes": "3.0.0",
|
||||
"ansi-regex": "3.0.0",
|
||||
"archiver": "2.0.3",
|
||||
"arg": "2.0.0",
|
||||
"arr-flatten": "1.1.0",
|
||||
"async-retry": "1.1.3",
|
||||
"async-sema": "1.2.0",
|
||||
"ava": "0.25.0",
|
||||
"aws-sdk": "2.124.0",
|
||||
"babel-eslint": "8.2.2",
|
||||
"babel-loader": "8.0.0-beta.2",
|
||||
"bytes": "3.0.0",
|
||||
"chalk": "2.3.1",
|
||||
"child-process-promise": "2.2.1",
|
||||
"clipboardy": "1.1.4",
|
||||
"convert-stream": "1.0.2",
|
||||
"copy-webpack-plugin": "4.0.1",
|
||||
"credit-card": "3.0.1",
|
||||
"death": "1.1.0",
|
||||
"debug": "3.1.0",
|
||||
"deployment-type": "1.0.1",
|
||||
"docker-file-parser": "1.0.2",
|
||||
"dotenv": "4.0.0",
|
||||
"download": "6.2.5",
|
||||
"email-prompt": "0.3.2",
|
||||
"email-validator": "1.1.1",
|
||||
"epipebomb": "1.0.0",
|
||||
"es7-sleep": "1.0.0",
|
||||
"eslint": "4.7.2",
|
||||
"eslint-plugin-flowtype": "2.46.1",
|
||||
"execa": "0.9.0",
|
||||
"fetch-h2": "0.2.5",
|
||||
"flow-babel-webpack-plugin": "1.1.1",
|
||||
"fs-extra": "4.0.2",
|
||||
"glob": "7.1.2",
|
||||
"ignore": "3.3.5",
|
||||
"ini": "1.3.4",
|
||||
"inquirer": "3.3.0",
|
||||
"is-url": "1.2.2",
|
||||
"jsonlines": "0.1.1",
|
||||
"lint-staged": "4.2.3",
|
||||
"load-json-file": "3.0.0",
|
||||
"lodash.range": "3.2.0",
|
||||
"micro": "9.1.2",
|
||||
"mkdirp-promise": "5.0.1",
|
||||
"mri": "1.1.0",
|
||||
"ms": "2.0.0",
|
||||
"node-fetch": "1.7.3",
|
||||
"opn": "5.1.0",
|
||||
"ora": "1.3.0",
|
||||
"pipe-streams-to-promise": "0.2.0",
|
||||
"pkg": "4.3.0",
|
||||
"pluralize": "7.0.0",
|
||||
"pre-commit": "1.2.2",
|
||||
"prettier": "1.7.2",
|
||||
"printf": "0.2.5",
|
||||
"progress": "2.0.0",
|
||||
"psl": "1.1.20",
|
||||
"qr-image": "3.2.0",
|
||||
"read-pkg": "2.0.0",
|
||||
"semver": "5.5.0",
|
||||
"shebang-loader": "0.0.1",
|
||||
"single-line-log": "1.1.2",
|
||||
"sinon": "4.4.2",
|
||||
"strip-ansi": "4.0.0",
|
||||
"stripe": "5.1.0",
|
||||
"supports-color": "5.2.0",
|
||||
"tar-fs": "1.15.3",
|
||||
"test-listen": "1.1.0",
|
||||
"text-table": "0.2.0",
|
||||
"then-sleep": "1.0.1",
|
||||
"through2": "2.0.3",
|
||||
"tmp-promise": "1.0.3",
|
||||
"uid-promise": "1.0.0",
|
||||
"update-check": "1.5.0",
|
||||
"webpack": "3.6.0",
|
||||
"webpack-node-externals": "1.6.0",
|
||||
"which-promise": "1.0.0",
|
||||
"write-json-file": "2.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# build fixtures for tests
|
||||
yarn --cwd test/fixtures install
|
||||
yarn --cwd test/fixtures run build
|
||||
@@ -1,33 +0,0 @@
|
||||
const path = require('path');
|
||||
const writeFile = require('util').promisify(require('fs').writeFile);
|
||||
|
||||
const REDIRECT_FILE_NAME = '__now_routes_g4t5bY.json';
|
||||
|
||||
exports.onPostBuild = async ({ store }) => {
|
||||
const { redirects, program } = store.getState();
|
||||
|
||||
if (!redirects.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const routes = [{ handle: 'filesystem' }];
|
||||
|
||||
for (const redirect of redirects) {
|
||||
const route = {
|
||||
src: redirect.fromPath,
|
||||
status: redirect.statusCode || (redirect.isPermanent ? 301 : 302),
|
||||
headers: { Location: redirect.toPath }
|
||||
};
|
||||
|
||||
if (redirect.force) {
|
||||
routes.unshift(route);
|
||||
} else {
|
||||
routes.push(route);
|
||||
}
|
||||
}
|
||||
|
||||
await writeFile(
|
||||
path.join(program.directory, 'public', REDIRECT_FILE_NAME),
|
||||
JSON.stringify(routes)
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
// noop
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "gatsby-plugin-now",
|
||||
"version": "1.2.1",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/zeit/now/tree/canary/packages/gatsby-plugin-now",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zeit/now.git",
|
||||
"directory": "packages/gatsby-plugin-now"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./build.sh",
|
||||
"test-integration-once": "jest --verbose"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"gatsby-node.js"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"gatsby": ">=2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "24.9.0"
|
||||
},
|
||||
"jest": {
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
"<rootDir>/test/fixtures/"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
# gatsby-plugin-now
|
||||
|
||||
This plugin generates [Now Routes](https://zeit.co/docs/v2/advanced/routes) for [redirects](https://www.gatsbyjs.org/docs/actions/#createRedirect) you configured for to your Gatsby project.
|
||||
|
||||
### Usage
|
||||
|
||||
1. Install the plugin:
|
||||
|
||||
```
|
||||
npm install gatsby-plugin-now --save-dev
|
||||
```
|
||||
|
||||
2. Add it to the configuration file:
|
||||
|
||||
```
|
||||
// gatsby-config.js
|
||||
module.exports = {
|
||||
plugins: [
|
||||
'gatsby-plugin-now'
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3. [Deploy your project to ZEIT Now](https://www.gatsbyjs.org/docs/deploying-to-zeit-now/)
|
||||
@@ -1,86 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`test generated now routes 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/",
|
||||
},
|
||||
"src": "/my-special-redirect",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"handle": "filesystem",
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/page-2",
|
||||
},
|
||||
"src": "/page2",
|
||||
"status": 301,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/page-2/",
|
||||
},
|
||||
"src": "/page2/",
|
||||
"status": 301,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/",
|
||||
},
|
||||
"src": "/orange",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/",
|
||||
},
|
||||
"src": "/grape",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/page-2/",
|
||||
},
|
||||
"src": "/blue",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/page-2/",
|
||||
},
|
||||
"src": "/randirect",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/",
|
||||
},
|
||||
"src": "/juice",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/",
|
||||
},
|
||||
"src": "/soda",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/page-2/",
|
||||
},
|
||||
"src": "/donut",
|
||||
"status": 302,
|
||||
},
|
||||
Object {
|
||||
"headers": Object {
|
||||
"Location": "/page-2/",
|
||||
},
|
||||
"src": "/randorect",
|
||||
"status": 302,
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -1,4 +0,0 @@
|
||||
public
|
||||
node_modules
|
||||
.cache
|
||||
yarn.lock
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: [{ resolve: require.resolve('../../') }]
|
||||
};
|
||||
@@ -1,105 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Implement the Gatsby API “createPages”. This is called once the
|
||||
// data layer is bootstrapped to let plugins create pages from data.
|
||||
exports.createPages = ({ actions }) => {
|
||||
// need createRedirect action in actions collection
|
||||
// to make the redirection magic happen.
|
||||
// https://www.gatsbyjs.org/docs/bound-action-creators/
|
||||
const { createRedirect } = actions;
|
||||
|
||||
// Let’s set up some string consts to use thoroughout the following.
|
||||
// MDN > JavaScript reference > Statements and declarations
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
|
||||
// Maybe we usually redirect to page 2, with trailing slash.
|
||||
const page2Path = `/page-2/`;
|
||||
// But sometimes to homepage.
|
||||
const homePath = `/`;
|
||||
|
||||
// One-off redirect, note trailing slash missing on fromPath and
|
||||
// toPath here.
|
||||
createRedirect({
|
||||
fromPath: `/page2`,
|
||||
isPermanent: true,
|
||||
redirectInBrowser: true,
|
||||
toPath: `/page-2`
|
||||
});
|
||||
|
||||
// Another one-off redirect, note trailing slash on toPath here.
|
||||
// This time we want trailing slash on toPath so we use
|
||||
// page2Path. Need to handle trailing-slashed and non-trailing-
|
||||
// slashed fromPaths separately, Gatsby serves page components
|
||||
// at either version by default, but we need to explicitly redirect
|
||||
// both versions independently, more on page components:
|
||||
// Docs > Building with Components
|
||||
// https://www.gatsbyjs.org/docs/building-with-components/
|
||||
// and handling trailing slashes:
|
||||
// Docs > Creating and modifying pages > Removing trailing slashes
|
||||
// https://www.gatsbyjs.org/docs/creating-and-modifying-pages/#removing-trailing-slashes
|
||||
createRedirect({
|
||||
fromPath: `/page2/`,
|
||||
isPermanent: true,
|
||||
redirectInBrowser: true,
|
||||
toPath: page2Path
|
||||
});
|
||||
|
||||
// One approach to handle several redirects at once is to create an
|
||||
// array of from/to path pairs.
|
||||
let redirectBatch1 = [
|
||||
{ f: `/orange`, t: `/` },
|
||||
// We could use homePath and page2Path directly here.
|
||||
{ f: `/grape`, t: homePath },
|
||||
{ f: `/blue`, t: page2Path },
|
||||
// or leave to empty and swap for page2Path later on.
|
||||
{ f: `/randirect`, t: `` }
|
||||
];
|
||||
|
||||
// Then we can loop through the array of object literals to create
|
||||
// each redirect. A for loop would do the trick
|
||||
for (var { f: f, t: t } of redirectBatch1) {
|
||||
// Here we swap any empty toPath values for trusty page 2 via
|
||||
// page2Path.
|
||||
if (t === ``) {
|
||||
t = page2Path;
|
||||
}
|
||||
createRedirect({
|
||||
fromPath: f,
|
||||
redirectInBrowser: true,
|
||||
toPath: t
|
||||
});
|
||||
// Uncomment next line to see loop in action during build
|
||||
// console.log('\nRedirecting:\n' + f + '\nTo:\n' + t + '\n');
|
||||
// or check .cache/redirects.json post-compile.
|
||||
}
|
||||
|
||||
// A more modern approach might use forEach rather than for...of
|
||||
// Compare
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration#for...of_statement
|
||||
// and
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
|
||||
let redirectBatch2 = [
|
||||
{ f: `/juice`, t: `/` },
|
||||
{ f: `/soda`, t: `/` },
|
||||
{ f: `/donut`, t: page2Path },
|
||||
{ f: `/randorect`, t: `` }
|
||||
];
|
||||
|
||||
redirectBatch2.forEach(({ f, t }) => {
|
||||
if (t === ``) {
|
||||
t = page2Path;
|
||||
}
|
||||
createRedirect({
|
||||
fromPath: f,
|
||||
redirectInBrowser: true,
|
||||
toPath: t
|
||||
});
|
||||
// Uncomment next line to see forEach in action during build
|
||||
// console.log('\nRedirecting:\n' + f + '\nTo:\n' + t + '\n');
|
||||
});
|
||||
|
||||
createRedirect({
|
||||
fromPath: '/my-special-redirect',
|
||||
toPath: homePath,
|
||||
force: true
|
||||
});
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "fixtures",
|
||||
"dependencies": {
|
||||
"gatsby": "2.14.0",
|
||||
"react": "16.9.0",
|
||||
"react-dom": "16.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gatsby build"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const IndexPage = () => (
|
||||
<div>
|
||||
<h1>Hi people</h1>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default IndexPage;
|
||||
@@ -1,5 +0,0 @@
|
||||
test('test generated now routes', async () => {
|
||||
const nowRoutes = require('./fixtures/public/__now_routes_g4t5bY.json');
|
||||
|
||||
expect(nowRoutes).toMatchSnapshot();
|
||||
});
|
||||
3
packages/now-build-utils/.gitignore
vendored
3
packages/now-build-utils/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
dist
|
||||
test/symlinks-out
|
||||
test/symlinks.zip
|
||||
@@ -1,3 +0,0 @@
|
||||
/src
|
||||
/test
|
||||
tmp
|
||||
@@ -1,6 +0,0 @@
|
||||
tsc
|
||||
|
||||
rm dist/index.js
|
||||
ncc build src/index.ts -o dist/main
|
||||
mv dist/main/index.js dist/index.js
|
||||
rm -rf dist/main
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('./dist/index').FileBlob;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('./dist/index').FileFsRef;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('./dist/index').FileRef;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../dist/index').download;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../dist/index').getWriteableDirectory;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../dist/index').glob;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../dist/index').rename;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../dist/index');
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../dist/index').streamToBuffer;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('./dist/index');
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "@now/build-utils",
|
||||
"version": "0.10.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
"homepage": "https://zeit.co/docs/v2/deployments/builders/developer-guide",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zeit/now.git",
|
||||
"directory": "packages/now-build-utils"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./build.sh",
|
||||
"test-integration-once": "jest --env node --verbose --runInBand",
|
||||
"prepublishOnly": "./build.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/async-retry": "^1.2.1",
|
||||
"@types/cross-spawn": "6.0.0",
|
||||
"@types/end-of-stream": "^1.4.0",
|
||||
"@types/fs-extra": "^5.0.5",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/multistream": "2.1.1",
|
||||
"@types/node-fetch": "^2.1.6",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "^2.4.1",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "2.1.4",
|
||||
"cross-spawn": "6.0.5",
|
||||
"end-of-stream": "1.4.1",
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "7.0.0",
|
||||
"glob": "7.1.3",
|
||||
"into-stream": "5.0.0",
|
||||
"minimatch": "3.0.4",
|
||||
"multistream": "2.1.1",
|
||||
"node-fetch": "2.2.0",
|
||||
"semver": "6.1.1",
|
||||
"typescript": "3.5.2",
|
||||
"yazl": "2.4.3"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function debug(message: string, ...additional: any[]) {
|
||||
if (process.env.NOW_BUILDER_DEBUG) {
|
||||
console.log(message, ...additional);
|
||||
} else if (process.env.NOW_BUILDER_ANNOTATE) {
|
||||
console.log(`[now-builder-debug] ${message}`, ...additional);
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
import { PackageJson, Builder, Config } from './types';
|
||||
import minimatch from 'minimatch';
|
||||
|
||||
interface ErrorResponse {
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
tag?: 'canary' | 'latest' | string;
|
||||
}
|
||||
|
||||
const src = 'package.json';
|
||||
const config: Config = { zeroConfig: true };
|
||||
|
||||
const MISSING_BUILD_SCRIPT_ERROR: ErrorResponse = {
|
||||
code: 'missing_build_script',
|
||||
message:
|
||||
'Your `package.json` file is missing a `build` property inside the `script` property.' +
|
||||
'\nMore details: https://zeit.co/docs/v2/advanced/platform/frequently-asked-questions#missing-build-script'
|
||||
};
|
||||
|
||||
// Static builders are special cased in `@now/static-build`
|
||||
function getBuilders(): Map<string, Builder> {
|
||||
return new Map<string, Builder>([
|
||||
['next', { src, use: '@now/next', config }]
|
||||
]);
|
||||
}
|
||||
|
||||
// Must be a function to ensure that the returned
|
||||
// object won't be a reference
|
||||
function getApiBuilders(): Builder[] {
|
||||
return [
|
||||
{ src: 'api/**/*.js', use: '@now/node', config },
|
||||
{ src: 'api/**/*.ts', use: '@now/node', config },
|
||||
{ src: 'api/**/*.go', use: '@now/go', config },
|
||||
{ src: 'api/**/*.py', use: '@now/python', config },
|
||||
{ src: 'api/**/*.rb', use: '@now/ruby', config }
|
||||
];
|
||||
}
|
||||
|
||||
function hasPublicDirectory(files: string[]) {
|
||||
return files.some(name => name.startsWith('public/'));
|
||||
}
|
||||
|
||||
function hasBuildScript(pkg: PackageJson | undefined) {
|
||||
const { scripts = {} } = pkg || {};
|
||||
return Boolean(scripts && scripts['build']);
|
||||
}
|
||||
|
||||
async function detectBuilder(pkg: PackageJson): Promise<Builder> {
|
||||
for (const [dependency, builder] of getBuilders()) {
|
||||
const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
|
||||
|
||||
// Return the builder when a dependency matches
|
||||
if (deps[dependency]) {
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
// By default we'll choose the `static-build` builder
|
||||
return { src, use: '@now/static-build', config };
|
||||
}
|
||||
|
||||
// Files that match a specific pattern will get ignored
|
||||
export function ignoreApiFilter(file: string) {
|
||||
if (file.includes('/.')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.includes('/_')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.endsWith('.d.ts')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the file does not match any builder we also
|
||||
// don't want to create a route e.g. `package.json`
|
||||
if (getApiBuilders().every(({ src }) => !minimatch(file, src))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We need to sort the file paths by alphabet to make
|
||||
// sure the routes stay in the same order e.g. for deduping
|
||||
export function sortFiles(fileA: string, fileB: string) {
|
||||
return fileA.localeCompare(fileB);
|
||||
}
|
||||
|
||||
async function detectApiBuilders(files: string[]): Promise<Builder[]> {
|
||||
const builds = files
|
||||
.sort(sortFiles)
|
||||
.filter(ignoreApiFilter)
|
||||
.map(file => {
|
||||
const result = getApiBuilders().find(({ src }): boolean =>
|
||||
minimatch(file, src)
|
||||
);
|
||||
|
||||
return result ? { ...result, src: file } : null;
|
||||
});
|
||||
|
||||
const finishedBuilds = builds.filter(Boolean);
|
||||
return finishedBuilds as Builder[];
|
||||
}
|
||||
|
||||
// When zero config is used we can call this function
|
||||
// to determine what builders to use
|
||||
export async function detectBuilders(
|
||||
files: string[],
|
||||
pkg?: PackageJson | undefined | null,
|
||||
options?: Options
|
||||
): Promise<{
|
||||
builders: Builder[] | null;
|
||||
errors: ErrorResponse[] | null;
|
||||
}> {
|
||||
const errors: ErrorResponse[] = [];
|
||||
|
||||
// Detect all builders for the `api` directory before anything else
|
||||
let builders = await detectApiBuilders(files);
|
||||
|
||||
if (pkg && hasBuildScript(pkg)) {
|
||||
builders.push(await detectBuilder(pkg));
|
||||
} else {
|
||||
if (pkg && builders.length === 0) {
|
||||
// We only show this error when there are no api builders
|
||||
// since the dependencies of the pkg could be used for those
|
||||
errors.push(MISSING_BUILD_SCRIPT_ERROR);
|
||||
return { errors, builders: null };
|
||||
}
|
||||
|
||||
// We allow a `public` directory
|
||||
// when there are no build steps
|
||||
if (hasPublicDirectory(files)) {
|
||||
builders.push({
|
||||
use: '@now/static',
|
||||
src: 'public/**/*',
|
||||
config
|
||||
});
|
||||
} else if (builders.length > 0) {
|
||||
// We can't use pattern matching, since `!(api)` and `!(api)/**/*`
|
||||
// won't give the correct results
|
||||
builders.push(
|
||||
...files
|
||||
.filter(name => !name.startsWith('api/'))
|
||||
.filter(name => !(name === 'package.json'))
|
||||
.map(name => ({
|
||||
use: '@now/static',
|
||||
src: name,
|
||||
config
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Change the tag for the builders
|
||||
if (builders && builders.length) {
|
||||
const tag = options && options.tag;
|
||||
|
||||
if (tag) {
|
||||
builders = builders.map((originBuilder: Builder) => {
|
||||
// Copy builder to make sure it is not a reference
|
||||
const builder = { ...originBuilder };
|
||||
|
||||
// @now/static has no canary builder
|
||||
if (builder.use !== '@now/static') {
|
||||
builder.use = `${builder.use}@${tag}`;
|
||||
}
|
||||
|
||||
return builder;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
builders: builders.length ? builders : null,
|
||||
errors: errors.length ? errors : null
|
||||
};
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
import { Route, Builder } from './types';
|
||||
import { parse as parsePath } from 'path';
|
||||
import { ignoreApiFilter, sortFiles } from './detect-builders';
|
||||
|
||||
function escapeName(name: string) {
|
||||
const special = '[]^$.|?*+()'.split('');
|
||||
|
||||
for (const char of special) {
|
||||
name = name.replace(new RegExp(`\\${char}`, 'g'), `\\${char}`);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function joinPath(...segments: string[]) {
|
||||
const joinedPath = segments.join('/');
|
||||
return joinedPath.replace(/\/{2,}/g, '/');
|
||||
}
|
||||
|
||||
function concatArrayOfText(texts: string[]): string {
|
||||
if (texts.length <= 2) {
|
||||
return texts.join(' and ');
|
||||
}
|
||||
|
||||
const last = texts.pop();
|
||||
return `${texts.join(', ')}, and ${last}`;
|
||||
}
|
||||
|
||||
// Takes a filename or foldername, strips the extension
|
||||
// gets the part between the "[]" brackets.
|
||||
// It will return `null` if there are no brackets
|
||||
// and therefore no segment.
|
||||
function getSegmentName(segment: string): string | null {
|
||||
const { name } = parsePath(segment);
|
||||
|
||||
if (name.startsWith('[') && name.endsWith(']')) {
|
||||
return name.slice(1, -1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function createRouteFromPath(filePath: string): Route {
|
||||
const parts = filePath.split('/');
|
||||
|
||||
let counter = 1;
|
||||
const query: string[] = [];
|
||||
|
||||
const srcParts = parts.map((segment, index): string => {
|
||||
const name = getSegmentName(segment);
|
||||
const isLast = index === parts.length - 1;
|
||||
|
||||
if (name !== null) {
|
||||
// We can't use `URLSearchParams` because `$` would get escaped
|
||||
query.push(`${name}=$${counter++}`);
|
||||
return `([^\\/]+)`;
|
||||
} else if (isLast) {
|
||||
const { name: fileName, ext } = parsePath(segment);
|
||||
const isIndex = fileName === 'index';
|
||||
const prefix = isIndex ? '\\/' : '';
|
||||
|
||||
const names = [
|
||||
prefix,
|
||||
prefix + escapeName(fileName),
|
||||
prefix + escapeName(fileName) + escapeName(ext)
|
||||
].filter(Boolean);
|
||||
|
||||
// Either filename with extension, filename without extension
|
||||
// or nothing when the filename is `index`
|
||||
return `(${names.join('|')})${isIndex ? '?' : ''}`;
|
||||
}
|
||||
|
||||
return segment;
|
||||
});
|
||||
|
||||
const { name: fileName } = parsePath(filePath);
|
||||
const isIndex = fileName === 'index';
|
||||
|
||||
const src = isIndex
|
||||
? `^/${srcParts.slice(0, -1).join('/')}${srcParts.slice(-1)[0]}$`
|
||||
: `^/${srcParts.join('/')}$`;
|
||||
|
||||
const dest = `/${filePath}${query.length ? '?' : ''}${query.join('&')}`;
|
||||
|
||||
return { src, dest };
|
||||
}
|
||||
|
||||
// Check if the path partially matches and has the same
|
||||
// name for the path segment at the same position
|
||||
function partiallyMatches(pathA: string, pathB: string): boolean {
|
||||
const partsA = pathA.split('/');
|
||||
const partsB = pathB.split('/');
|
||||
|
||||
const long = partsA.length > partsB.length ? partsA : partsB;
|
||||
const short = long === partsA ? partsB : partsA;
|
||||
|
||||
let index = 0;
|
||||
|
||||
for (const segmentShort of short) {
|
||||
const segmentLong = long[index];
|
||||
|
||||
const nameLong = getSegmentName(segmentLong);
|
||||
const nameShort = getSegmentName(segmentShort);
|
||||
|
||||
// If there are no segments or the paths differ we
|
||||
// return as they are not matching
|
||||
if (segmentShort !== segmentLong && (!nameLong || !nameShort)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nameLong !== nameShort) {
|
||||
return true;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Counts how often a path occurres when all placeholders
|
||||
// got resolved, so we can check if they have conflicts
|
||||
function pathOccurrences(filePath: string, files: string[]): string[] {
|
||||
const getAbsolutePath = (unresolvedPath: string): string => {
|
||||
const { dir, name } = parsePath(unresolvedPath);
|
||||
const parts = joinPath(dir, name).split('/');
|
||||
return parts.map(part => part.replace(/\[.*\]/, '1')).join('/');
|
||||
};
|
||||
|
||||
const currentAbsolutePath = getAbsolutePath(filePath);
|
||||
|
||||
return files.reduce((prev: string[], file: string): string[] => {
|
||||
const absolutePath = getAbsolutePath(file);
|
||||
|
||||
if (absolutePath === currentAbsolutePath) {
|
||||
prev.push(file);
|
||||
} else if (partiallyMatches(filePath, file)) {
|
||||
prev.push(file);
|
||||
}
|
||||
|
||||
return prev;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// Checks if a placeholder with the same name is used
|
||||
// multiple times inside the same path
|
||||
function getConflictingSegment(filePath: string): string | null {
|
||||
const segments = new Set<string>();
|
||||
|
||||
for (const segment of filePath.split('/')) {
|
||||
const name = getSegmentName(segment);
|
||||
|
||||
if (name !== null && segments.has(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
segments.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function sortFilesBySegmentCount(fileA: string, fileB: string): number {
|
||||
const lengthA = fileA.split('/').length;
|
||||
const lengthB = fileB.split('/').length;
|
||||
|
||||
if (lengthA > lengthB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lengthA < lengthB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Paths that have the same segment length but
|
||||
// less placeholders are preferred
|
||||
const countSegments = (prev: number, segment: string) =>
|
||||
getSegmentName(segment) ? prev + 1 : 0;
|
||||
const segmentLengthA = fileA.split('/').reduce(countSegments, 0);
|
||||
const segmentLengthB = fileB.split('/').reduce(countSegments, 0);
|
||||
|
||||
if (segmentLengthA > segmentLengthB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (segmentLengthA < segmentLengthB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
interface RoutesResult {
|
||||
defaultRoutes: Route[] | null;
|
||||
error: { [key: string]: string } | null;
|
||||
}
|
||||
|
||||
async function detectApiRoutes(files: string[]): Promise<RoutesResult> {
|
||||
if (!files || files.length === 0) {
|
||||
return { defaultRoutes: null, error: null };
|
||||
}
|
||||
|
||||
// The deepest routes need to be
|
||||
// the first ones to get handled
|
||||
const sortedFiles = files
|
||||
.filter(ignoreApiFilter)
|
||||
.sort(sortFiles)
|
||||
.sort(sortFilesBySegmentCount);
|
||||
|
||||
const defaultRoutes: Route[] = [];
|
||||
|
||||
for (const file of sortedFiles) {
|
||||
// We only consider every file in the api directory
|
||||
// as we will strip extensions as well as resolving "[segments]"
|
||||
if (!file.startsWith('api/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const conflictingSegment = getConflictingSegment(file);
|
||||
|
||||
if (conflictingSegment) {
|
||||
return {
|
||||
defaultRoutes: null,
|
||||
error: {
|
||||
code: 'conflicting_path_segment',
|
||||
message:
|
||||
`The segment "${conflictingSegment}" occurres more than ` +
|
||||
`one time in your path "${file}". Please make sure that ` +
|
||||
`every segment in a path is unique`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const occurrences = pathOccurrences(file, sortedFiles).filter(
|
||||
name => name !== file
|
||||
);
|
||||
|
||||
if (occurrences.length > 0) {
|
||||
const messagePaths = concatArrayOfText(
|
||||
occurrences.map(name => `"${name}"`)
|
||||
);
|
||||
|
||||
return {
|
||||
defaultRoutes: null,
|
||||
error: {
|
||||
code: 'conflicting_file_path',
|
||||
message:
|
||||
`Two or more files have conflicting paths or names. ` +
|
||||
`Please make sure path segments and filenames, without their extension, are unique. ` +
|
||||
`The path "${file}" has conflicts with ${messagePaths}`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
defaultRoutes.push(createRouteFromPath(file));
|
||||
}
|
||||
|
||||
// 404 Route to disable directory listing
|
||||
if (defaultRoutes.length) {
|
||||
defaultRoutes.push({
|
||||
status: 404,
|
||||
src: '/api(\\/.*)?$'
|
||||
});
|
||||
}
|
||||
|
||||
return { defaultRoutes, error: null };
|
||||
}
|
||||
|
||||
function hasPublicBuilder(builders: Builder[]): boolean {
|
||||
return builders.some(
|
||||
builder =>
|
||||
builder.use === '@now/static' &&
|
||||
builder.src === 'public/**/*' &&
|
||||
builder.config &&
|
||||
builder.config.zeroConfig === true
|
||||
);
|
||||
}
|
||||
|
||||
export async function detectRoutes(
|
||||
files: string[],
|
||||
builders: Builder[]
|
||||
): Promise<RoutesResult> {
|
||||
const routesResult = await detectApiRoutes(files);
|
||||
|
||||
if (routesResult.defaultRoutes && hasPublicBuilder(builders)) {
|
||||
routesResult.defaultRoutes.push({
|
||||
src: '/(.*)',
|
||||
dest: '/public/$1'
|
||||
});
|
||||
}
|
||||
|
||||
return routesResult;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import assert from 'assert';
|
||||
import intoStream from 'into-stream';
|
||||
import { File } from './types';
|
||||
|
||||
interface FileBlobOptions {
|
||||
mode?: number;
|
||||
data: string | Buffer;
|
||||
}
|
||||
|
||||
interface FromStreamOptions {
|
||||
mode?: number;
|
||||
stream: NodeJS.ReadableStream;
|
||||
}
|
||||
|
||||
export default class FileBlob implements File {
|
||||
public type: 'FileBlob';
|
||||
public mode: number;
|
||||
public data: string | Buffer;
|
||||
|
||||
constructor({ mode = 0o100644, data }: FileBlobOptions) {
|
||||
assert(typeof mode === 'number');
|
||||
assert(typeof data === 'string' || Buffer.isBuffer(data));
|
||||
this.type = 'FileBlob';
|
||||
this.mode = mode;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
static async fromStream({ mode = 0o100644, stream }: FromStreamOptions) {
|
||||
assert(typeof mode === 'number');
|
||||
assert(typeof stream.pipe === 'function'); // is-stream
|
||||
const chunks: Buffer[] = [];
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
||||
stream.on('error', error => reject(error));
|
||||
stream.on('end', () => resolve());
|
||||
});
|
||||
|
||||
const data = Buffer.concat(chunks);
|
||||
return new FileBlob({ mode, data });
|
||||
}
|
||||
|
||||
toStream(): NodeJS.ReadableStream {
|
||||
return intoStream(this.data);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
import assert from 'assert';
|
||||
import fs from 'fs-extra';
|
||||
import multiStream from 'multistream';
|
||||
import path from 'path';
|
||||
import Sema from 'async-sema';
|
||||
import { File } from './types';
|
||||
|
||||
const semaToPreventEMFILE = new Sema(20);
|
||||
|
||||
interface FileFsRefOptions {
|
||||
mode?: number;
|
||||
fsPath: string;
|
||||
}
|
||||
|
||||
interface FromStreamOptions {
|
||||
mode: number;
|
||||
stream: NodeJS.ReadableStream;
|
||||
fsPath: string;
|
||||
}
|
||||
|
||||
class FileFsRef implements File {
|
||||
public type: 'FileFsRef';
|
||||
public mode: number;
|
||||
public fsPath: string;
|
||||
|
||||
constructor({ mode = 0o100644, fsPath }: FileFsRefOptions) {
|
||||
assert(typeof mode === 'number');
|
||||
assert(typeof fsPath === 'string');
|
||||
this.type = 'FileFsRef';
|
||||
this.mode = mode;
|
||||
this.fsPath = fsPath;
|
||||
}
|
||||
|
||||
static async fromFsPath({
|
||||
mode,
|
||||
fsPath,
|
||||
}: FileFsRefOptions): Promise<FileFsRef> {
|
||||
let m = mode;
|
||||
if (!m) {
|
||||
const stat = await fs.lstat(fsPath);
|
||||
m = stat.mode;
|
||||
}
|
||||
return new FileFsRef({ mode: m, fsPath });
|
||||
}
|
||||
|
||||
static async fromStream({
|
||||
mode = 0o100644,
|
||||
stream,
|
||||
fsPath,
|
||||
}: FromStreamOptions): Promise<FileFsRef> {
|
||||
assert(typeof mode === 'number');
|
||||
assert(typeof stream.pipe === 'function'); // is-stream
|
||||
assert(typeof fsPath === 'string');
|
||||
await fs.mkdirp(path.dirname(fsPath));
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const dest = fs.createWriteStream(fsPath, {
|
||||
mode: mode & 0o777,
|
||||
});
|
||||
stream.pipe(dest);
|
||||
stream.on('error', reject);
|
||||
dest.on('finish', resolve);
|
||||
dest.on('error', reject);
|
||||
});
|
||||
|
||||
return new FileFsRef({ mode, fsPath });
|
||||
}
|
||||
|
||||
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
|
||||
await semaToPreventEMFILE.acquire();
|
||||
const release = () => semaToPreventEMFILE.release();
|
||||
const stream = fs.createReadStream(this.fsPath);
|
||||
stream.on('close', release);
|
||||
stream.on('error', release);
|
||||
return stream;
|
||||
}
|
||||
|
||||
toStream(): NodeJS.ReadableStream {
|
||||
let flag = false;
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return multiStream(cb => {
|
||||
if (flag) return cb(null, null);
|
||||
flag = true;
|
||||
|
||||
this.toStreamAsync()
|
||||
.then(stream => {
|
||||
cb(null, stream);
|
||||
})
|
||||
.catch(error => {
|
||||
cb(error, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export = FileFsRef;
|
||||
@@ -1,100 +0,0 @@
|
||||
import assert from 'assert';
|
||||
import fetch from 'node-fetch';
|
||||
import multiStream from 'multistream';
|
||||
import retry from 'async-retry';
|
||||
import Sema from 'async-sema';
|
||||
import { File } from './types';
|
||||
|
||||
interface FileRefOptions {
|
||||
mode?: number;
|
||||
digest: string;
|
||||
mutable?: boolean;
|
||||
}
|
||||
|
||||
const semaToDownloadFromS3 = new Sema(5);
|
||||
|
||||
class BailableError extends Error {
|
||||
public bail: boolean;
|
||||
|
||||
constructor(...args: string[]) {
|
||||
super(...args);
|
||||
this.bail = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default class FileRef implements File {
|
||||
public type: 'FileRef';
|
||||
public mode: number;
|
||||
public digest: string;
|
||||
private mutable: boolean;
|
||||
|
||||
constructor({ mode = 0o100644, digest, mutable = false }: FileRefOptions) {
|
||||
assert(typeof mode === 'number');
|
||||
assert(typeof digest === 'string');
|
||||
this.type = 'FileRef';
|
||||
this.mode = mode;
|
||||
this.digest = digest;
|
||||
this.mutable = mutable;
|
||||
}
|
||||
|
||||
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
|
||||
let url = '';
|
||||
// sha:24be087eef9fac01d61b30a725c1a10d7b45a256
|
||||
const [digestType, digestHash] = this.digest.split(':');
|
||||
if (digestType === 'sha') {
|
||||
// This CloudFront URL edge caches the `now-files` S3 bucket to prevent
|
||||
// overloading it. Mutable files cannot be cached.
|
||||
// `https://now-files.s3.amazonaws.com/${digestHash}`
|
||||
url = this.mutable
|
||||
? `https://now-files.s3.amazonaws.com/${digestHash}`
|
||||
: `https://dmmcy0pwk6bqi.cloudfront.net/${digestHash}`;
|
||||
} else if (digestType === 'sha+ephemeral') {
|
||||
// This URL is currently only used for cache files that constantly
|
||||
// change. We shouldn't cache it on CloudFront because it'd always be a
|
||||
// MISS.
|
||||
url = `https://now-ephemeral-files.s3.amazonaws.com/${digestHash}`;
|
||||
} else {
|
||||
throw new Error('Expected digest to be sha');
|
||||
}
|
||||
|
||||
await semaToDownloadFromS3.acquire();
|
||||
// console.time(`downloading ${url}`);
|
||||
try {
|
||||
return await retry(
|
||||
async () => {
|
||||
const resp = await fetch(url);
|
||||
if (!resp.ok) {
|
||||
const error = new BailableError(
|
||||
`download: ${resp.status} ${resp.statusText} for ${url}`
|
||||
);
|
||||
if (resp.status === 403) error.bail = true;
|
||||
throw error;
|
||||
}
|
||||
return resp.body;
|
||||
},
|
||||
{ factor: 1, retries: 3 }
|
||||
);
|
||||
} finally {
|
||||
// console.timeEnd(`downloading ${url}`);
|
||||
semaToDownloadFromS3.release();
|
||||
}
|
||||
}
|
||||
|
||||
toStream(): NodeJS.ReadableStream {
|
||||
let flag = false;
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return multiStream(cb => {
|
||||
if (flag) return cb(null, null);
|
||||
flag = true;
|
||||
|
||||
this.toStreamAsync()
|
||||
.then(stream => {
|
||||
cb(null, stream);
|
||||
})
|
||||
.catch(error => {
|
||||
cb(error, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
import path from 'path';
|
||||
import FileFsRef from '../file-fs-ref';
|
||||
import { File, Files, Meta } from '../types';
|
||||
import { remove, mkdirp, readlink, symlink } from 'fs-extra';
|
||||
|
||||
export interface DownloadedFiles {
|
||||
[filePath: string]: FileFsRef;
|
||||
}
|
||||
|
||||
const S_IFMT = 61440; /* 0170000 type of file */
|
||||
const S_IFLNK = 40960; /* 0120000 symbolic link */
|
||||
|
||||
export function isSymbolicLink(mode: number): boolean {
|
||||
return (mode & S_IFMT) === S_IFLNK;
|
||||
}
|
||||
|
||||
async function downloadFile(file: File, fsPath: string): Promise<FileFsRef> {
|
||||
const { mode } = file;
|
||||
if (mode && isSymbolicLink(mode) && file.type === 'FileFsRef') {
|
||||
const [target] = await Promise.all([
|
||||
readlink((file as FileFsRef).fsPath),
|
||||
mkdirp(path.dirname(fsPath)),
|
||||
]);
|
||||
await symlink(target, fsPath);
|
||||
return FileFsRef.fromFsPath({ mode, fsPath });
|
||||
} else {
|
||||
const stream = file.toStream();
|
||||
return FileFsRef.fromStream({ mode, stream, fsPath });
|
||||
}
|
||||
}
|
||||
|
||||
async function removeFile(basePath: string, fileMatched: string) {
|
||||
const file = path.join(basePath, fileMatched);
|
||||
await remove(file);
|
||||
}
|
||||
|
||||
export default async function download(
|
||||
files: Files,
|
||||
basePath: string,
|
||||
meta?: Meta
|
||||
): Promise<DownloadedFiles> {
|
||||
const { isDev = false, skipDownload = false, filesChanged = null, filesRemoved = null } =
|
||||
meta || {};
|
||||
|
||||
if (isDev || skipDownload) {
|
||||
// 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 = {};
|
||||
|
||||
await Promise.all(
|
||||
Object.keys(files).map(async name => {
|
||||
// If the file does not exist anymore, remove it.
|
||||
if (Array.isArray(filesRemoved) && filesRemoved.includes(name)) {
|
||||
await removeFile(basePath, name);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a file didn't change, do not re-download it.
|
||||
if (Array.isArray(filesChanged) && !filesChanged.includes(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = files[name];
|
||||
const fsPath = path.join(basePath, name);
|
||||
|
||||
files2[name] = await downloadFile(file, fsPath);
|
||||
})
|
||||
);
|
||||
|
||||
return files2;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
import { mkdirp } from 'fs-extra';
|
||||
|
||||
export default async function getWritableDirectory() {
|
||||
const name = Math.floor(Math.random() * 0x7fffffff).toString(16);
|
||||
const directory = join(tmpdir(), name);
|
||||
await mkdirp(directory);
|
||||
return directory;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import path from 'path';
|
||||
import assert from 'assert';
|
||||
import vanillaGlob_ from 'glob';
|
||||
import { promisify } from 'util';
|
||||
import { lstat, Stats } from 'fs-extra';
|
||||
import FileFsRef from '../file-fs-ref';
|
||||
|
||||
type GlobOptions = vanillaGlob_.IOptions;
|
||||
|
||||
interface FsFiles {
|
||||
[filePath: string]: FileFsRef;
|
||||
}
|
||||
|
||||
const vanillaGlob = promisify(vanillaGlob_);
|
||||
|
||||
export default async function glob(
|
||||
pattern: string,
|
||||
opts: GlobOptions | string,
|
||||
mountpoint?: string
|
||||
): Promise<FsFiles> {
|
||||
let options: GlobOptions;
|
||||
if (typeof opts === 'string') {
|
||||
options = { cwd: opts };
|
||||
} else {
|
||||
options = opts;
|
||||
}
|
||||
|
||||
if (!options.cwd) {
|
||||
throw new Error(
|
||||
'Second argument (basePath) must be specified for names of resulting files'
|
||||
);
|
||||
}
|
||||
|
||||
if (!path.isAbsolute(options.cwd)) {
|
||||
throw new Error(`basePath/cwd must be an absolute path (${options.cwd})`);
|
||||
}
|
||||
|
||||
const results: FsFiles = {};
|
||||
|
||||
options.symlinks = {};
|
||||
options.statCache = {};
|
||||
options.stat = true;
|
||||
options.dot = true;
|
||||
|
||||
const files = await vanillaGlob(pattern, options);
|
||||
|
||||
for (const relativePath of files) {
|
||||
const fsPath = path.join(options.cwd!, relativePath).replace(/\\/g, '/');
|
||||
let stat: Stats = options.statCache![fsPath] as Stats;
|
||||
assert(
|
||||
stat,
|
||||
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`
|
||||
);
|
||||
if (stat.isFile()) {
|
||||
const isSymlink = options.symlinks![fsPath];
|
||||
if (isSymlink) {
|
||||
stat = await lstat(fsPath);
|
||||
}
|
||||
|
||||
let finalPath = relativePath;
|
||||
if (mountpoint) {
|
||||
finalPath = path.join(mountpoint, finalPath);
|
||||
}
|
||||
|
||||
results[finalPath] = new FileFsRef({ mode: stat.mode, fsPath });
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { intersects } from 'semver';
|
||||
import { NodeVersion } from '../types';
|
||||
|
||||
const supportedOptions: NodeVersion[] = [
|
||||
{ major: 10, range: '10.x', runtime: 'nodejs10.x' },
|
||||
{ major: 8, range: '8.10.x', runtime: 'nodejs8.10' },
|
||||
];
|
||||
|
||||
// This version should match Fargate's default in the PATH
|
||||
// Today that is Node 8
|
||||
export const defaultSelection = supportedOptions.find(
|
||||
o => o.major === 8
|
||||
) as NodeVersion;
|
||||
|
||||
export async function getSupportedNodeVersion(
|
||||
engineRange?: string,
|
||||
silent?: boolean
|
||||
): Promise<NodeVersion> {
|
||||
let selection = defaultSelection;
|
||||
|
||||
if (!engineRange) {
|
||||
if (!silent) {
|
||||
console.log(
|
||||
'missing `engines` in `package.json`, using default range: ' +
|
||||
selection.range
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const found = supportedOptions.some(o => {
|
||||
// the array is already in order so return the first
|
||||
// match which will be the newest version of node
|
||||
selection = o;
|
||||
return intersects(o.range, engineRange);
|
||||
});
|
||||
if (found) {
|
||||
if (!silent) {
|
||||
console.log(
|
||||
'Found `engines` in `package.json`, selecting range: ' +
|
||||
selection.range
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!silent) {
|
||||
throw new Error(
|
||||
'found `engines` in `package.json` with an unsupported node range: ' +
|
||||
engineRange +
|
||||
'\nplease use `10.x` or `8.10.x` instead'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Files } from '../types';
|
||||
type Delegate = (name: string) => string;
|
||||
|
||||
export default function rename(files: Files, delegate: Delegate): Files {
|
||||
return Object.keys(files).reduce(
|
||||
(newFiles, name) => ({
|
||||
...newFiles,
|
||||
[delegate(name)]: files[name],
|
||||
}),
|
||||
{}
|
||||
);
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
import assert from 'assert';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import debug from '../debug';
|
||||
import spawn from 'cross-spawn';
|
||||
import { SpawnOptions } from 'child_process';
|
||||
import { deprecate } from 'util';
|
||||
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
||||
import { getSupportedNodeVersion } from './node-version';
|
||||
|
||||
function spawnAsync(
|
||||
command: string,
|
||||
args: string[],
|
||||
cwd: string,
|
||||
opts: SpawnOptions = {}
|
||||
) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const stderrLogs: Buffer[] = [];
|
||||
opts = { stdio: 'inherit', cwd, ...opts };
|
||||
const child = spawn(command, args, opts);
|
||||
|
||||
if (opts.stdio === 'pipe' && child.stderr) {
|
||||
child.stderr.on('data', data => stderrLogs.push(data));
|
||||
}
|
||||
|
||||
child.on('error', reject);
|
||||
child.on('close', (code, signal) => {
|
||||
if (code === 0) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
const errorLogs = stderrLogs.map(line => line.toString()).join('');
|
||||
if (opts.stdio !== 'inherit') {
|
||||
reject(new Error(`Exited with ${code || signal}\n${errorLogs}`));
|
||||
} else {
|
||||
reject(new Error(`Exited with ${code || signal}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function chmodPlusX(fsPath: string) {
|
||||
const s = await fs.stat(fsPath);
|
||||
const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise
|
||||
if (s.mode === newMode) return;
|
||||
const base8 = newMode.toString(8).slice(-3);
|
||||
await fs.chmod(fsPath, base8);
|
||||
}
|
||||
|
||||
export async function runShellScript(
|
||||
fsPath: string,
|
||||
args: string[] = [],
|
||||
spawnOpts?: SpawnOptions
|
||||
) {
|
||||
assert(path.isAbsolute(fsPath));
|
||||
const destPath = path.dirname(fsPath);
|
||||
await chmodPlusX(fsPath);
|
||||
await spawnAsync(`./${path.basename(fsPath)}`, args, destPath, spawnOpts);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getSpawnOptions(
|
||||
meta: Meta,
|
||||
nodeVersion: NodeVersion
|
||||
): SpawnOptions {
|
||||
const opts = {
|
||||
env: { ...process.env },
|
||||
};
|
||||
|
||||
if (!meta.isDev) {
|
||||
opts.env.PATH = `/node${nodeVersion.major}/bin:${opts.env.PATH}`;
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
export async function getNodeVersion(
|
||||
destPath: string,
|
||||
minNodeVersion?: string,
|
||||
config?: Config
|
||||
): Promise<NodeVersion> {
|
||||
const { packageJson } = await scanParentDirs(destPath, true);
|
||||
let range: string | undefined;
|
||||
let silent = false;
|
||||
if (packageJson && packageJson.engines && packageJson.engines.node) {
|
||||
range = packageJson.engines.node;
|
||||
} else if (minNodeVersion) {
|
||||
range = minNodeVersion;
|
||||
silent = true;
|
||||
} else if (config && config.zeroConfig) {
|
||||
// Use latest node version zero config detected
|
||||
range = '10.x';
|
||||
silent = true;
|
||||
}
|
||||
return getSupportedNodeVersion(range, silent);
|
||||
}
|
||||
|
||||
async function scanParentDirs(destPath: string, readPackageJson = false) {
|
||||
assert(path.isAbsolute(destPath));
|
||||
|
||||
let hasPackageLockJson = false;
|
||||
let packageJson: PackageJson | undefined;
|
||||
let currentDestPath = destPath;
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const packageJsonPath = path.join(currentDestPath, 'package.json');
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (readPackageJson) {
|
||||
packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
||||
}
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
hasPackageLockJson = await fs.pathExists(
|
||||
path.join(currentDestPath, 'package-lock.json')
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const newDestPath = path.dirname(currentDestPath);
|
||||
if (currentDestPath === newDestPath) break;
|
||||
currentDestPath = newDestPath;
|
||||
}
|
||||
|
||||
return { hasPackageLockJson, packageJson };
|
||||
}
|
||||
|
||||
export async function runNpmInstall(
|
||||
destPath: string,
|
||||
args: string[] = [],
|
||||
spawnOpts?: SpawnOptions,
|
||||
meta?: Meta
|
||||
) {
|
||||
if (meta && meta.isDev) {
|
||||
debug('Skipping dependency installation because dev mode is enabled');
|
||||
return;
|
||||
}
|
||||
|
||||
assert(path.isAbsolute(destPath));
|
||||
|
||||
let commandArgs = args;
|
||||
console.log(`installing to ${destPath}`);
|
||||
const { hasPackageLockJson } = await scanParentDirs(destPath);
|
||||
|
||||
const opts = spawnOpts || { env: process.env };
|
||||
|
||||
if (hasPackageLockJson) {
|
||||
commandArgs = args.filter(a => a !== '--prefer-offline');
|
||||
await spawnAsync(
|
||||
'npm',
|
||||
commandArgs.concat(['install', '--unsafe-perm']),
|
||||
destPath,
|
||||
opts
|
||||
);
|
||||
} else {
|
||||
await spawnAsync(
|
||||
'yarn',
|
||||
commandArgs.concat(['--ignore-engines', '--cwd', destPath]),
|
||||
destPath,
|
||||
opts
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function runPackageJsonScript(
|
||||
destPath: string,
|
||||
scriptName: string,
|
||||
opts?: SpawnOptions
|
||||
) {
|
||||
assert(path.isAbsolute(destPath));
|
||||
const { packageJson, hasPackageLockJson } = await scanParentDirs(
|
||||
destPath,
|
||||
true
|
||||
);
|
||||
const hasScript = Boolean(
|
||||
packageJson &&
|
||||
packageJson.scripts &&
|
||||
scriptName &&
|
||||
packageJson.scripts[scriptName]
|
||||
);
|
||||
if (!hasScript) return false;
|
||||
|
||||
if (hasPackageLockJson) {
|
||||
console.log(`running "npm run ${scriptName}"`);
|
||||
await spawnAsync('npm', ['run', scriptName], destPath, opts);
|
||||
} else {
|
||||
console.log(`running "yarn run ${scriptName}"`);
|
||||
await spawnAsync(
|
||||
'yarn',
|
||||
['--cwd', destPath, 'run', scriptName],
|
||||
destPath,
|
||||
opts
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecate installDependencies() is deprecated.
|
||||
* Please use runNpmInstall() instead.
|
||||
*/
|
||||
export const installDependencies = deprecate(
|
||||
runNpmInstall,
|
||||
'installDependencies() is deprecated. Please use runNpmInstall() instead.'
|
||||
);
|
||||
@@ -1,28 +0,0 @@
|
||||
import eos from 'end-of-stream';
|
||||
|
||||
export default function streamToBuffer(
|
||||
stream: NodeJS.ReadableStream
|
||||
): Promise<Buffer> {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
const buffers: Buffer[] = [];
|
||||
|
||||
stream.on('data', buffers.push.bind(buffers));
|
||||
|
||||
eos(stream, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
switch (buffers.length) {
|
||||
case 0:
|
||||
resolve(Buffer.allocUnsafe(0));
|
||||
break;
|
||||
case 1:
|
||||
resolve(buffers[0]);
|
||||
break;
|
||||
default:
|
||||
resolve(Buffer.concat(buffers));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import FileBlob from './file-blob';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
import FileRef from './file-ref';
|
||||
import { Lambda, createLambda } from './lambda';
|
||||
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,
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
} from './fs/run-user-scripts';
|
||||
import streamToBuffer from './fs/stream-to-buffer';
|
||||
import shouldServe from './should-serve';
|
||||
import { detectBuilders } from './detect-builders';
|
||||
import { detectRoutes } from './detect-routes';
|
||||
import debug from './debug';
|
||||
|
||||
export {
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
FileRef,
|
||||
Lambda,
|
||||
createLambda,
|
||||
download,
|
||||
DownloadedFiles,
|
||||
getWriteableDirectory,
|
||||
glob,
|
||||
rename,
|
||||
installDependencies,
|
||||
runPackageJsonScript,
|
||||
runNpmInstall,
|
||||
runShellScript,
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
streamToBuffer,
|
||||
shouldServe,
|
||||
detectBuilders,
|
||||
detectRoutes,
|
||||
debug,
|
||||
};
|
||||
|
||||
export * from './types';
|
||||
@@ -1,107 +0,0 @@
|
||||
import assert from 'assert';
|
||||
import Sema from 'async-sema';
|
||||
import { ZipFile } from 'yazl';
|
||||
import { readlink } from 'fs-extra';
|
||||
import { Files } from './types';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
import { isSymbolicLink } from './fs/download';
|
||||
import streamToBuffer from './fs/stream-to-buffer';
|
||||
|
||||
interface Environment {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface LambdaOptions {
|
||||
zipBuffer: Buffer;
|
||||
handler: string;
|
||||
runtime: string;
|
||||
environment: Environment;
|
||||
}
|
||||
|
||||
interface CreateLambdaOptions {
|
||||
files: Files;
|
||||
handler: string;
|
||||
runtime: string;
|
||||
environment?: Environment;
|
||||
}
|
||||
|
||||
export class Lambda {
|
||||
public type: 'Lambda';
|
||||
public zipBuffer: Buffer;
|
||||
public handler: string;
|
||||
public runtime: string;
|
||||
public environment: Environment;
|
||||
|
||||
constructor({ zipBuffer, handler, runtime, environment }: LambdaOptions) {
|
||||
this.type = 'Lambda';
|
||||
this.zipBuffer = zipBuffer;
|
||||
this.handler = handler;
|
||||
this.runtime = runtime;
|
||||
this.environment = environment;
|
||||
}
|
||||
}
|
||||
|
||||
const sema = new Sema(10);
|
||||
const mtime = new Date(1540000000000);
|
||||
|
||||
export async function createLambda({
|
||||
files,
|
||||
handler,
|
||||
runtime,
|
||||
environment = {},
|
||||
}: CreateLambdaOptions): Promise<Lambda> {
|
||||
assert(typeof files === 'object', '"files" must be an object');
|
||||
assert(typeof handler === 'string', '"handler" is not a string');
|
||||
assert(typeof runtime === 'string', '"runtime" is not a string');
|
||||
assert(typeof environment === 'object', '"environment" is not an object');
|
||||
|
||||
await sema.acquire();
|
||||
|
||||
try {
|
||||
const zipBuffer = await createZip(files);
|
||||
return new Lambda({
|
||||
zipBuffer,
|
||||
handler,
|
||||
runtime,
|
||||
environment,
|
||||
});
|
||||
} finally {
|
||||
sema.release();
|
||||
}
|
||||
}
|
||||
|
||||
export async function createZip(files: Files): Promise<Buffer> {
|
||||
const names = Object.keys(files).sort();
|
||||
|
||||
const symlinkTargets = new Map<string, string>();
|
||||
for (const name of names) {
|
||||
const file = files[name];
|
||||
if (file.mode && isSymbolicLink(file.mode) && file.type === 'FileFsRef') {
|
||||
const symlinkTarget = await readlink((file as FileFsRef).fsPath);
|
||||
symlinkTargets.set(name, symlinkTarget);
|
||||
}
|
||||
}
|
||||
|
||||
const zipFile = new ZipFile();
|
||||
const zipBuffer = await new Promise<Buffer>((resolve, reject) => {
|
||||
for (const name of names) {
|
||||
const file = files[name];
|
||||
const opts = { mode: file.mode, mtime };
|
||||
const symlinkTarget = symlinkTargets.get(name);
|
||||
if (typeof symlinkTarget === 'string') {
|
||||
zipFile.addBuffer(Buffer.from(symlinkTarget, 'utf8'), name, opts);
|
||||
} else {
|
||||
const stream = file.toStream() as import('stream').Readable;
|
||||
stream.on('error', reject);
|
||||
zipFile.addReadStream(stream, name, opts);
|
||||
}
|
||||
}
|
||||
|
||||
zipFile.end();
|
||||
streamToBuffer(zipFile.outputStream)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
|
||||
return zipBuffer;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { parse } from 'path';
|
||||
import { ShouldServeOptions } from './types';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
|
||||
export default function shouldServe({
|
||||
entrypoint,
|
||||
files,
|
||||
requestPath,
|
||||
}: ShouldServeOptions): boolean {
|
||||
requestPath = requestPath.replace(/\/$/, ''); // sanitize trailing '/'
|
||||
entrypoint = entrypoint.replace(/\\/, '/'); // windows compatibility
|
||||
|
||||
if (entrypoint === requestPath && hasProp(files, entrypoint)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { dir, name } = parse(entrypoint);
|
||||
if (name === 'index' && dir === requestPath && hasProp(files, entrypoint)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasProp(obj: { [path: string]: FileFsRef }, key: string): boolean {
|
||||
return Object.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
import FileRef from './file-ref';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
|
||||
export interface File {
|
||||
type: string;
|
||||
mode: number;
|
||||
toStream: () => NodeJS.ReadableStream;
|
||||
/**
|
||||
* The absolute path to the file in the filesystem
|
||||
*/
|
||||
fsPath?: string;
|
||||
}
|
||||
|
||||
export interface Files {
|
||||
[filePath: string]: File;
|
||||
}
|
||||
|
||||
export interface Route {
|
||||
src?: string;
|
||||
dest?: string;
|
||||
handle?: string;
|
||||
type?: string;
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
continue?: boolean;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
[key: string]:
|
||||
| string
|
||||
| string[]
|
||||
| boolean
|
||||
| number
|
||||
| { [key: string]: string }
|
||||
| undefined;
|
||||
maxLambdaSize?: string;
|
||||
includeFiles?: string | string[];
|
||||
bundle?: boolean;
|
||||
ldsflags?: string;
|
||||
helpers?: boolean;
|
||||
rust?: string;
|
||||
debug?: boolean;
|
||||
zeroConfig?: boolean;
|
||||
import?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
isDev?: boolean;
|
||||
skipDownload?: boolean;
|
||||
requestPath?: string;
|
||||
filesChanged?: string[];
|
||||
filesRemoved?: string[];
|
||||
}
|
||||
|
||||
export interface AnalyzeOptions {
|
||||
/**
|
||||
* All source files of the project
|
||||
*/
|
||||
files: {
|
||||
[filePath: string]: FileRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* Name of entrypoint file for this particular build job. Value
|
||||
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
|
||||
* `entrypoint` is always a discrete file and never a glob, since globs are
|
||||
* expanded into separate builds at deployment time.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you are encouraged to perform your
|
||||
* build process. This directory will be populated with the restored cache.
|
||||
*/
|
||||
workPath: string;
|
||||
|
||||
/**
|
||||
* An arbitrary object passed by the user in the build definition defined
|
||||
* in `now.json`.
|
||||
*/
|
||||
config: Config;
|
||||
}
|
||||
|
||||
export interface BuildOptions {
|
||||
/**
|
||||
* All source files of the project
|
||||
*/
|
||||
files: Files;
|
||||
|
||||
/**
|
||||
* Name of entrypoint file for this particular build job. Value
|
||||
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
|
||||
* `entrypoint` is always a discrete file and never a glob, since globs are
|
||||
* expanded into separate builds at deployment time.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you are encouraged to perform your
|
||||
* build process. This directory will be populated with the restored cache.
|
||||
*/
|
||||
workPath: string;
|
||||
|
||||
/**
|
||||
* An arbitrary object passed by the user in the build definition defined
|
||||
* in `now.json`.
|
||||
*/
|
||||
config: Config;
|
||||
|
||||
/**
|
||||
* Metadata related to the invoker of the builder, used by `now dev`.
|
||||
* Builders may use the properties on this object to change behavior based
|
||||
* on the build environment.
|
||||
*/
|
||||
meta?: Meta;
|
||||
}
|
||||
|
||||
export interface PrepareCacheOptions {
|
||||
/**
|
||||
* All source files of the project
|
||||
*/
|
||||
files: Files;
|
||||
|
||||
/**
|
||||
* Name of entrypoint file for this particular build job. Value
|
||||
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
|
||||
* `entrypoint` is always a discrete file and never a glob, since globs are
|
||||
* expanded into separate builds at deployment time.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you are encouraged to perform your
|
||||
* build process.
|
||||
*/
|
||||
workPath: string;
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you can build a cache to use for
|
||||
* the next run.
|
||||
*/
|
||||
cachePath: string;
|
||||
|
||||
/**
|
||||
* An arbitrary object passed by the user in the build definition defined
|
||||
* in `now.json`.
|
||||
*/
|
||||
config: Config;
|
||||
}
|
||||
|
||||
export interface ShouldServeOptions {
|
||||
/**
|
||||
* A path string from a request.
|
||||
*/
|
||||
requestPath: string;
|
||||
|
||||
/**
|
||||
* Name of entrypoint file for this particular build job. Value
|
||||
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
|
||||
* `entrypoint` is always a discrete file and never a glob, since globs are
|
||||
* expanded into separate builds at deployment time.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* All source files of the project
|
||||
*/
|
||||
files: {
|
||||
[path: string]: FileFsRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you are encouraged to perform your
|
||||
* build process. This directory will be populated with the restored cache.
|
||||
*/
|
||||
workPath: string;
|
||||
|
||||
/**
|
||||
* An arbitrary object passed by the user in the build definition defined
|
||||
* in `now.json`.
|
||||
*/
|
||||
config: Config;
|
||||
}
|
||||
|
||||
export interface PackageJson {
|
||||
name?: string;
|
||||
version?: string;
|
||||
engines?: {
|
||||
[key: string]: string;
|
||||
node: string;
|
||||
npm: string;
|
||||
};
|
||||
scripts?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
dependencies?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
devDependencies?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface NodeVersion {
|
||||
major: number;
|
||||
range: string;
|
||||
runtime: string;
|
||||
}
|
||||
|
||||
export interface Builder {
|
||||
use: string;
|
||||
src: string;
|
||||
config?: Config;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
now.json
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = (req, res) => {
|
||||
res.end(req.query.endpoint);
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = (req, res) => {
|
||||
res.end(`${req.query.endpoint}/${req.query.id}`);
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "mkdir -p public && echo 'hello from index.txt' > public/index.txt"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
now.json
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = (req, res) => {
|
||||
res.end('hello from api/date.js');
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = (req, res) => {
|
||||
res.end('hello from api/date/index.js');
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = (req, res) => {
|
||||
res.end('hello from api/index.js');
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "mkdir -p public && echo 'hello from index.txt' > public/index.txt"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
const cowsay = require('cowsay').say;
|
||||
|
||||
module.exports = (req, resp) => {
|
||||
resp.end(cowsay({ text: 'cross-cow:RANDOMNESS_PLACEHOLDER' }));
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"lib": "../lib"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "lib",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"cowsay": "*"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "api/index.js", "use": "@now/node" }],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/api/index.js",
|
||||
"mustContain": "cross-cow:RANDOMNESS_PLACEHOLDER"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{ "src": "with-npm/index.js", "use": "@now/node" },
|
||||
{ "src": "with-yarn/index.js", "use": "@now/node" }
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/with-npm", "mustContain": "npm:RANDOMNESS_PLACEHOLDER" },
|
||||
{ "path": "/with-yarn", "mustContain": "yarn:RANDOMNESS_PLACEHOLDER" }
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user