mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-06 12:57:46 +00:00
Create monorepo (#2812)
* Move now-cli to /packages/now-cli * Fix .gitignore paths * Add now-client * Add lerna to top level * Add scripts * Update codeowners * Fix `/now-cli/build.ts` script * Fix circleci path to artifacts * Use relative paths * Fix path to scripts * Add test-lint script * Add missing return type * Fix typo in test-lint * Fix string match in shell scripts * Fix path to hugo * Add package node_modules * Delete lock files in packages, use root yarn.lock * Add missing b.js file * Add test-integration-now-dev script * Add missing test files * Add missing integration test script * Add missing test files * Delete travis.yml * Fix ts-jest in now-client * Add support for Node 8 (ES2015 target) * Add support for Node 8 * Add polyfill for Node 8 * Fix polyfill for Node 8 * Only run coverage for now-cli * Add packages from now-builders * Run integration tests for builders * Add node_modules to cache * Add root readme.md * Move readme to top level * Add yarn bootstrap * Add bootstrap step * Add dist to `persist_to_workspace` * Fix 08-yarn-npm integration test * Remove duplicate path * Change stdio to inherit * Add back store_artifacts * testing - remove bootstrap step * Add back now-build-utils * Remove bootstrap step * Fix test again * Add console.log() * Fix lint * Use local ncc version * Install go * Revert changes to stdio and console.log() * Add missing now-go test * Add missing integration tests * Add --runInBand flag * Fix now-node-bridge persistence * Add missing symlinks * Add codeowners * Consolidate into single run.sh function * Run uniq * Fix typo * Change now-routing-utils to test-unit * Special case test for node 8 * Add docs from builders * Only run script for modified packages * Add test-integration-once which only runs once * Fix set intersection
This commit is contained in:
@@ -5,6 +5,8 @@ jobs:
|
|||||||
docker:
|
docker:
|
||||||
- image: circleci/node:10
|
- image: circleci/node:10
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
|
environment:
|
||||||
|
GOPATH: $HOME/go
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -12,17 +14,47 @@ jobs:
|
|||||||
- v1-dependencies-{{ checksum "yarn.lock" }}
|
- v1-dependencies-{{ checksum "yarn.lock" }}
|
||||||
# fallback to using the latest cache if no exact match is found
|
# fallback to using the latest cache if no exact match is found
|
||||||
- v1-dependencies-
|
- 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
|
||||||
- run:
|
- run:
|
||||||
name: Installing Dependencies
|
name: Installing Dependencies
|
||||||
command: yarn
|
command: yarn install
|
||||||
- save_cache:
|
- save_cache:
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- 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" }}
|
key: v1-dependencies-{{ checksum "yarn.lock" }}
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: .
|
root: .
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- 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:
|
build:
|
||||||
docker:
|
docker:
|
||||||
@@ -39,12 +71,26 @@ jobs:
|
|||||||
name: Building
|
name: Building
|
||||||
command: yarn build
|
command: yarn build
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: dist
|
path: packages/now-cli/dist
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: .
|
root: .
|
||||||
paths:
|
paths:
|
||||||
- assets
|
- packages/now-build-utils/dist
|
||||||
- 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
|
||||||
|
|
||||||
test-lint:
|
test-lint:
|
||||||
docker:
|
docker:
|
||||||
@@ -56,7 +102,7 @@ jobs:
|
|||||||
at: .
|
at: .
|
||||||
- run:
|
- run:
|
||||||
name: Compiling `now dev` HTML error templates
|
name: Compiling `now dev` HTML error templates
|
||||||
command: node ./scripts/compile-templates.js
|
command: node packages/now-cli/scripts/compile-templates.js
|
||||||
- run:
|
- run:
|
||||||
name: Linting Code
|
name: Linting Code
|
||||||
command: yarn test-lint
|
command: yarn test-lint
|
||||||
@@ -71,14 +117,14 @@ jobs:
|
|||||||
at: .
|
at: .
|
||||||
- run:
|
- run:
|
||||||
name: Compiling `now dev` HTML error templates
|
name: Compiling `now dev` HTML error templates
|
||||||
command: node ./scripts/compile-templates.js
|
command: node packages/now-cli/scripts/compile-templates.js
|
||||||
- run:
|
- run:
|
||||||
name: Running Unit Tests
|
name: Running Unit Tests
|
||||||
command: yarn test-unit --clean false
|
command: yarn test-unit --clean false
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: .
|
root: .
|
||||||
paths:
|
paths:
|
||||||
- .nyc_output
|
- packages/now-cli/.nyc_output
|
||||||
|
|
||||||
test-integration-macos-node-8:
|
test-integration-macos-node-8:
|
||||||
macos:
|
macos:
|
||||||
@@ -189,7 +235,7 @@ jobs:
|
|||||||
command: node --version
|
command: node --version
|
||||||
- run:
|
- run:
|
||||||
name: Downloading Hugo
|
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 test/dev/fixtures/08-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:
|
- run:
|
||||||
name: Running Integration Tests for `now dev`
|
name: Running Integration Tests for `now dev`
|
||||||
command: yarn test-integration-now-dev --clean false
|
command: yarn test-integration-now-dev --clean false
|
||||||
@@ -207,7 +253,7 @@ jobs:
|
|||||||
command: node --version
|
command: node --version
|
||||||
- run:
|
- run:
|
||||||
name: Downloading Hugo
|
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 test/dev/fixtures/08-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:
|
- run:
|
||||||
name: Running Integration Tests for `now dev`
|
name: Running Integration Tests for `now dev`
|
||||||
command: yarn test-integration-now-dev --clean false
|
command: yarn test-integration-now-dev --clean false
|
||||||
@@ -225,7 +271,7 @@ jobs:
|
|||||||
command: node --version
|
command: node --version
|
||||||
- run:
|
- run:
|
||||||
name: Downloading Hugo
|
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 test/dev/fixtures/08-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:
|
- run:
|
||||||
name: Running Integration Tests for `now dev`
|
name: Running Integration Tests for `now dev`
|
||||||
command: yarn test-integration-now-dev --clean false
|
command: yarn test-integration-now-dev --clean false
|
||||||
@@ -243,7 +289,7 @@ jobs:
|
|||||||
command: node --version
|
command: node --version
|
||||||
- run:
|
- run:
|
||||||
name: Downloading Hugo
|
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 test/dev/fixtures/08-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:
|
- run:
|
||||||
name: Running Integration Tests for `now dev`
|
name: Running Integration Tests for `now dev`
|
||||||
command: yarn test-integration-now-dev --clean false
|
command: yarn test-integration-now-dev --clean false
|
||||||
@@ -261,7 +307,7 @@ jobs:
|
|||||||
command: node --version
|
command: node --version
|
||||||
- run:
|
- run:
|
||||||
name: Downloading Hugo
|
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 test/dev/fixtures/08-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:
|
- run:
|
||||||
name: Running Integration Tests for `now dev`
|
name: Running Integration Tests for `now dev`
|
||||||
command: yarn test-integration-now-dev --clean false
|
command: yarn test-integration-now-dev --clean false
|
||||||
@@ -279,11 +325,26 @@ jobs:
|
|||||||
command: node --version
|
command: node --version
|
||||||
- run:
|
- run:
|
||||||
name: Downloading Hugo
|
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 test/dev/fixtures/08-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:
|
- run:
|
||||||
name: Running Integration Tests for `now dev`
|
name: Running Integration Tests for `now dev`
|
||||||
command: yarn test-integration-now-dev --clean false
|
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:
|
coverage:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:10
|
- image: circleci/node:10
|
||||||
@@ -294,7 +355,7 @@ jobs:
|
|||||||
at: .
|
at: .
|
||||||
- run:
|
- run:
|
||||||
name: Run coverage report
|
name: Run coverage report
|
||||||
command: yarn coverage
|
command: yarn workspace now run coverage
|
||||||
|
|
||||||
source-maps:
|
source-maps:
|
||||||
docker:
|
docker:
|
||||||
@@ -445,6 +506,12 @@ workflows:
|
|||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
|
- test-integration-once:
|
||||||
|
requires:
|
||||||
|
- build
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
- coverage:
|
- coverage:
|
||||||
requires:
|
requires:
|
||||||
- test-unit
|
- test-unit
|
||||||
@@ -460,6 +527,7 @@ workflows:
|
|||||||
- test-integration-linux-now-dev-node-8
|
- test-integration-linux-now-dev-node-8
|
||||||
- test-integration-linux-now-dev-node-10
|
- test-integration-linux-now-dev-node-10
|
||||||
- test-integration-linux-now-dev-node-12
|
- test-integration-linux-now-dev-node-12
|
||||||
|
- test-integration-once
|
||||||
- test-lint
|
- test-lint
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
34
.circleci/publish.sh
Executable file
34
.circleci/publish.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/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
|
||||||
23
.circleci/run.sh
Executable file
23
.circleci/run.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ "$1" == "" ]; then
|
||||||
|
echo "Please provide at least one argument"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
circleci_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
found="$(grep -rn \"""$1"\"": packages/*/package.json | cut -d '/' -f2 | uniq)"
|
||||||
|
modified="$(git diff origin/canary...HEAD --name-only | grep '^packages/' | cut -d '/' -f2 | uniq)"
|
||||||
|
echo "$found" > /tmp/found.txt
|
||||||
|
echo "$modified" > /tmp/modified.txt
|
||||||
|
pkgs="$(comm -12 <(sort /tmp/found.txt) <(sort /tmp/modified.txt))"
|
||||||
|
echo "The following packages were modified: "
|
||||||
|
echo "$pkgs" | wc -l
|
||||||
|
echo "$pkgs"
|
||||||
|
|
||||||
|
for dir in $pkgs; do
|
||||||
|
echo "cd packages/$dir && yarn $1"
|
||||||
|
cd "$circleci_dir/../packages/$dir"
|
||||||
|
yarn "$1"
|
||||||
|
done
|
||||||
18
.github/CODEOWNERS
vendored
18
.github/CODEOWNERS
vendored
@@ -2,7 +2,17 @@
|
|||||||
# https://help.github.com/en/articles/about-code-owners
|
# https://help.github.com/en/articles/about-code-owners
|
||||||
|
|
||||||
* @tootallnate @leo
|
* @tootallnate @leo
|
||||||
/src/commands/dev/ @tootallnate @leo @styfle @AndyBitz
|
/packages/now-cli/src/commands/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||||
/src/util/dev/ @tootallnate @leo @styfle @AndyBitz
|
/packages/now-cli/src/util/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||||
/src/commands/domains/ @javivelasco @mglagola @anatrajkovska
|
/packages/now-cli/src/commands/domains/ @javivelasco @mglagola @anatrajkovska
|
||||||
/src/commands/certs/ @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
|
||||||
|
|||||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,7 +1,6 @@
|
|||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
dist
|
dist
|
||||||
test/fixtures/integration
|
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
.nyc_output
|
.nyc_output
|
||||||
@@ -9,11 +8,11 @@ coverage
|
|||||||
*.swp
|
*.swp
|
||||||
*.bak
|
*.bak
|
||||||
*.tgz
|
*.tgz
|
||||||
.builders
|
packages/now-cli/.builders
|
||||||
/assets
|
packages/now-cli/assets
|
||||||
/src/util/dev/templates/*.ts
|
packages/now-cli/src/util/dev/templates/*.ts
|
||||||
/test/**/yarn.lock
|
packages/now-cli/test/**/yarn.lock
|
||||||
/test/**/node_modules
|
packages/now-cli/test/**/node_modules
|
||||||
/test/dev/fixtures/08-hugo/hugo
|
packages/now-cli/test/dev/fixtures/08-hugo/hugo
|
||||||
/test/dev/fixtures/**/dist
|
packages/now-cli/test/dev/fixtures/**/dist
|
||||||
/test/dev/fixtures/**/public
|
packages/now-cli/test/dev/fixtures/**/public
|
||||||
|
|||||||
96
CONTRIBUTING.md
Normal file
96
CONTRIBUTING.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
When contributing to this repository, please first discuss the change you wish to make via [GitHub Issue](https://github.com/zeit/now-builders/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-builders
|
||||||
|
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
|
||||||
447
DEVELOPING_A_BUILDER.md
Normal file
447
DEVELOPING_A_BUILDER.md
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
# 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-builders/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$/, '')
|
||||||
|
```
|
||||||
40
PUBLISHING.md
Normal file
40
PUBLISHING.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# 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!
|
||||||
25
VERSIONING.md
Normal file
25
VERSIONING.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Versioning
|
||||||
|
|
||||||
|
Builders are released to two different channels.
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
| Channel | Git Branch | npm dist-tag | use example |
|
||||||
|
| ------- | ------------------------------------------------------------- | ------------ | ------------------ |
|
||||||
|
| Canary | [canary](https://github.com/zeit/now-builders/commits/canary) | `@canary` | `@now/node@canary` |
|
||||||
|
| Stable | [master](https://github.com/zeit/now-builders/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.
|
||||||
14
lerna.json
Normal file
14
lerna.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"npmClient": "yarn",
|
||||||
|
"useWorkspaces": true,
|
||||||
|
"packages": ["packages/*"],
|
||||||
|
"command": {
|
||||||
|
"publish": {
|
||||||
|
"npmClient": "npm",
|
||||||
|
"allowBranch": ["master", "canary"],
|
||||||
|
"registry": "https://registry.npmjs.org/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "independent"
|
||||||
|
}
|
||||||
|
|
||||||
222
package.json
222
package.json
@@ -1,200 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "now",
|
"name": "now-builders",
|
||||||
"version": "16.1.2",
|
"version": "0.0.0",
|
||||||
"preferGlobal": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "MIT",
|
||||||
"description": "The command-line interface for Now",
|
"workspaces": [
|
||||||
"repository": "zeit/now-cli",
|
"packages/*"
|
||||||
"scripts": {
|
|
||||||
"test": "yarn test-lint",
|
|
||||||
"preinstall": "node ./scripts/preinstall.js",
|
|
||||||
"test-unit": "nyc ava test/*unit.js --serial --fail-fast --verbose",
|
|
||||||
"test-integration": "ava test/integration.js --serial --fail-fast",
|
|
||||||
"test-integration-now-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
|
||||||
"test-lint": "eslint . --ext .js,.ts",
|
|
||||||
"prepublishOnly": "yarn build",
|
|
||||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
|
||||||
"build": "ts-node ./scripts/build.ts",
|
|
||||||
"build-dev": "ts-node ./scripts/build.ts --dev",
|
|
||||||
"format-modified": "prettier --parser typescript --write --single-quote `git diff --name-only HEAD * | grep -e \".*\\.ts$\" -e \".*\\.js$\" | xargs echo`"
|
|
||||||
},
|
|
||||||
"nyc": {
|
|
||||||
"include": [
|
|
||||||
"src/**"
|
|
||||||
],
|
],
|
||||||
"extension": [
|
"dependencies": {
|
||||||
".js",
|
"lerna": "3.16.4"
|
||||||
".ts"
|
|
||||||
],
|
|
||||||
"require": [
|
|
||||||
"ts-node/register"
|
|
||||||
],
|
|
||||||
"reporter": [
|
|
||||||
"text",
|
|
||||||
"html"
|
|
||||||
],
|
|
||||||
"sourceMap": true,
|
|
||||||
"instrument": true,
|
|
||||||
"all": true
|
|
||||||
},
|
|
||||||
"git": {
|
|
||||||
"pre-commit": [
|
|
||||||
"test-lint",
|
|
||||||
"format-modified"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"now": "./dist/index.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist",
|
|
||||||
"scripts/preinstall.js"
|
|
||||||
],
|
|
||||||
"ava": {
|
|
||||||
"compileEnhancements": false,
|
|
||||||
"extensions": [
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"require": [
|
|
||||||
"ts-node/register/transpile-only",
|
|
||||||
"esm"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8.11"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@now/build-utils": "0.9.13",
|
"@zeit/ncc": "0.20.4",
|
||||||
"@now/go": "latest",
|
"async-retry": "1.2.3",
|
||||||
"@now/next": "latest",
|
"buffer-replace": "1.0.0",
|
||||||
"@now/node": "latest",
|
"node-fetch": "2.6.0"
|
||||||
"@now/php": "latest",
|
},
|
||||||
"@now/static-build": "latest",
|
"scripts": {
|
||||||
"@sentry/node": "5.5.0",
|
"lerna": "lerna",
|
||||||
"@types/ansi-escapes": "3.0.0",
|
"bootstrap": "lerna bootstrap",
|
||||||
"@types/ansi-regex": "4.0.0",
|
"publish-stable": "git checkout master && git pull && lerna version --exact",
|
||||||
"@types/async-retry": "1.2.1",
|
"publish-canary": "git checkout canary && git pull && lerna version prerelease --preid canary --exact",
|
||||||
"@types/bytes": "3.0.0",
|
"publish-from-github": "./.circleci/publish.sh",
|
||||||
"@types/debug": "0.0.31",
|
"build": "./.circleci/run.sh build",
|
||||||
"@types/dotenv": "6.1.1",
|
"test-lint": "./.circleci/run.sh test-lint",
|
||||||
"@types/escape-html": "0.0.20",
|
"test-unit": "./.circleci/run.sh test-unit",
|
||||||
"@types/execa": "0.9.0",
|
"test-integration": "./.circleci/run.sh test-integration",
|
||||||
"@types/fs-extra": "5.0.5",
|
"test-integration-once": "./.circleci/run.sh test-integration-once",
|
||||||
"@types/glob": "7.1.1",
|
"test-integration-now-dev": "./.circleci/run.sh test-integration-now-dev"
|
||||||
"@types/http-proxy": "1.16.2",
|
|
||||||
"@types/load-json-file": "2.0.7",
|
|
||||||
"@types/micro": "7.3.3",
|
|
||||||
"@types/mime-types": "2.1.0",
|
|
||||||
"@types/minimatch": "3.0.3",
|
|
||||||
"@types/mri": "1.1.0",
|
|
||||||
"@types/ms": "0.7.30",
|
|
||||||
"@types/node": "11.11.0",
|
|
||||||
"@types/node-fetch": "2.1.4",
|
|
||||||
"@types/npm-package-arg": "6.1.0",
|
|
||||||
"@types/pluralize": "0.0.29",
|
|
||||||
"@types/progress": "2.0.3",
|
|
||||||
"@types/psl": "1.1.0",
|
|
||||||
"@types/semver": "6.0.1",
|
|
||||||
"@types/tar-fs": "1.16.1",
|
|
||||||
"@types/text-table": "0.2.0",
|
|
||||||
"@types/universal-analytics": "0.4.2",
|
|
||||||
"@types/which": "1.3.1",
|
|
||||||
"@types/write-json-file": "2.2.1",
|
|
||||||
"@typescript-eslint/eslint-plugin": "1.6.0",
|
|
||||||
"@typescript-eslint/parser": "1.1.0",
|
|
||||||
"@zeit/dockerignore": "0.0.5",
|
|
||||||
"@zeit/fun": "0.9.1",
|
|
||||||
"@zeit/git-hooks": "0.1.4",
|
|
||||||
"@zeit/ncc": "0.18.5",
|
|
||||||
"@zeit/source-map-support": "0.6.2",
|
|
||||||
"alpha-sort": "2.0.1",
|
|
||||||
"ansi-escapes": "3.0.0",
|
|
||||||
"ansi-regex": "3.0.0",
|
|
||||||
"arg": "2.0.0",
|
|
||||||
"async-listen": "1.2.0",
|
|
||||||
"async-retry": "1.1.3",
|
|
||||||
"async-sema": "2.1.4",
|
|
||||||
"ava": "2.2.0",
|
|
||||||
"bytes": "3.0.0",
|
|
||||||
"cache-or-tmp-directory": "1.0.0",
|
|
||||||
"chalk": "2.4.2",
|
|
||||||
"chokidar": "2.1.6",
|
|
||||||
"clipboardy": "2.1.0",
|
|
||||||
"codecov": "3.1.0",
|
|
||||||
"cpy": "7.2.0",
|
|
||||||
"credit-card": "3.0.1",
|
|
||||||
"date-fns": "1.29.0",
|
|
||||||
"death": "1.1.0",
|
|
||||||
"debug": "3.1.0",
|
|
||||||
"deployment-type": "1.0.1",
|
|
||||||
"docker-file-parser": "1.0.2",
|
|
||||||
"dot": "1.1.2",
|
|
||||||
"dotenv": "4.0.0",
|
|
||||||
"download": "6.2.5",
|
|
||||||
"email-prompt": "0.3.2",
|
|
||||||
"email-validator": "1.1.1",
|
|
||||||
"epipebomb": "1.0.0",
|
|
||||||
"escape-html": "1.0.3",
|
|
||||||
"eslint": "5.16.0",
|
|
||||||
"eslint-config-airbnb": "17.1.0",
|
|
||||||
"eslint-config-prettier": "4.1.0",
|
|
||||||
"eslint-import-resolver-typescript": "1.1.1",
|
|
||||||
"eslint-plugin-import": "2.16.0",
|
|
||||||
"eslint-plugin-jsx-a11y": "6.2.1",
|
|
||||||
"eslint-plugin-react": "7.12.4",
|
|
||||||
"esm": "3.1.4",
|
|
||||||
"execa": "1.0.0",
|
|
||||||
"fetch-h2": "2.0.3",
|
|
||||||
"fs-extra": "7.0.1",
|
|
||||||
"glob": "7.1.2",
|
|
||||||
"http-proxy": "1.17.0",
|
|
||||||
"ini": "1.3.4",
|
|
||||||
"inquirer": "3.3.0",
|
|
||||||
"is-url": "1.2.2",
|
|
||||||
"jaro-winkler": "0.2.8",
|
|
||||||
"jsonlines": "0.1.1",
|
|
||||||
"load-json-file": "3.0.0",
|
|
||||||
"micro": "9.1.2",
|
|
||||||
"mime-types": "2.1.24",
|
|
||||||
"minimatch": "3.0.4",
|
|
||||||
"mri": "1.1.0",
|
|
||||||
"ms": "2.1.1",
|
|
||||||
"node-fetch": "1.7.3",
|
|
||||||
"npm-package-arg": "6.1.0",
|
|
||||||
"nyc": "13.2.0",
|
|
||||||
"ora": "3.4.0",
|
|
||||||
"pcre-to-regexp": "0.0.5",
|
|
||||||
"pluralize": "7.0.0",
|
|
||||||
"pre-commit": "1.2.2",
|
|
||||||
"prettier": "1.16.2",
|
|
||||||
"printf": "0.2.5",
|
|
||||||
"progress": "2.0.3",
|
|
||||||
"promisepipe": "3.0.0",
|
|
||||||
"psl": "1.1.31",
|
|
||||||
"qr-image": "3.2.0",
|
|
||||||
"raw-body": "2.4.1",
|
|
||||||
"read-pkg": "2.0.0",
|
|
||||||
"rx-lite-aggregates": "4.0.8",
|
|
||||||
"semver": "5.5.0",
|
|
||||||
"serve-handler": "6.1.1",
|
|
||||||
"sinon": "4.4.2",
|
|
||||||
"strip-ansi": "5.2.0",
|
|
||||||
"stripe": "5.1.0",
|
|
||||||
"tar-fs": "1.16.3",
|
|
||||||
"test-listen": "1.1.0",
|
|
||||||
"text-table": "0.2.0",
|
|
||||||
"then-sleep": "1.0.1",
|
|
||||||
"through2": "2.0.3",
|
|
||||||
"title": "3.4.1",
|
|
||||||
"tmp-promise": "1.0.3",
|
|
||||||
"ts-node": "8.3.0",
|
|
||||||
"typescript": "3.2.4",
|
|
||||||
"universal-analytics": "0.4.20",
|
|
||||||
"update-check": "1.5.3",
|
|
||||||
"utility-types": "2.1.0",
|
|
||||||
"which": "1.3.1",
|
|
||||||
"which-promise": "1.0.0",
|
|
||||||
"write-json-file": "2.2.0",
|
|
||||||
"yarn": "1.17.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
3
packages/now-build-utils/.gitignore
vendored
Normal file
3
packages/now-build-utils/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dist
|
||||||
|
test/symlinks-out
|
||||||
|
test/symlinks.zip
|
||||||
3
packages/now-build-utils/.npmignore
Normal file
3
packages/now-build-utils/.npmignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/src
|
||||||
|
/test
|
||||||
|
tmp
|
||||||
6
packages/now-build-utils/build.sh
Executable file
6
packages/now-build-utils/build.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
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
packages/now-build-utils/file-blob.js
Normal file
1
packages/now-build-utils/file-blob.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./dist/index').FileBlob;
|
||||||
1
packages/now-build-utils/file-fs-ref.js
Normal file
1
packages/now-build-utils/file-fs-ref.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./dist/index').FileFsRef;
|
||||||
1
packages/now-build-utils/file-ref.js
Normal file
1
packages/now-build-utils/file-ref.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./dist/index').FileRef;
|
||||||
1
packages/now-build-utils/fs/download.js
Normal file
1
packages/now-build-utils/fs/download.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../dist/index').download;
|
||||||
1
packages/now-build-utils/fs/get-writable-directory.js
Normal file
1
packages/now-build-utils/fs/get-writable-directory.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../dist/index').getWriteableDirectory;
|
||||||
1
packages/now-build-utils/fs/glob.js
Normal file
1
packages/now-build-utils/fs/glob.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../dist/index').glob;
|
||||||
1
packages/now-build-utils/fs/rename.js
Normal file
1
packages/now-build-utils/fs/rename.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../dist/index').rename;
|
||||||
1
packages/now-build-utils/fs/run-user-scripts.js
Normal file
1
packages/now-build-utils/fs/run-user-scripts.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../dist/index');
|
||||||
1
packages/now-build-utils/fs/stream-to-buffer.js
Normal file
1
packages/now-build-utils/fs/stream-to-buffer.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../dist/index').streamToBuffer;
|
||||||
1
packages/now-build-utils/lambda.js
Normal file
1
packages/now-build-utils/lambda.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./dist/index');
|
||||||
43
packages/now-build-utils/package.json
Normal file
43
packages/now-build-utils/package.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@now/build-utils",
|
||||||
|
"version": "0.9.13",
|
||||||
|
"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-builders.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"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/now-build-utils/src/debug.ts
Normal file
7
packages/now-build-utils/src/debug.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
182
packages/now-build-utils/src/detect-builders.ts
Normal file
182
packages/now-build-utils/src/detect-builders.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { PackageJson, Builder, Config } from './types';
|
||||||
|
import minimatch from 'minimatch';
|
||||||
|
|
||||||
|
interface ErrorResponse {
|
||||||
|
code: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
tag?: 'canary' | 'latest' | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const src: string = '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,
|
||||||
|
};
|
||||||
|
}
|
||||||
297
packages/now-build-utils/src/detect-routes.ts
Normal file
297
packages/now-build-utils/src/detect-routes.ts
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
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: number = 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;
|
||||||
|
}
|
||||||
46
packages/now-build-utils/src/file-blob.ts
Normal file
46
packages/now-build-utils/src/file-blob.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
import intoStream from 'into-stream';
|
||||||
|
import { File } from './types';
|
||||||
|
|
||||||
|
interface FileBlobOptions {
|
||||||
|
mode?: number;
|
||||||
|
data: string | Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FromStreamOptions {
|
||||||
|
mode?: number;
|
||||||
|
stream: NodeJS.ReadableStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FileBlob implements File {
|
||||||
|
public type: 'FileBlob';
|
||||||
|
public mode: number;
|
||||||
|
public data: string | Buffer;
|
||||||
|
|
||||||
|
constructor({ mode = 0o100644, data }: FileBlobOptions) {
|
||||||
|
assert(typeof mode === 'number');
|
||||||
|
assert(typeof data === 'string' || Buffer.isBuffer(data));
|
||||||
|
this.type = 'FileBlob';
|
||||||
|
this.mode = mode;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fromStream({ mode = 0o100644, stream }: FromStreamOptions) {
|
||||||
|
assert(typeof mode === 'number');
|
||||||
|
assert(typeof stream.pipe === 'function'); // is-stream
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
||||||
|
stream.on('error', error => reject(error));
|
||||||
|
stream.on('end', () => resolve());
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = Buffer.concat(chunks);
|
||||||
|
return new FileBlob({ mode, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
toStream(): NodeJS.ReadableStream {
|
||||||
|
return intoStream(this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
97
packages/now-build-utils/src/file-fs-ref.ts
Normal file
97
packages/now-build-utils/src/file-fs-ref.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
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;
|
||||||
95
packages/now-build-utils/src/file-ref.ts
Normal file
95
packages/now-build-utils/src/file-ref.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
constructor({ mode = 0o100644, digest }: FileRefOptions) {
|
||||||
|
assert(typeof mode === 'number');
|
||||||
|
assert(typeof digest === 'string');
|
||||||
|
this.type = 'FileRef';
|
||||||
|
this.mode = mode;
|
||||||
|
this.digest = digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// `https://now-files.s3.amazonaws.com/${digestHash}`
|
||||||
|
url = `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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
75
packages/now-build-utils/src/fs/download.ts
Normal file
75
packages/now-build-utils/src/fs/download.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
10
packages/now-build-utils/src/fs/get-writable-directory.ts
Normal file
10
packages/now-build-utils/src/fs/get-writable-directory.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
import { mkdirp } from 'fs-extra';
|
||||||
|
|
||||||
|
export default async function getWritableDirectory() {
|
||||||
|
const name = Math.floor(Math.random() * 0x7fffffff).toString(16);
|
||||||
|
const directory = join(tmpdir(), name);
|
||||||
|
await mkdirp(directory);
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
70
packages/now-build-utils/src/fs/glob.ts
Normal file
70
packages/now-build-utils/src/fs/glob.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
53
packages/now-build-utils/src/fs/node-version.ts
Normal file
53
packages/now-build-utils/src/fs/node-version.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
12
packages/now-build-utils/src/fs/rename.ts
Normal file
12
packages/now-build-utils/src/fs/rename.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Files } from '../types';
|
||||||
|
type Delegate = (name: string) => string;
|
||||||
|
|
||||||
|
export default function rename(files: Files, delegate: Delegate): Files {
|
||||||
|
return Object.keys(files).reduce(
|
||||||
|
(newFiles, name) => ({
|
||||||
|
...newFiles,
|
||||||
|
[delegate(name)]: files[name],
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
200
packages/now-build-utils/src/fs/run-user-scripts.ts
Normal file
200
packages/now-build-utils/src/fs/run-user-scripts.ts
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
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.'
|
||||||
|
);
|
||||||
28
packages/now-build-utils/src/fs/stream-to-buffer.ts
Normal file
28
packages/now-build-utils/src/fs/stream-to-buffer.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
47
packages/now-build-utils/src/index.ts
Normal file
47
packages/now-build-utils/src/index.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
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';
|
||||||
107
packages/now-build-utils/src/lambda.ts
Normal file
107
packages/now-build-utils/src/lambda.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
27
packages/now-build-utils/src/should-serve.ts
Normal file
27
packages/now-build-utils/src/should-serve.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { parse } from 'path';
|
||||||
|
import { ShouldServeOptions } from './types';
|
||||||
|
import FileFsRef from './file-fs-ref';
|
||||||
|
|
||||||
|
export default function shouldServe({
|
||||||
|
entrypoint,
|
||||||
|
files,
|
||||||
|
requestPath,
|
||||||
|
}: ShouldServeOptions): boolean {
|
||||||
|
requestPath = requestPath.replace(/\/$/, ''); // sanitize trailing '/'
|
||||||
|
entrypoint = entrypoint.replace(/\\/, '/'); // windows compatibility
|
||||||
|
|
||||||
|
if (entrypoint === requestPath && hasProp(files, entrypoint)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { dir, name } = parse(entrypoint);
|
||||||
|
if (name === 'index' && dir === requestPath && hasProp(files, entrypoint)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasProp(obj: { [path: string]: FileFsRef }, key: string): boolean {
|
||||||
|
return Object.hasOwnProperty.call(obj, key);
|
||||||
|
}
|
||||||
216
packages/now-build-utils/src/types.ts
Normal file
216
packages/now-build-utils/src/types.ts
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
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
packages/now-build-utils/test/fixtures/01-zero-config-api/.gitignore
vendored
Normal file
1
packages/now-build-utils/test/fixtures/01-zero-config-api/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
now.json
|
||||||
3
packages/now-build-utils/test/fixtures/01-zero-config-api/api/[endpoint].js
vendored
Normal file
3
packages/now-build-utils/test/fixtures/01-zero-config-api/api/[endpoint].js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = (req, res) => {
|
||||||
|
res.end(req.query.endpoint);
|
||||||
|
};
|
||||||
5
packages/now-build-utils/test/fixtures/01-zero-config-api/package.json
vendored
Normal file
5
packages/now-build-utils/test/fixtures/01-zero-config-api/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "mkdir -p public && echo 'hello from index.txt' > public/index.txt"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
packages/now-build-utils/test/fixtures/02-zero-config-api/.gitignore
vendored
Normal file
1
packages/now-build-utils/test/fixtures/02-zero-config-api/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
now.json
|
||||||
3
packages/now-build-utils/test/fixtures/02-zero-config-api/api/date.js
vendored
Normal file
3
packages/now-build-utils/test/fixtures/02-zero-config-api/api/date.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = (req, res) => {
|
||||||
|
res.end('hello from api/date.js');
|
||||||
|
};
|
||||||
3
packages/now-build-utils/test/fixtures/02-zero-config-api/api/date/index.js
vendored
Normal file
3
packages/now-build-utils/test/fixtures/02-zero-config-api/api/date/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = (req, res) => {
|
||||||
|
res.end('hello from api/date/index.js');
|
||||||
|
};
|
||||||
3
packages/now-build-utils/test/fixtures/02-zero-config-api/api/index.js
vendored
Normal file
3
packages/now-build-utils/test/fixtures/02-zero-config-api/api/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = (req, res) => {
|
||||||
|
res.end('hello from api/index.js');
|
||||||
|
};
|
||||||
5
packages/now-build-utils/test/fixtures/02-zero-config-api/package.json
vendored
Normal file
5
packages/now-build-utils/test/fixtures/02-zero-config-api/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "mkdir -p public && echo 'hello from index.txt' > public/index.txt"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/now-build-utils/test/fixtures/07-cross-install/api/index.js
vendored
Normal file
5
packages/now-build-utils/test/fixtures/07-cross-install/api/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const cowsay = require('cowsay').say;
|
||||||
|
|
||||||
|
module.exports = (req, resp) => {
|
||||||
|
resp.end(cowsay({ text: 'cross-cow:RANDOMNESS_PLACEHOLDER' }));
|
||||||
|
};
|
||||||
5
packages/now-build-utils/test/fixtures/07-cross-install/api/package.json
vendored
Normal file
5
packages/now-build-utils/test/fixtures/07-cross-install/api/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"lib": "../lib"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/now-build-utils/test/fixtures/07-cross-install/lib/package.json
vendored
Normal file
7
packages/now-build-utils/test/fixtures/07-cross-install/lib/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "lib",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"dependencies": {
|
||||||
|
"cowsay": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
packages/now-build-utils/test/fixtures/07-cross-install/now.json
vendored
Normal file
10
packages/now-build-utils/test/fixtures/07-cross-install/now.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"builds": [{ "src": "api/index.js", "use": "@now/node" }],
|
||||||
|
"probes": [
|
||||||
|
{
|
||||||
|
"path": "/api/index.js",
|
||||||
|
"mustContain": "cross-cow:RANDOMNESS_PLACEHOLDER"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
packages/now-build-utils/test/fixtures/08-yarn-npm/now.json
vendored
Normal file
11
packages/now-build-utils/test/fixtures/08-yarn-npm/now.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"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" }
|
||||||
|
]
|
||||||
|
}
|
||||||
0
test/fixtures/unit/directory-main/a/index.js → packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/index.js
vendored
Executable file → Normal file
0
test/fixtures/unit/directory-main/a/index.js → packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/index.js
vendored
Executable file → Normal file
14
packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/must-be-npm.js
vendored
Normal file
14
packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/must-be-npm.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const execpath = path.basename(process.env.npm_execpath);
|
||||||
|
console.log('execpath', execpath);
|
||||||
|
|
||||||
|
if (execpath === 'npm-cli.js') {
|
||||||
|
fs.writeFileSync(
|
||||||
|
'index.js',
|
||||||
|
'module.exports = (_, resp) => resp.end("npm:RANDOMNESS_PLACEHOLDER");',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error('npm is expected');
|
||||||
|
}
|
||||||
3
packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/package-lock.json
generated
vendored
Normal file
3
packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1
|
||||||
|
}
|
||||||
5
packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/package.json
vendored
Normal file
5
packages/now-build-utils/test/fixtures/08-yarn-npm/with-npm/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"now-build": "node must-be-npm.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
0
test/fixtures/unit/extensionless-main/index.js → packages/now-build-utils/test/fixtures/08-yarn-npm/with-yarn/index.js
vendored
Executable file → Normal file
0
test/fixtures/unit/extensionless-main/index.js → packages/now-build-utils/test/fixtures/08-yarn-npm/with-yarn/index.js
vendored
Executable file → Normal file
14
packages/now-build-utils/test/fixtures/08-yarn-npm/with-yarn/must-be-yarn.js
vendored
Normal file
14
packages/now-build-utils/test/fixtures/08-yarn-npm/with-yarn/must-be-yarn.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const execpath = path.basename(process.env.npm_execpath);
|
||||||
|
console.log('execpath', execpath);
|
||||||
|
|
||||||
|
if (execpath === 'yarn.js' || execpath === 'yarn') {
|
||||||
|
fs.writeFileSync(
|
||||||
|
'index.js',
|
||||||
|
'module.exports = (_, resp) => resp.end("yarn:RANDOMNESS_PLACEHOLDER");',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error('yarn is expected');
|
||||||
|
}
|
||||||
5
packages/now-build-utils/test/fixtures/08-yarn-npm/with-yarn/package.json
vendored
Normal file
5
packages/now-build-utils/test/fixtures/08-yarn-npm/with-yarn/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"now-build": "node must-be-yarn.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
packages/now-build-utils/test/fixtures/15-yarn-ignore-engines/index.js
vendored
Normal file
9
packages/now-build-utils/test/fixtures/15-yarn-ignore-engines/index.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const scheduler = require('@google-cloud/scheduler');
|
||||||
|
|
||||||
|
module.exports = (_, res) => {
|
||||||
|
if (scheduler) {
|
||||||
|
res.end('found:RANDOMNESS_PLACEHOLDER');
|
||||||
|
} else {
|
||||||
|
res.end('nope:RANDOMNESS_PLACEHOLDER');
|
||||||
|
}
|
||||||
|
};
|
||||||
10
packages/now-build-utils/test/fixtures/15-yarn-ignore-engines/now.json
vendored
Normal file
10
packages/now-build-utils/test/fixtures/15-yarn-ignore-engines/now.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"src": "index.js",
|
||||||
|
"use": "@now/node"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"probes": [{ "path": "/", "mustContain": "found:RANDOMNESS_PLACEHOLDER" }]
|
||||||
|
}
|
||||||
8
packages/now-build-utils/test/fixtures/15-yarn-ignore-engines/package.json
vendored
Normal file
8
packages/now-build-utils/test/fixtures/15-yarn-ignore-engines/package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "15-yarn-ignore-engines",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"@google-cloud/scheduler": "0.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
packages/now-build-utils/test/symlinks/a.txt
Normal file
1
packages/now-build-utils/test/symlinks/a.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
contents
|
||||||
1
packages/now-build-utils/test/symlinks/link.txt
Symbolic link
1
packages/now-build-utils/test/symlinks/link.txt
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
./a.txt
|
||||||
779
packages/now-build-utils/test/test.js
Normal file
779
packages/now-build-utils/test/test.js
Normal file
@@ -0,0 +1,779 @@
|
|||||||
|
/* global beforeAll, expect, it, jest */
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
const execa = require('execa');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { createZip } = require('../dist/lambda');
|
||||||
|
const {
|
||||||
|
glob, download, detectBuilders, detectRoutes,
|
||||||
|
} = require('../');
|
||||||
|
const {
|
||||||
|
getSupportedNodeVersion,
|
||||||
|
defaultSelection,
|
||||||
|
} = require('../dist/fs/node-version');
|
||||||
|
const {
|
||||||
|
packAndDeploy,
|
||||||
|
testDeployment,
|
||||||
|
} = require('../../../test/lib/deployment/test-deployment');
|
||||||
|
|
||||||
|
jest.setTimeout(4 * 60 * 1000);
|
||||||
|
|
||||||
|
const builderUrl = '@canary';
|
||||||
|
let buildUtilsUrl;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const buildUtilsPath = path.resolve(__dirname, '..');
|
||||||
|
buildUtilsUrl = await packAndDeploy(buildUtilsPath);
|
||||||
|
console.log('buildUtilsUrl', buildUtilsUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
// unit tests
|
||||||
|
|
||||||
|
it('should re-create symlinks properly', async () => {
|
||||||
|
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||||
|
assert.equal(Object.keys(files).length, 2);
|
||||||
|
|
||||||
|
const outDir = path.join(__dirname, 'symlinks-out');
|
||||||
|
await fs.remove(outDir);
|
||||||
|
|
||||||
|
const files2 = await download(files, outDir);
|
||||||
|
assert.equal(Object.keys(files2).length, 2);
|
||||||
|
|
||||||
|
const [linkStat, aStat] = await Promise.all([
|
||||||
|
fs.lstat(path.join(outDir, 'link.txt')),
|
||||||
|
fs.lstat(path.join(outDir, 'a.txt')),
|
||||||
|
]);
|
||||||
|
assert(linkStat.isSymbolicLink());
|
||||||
|
assert(aStat.isFile());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create zip files with symlinks properly', async () => {
|
||||||
|
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||||
|
assert.equal(Object.keys(files).length, 2);
|
||||||
|
|
||||||
|
const outFile = path.join(__dirname, 'symlinks.zip');
|
||||||
|
await fs.remove(outFile);
|
||||||
|
|
||||||
|
const outDir = path.join(__dirname, 'symlinks-out');
|
||||||
|
await fs.remove(outDir);
|
||||||
|
await fs.mkdirp(outDir);
|
||||||
|
|
||||||
|
await fs.writeFile(outFile, await createZip(files));
|
||||||
|
await execa('unzip', [outFile], { cwd: outDir });
|
||||||
|
|
||||||
|
const [linkStat, aStat] = await Promise.all([
|
||||||
|
fs.lstat(path.join(outDir, 'link.txt')),
|
||||||
|
fs.lstat(path.join(outDir, 'a.txt')),
|
||||||
|
]);
|
||||||
|
assert(linkStat.isSymbolicLink());
|
||||||
|
assert(aStat.isFile());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should only match supported node versions', () => {
|
||||||
|
expect(getSupportedNodeVersion('10.x')).resolves.toHaveProperty('major', 10);
|
||||||
|
expect(getSupportedNodeVersion('8.10.x')).resolves.toHaveProperty('major', 8);
|
||||||
|
expect(getSupportedNodeVersion('8.11.x')).rejects.toThrow();
|
||||||
|
expect(getSupportedNodeVersion('6.x')).rejects.toThrow();
|
||||||
|
expect(getSupportedNodeVersion('999.x')).rejects.toThrow();
|
||||||
|
expect(getSupportedNodeVersion('foo')).rejects.toThrow();
|
||||||
|
expect(getSupportedNodeVersion('')).resolves.toBe(defaultSelection);
|
||||||
|
expect(getSupportedNodeVersion(null)).resolves.toBe(defaultSelection);
|
||||||
|
expect(getSupportedNodeVersion(undefined)).resolves.toBe(defaultSelection);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match all semver ranges', () => {
|
||||||
|
// See https://docs.npmjs.com/files/package.json#engines
|
||||||
|
expect(getSupportedNodeVersion('10.0.0')).resolves.toHaveProperty(
|
||||||
|
'major',
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(getSupportedNodeVersion('10.x')).resolves.toHaveProperty('major', 10);
|
||||||
|
expect(getSupportedNodeVersion('>=10')).resolves.toHaveProperty('major', 10);
|
||||||
|
expect(getSupportedNodeVersion('>=10.3.0')).resolves.toHaveProperty(
|
||||||
|
'major',
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(getSupportedNodeVersion('8.5.0 - 10.5.0')).resolves.toHaveProperty(
|
||||||
|
'major',
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(getSupportedNodeVersion('>=9.0.0')).resolves.toHaveProperty(
|
||||||
|
'major',
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(getSupportedNodeVersion('>=9.5.0 <=10.5.0')).resolves.toHaveProperty(
|
||||||
|
'major',
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(getSupportedNodeVersion('~10.5.0')).resolves.toHaveProperty(
|
||||||
|
'major',
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(getSupportedNodeVersion('^10.5.0')).resolves.toHaveProperty(
|
||||||
|
'major',
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support require by path for legacy builders', () => {
|
||||||
|
const index = require('@now/build-utils');
|
||||||
|
|
||||||
|
const download2 = require('@now/build-utils/fs/download.js');
|
||||||
|
const getWriteableDirectory2 = require('@now/build-utils/fs/get-writable-directory.js');
|
||||||
|
const glob2 = require('@now/build-utils/fs/glob.js');
|
||||||
|
const rename2 = require('@now/build-utils/fs/rename.js');
|
||||||
|
const {
|
||||||
|
runNpmInstall: runNpmInstall2,
|
||||||
|
} = require('@now/build-utils/fs/run-user-scripts.js');
|
||||||
|
const streamToBuffer2 = require('@now/build-utils/fs/stream-to-buffer.js');
|
||||||
|
|
||||||
|
const FileBlob2 = require('@now/build-utils/file-blob.js');
|
||||||
|
const FileFsRef2 = require('@now/build-utils/file-fs-ref.js');
|
||||||
|
const FileRef2 = require('@now/build-utils/file-ref.js');
|
||||||
|
const { Lambda: Lambda2 } = require('@now/build-utils/lambda.js');
|
||||||
|
|
||||||
|
expect(download2).toBe(index.download);
|
||||||
|
expect(getWriteableDirectory2).toBe(index.getWriteableDirectory);
|
||||||
|
expect(glob2).toBe(index.glob);
|
||||||
|
expect(rename2).toBe(index.rename);
|
||||||
|
expect(runNpmInstall2).toBe(index.runNpmInstall);
|
||||||
|
expect(streamToBuffer2).toBe(index.streamToBuffer);
|
||||||
|
|
||||||
|
expect(FileBlob2).toBe(index.FileBlob);
|
||||||
|
expect(FileFsRef2).toBe(index.FileFsRef);
|
||||||
|
expect(FileRef2).toBe(index.FileRef);
|
||||||
|
expect(Lambda2).toBe(index.Lambda);
|
||||||
|
});
|
||||||
|
|
||||||
|
// own fixtures
|
||||||
|
|
||||||
|
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const fixture of fs.readdirSync(fixturesPath)) {
|
||||||
|
if (fixture.includes('zero-config')) {
|
||||||
|
// Those have separate tests
|
||||||
|
continue; // eslint-disable-line no-continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
it(`should build ${fixture}`, async () => {
|
||||||
|
await expect(
|
||||||
|
testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
path.join(fixturesPath, fixture),
|
||||||
|
),
|
||||||
|
).resolves.toBeDefined();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// few foreign tests
|
||||||
|
|
||||||
|
const buildersToTestWith = ['now-next', 'now-node', 'now-static-build'];
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const builder of buildersToTestWith) {
|
||||||
|
const fixturesPath2 = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
`../../${builder}/test/fixtures`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const fixture of fs.readdirSync(fixturesPath2)) {
|
||||||
|
// don't run all foreign fixtures, just some
|
||||||
|
if (['01-cowsay', '01-cache-headers', '03-env-vars'].includes(fixture)) {
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
it(`should build ${builder}/${fixture}`, async () => {
|
||||||
|
await expect(
|
||||||
|
testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
path.join(fixturesPath2, fixture),
|
||||||
|
),
|
||||||
|
).resolves.toBeDefined();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('Test `detectBuilders`', async () => {
|
||||||
|
{
|
||||||
|
// package.json + no build
|
||||||
|
const pkg = { dependencies: { next: '9.0.0' } };
|
||||||
|
const files = ['package.json', 'pages/index.js', 'public/index.html'];
|
||||||
|
const { builders, errors } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
expect(errors.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json + no build + next
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
dependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = ['package.json', 'pages/index.js'];
|
||||||
|
const { builders, errors } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders[0].use).toBe('@now/next');
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json + no build + next
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
devDependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = ['package.json', 'pages/index.js'];
|
||||||
|
const { builders, errors } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders[0].use).toBe('@now/next');
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json + no build
|
||||||
|
const pkg = {};
|
||||||
|
const files = ['package.json'];
|
||||||
|
const { builders, errors } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
expect(errors.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// static file
|
||||||
|
const files = ['index.html'];
|
||||||
|
const { builders, errors } = await detectBuilders(files);
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// no package.json + public
|
||||||
|
const files = ['api/users.js', 'public/index.html'];
|
||||||
|
const { builders, errors } = await detectBuilders(files);
|
||||||
|
expect(builders[1].use).toBe('@now/static');
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// no package.json + no build + raw static + api
|
||||||
|
const files = ['api/users.js', 'index.html'];
|
||||||
|
const { builders, errors } = await detectBuilders(files);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/users.js');
|
||||||
|
expect(builders[1].use).toBe('@now/static');
|
||||||
|
expect(builders[1].src).toBe('index.html');
|
||||||
|
expect(builders.length).toBe(2);
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json + no build + root + api
|
||||||
|
const files = ['index.html', 'api/[endpoint].js', 'static/image.png'];
|
||||||
|
const { builders, errors } = await detectBuilders(files);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/[endpoint].js');
|
||||||
|
expect(builders[1].use).toBe('@now/static');
|
||||||
|
expect(builders[1].src).toBe('index.html');
|
||||||
|
expect(builders[2].use).toBe('@now/static');
|
||||||
|
expect(builders[2].src).toBe('static/image.png');
|
||||||
|
expect(builders.length).toBe(3);
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// api + ignore files
|
||||||
|
const files = [
|
||||||
|
'api/_utils/handler.js',
|
||||||
|
'api/[endpoint]/.helper.js',
|
||||||
|
'api/[endpoint]/[id].js',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/[endpoint]/[id].js');
|
||||||
|
expect(builders.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// api + next + public
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
devDependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = ['package.json', 'api/endpoint.js', 'public/index.html'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/endpoint.js');
|
||||||
|
expect(builders[1].use).toBe('@now/next');
|
||||||
|
expect(builders[1].src).toBe('package.json');
|
||||||
|
expect(builders.length).toBe(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// api + next + raw static
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
devDependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = ['package.json', 'api/endpoint.js', 'index.html'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/endpoint.js');
|
||||||
|
expect(builders[1].use).toBe('@now/next');
|
||||||
|
expect(builders[1].src).toBe('package.json');
|
||||||
|
expect(builders.length).toBe(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// api + raw static
|
||||||
|
const files = ['api/endpoint.js', 'index.html', 'favicon.ico'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/endpoint.js');
|
||||||
|
expect(builders[1].use).toBe('@now/static');
|
||||||
|
expect(builders[1].src).toBe('favicon.ico');
|
||||||
|
expect(builders[2].use).toBe('@now/static');
|
||||||
|
expect(builders[2].src).toBe('index.html');
|
||||||
|
expect(builders.length).toBe(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// api + public
|
||||||
|
const files = [
|
||||||
|
'api/endpoint.js',
|
||||||
|
'public/index.html',
|
||||||
|
'public/favicon.ico',
|
||||||
|
'README.md',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/endpoint.js');
|
||||||
|
expect(builders[1].use).toBe('@now/static');
|
||||||
|
expect(builders[1].src).toBe('public/**/*');
|
||||||
|
expect(builders.length).toBe(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// just public
|
||||||
|
const files = ['public/index.html', 'public/favicon.ico', 'README.md'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
expect(builders[0].src).toBe('public/**/*');
|
||||||
|
expect(builders.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// next + public
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
devDependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = ['package.json', 'public/index.html', 'README.md'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders[0].use).toBe('@now/next');
|
||||||
|
expect(builders[0].src).toBe('package.json');
|
||||||
|
expect(builders.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// nuxt
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'nuxt build' },
|
||||||
|
dependencies: { nuxt: '2.8.1' },
|
||||||
|
};
|
||||||
|
const files = ['package.json', 'pages/index.js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders[0].use).toBe('@now/static-build');
|
||||||
|
expect(builders[0].src).toBe('package.json');
|
||||||
|
expect(builders.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json with no build + api
|
||||||
|
const pkg = { dependencies: { next: '9.0.0' } };
|
||||||
|
const files = ['package.json', 'api/[endpoint].js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/[endpoint].js');
|
||||||
|
expect(builders.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json with no build + public directory
|
||||||
|
const pkg = { dependencies: { next: '9.0.0' } };
|
||||||
|
const files = ['package.json', 'public/index.html'];
|
||||||
|
|
||||||
|
const { builders, errors } = await detectBuilders(files, pkg);
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
expect(errors.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// no package.json + api
|
||||||
|
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
expect(builders.length).toBe(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// no package.json + no api
|
||||||
|
const files = ['index.html'];
|
||||||
|
|
||||||
|
const { builders, errors } = await detectBuilders(files);
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json + api + canary
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
dependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = [
|
||||||
|
'pages/index.js',
|
||||||
|
'api/[endpoint].js',
|
||||||
|
'api/[endpoint]/[id].js',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg, { tag: 'canary' });
|
||||||
|
expect(builders[0].use).toBe('@now/node@canary');
|
||||||
|
expect(builders[1].use).toBe('@now/node@canary');
|
||||||
|
expect(builders[2].use).toBe('@now/next@canary');
|
||||||
|
expect(builders.length).toBe(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json + api + latest
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
dependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = [
|
||||||
|
'pages/index.js',
|
||||||
|
'api/[endpoint].js',
|
||||||
|
'api/[endpoint]/[id].js',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg, { tag: 'latest' });
|
||||||
|
expect(builders[0].use).toBe('@now/node@latest');
|
||||||
|
expect(builders[1].use).toBe('@now/node@latest');
|
||||||
|
expect(builders[2].use).toBe('@now/next@latest');
|
||||||
|
expect(builders.length).toBe(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// package.json + api + random tag
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
dependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = [
|
||||||
|
'pages/index.js',
|
||||||
|
'api/[endpoint].js',
|
||||||
|
'api/[endpoint]/[id].js',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg, { tag: 'haha' });
|
||||||
|
expect(builders[0].use).toBe('@now/node@haha');
|
||||||
|
expect(builders[1].use).toBe('@now/node@haha');
|
||||||
|
expect(builders[2].use).toBe('@now/next@haha');
|
||||||
|
expect(builders.length).toBe(3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Test `detectRoutes`', async () => {
|
||||||
|
{
|
||||||
|
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
expect(defaultRoutes.length).toBe(3);
|
||||||
|
expect(defaultRoutes[0].dest).toBe('/api/team.js');
|
||||||
|
expect(defaultRoutes[1].dest).toBe('/api/user.go');
|
||||||
|
expect(defaultRoutes[2].dest).not.toBeDefined();
|
||||||
|
expect(defaultRoutes[2].status).toBe(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['api/user.go', 'api/user.js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { error } = await detectRoutes(files, builders);
|
||||||
|
expect(error.code).toBe('conflicting_file_path');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['api/[user].go', 'api/[team]/[id].js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { error } = await detectRoutes(files, builders);
|
||||||
|
expect(error.code).toBe('conflicting_file_path');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['api/[team]/[team].js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { error } = await detectRoutes(files, builders);
|
||||||
|
expect(error.code).toBe('conflicting_path_segment');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['api/date/index.js', 'api/date/index.go'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes, error } = await detectRoutes(files, builders);
|
||||||
|
expect(defaultRoutes).toBe(null);
|
||||||
|
expect(error.code).toBe('conflicting_file_path');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
expect(defaultRoutes.length).toBe(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = [
|
||||||
|
'public/index.html',
|
||||||
|
'api/[endpoint].js',
|
||||||
|
'api/[endpoint]/[id].js',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
expect(defaultRoutes[2].status).toBe(404);
|
||||||
|
expect(defaultRoutes[2].src).toBe('/api(\\/.*)?$');
|
||||||
|
expect(defaultRoutes[3].src).toBe('/(.*)');
|
||||||
|
expect(defaultRoutes[3].dest).toBe('/public/$1');
|
||||||
|
expect(defaultRoutes.length).toBe(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
devDependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = ['public/index.html', 'api/[endpoint].js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
expect(defaultRoutes[1].status).toBe(404);
|
||||||
|
expect(defaultRoutes[1].src).toBe('/api(\\/.*)?$');
|
||||||
|
expect(defaultRoutes.length).toBe(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['public/index.html'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
expect(defaultRoutes.length).toBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['api/date/index.js', 'api/date.js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
expect(defaultRoutes.length).toBe(3);
|
||||||
|
expect(defaultRoutes[0].src).toBe(
|
||||||
|
'^/api/date(\\/|\\/index|\\/index\\.js)?$',
|
||||||
|
);
|
||||||
|
expect(defaultRoutes[0].dest).toBe('/api/date/index.js');
|
||||||
|
expect(defaultRoutes[1].src).toBe('^/api/(date|date\\.js)$');
|
||||||
|
expect(defaultRoutes[1].dest).toBe('/api/date.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = ['api/date.js', 'api/[date]/index.js'];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
expect(defaultRoutes.length).toBe(3);
|
||||||
|
expect(defaultRoutes[0].src).toBe(
|
||||||
|
'^/api/([^\\/]+)(\\/|\\/index|\\/index\\.js)?$',
|
||||||
|
);
|
||||||
|
expect(defaultRoutes[0].dest).toBe('/api/[date]/index.js?date=$1');
|
||||||
|
expect(defaultRoutes[1].src).toBe('^/api/(date|date\\.js)$');
|
||||||
|
expect(defaultRoutes[1].dest).toBe('/api/date.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const files = [
|
||||||
|
'api/index.ts',
|
||||||
|
'api/index.d.ts',
|
||||||
|
'api/users/index.ts',
|
||||||
|
'api/users/index.d.ts',
|
||||||
|
'api/food.ts',
|
||||||
|
'api/ts/gold.ts',
|
||||||
|
];
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
expect(builders.length).toBe(4);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[1].use).toBe('@now/node');
|
||||||
|
expect(builders[2].use).toBe('@now/node');
|
||||||
|
expect(builders[3].use).toBe('@now/node');
|
||||||
|
expect(defaultRoutes.length).toBe(5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Test `detectBuilders` and `detectRoutes`', async () => {
|
||||||
|
const fixture = path.join(__dirname, 'fixtures', '01-zero-config-api');
|
||||||
|
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
||||||
|
const fileList = await glob('**', fixture);
|
||||||
|
const files = Object.keys(fileList);
|
||||||
|
|
||||||
|
const probes = [
|
||||||
|
{
|
||||||
|
path: '/api/my-endpoint',
|
||||||
|
mustContain: 'my-endpoint',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/other-endpoint',
|
||||||
|
mustContain: 'other-endpoint',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/team/zeit',
|
||||||
|
mustContain: 'team/zeit',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/user/myself',
|
||||||
|
mustContain: 'user/myself',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/not-okay/',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
mustContain: 'hello from index.txt',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixture, 'now.json'),
|
||||||
|
JSON.stringify(nowConfig, null, 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
const deployment = await testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
fixture,
|
||||||
|
);
|
||||||
|
expect(deployment).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Test `detectBuilders` and `detectRoutes` with `index` files', async () => {
|
||||||
|
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
||||||
|
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
||||||
|
const fileList = await glob('**', fixture);
|
||||||
|
const files = Object.keys(fileList);
|
||||||
|
|
||||||
|
const probes = [
|
||||||
|
{
|
||||||
|
path: '/api/not-okay',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/index',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/index.js',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date.js',
|
||||||
|
mustContain: 'hello from api/date.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Someone might expect this to be `date.js`,
|
||||||
|
// but I doubt that there is any case were both
|
||||||
|
// `date/index.js` and `date.js` exists,
|
||||||
|
// so it is not special cased
|
||||||
|
path: '/api/date',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date/',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date/index',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date/index.js',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
mustContain: 'hello from index.txt',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixture, 'now.json'),
|
||||||
|
JSON.stringify(nowConfig, null, 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
const deployment = await testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
fixture,
|
||||||
|
);
|
||||||
|
expect(deployment).toBeDefined();
|
||||||
|
});
|
||||||
20
packages/now-build-utils/tsconfig.json
Normal file
20
packages/now-build-utils/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": ["esnext"],
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"noEmitOnError": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"types": ["node"],
|
||||||
|
"strict": true,
|
||||||
|
"target": "esnext"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
32
packages/now-cgi/.editorconfig
Normal file
32
packages/now-cgi/.editorconfig
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[{*.json,*.json.example,*.gyp,*.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
# Ideal settings - some plugins might support these.
|
||||||
|
[*.js]
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
[{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.js,*.java,*.go,*.rs,*.php,*.ng,*.jsx,*.ts,*.d,*.cs,*.swift}]
|
||||||
|
curly_bracket_next_line = false
|
||||||
|
spaces_around_operators = true
|
||||||
|
spaces_around_brackets = outside
|
||||||
|
# close enough to 1TB
|
||||||
|
indent_brace_style = K&R
|
||||||
2
packages/now-cgi/.gitignore
vendored
Normal file
2
packages/now-cgi/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
handler
|
||||||
7
packages/now-cgi/build.sh
Executable file
7
packages/now-cgi/build.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
export GOOS=linux
|
||||||
|
export GOARCH=amd64
|
||||||
|
export GOPATH=$HOME/go
|
||||||
|
go get github.com/aws/aws-lambda-go/events
|
||||||
|
go get github.com/aws/aws-lambda-go/lambda
|
||||||
|
go build -o handler main.go
|
||||||
46
packages/now-cgi/index.js
Normal file
46
packages/now-cgi/index.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const { mkdirp, copyFile } = require('fs-extra');
|
||||||
|
|
||||||
|
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
|
||||||
|
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
|
||||||
|
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
|
||||||
|
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
|
||||||
|
const { shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
|
||||||
|
|
||||||
|
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
|
||||||
|
|
||||||
|
exports.build = async ({
|
||||||
|
workPath, files, entrypoint, meta,
|
||||||
|
}) => {
|
||||||
|
console.log('downloading files...');
|
||||||
|
const outDir = await getWritableDirectory();
|
||||||
|
|
||||||
|
await download(files, workPath, meta);
|
||||||
|
|
||||||
|
const handlerPath = path.join(__dirname, 'handler');
|
||||||
|
await copyFile(handlerPath, path.join(outDir, 'handler'));
|
||||||
|
|
||||||
|
const entrypointOutDir = path.join(outDir, path.dirname(entrypoint));
|
||||||
|
await mkdirp(entrypointOutDir);
|
||||||
|
|
||||||
|
// For now only the entrypoint file is copied into the lambda
|
||||||
|
await copyFile(
|
||||||
|
path.join(workPath, entrypoint),
|
||||||
|
path.join(outDir, entrypoint),
|
||||||
|
);
|
||||||
|
|
||||||
|
const lambda = await createLambda({
|
||||||
|
files: await glob('**', outDir),
|
||||||
|
handler: 'handler',
|
||||||
|
runtime: 'go1.x',
|
||||||
|
environment: {
|
||||||
|
SCRIPT_FILENAME: entrypoint,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
[entrypoint]: lambda,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.shouldServe = shouldServe;
|
||||||
36
packages/now-cgi/main.go
Normal file
36
packages/now-cgi/main.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
now "../../utils/go/bridge"
|
||||||
|
"net/http"
|
||||||
|
"net/http/cgi"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CgiHandler struct {
|
||||||
|
http.Handler
|
||||||
|
Dir string
|
||||||
|
Script string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CgiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
handler := cgi.Handler{
|
||||||
|
Path: h.Script,
|
||||||
|
Root: "/" + h.Script,
|
||||||
|
Dir: h.Dir,
|
||||||
|
Env: []string{
|
||||||
|
"HTTPS=on",
|
||||||
|
"SERVER_PORT=443",
|
||||||
|
"SERVER_SOFTWARE=@now/cgi",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
workdir, _ := filepath.Abs(".")
|
||||||
|
script := os.Getenv("SCRIPT_FILENAME")
|
||||||
|
handler := &CgiHandler{nil, workdir, script}
|
||||||
|
now.Start(handler)
|
||||||
|
}
|
||||||
25
packages/now-cgi/package.json
Normal file
25
packages/now-cgi/package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "@now/cgi",
|
||||||
|
"version": "0.1.4",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/zeit/now-builders.git",
|
||||||
|
"directory": "packages/now-cgi"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test-integration": "best -I test/*.js",
|
||||||
|
"prepublish": "./build.sh"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"handler"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"fs-extra": "7.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@zeit/best": "0.4.3",
|
||||||
|
"rmfr": "2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
862
packages/now-cgi/yarn.lock
Normal file
862
packages/now-cgi/yarn.lock
Normal file
@@ -0,0 +1,862 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@mrmlnc/readdir-enhanced@^2.2.1":
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
|
||||||
|
dependencies:
|
||||||
|
call-me-maybe "^1.0.1"
|
||||||
|
glob-to-regexp "^0.3.0"
|
||||||
|
|
||||||
|
"@nodelib/fs.stat@^1.0.1":
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
|
||||||
|
|
||||||
|
"@zeit/best@0.4.3":
|
||||||
|
version "0.4.3"
|
||||||
|
resolved "http://registry.npmjs.org/@zeit/best/-/best-0.4.3.tgz#eaebdfa8b24121a97b1753501ea8c9330d549b30"
|
||||||
|
dependencies:
|
||||||
|
arg "1.0.0"
|
||||||
|
chalk "2.3.1"
|
||||||
|
diff "3.5.0"
|
||||||
|
globby "8.0.0"
|
||||||
|
signal-exit "3.0.2"
|
||||||
|
|
||||||
|
ansi-styles@^3.2.0:
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||||
|
dependencies:
|
||||||
|
color-convert "^1.9.0"
|
||||||
|
|
||||||
|
append-type@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/append-type/-/append-type-1.0.1.tgz#e29a6eb22cec0c0b9b93063b3cf6b10d9e0c60f4"
|
||||||
|
|
||||||
|
arg@1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/arg/-/arg-1.0.0.tgz#444d885a4e25b121640b55155ef7cd03975d6050"
|
||||||
|
|
||||||
|
arr-diff@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
||||||
|
|
||||||
|
arr-flatten@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
|
||||||
|
|
||||||
|
arr-union@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
|
||||||
|
|
||||||
|
array-to-sentence@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-to-sentence/-/array-to-sentence-1.1.0.tgz#c804956dafa53232495b205a9452753a258d39fc"
|
||||||
|
|
||||||
|
array-union@^1.0.1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
|
||||||
|
dependencies:
|
||||||
|
array-uniq "^1.0.1"
|
||||||
|
|
||||||
|
array-uniq@^1.0.1:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
|
||||||
|
|
||||||
|
array-unique@^0.3.2:
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||||
|
|
||||||
|
arrify@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||||
|
|
||||||
|
assert-valid-glob-opts@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/assert-valid-glob-opts/-/assert-valid-glob-opts-1.0.0.tgz#ab9b5438ec5e929f5bb08201819affb1227f730a"
|
||||||
|
dependencies:
|
||||||
|
glob-option-error "^1.0.0"
|
||||||
|
validate-glob-opts "^1.0.0"
|
||||||
|
|
||||||
|
assign-symbols@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
|
||||||
|
|
||||||
|
atob@^2.1.1:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
|
|
||||||
|
balanced-match@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
|
|
||||||
|
base@^0.11.1:
|
||||||
|
version "0.11.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||||
|
dependencies:
|
||||||
|
cache-base "^1.0.1"
|
||||||
|
class-utils "^0.3.5"
|
||||||
|
component-emitter "^1.2.1"
|
||||||
|
define-property "^1.0.0"
|
||||||
|
isobject "^3.0.1"
|
||||||
|
mixin-deep "^1.2.0"
|
||||||
|
pascalcase "^0.1.1"
|
||||||
|
|
||||||
|
brace-expansion@^1.1.7:
|
||||||
|
version "1.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^1.0.0"
|
||||||
|
concat-map "0.0.1"
|
||||||
|
|
||||||
|
braces@^2.3.1:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
|
||||||
|
dependencies:
|
||||||
|
arr-flatten "^1.1.0"
|
||||||
|
array-unique "^0.3.2"
|
||||||
|
extend-shallow "^2.0.1"
|
||||||
|
fill-range "^4.0.0"
|
||||||
|
isobject "^3.0.1"
|
||||||
|
repeat-element "^1.1.2"
|
||||||
|
snapdragon "^0.8.1"
|
||||||
|
snapdragon-node "^2.0.1"
|
||||||
|
split-string "^3.0.2"
|
||||||
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
|
cache-base@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
|
||||||
|
dependencies:
|
||||||
|
collection-visit "^1.0.0"
|
||||||
|
component-emitter "^1.2.1"
|
||||||
|
get-value "^2.0.6"
|
||||||
|
has-value "^1.0.0"
|
||||||
|
isobject "^3.0.1"
|
||||||
|
set-value "^2.0.0"
|
||||||
|
to-object-path "^0.3.0"
|
||||||
|
union-value "^1.0.0"
|
||||||
|
unset-value "^1.0.0"
|
||||||
|
|
||||||
|
call-me-maybe@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
|
||||||
|
|
||||||
|
chalk@2.3.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "http://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796"
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^3.2.0"
|
||||||
|
escape-string-regexp "^1.0.5"
|
||||||
|
supports-color "^5.2.0"
|
||||||
|
|
||||||
|
class-utils@^0.3.5:
|
||||||
|
version "0.3.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
|
||||||
|
dependencies:
|
||||||
|
arr-union "^3.1.0"
|
||||||
|
define-property "^0.2.5"
|
||||||
|
isobject "^3.0.0"
|
||||||
|
static-extend "^0.1.1"
|
||||||
|
|
||||||
|
collection-visit@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
|
||||||
|
dependencies:
|
||||||
|
map-visit "^1.0.0"
|
||||||
|
object-visit "^1.0.0"
|
||||||
|
|
||||||
|
color-convert@^1.9.0:
|
||||||
|
version "1.9.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
|
dependencies:
|
||||||
|
color-name "1.1.3"
|
||||||
|
|
||||||
|
color-name@1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||||
|
|
||||||
|
component-emitter@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
|
||||||
|
|
||||||
|
concat-map@0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
|
|
||||||
|
copy-descriptor@^0.1.0:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||||
|
|
||||||
|
debug@^2.2.0, debug@^2.3.3:
|
||||||
|
version "2.6.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
|
decode-uri-component@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||||
|
|
||||||
|
define-property@^0.2.5:
|
||||||
|
version "0.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
|
||||||
|
dependencies:
|
||||||
|
is-descriptor "^0.1.0"
|
||||||
|
|
||||||
|
define-property@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
|
||||||
|
dependencies:
|
||||||
|
is-descriptor "^1.0.0"
|
||||||
|
|
||||||
|
define-property@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
|
||||||
|
dependencies:
|
||||||
|
is-descriptor "^1.0.2"
|
||||||
|
isobject "^3.0.1"
|
||||||
|
|
||||||
|
diff@3.5.0:
|
||||||
|
version "3.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||||
|
|
||||||
|
dir-glob@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034"
|
||||||
|
dependencies:
|
||||||
|
arrify "^1.0.1"
|
||||||
|
path-type "^3.0.0"
|
||||||
|
|
||||||
|
escape-string-regexp@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
|
|
||||||
|
expand-brackets@^2.1.4:
|
||||||
|
version "2.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
|
||||||
|
dependencies:
|
||||||
|
debug "^2.3.3"
|
||||||
|
define-property "^0.2.5"
|
||||||
|
extend-shallow "^2.0.1"
|
||||||
|
posix-character-classes "^0.1.0"
|
||||||
|
regex-not "^1.0.0"
|
||||||
|
snapdragon "^0.8.1"
|
||||||
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
|
extend-shallow@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
|
||||||
|
dependencies:
|
||||||
|
is-extendable "^0.1.0"
|
||||||
|
|
||||||
|
extend-shallow@^3.0.0, extend-shallow@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
|
||||||
|
dependencies:
|
||||||
|
assign-symbols "^1.0.0"
|
||||||
|
is-extendable "^1.0.1"
|
||||||
|
|
||||||
|
extglob@^2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
|
||||||
|
dependencies:
|
||||||
|
array-unique "^0.3.2"
|
||||||
|
define-property "^1.0.0"
|
||||||
|
expand-brackets "^2.1.4"
|
||||||
|
extend-shallow "^2.0.1"
|
||||||
|
fragment-cache "^0.2.1"
|
||||||
|
regex-not "^1.0.0"
|
||||||
|
snapdragon "^0.8.1"
|
||||||
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
|
fast-glob@^2.0.2:
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.3.tgz#d09d378e9ef6b0076a0fa1ba7519d9d4d9699c28"
|
||||||
|
dependencies:
|
||||||
|
"@mrmlnc/readdir-enhanced" "^2.2.1"
|
||||||
|
"@nodelib/fs.stat" "^1.0.1"
|
||||||
|
glob-parent "^3.1.0"
|
||||||
|
is-glob "^4.0.0"
|
||||||
|
merge2 "^1.2.1"
|
||||||
|
micromatch "^3.1.10"
|
||||||
|
|
||||||
|
fill-range@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
|
||||||
|
dependencies:
|
||||||
|
extend-shallow "^2.0.1"
|
||||||
|
is-number "^3.0.0"
|
||||||
|
repeat-string "^1.6.1"
|
||||||
|
to-regex-range "^2.1.0"
|
||||||
|
|
||||||
|
for-in@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
|
|
||||||
|
fragment-cache@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
||||||
|
dependencies:
|
||||||
|
map-cache "^0.2.2"
|
||||||
|
|
||||||
|
fs-extra@7.0.0:
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.0.tgz#8cc3f47ce07ef7b3593a11b9fb245f7e34c041d6"
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.2"
|
||||||
|
jsonfile "^4.0.0"
|
||||||
|
universalify "^0.1.0"
|
||||||
|
|
||||||
|
fs.realpath@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||||
|
|
||||||
|
get-value@^2.0.3, get-value@^2.0.6:
|
||||||
|
version "2.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
|
||||||
|
|
||||||
|
glob-option-error@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob-option-error/-/glob-option-error-1.0.0.tgz#57cc65def9c7d5c1461baf13129bb5403cff6176"
|
||||||
|
|
||||||
|
glob-parent@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
|
||||||
|
dependencies:
|
||||||
|
is-glob "^3.1.0"
|
||||||
|
path-dirname "^1.0.0"
|
||||||
|
|
||||||
|
glob-to-regexp@^0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
|
||||||
|
|
||||||
|
glob@^7.0.5, glob@^7.1.2:
|
||||||
|
version "7.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
globby@8.0.0:
|
||||||
|
version "8.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.0.tgz#e6f8340ead9a52fa417ec0e75ae664ae0026f5c6"
|
||||||
|
dependencies:
|
||||||
|
array-union "^1.0.1"
|
||||||
|
dir-glob "^2.0.0"
|
||||||
|
fast-glob "^2.0.2"
|
||||||
|
glob "^7.1.2"
|
||||||
|
ignore "^3.3.5"
|
||||||
|
pify "^3.0.0"
|
||||||
|
slash "^1.0.0"
|
||||||
|
|
||||||
|
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
|
||||||
|
version "4.1.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.14.tgz#1b6e8362ef8c5ecb5da799901f39297e3054773a"
|
||||||
|
|
||||||
|
has-flag@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||||
|
|
||||||
|
has-value@^0.3.1:
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
|
||||||
|
dependencies:
|
||||||
|
get-value "^2.0.3"
|
||||||
|
has-values "^0.1.4"
|
||||||
|
isobject "^2.0.0"
|
||||||
|
|
||||||
|
has-value@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
|
||||||
|
dependencies:
|
||||||
|
get-value "^2.0.6"
|
||||||
|
has-values "^1.0.0"
|
||||||
|
isobject "^3.0.0"
|
||||||
|
|
||||||
|
has-values@^0.1.4:
|
||||||
|
version "0.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
|
||||||
|
|
||||||
|
has-values@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
|
||||||
|
dependencies:
|
||||||
|
is-number "^3.0.0"
|
||||||
|
kind-of "^4.0.0"
|
||||||
|
|
||||||
|
ignore@^3.3.5:
|
||||||
|
version "3.3.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||||
|
|
||||||
|
indexed-filter@^1.0.0:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/indexed-filter/-/indexed-filter-1.0.3.tgz#7911439191cac588188464640a8db4f6b324973d"
|
||||||
|
dependencies:
|
||||||
|
append-type "^1.0.1"
|
||||||
|
|
||||||
|
inflight@^1.0.4:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||||
|
dependencies:
|
||||||
|
once "^1.3.0"
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
inherits@2:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
|
|
||||||
|
inspect-with-kind@^1.0.4:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/inspect-with-kind/-/inspect-with-kind-1.0.5.tgz#fce151d4ce89722c82ca8e9860bb96f9167c316c"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^6.0.2"
|
||||||
|
|
||||||
|
is-accessor-descriptor@^0.1.6:
|
||||||
|
version "0.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^3.0.2"
|
||||||
|
|
||||||
|
is-accessor-descriptor@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^6.0.0"
|
||||||
|
|
||||||
|
is-buffer@^1.1.5:
|
||||||
|
version "1.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
|
|
||||||
|
is-data-descriptor@^0.1.4:
|
||||||
|
version "0.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^3.0.2"
|
||||||
|
|
||||||
|
is-data-descriptor@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^6.0.0"
|
||||||
|
|
||||||
|
is-descriptor@^0.1.0:
|
||||||
|
version "0.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
|
||||||
|
dependencies:
|
||||||
|
is-accessor-descriptor "^0.1.6"
|
||||||
|
is-data-descriptor "^0.1.4"
|
||||||
|
kind-of "^5.0.0"
|
||||||
|
|
||||||
|
is-descriptor@^1.0.0, is-descriptor@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
|
||||||
|
dependencies:
|
||||||
|
is-accessor-descriptor "^1.0.0"
|
||||||
|
is-data-descriptor "^1.0.0"
|
||||||
|
kind-of "^6.0.2"
|
||||||
|
|
||||||
|
is-extendable@^0.1.0, is-extendable@^0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
|
||||||
|
|
||||||
|
is-extendable@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
|
||||||
|
dependencies:
|
||||||
|
is-plain-object "^2.0.4"
|
||||||
|
|
||||||
|
is-extglob@^2.1.0, is-extglob@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||||
|
|
||||||
|
is-glob@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
|
||||||
|
dependencies:
|
||||||
|
is-extglob "^2.1.0"
|
||||||
|
|
||||||
|
is-glob@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0"
|
||||||
|
dependencies:
|
||||||
|
is-extglob "^2.1.1"
|
||||||
|
|
||||||
|
is-number@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^3.0.2"
|
||||||
|
|
||||||
|
is-plain-obj@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||||
|
|
||||||
|
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||||
|
dependencies:
|
||||||
|
isobject "^3.0.1"
|
||||||
|
|
||||||
|
is-windows@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||||
|
|
||||||
|
isarray@1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
|
|
||||||
|
isobject@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
|
||||||
|
dependencies:
|
||||||
|
isarray "1.0.0"
|
||||||
|
|
||||||
|
isobject@^3.0.0, isobject@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
|
|
||||||
|
jsonfile@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||||
|
optionalDependencies:
|
||||||
|
graceful-fs "^4.1.6"
|
||||||
|
|
||||||
|
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
||||||
|
version "3.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
|
||||||
|
dependencies:
|
||||||
|
is-buffer "^1.1.5"
|
||||||
|
|
||||||
|
kind-of@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
|
||||||
|
dependencies:
|
||||||
|
is-buffer "^1.1.5"
|
||||||
|
|
||||||
|
kind-of@^5.0.0:
|
||||||
|
version "5.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
|
||||||
|
|
||||||
|
kind-of@^6.0.0, kind-of@^6.0.2:
|
||||||
|
version "6.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
|
||||||
|
|
||||||
|
map-cache@^0.2.2:
|
||||||
|
version "0.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
||||||
|
|
||||||
|
map-visit@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
|
||||||
|
dependencies:
|
||||||
|
object-visit "^1.0.0"
|
||||||
|
|
||||||
|
merge2@^1.2.1:
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5"
|
||||||
|
|
||||||
|
micromatch@^3.1.10:
|
||||||
|
version "3.1.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
|
||||||
|
dependencies:
|
||||||
|
arr-diff "^4.0.0"
|
||||||
|
array-unique "^0.3.2"
|
||||||
|
braces "^2.3.1"
|
||||||
|
define-property "^2.0.2"
|
||||||
|
extend-shallow "^3.0.2"
|
||||||
|
extglob "^2.0.4"
|
||||||
|
fragment-cache "^0.2.1"
|
||||||
|
kind-of "^6.0.2"
|
||||||
|
nanomatch "^1.2.9"
|
||||||
|
object.pick "^1.3.0"
|
||||||
|
regex-not "^1.0.0"
|
||||||
|
snapdragon "^0.8.1"
|
||||||
|
to-regex "^3.0.2"
|
||||||
|
|
||||||
|
minimatch@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
mixin-deep@^1.2.0:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
|
||||||
|
dependencies:
|
||||||
|
for-in "^1.0.2"
|
||||||
|
is-extendable "^1.0.1"
|
||||||
|
|
||||||
|
ms@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
|
||||||
|
nanomatch@^1.2.9:
|
||||||
|
version "1.2.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||||
|
dependencies:
|
||||||
|
arr-diff "^4.0.0"
|
||||||
|
array-unique "^0.3.2"
|
||||||
|
define-property "^2.0.2"
|
||||||
|
extend-shallow "^3.0.2"
|
||||||
|
fragment-cache "^0.2.1"
|
||||||
|
is-windows "^1.0.2"
|
||||||
|
kind-of "^6.0.2"
|
||||||
|
object.pick "^1.3.0"
|
||||||
|
regex-not "^1.0.0"
|
||||||
|
snapdragon "^0.8.1"
|
||||||
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
|
object-copy@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
|
||||||
|
dependencies:
|
||||||
|
copy-descriptor "^0.1.0"
|
||||||
|
define-property "^0.2.5"
|
||||||
|
kind-of "^3.0.3"
|
||||||
|
|
||||||
|
object-visit@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
|
||||||
|
dependencies:
|
||||||
|
isobject "^3.0.0"
|
||||||
|
|
||||||
|
object.pick@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
|
||||||
|
dependencies:
|
||||||
|
isobject "^3.0.1"
|
||||||
|
|
||||||
|
once@^1.3.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
|
dependencies:
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
pascalcase@^0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||||
|
|
||||||
|
path-dirname@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
|
||||||
|
|
||||||
|
path-is-absolute@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||||
|
|
||||||
|
path-type@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
|
||||||
|
dependencies:
|
||||||
|
pify "^3.0.0"
|
||||||
|
|
||||||
|
pify@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||||
|
|
||||||
|
posix-character-classes@^0.1.0:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||||
|
|
||||||
|
regex-not@^1.0.0, regex-not@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
||||||
|
dependencies:
|
||||||
|
extend-shallow "^3.0.2"
|
||||||
|
safe-regex "^1.1.0"
|
||||||
|
|
||||||
|
repeat-element@^1.1.2:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
|
||||||
|
|
||||||
|
repeat-string@^1.6.1:
|
||||||
|
version "1.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||||
|
|
||||||
|
resolve-url@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||||
|
|
||||||
|
ret@~0.1.10:
|
||||||
|
version "0.1.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||||
|
|
||||||
|
rimraf@^2.6.2:
|
||||||
|
version "2.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
|
||||||
|
dependencies:
|
||||||
|
glob "^7.0.5"
|
||||||
|
|
||||||
|
rmfr@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/rmfr/-/rmfr-2.0.0.tgz#8a42e81332550b3f0019b8fb8ab245bea81b6d1c"
|
||||||
|
dependencies:
|
||||||
|
assert-valid-glob-opts "^1.0.0"
|
||||||
|
glob "^7.1.2"
|
||||||
|
graceful-fs "^4.1.11"
|
||||||
|
inspect-with-kind "^1.0.4"
|
||||||
|
rimraf "^2.6.2"
|
||||||
|
|
||||||
|
safe-regex@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
||||||
|
dependencies:
|
||||||
|
ret "~0.1.10"
|
||||||
|
|
||||||
|
set-value@^0.4.3:
|
||||||
|
version "0.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
|
||||||
|
dependencies:
|
||||||
|
extend-shallow "^2.0.1"
|
||||||
|
is-extendable "^0.1.1"
|
||||||
|
is-plain-object "^2.0.1"
|
||||||
|
to-object-path "^0.3.0"
|
||||||
|
|
||||||
|
set-value@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274"
|
||||||
|
dependencies:
|
||||||
|
extend-shallow "^2.0.1"
|
||||||
|
is-extendable "^0.1.1"
|
||||||
|
is-plain-object "^2.0.3"
|
||||||
|
split-string "^3.0.1"
|
||||||
|
|
||||||
|
signal-exit@3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||||
|
|
||||||
|
slash@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||||
|
|
||||||
|
snapdragon-node@^2.0.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
|
||||||
|
dependencies:
|
||||||
|
define-property "^1.0.0"
|
||||||
|
isobject "^3.0.0"
|
||||||
|
snapdragon-util "^3.0.1"
|
||||||
|
|
||||||
|
snapdragon-util@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^3.2.0"
|
||||||
|
|
||||||
|
snapdragon@^0.8.1:
|
||||||
|
version "0.8.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
|
||||||
|
dependencies:
|
||||||
|
base "^0.11.1"
|
||||||
|
debug "^2.2.0"
|
||||||
|
define-property "^0.2.5"
|
||||||
|
extend-shallow "^2.0.1"
|
||||||
|
map-cache "^0.2.2"
|
||||||
|
source-map "^0.5.6"
|
||||||
|
source-map-resolve "^0.5.0"
|
||||||
|
use "^3.1.0"
|
||||||
|
|
||||||
|
source-map-resolve@^0.5.0:
|
||||||
|
version "0.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
|
||||||
|
dependencies:
|
||||||
|
atob "^2.1.1"
|
||||||
|
decode-uri-component "^0.2.0"
|
||||||
|
resolve-url "^0.2.1"
|
||||||
|
source-map-url "^0.4.0"
|
||||||
|
urix "^0.1.0"
|
||||||
|
|
||||||
|
source-map-url@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
|
||||||
|
|
||||||
|
source-map@^0.5.6:
|
||||||
|
version "0.5.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
|
|
||||||
|
split-string@^3.0.1, split-string@^3.0.2:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
||||||
|
dependencies:
|
||||||
|
extend-shallow "^3.0.0"
|
||||||
|
|
||||||
|
static-extend@^0.1.1:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
|
||||||
|
dependencies:
|
||||||
|
define-property "^0.2.5"
|
||||||
|
object-copy "^0.1.0"
|
||||||
|
|
||||||
|
supports-color@^5.2.0:
|
||||||
|
version "5.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
|
dependencies:
|
||||||
|
has-flag "^3.0.0"
|
||||||
|
|
||||||
|
to-object-path@^0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
|
||||||
|
dependencies:
|
||||||
|
kind-of "^3.0.2"
|
||||||
|
|
||||||
|
to-regex-range@^2.1.0:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
|
||||||
|
dependencies:
|
||||||
|
is-number "^3.0.0"
|
||||||
|
repeat-string "^1.6.1"
|
||||||
|
|
||||||
|
to-regex@^3.0.1, to-regex@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
|
||||||
|
dependencies:
|
||||||
|
define-property "^2.0.2"
|
||||||
|
extend-shallow "^3.0.2"
|
||||||
|
regex-not "^1.0.2"
|
||||||
|
safe-regex "^1.1.0"
|
||||||
|
|
||||||
|
union-value@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
|
||||||
|
dependencies:
|
||||||
|
arr-union "^3.1.0"
|
||||||
|
get-value "^2.0.6"
|
||||||
|
is-extendable "^0.1.1"
|
||||||
|
set-value "^0.4.3"
|
||||||
|
|
||||||
|
universalify@^0.1.0:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||||
|
|
||||||
|
unset-value@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
|
||||||
|
dependencies:
|
||||||
|
has-value "^0.3.1"
|
||||||
|
isobject "^3.0.0"
|
||||||
|
|
||||||
|
urix@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
|
||||||
|
|
||||||
|
use@^3.1.0:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
|
|
||||||
|
validate-glob-opts@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/validate-glob-opts/-/validate-glob-opts-1.0.2.tgz#ef9f98977d965537ea4f51fa7d5799e9c6ebca91"
|
||||||
|
dependencies:
|
||||||
|
array-to-sentence "^1.1.0"
|
||||||
|
indexed-filter "^1.0.0"
|
||||||
|
inspect-with-kind "^1.0.4"
|
||||||
|
is-plain-obj "^1.1.0"
|
||||||
|
|
||||||
|
wrappy@1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
41
packages/now-cli/README.md
Normal file
41
packages/now-cli/README.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|

|
||||||
|
|
||||||
|
[](https://circleci.com/gh/zeit/workflows/now-cli)
|
||||||
|
[](https://spectrum.chat/zeit)
|
||||||
|
|
||||||
|
**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:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i -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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For details on how to use Now CLI, check out our [documentation](https://zeit.co/docs).
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
As always, you should 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-cli/wiki/Creating-a-Release).
|
||||||
200
packages/now-cli/package.json
Normal file
200
packages/now-cli/package.json
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
{
|
||||||
|
"name": "now",
|
||||||
|
"version": "16.1.2",
|
||||||
|
"preferGlobal": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"description": "The command-line interface for Now",
|
||||||
|
"repository": "zeit/now-cli",
|
||||||
|
"scripts": {
|
||||||
|
"test": "yarn test-lint",
|
||||||
|
"preinstall": "node ./scripts/preinstall.js",
|
||||||
|
"test-unit": "nyc ava test/*unit.js --serial --fail-fast --verbose",
|
||||||
|
"test-integration": "ava test/integration.js --serial --fail-fast",
|
||||||
|
"test-integration-now-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||||
|
"test-lint": "eslint . --ext .js,.ts",
|
||||||
|
"prepublishOnly": "yarn build",
|
||||||
|
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||||
|
"build": "ts-node ./scripts/build.ts",
|
||||||
|
"build-dev": "ts-node ./scripts/build.ts --dev",
|
||||||
|
"format-modified": "prettier --parser typescript --write --single-quote `git diff --name-only HEAD * | grep -e \".*\\.ts$\" -e \".*\\.js$\" | xargs echo`"
|
||||||
|
},
|
||||||
|
"nyc": {
|
||||||
|
"include": [
|
||||||
|
"src/**"
|
||||||
|
],
|
||||||
|
"extension": [
|
||||||
|
".js",
|
||||||
|
".ts"
|
||||||
|
],
|
||||||
|
"require": [
|
||||||
|
"ts-node/register"
|
||||||
|
],
|
||||||
|
"reporter": [
|
||||||
|
"text",
|
||||||
|
"html"
|
||||||
|
],
|
||||||
|
"sourceMap": true,
|
||||||
|
"instrument": true,
|
||||||
|
"all": true
|
||||||
|
},
|
||||||
|
"git": {
|
||||||
|
"pre-commit": [
|
||||||
|
"test-lint",
|
||||||
|
"format-modified"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"now": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"scripts/preinstall.js"
|
||||||
|
],
|
||||||
|
"ava": {
|
||||||
|
"compileEnhancements": false,
|
||||||
|
"extensions": [
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"require": [
|
||||||
|
"ts-node/register/transpile-only",
|
||||||
|
"esm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@now/build-utils": "0.9.13",
|
||||||
|
"@now/go": "latest",
|
||||||
|
"@now/next": "latest",
|
||||||
|
"@now/node": "latest",
|
||||||
|
"@now/php": "latest",
|
||||||
|
"@now/static-build": "latest",
|
||||||
|
"@sentry/node": "5.5.0",
|
||||||
|
"@types/ansi-escapes": "3.0.0",
|
||||||
|
"@types/ansi-regex": "4.0.0",
|
||||||
|
"@types/async-retry": "1.2.1",
|
||||||
|
"@types/bytes": "3.0.0",
|
||||||
|
"@types/debug": "0.0.31",
|
||||||
|
"@types/dotenv": "6.1.1",
|
||||||
|
"@types/escape-html": "0.0.20",
|
||||||
|
"@types/execa": "0.9.0",
|
||||||
|
"@types/fs-extra": "5.0.5",
|
||||||
|
"@types/glob": "7.1.1",
|
||||||
|
"@types/http-proxy": "1.16.2",
|
||||||
|
"@types/load-json-file": "2.0.7",
|
||||||
|
"@types/micro": "7.3.3",
|
||||||
|
"@types/mime-types": "2.1.0",
|
||||||
|
"@types/minimatch": "3.0.3",
|
||||||
|
"@types/mri": "1.1.0",
|
||||||
|
"@types/ms": "0.7.30",
|
||||||
|
"@types/node": "11.11.0",
|
||||||
|
"@types/node-fetch": "2.1.4",
|
||||||
|
"@types/npm-package-arg": "6.1.0",
|
||||||
|
"@types/pluralize": "0.0.29",
|
||||||
|
"@types/progress": "2.0.3",
|
||||||
|
"@types/psl": "1.1.0",
|
||||||
|
"@types/semver": "6.0.1",
|
||||||
|
"@types/tar-fs": "1.16.1",
|
||||||
|
"@types/text-table": "0.2.0",
|
||||||
|
"@types/universal-analytics": "0.4.2",
|
||||||
|
"@types/which": "1.3.1",
|
||||||
|
"@types/write-json-file": "2.2.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "1.6.0",
|
||||||
|
"@typescript-eslint/parser": "1.1.0",
|
||||||
|
"@zeit/dockerignore": "0.0.5",
|
||||||
|
"@zeit/fun": "0.9.1",
|
||||||
|
"@zeit/git-hooks": "0.1.4",
|
||||||
|
"@zeit/ncc": "0.18.5",
|
||||||
|
"@zeit/source-map-support": "0.6.2",
|
||||||
|
"alpha-sort": "2.0.1",
|
||||||
|
"ansi-escapes": "3.0.0",
|
||||||
|
"ansi-regex": "3.0.0",
|
||||||
|
"arg": "2.0.0",
|
||||||
|
"async-listen": "1.2.0",
|
||||||
|
"async-retry": "1.1.3",
|
||||||
|
"async-sema": "2.1.4",
|
||||||
|
"ava": "2.2.0",
|
||||||
|
"bytes": "3.0.0",
|
||||||
|
"cache-or-tmp-directory": "1.0.0",
|
||||||
|
"chalk": "2.4.2",
|
||||||
|
"chokidar": "2.1.6",
|
||||||
|
"clipboardy": "2.1.0",
|
||||||
|
"codecov": "3.1.0",
|
||||||
|
"cpy": "7.2.0",
|
||||||
|
"credit-card": "3.0.1",
|
||||||
|
"date-fns": "1.29.0",
|
||||||
|
"death": "1.1.0",
|
||||||
|
"debug": "3.1.0",
|
||||||
|
"deployment-type": "1.0.1",
|
||||||
|
"docker-file-parser": "1.0.2",
|
||||||
|
"dot": "1.1.2",
|
||||||
|
"dotenv": "4.0.0",
|
||||||
|
"download": "6.2.5",
|
||||||
|
"email-prompt": "0.3.2",
|
||||||
|
"email-validator": "1.1.1",
|
||||||
|
"epipebomb": "1.0.0",
|
||||||
|
"escape-html": "1.0.3",
|
||||||
|
"eslint": "5.16.0",
|
||||||
|
"eslint-config-airbnb": "17.1.0",
|
||||||
|
"eslint-config-prettier": "4.1.0",
|
||||||
|
"eslint-import-resolver-typescript": "1.1.1",
|
||||||
|
"eslint-plugin-import": "2.16.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "6.2.1",
|
||||||
|
"eslint-plugin-react": "7.12.4",
|
||||||
|
"esm": "3.1.4",
|
||||||
|
"execa": "1.0.0",
|
||||||
|
"fetch-h2": "2.0.3",
|
||||||
|
"fs-extra": "7.0.1",
|
||||||
|
"glob": "7.1.2",
|
||||||
|
"http-proxy": "1.17.0",
|
||||||
|
"ini": "1.3.4",
|
||||||
|
"inquirer": "3.3.0",
|
||||||
|
"is-url": "1.2.2",
|
||||||
|
"jaro-winkler": "0.2.8",
|
||||||
|
"jsonlines": "0.1.1",
|
||||||
|
"load-json-file": "3.0.0",
|
||||||
|
"micro": "9.1.2",
|
||||||
|
"mime-types": "2.1.24",
|
||||||
|
"minimatch": "3.0.4",
|
||||||
|
"mri": "1.1.0",
|
||||||
|
"ms": "2.1.1",
|
||||||
|
"node-fetch": "1.7.3",
|
||||||
|
"npm-package-arg": "6.1.0",
|
||||||
|
"nyc": "13.2.0",
|
||||||
|
"ora": "3.4.0",
|
||||||
|
"pcre-to-regexp": "0.0.5",
|
||||||
|
"pluralize": "7.0.0",
|
||||||
|
"pre-commit": "1.2.2",
|
||||||
|
"prettier": "1.16.2",
|
||||||
|
"printf": "0.2.5",
|
||||||
|
"progress": "2.0.3",
|
||||||
|
"promisepipe": "3.0.0",
|
||||||
|
"psl": "1.1.31",
|
||||||
|
"qr-image": "3.2.0",
|
||||||
|
"raw-body": "2.4.1",
|
||||||
|
"read-pkg": "2.0.0",
|
||||||
|
"rx-lite-aggregates": "4.0.8",
|
||||||
|
"semver": "5.5.0",
|
||||||
|
"serve-handler": "6.1.1",
|
||||||
|
"sinon": "4.4.2",
|
||||||
|
"strip-ansi": "5.2.0",
|
||||||
|
"stripe": "5.1.0",
|
||||||
|
"tar-fs": "1.16.3",
|
||||||
|
"test-listen": "1.1.0",
|
||||||
|
"text-table": "0.2.0",
|
||||||
|
"then-sleep": "1.0.1",
|
||||||
|
"through2": "2.0.3",
|
||||||
|
"title": "3.4.1",
|
||||||
|
"tmp-promise": "1.0.3",
|
||||||
|
"ts-node": "8.3.0",
|
||||||
|
"typescript": "3.2.4",
|
||||||
|
"universal-analytics": "0.4.20",
|
||||||
|
"update-check": "1.5.3",
|
||||||
|
"utility-types": "2.1.0",
|
||||||
|
"which": "1.3.1",
|
||||||
|
"which-promise": "1.0.0",
|
||||||
|
"write-json-file": "2.2.0",
|
||||||
|
"yarn": "1.17.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ async function createBuildersTarball() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const yarn = join(dirRoot, 'node_modules/yarn/bin/yarn.js');
|
const yarn = join(dirRoot, '../../node_modules/yarn/bin/yarn.js');
|
||||||
await execa(process.execPath, [yarn, 'add', '--no-lockfile', ...builders], {
|
await execa(process.execPath, [yarn, 'add', '--no-lockfile', ...builders], {
|
||||||
cwd: buildersDir,
|
cwd: buildersDir,
|
||||||
stdio: 'inherit'
|
stdio: 'inherit'
|
||||||
@@ -61,7 +61,7 @@ async function main() {
|
|||||||
// `fsevents` feature using `useFsEvents: false`, so delete the module here so
|
// `fsevents` feature using `useFsEvents: false`, so delete the module here so
|
||||||
// that it is not compiled by ncc, which makes the npm package size larger
|
// that it is not compiled by ncc, which makes the npm package size larger
|
||||||
// than necessary.
|
// than necessary.
|
||||||
await remove(join(dirRoot, 'node_modules/fsevents'));
|
await remove(join(dirRoot, '../../node_modules/fsevents'));
|
||||||
|
|
||||||
// Compile the `doT.js` template files for `now dev`
|
// Compile the `doT.js` template files for `now dev`
|
||||||
console.log();
|
console.log();
|
||||||
@@ -92,7 +92,7 @@ async function main() {
|
|||||||
// get compiled into the final ncc bundle file, however, we want them to be
|
// get compiled into the final ncc bundle file, however, we want them to be
|
||||||
// present in the npm package because the contents of those files are involved
|
// present in the npm package because the contents of those files are involved
|
||||||
// with `fun`'s cache invalidation mechanism and they need to be shasum'd.
|
// with `fun`'s cache invalidation mechanism and they need to be shasum'd.
|
||||||
const runtimes = join(dirRoot, 'node_modules/@zeit/fun/dist/src/runtimes');
|
const runtimes = join(dirRoot, '../../node_modules/@zeit/fun/dist/src/runtimes');
|
||||||
const dest = join(dirRoot, 'dist/runtimes');
|
const dest = join(dirRoot, 'dist/runtimes');
|
||||||
await cpy('**/*', dest, { parents: true, cwd: runtimes });
|
await cpy('**/*', dest, { parents: true, cwd: runtimes });
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ async function main() {
|
|||||||
|
|
||||||
// Compile the `doT.js` template files for `now dev`
|
// Compile the `doT.js` template files for `now dev`
|
||||||
const templatesDir = join(dirRoot, 'src/util/dev/templates');
|
const templatesDir = join(dirRoot, 'src/util/dev/templates');
|
||||||
const dotPacker = join(dirRoot, 'node_modules/dot/bin/dot-packer');
|
const dotPacker = join(dirRoot, '../../node_modules/dot/bin/dot-packer');
|
||||||
await execa(process.execPath, [dotPacker], {
|
await execa(process.execPath, [dotPacker], {
|
||||||
cwd: templatesDir,
|
cwd: templatesDir,
|
||||||
stdio: 'inherit'
|
stdio: 'inherit'
|
||||||
@@ -18,7 +18,7 @@ async function main() {
|
|||||||
|
|
||||||
// Prettier
|
// Prettier
|
||||||
console.log('\nMaking the compiled template functions prettier...');
|
console.log('\nMaking the compiled template functions prettier...');
|
||||||
const prettier = join(dirRoot, 'node_modules/prettier/bin-prettier.js');
|
const prettier = join(dirRoot, '../../node_modules/prettier/bin-prettier.js');
|
||||||
await execa(
|
await execa(
|
||||||
process.execPath,
|
process.execPath,
|
||||||
[prettier, '--write', '--single-quote', ...compiledFiles],
|
[prettier, '--write', '--single-quote', ...compiledFiles],
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user