Compare commits

..

51 Commits

Author SHA1 Message Date
Steven
46c8cb1a68 Publish Stable
- @now/build-utils@1.3.2
 - @now/next@2.3.7
 - @now/node@1.3.3
 - @now/static-build@0.14.3
2020-01-06 18:44:26 -05:00
Steven
faeb053ea6 Publish Canary
- @now/build-utils@1.3.2-canary.1
 - now@16.7.2-canary.3
 - @now/next@2.3.7-canary.1
 - @now/node@1.3.3-canary.1
 - @now/static-build@0.14.3-canary.1
2020-01-06 18:30:34 -05:00
Steven
708a09b86a [now-build-utils] Add usage of package.json engines (#3512)
Add a better upgrade message that shows usage of `engines` in the `package.json` file.
2020-01-06 18:29:08 -05:00
Steven
89403e93e4 Publish Canary
- @now/build-utils@1.3.2-canary.0
 - @now/next@2.3.7-canary.0
 - @now/node@1.3.3-canary.0
 - @now/static-build@0.14.3-canary.0
2020-01-06 17:01:47 -05:00
Steven
ecb0c08fe2 [now-build-utils] Fix now dev to use system node (#3509)
This PR will use the system installed version of Node.js and avoid printing a warning or error if a discontinued version is selected.

This optimization was already in `@now/node` but for some reason it was never add to `@now/next`.

The reason why its relevant today is because the warnings turned into errors due to Node 8 deprecation and we don't have the "Project" in `now dev` so we don't know which version of node to select.

So instead of determining the version, `now dev` will always use `node` in the PATH and avoid printing warnings or errors. This also results in less FS reads since we no longer need to read package.json.
2020-01-06 17:00:46 -05:00
Andy Bitz
0b88c158b9 Publish Stable
- @now/frameworks@0.0.3
2020-01-06 20:15:04 +01:00
Andy
ec3a38107a [frameworks] Add settings to frameworks (#3506)
* [frameworks] Add `settings` to frameworks

* Fix svelte

* Add `outputDirectory`

* Update Next.js outputDirectory placeholder

* Type settings

Co-authored-by: Leo Lamprecht <mindrun@icloud.com>
2020-01-06 20:12:38 +01:00
Steven
0c0f1c6eb5 Publish Stable
- @now/build-utils@1.3.1
 - @now/next@2.3.6
 - @now/node@1.3.2
 - @now/static-build@0.14.2
2020-01-06 13:21:41 -05:00
Steven
ed296ef733 Publish Canary
- @now/build-utils@1.3.1-canary.1
 - now@16.7.2-canary.2
 - @now/static-build@0.14.2-canary.0
2020-01-06 13:10:05 -05:00
Steven
246f47ec95 [now-static-build] Fix distDir when zero config framework is detected (#3507)
There was a regression with framework detection that was preferring the frameworks output directory instead of the `distDir` defined in the `@now/static-build`.

The fix is to only run framework detection when `builds` is not defined (ie zero config).

Related to https://github.com/Ebury/chameleon/pull/63
2020-01-06 18:07:25 +00:00
Steven
9d95b99b72 [now-build-utils] Add support for dynamic handle: miss (#3457)
This extends the behavior of `featHandleMiss: true` flag to do the following:

- Reduce zero config API routes so that only dynamic path segment files (`api/[id].js`) get a route.
- Remove zero config out directory route (`public/`)—the files will be renamed instead.
- Use redirects for API routes when `cleanUrls: true` and use rewrites when `cleanUrls: false` from extension to the extension-less file.
- Normalize existing routes to begin with `^` and end with `$`.
2020-01-06 16:07:47 +00:00
Steven
3de8ae9d7e Publish Canary
- @now/build-utils@1.3.1-canary.0
 - @now/node@1.3.2-canary.0
2020-01-06 10:19:23 -05:00
Steven
44f6e1904e [now-build-utils][now-node] Throw new NowBuildError() (#3496)
This PR does the following

- Add and export class, `NowBuildError`, that can be thrown to stop the build and print an error but it will not print a stack trace.
- Improve logic for discontinued node versions and add more tests
- Remove hack (#3425) for undefined TERM, fixed by updating dependencies
- Rename `silent` variable to `isAuto` which means the node version was automatically selected, not defined in `package.json`
- Rename `test` deployments to `test2020` so that a fresh project is created with latest Node.js 12.x
2020-01-06 15:00:27 +00:00
Max Rovensky
d9c84fc4ce Publish Canary
- now@16.7.2-canary.1
 - @now/python@1.1.1-canary.0
2020-01-06 21:45:01 +08:00
Max
b5142d935b Fix event order in CLI (#3502)
This should fix [PRODUCT-893](https://zeit.atlassian.net/browse/PRODUCT-893) and [PRODUCT-941](https://zeit.atlassian.net/browse/PRODUCT-941)
2020-01-06 13:43:44 +00:00
luc
718a451110 Publish Canary
- now@16.7.2-canary.0
 - now-client@6.0.2-canary.0
2020-01-06 11:24:45 +01:00
Andy
9755847855 [now-client] Ignore .env.* files by default (#3436)
Ignore `.env.*` files by default.

[PRODUCT-804]

[PRODUCT-804]: https://zeit.atlassian.net/browse/PRODUCT-804
2020-01-06 10:19:55 +00:00
Luc
abc417b6b3 [now-client] Use @zeit/fetch instead of node-fetch (#3490)
We want to take advantage of the better defaults and retry policies (among others).

This bumps `@zeit/fetch` and replaces `node-fetch` with `@zeit/fetch` in `now-client`.

Also update retry policy for "uploading" files to:
- base 10ms
- 5 retries
- factor 6

which means the timeouts are 10, 60, 360, 2160, 12960
2020-01-06 09:01:17 +00:00
Steven
d6f71c8d7b Publish Stable
- @now/frameworks@0.0.2
 - @now/build-utils@1.3.0
 - now@16.7.1
 - now-client@6.0.1
 - @now/next@2.3.5
 - @now/node@1.3.1
 - @now/python@1.1.0
 - @now/routing-utils@1.5.0
 - @now/static-build@0.14.1
2020-01-03 12:39:08 -05:00
JJ Kasper
d90892dc9c Publish Stable
- @now/next@2.3.4
2020-01-03 08:45:34 -06:00
Tim Neutkens
60d2f8b96c Publish Canary
- @now/next@2.3.4-canary.1
2020-01-03 12:36:15 +01:00
JJ Kasper
2488adf80d [now-next] Add support for experimental basePath (#3478)
* Add support for experimental basePath

* Update base-path fixture

* Update type

* Update basePath route source and add invariants
2020-01-03 12:32:20 +01:00
Steven
9deb5b31d2 Publish Canary
- now@16.7.1-canary.5
2020-01-02 19:32:01 -05:00
Steven
ae55823c3c Publish Canary
- @now/frameworks@0.0.2-canary.3
 - @now/build-utils@1.2.1-canary.7
 - @now/static-build@0.14.1-canary.8
2020-01-02 18:09:49 -05:00
Andy
d3395553fe [frameworks][now-static-build] Add support for Scully (#3469)
Add support for Scully.
2020-01-02 22:25:57 +00:00
JJ Kasper
e742dd3a48 Publish Canary
- @now/next@2.3.4-canary.0
2020-01-02 13:24:06 -06:00
JJ Kasper
4f0f44e746 Publish Stable
- @now/next@2.3.3
2020-01-02 13:18:38 -06:00
JJ Kasper
0da98a7f5d Revert "[now-next] Implement handle: miss for custom-routes (#3456)" (#3488)
This reverts commit 40bbff9bee.
2020-01-02 13:15:16 -06:00
Steven
685989ae57 Publish Canary
- @now/build-utils@1.2.1-canary.6
 - @now/next@2.3.3-canary.0
 - @now/node@1.3.1-canary.1
 - @now/static-build@0.14.1-canary.7
2020-01-02 12:42:36 -05:00
Steven
6bc121e7b1 [now-build-utils] Add support for getLatestNodeVersion() and config.nodeVersion (#3483)
This PR does a few things:

1. Add functions `getLatestNodeVersion()` and `getOldestNodeVersion()` for use in api-project.
2. Add property `config.nodeVersion` which has precedence over default Node 8.

We want new projects to use the latest node version without setting any configuration but we don't want to break old projects. So during project creation, the value of `getLatestNodeVersion()` will be saved and then each deployment of that project will assign that value to `config.nodeVersion`. 

Implements [PRODUCT-837]

[PRODUCT-837]: https://zeit.atlassian.net/browse/PRODUCT-837
2020-01-02 17:34:31 +00:00
JJ Kasper
56d3fed954 Publish Stable
- @now/next@2.3.2
2019-12-31 15:56:54 -06:00
JJ Kasper
40bbff9bee [now-next] Implement handle: miss for custom-routes (#3456)
This implements `{ handle: 'miss' }` which will allow more efficiently checking dynamic routes after a matching rewrite. This is not fully available in production yet and the tests will fail for this PR until it is ready in Now

x-ref: https://github.com/zeit/next.js/pull/9568
2019-12-31 19:36:50 +00:00
Steven
66ab011f4a Publish Canary
- @now/next@2.3.2-canary.0
 - @now/node@1.3.1-canary.0
2019-12-30 13:48:36 -05:00
Steven
f4237d3db0 [now-node][now-next] Bump node-file-trace to 0.4.1 (#3481)
Bumps `node-file-trace` to version [0.4.1](https://github.com/zeit/node-file-trace/releases/tag/0.4.1).

- Add special case for semver@7: [#87](https://github.com/zeit/node-file-trace/pull/87)
- Handle babel/ts compiled imports: [#79](https://github.com/zeit/node-file-trace/pull/79)
2019-12-30 18:26:32 +00:00
JJ Kasper
6f9a083dba [now-next] Add handling for new routes-manifest version (#3482)
This handles the new `routes-manifest` version in the latest canary of Next.js since we throw an error if the version isn't handled by `@now/next`

unblocks: https://github.com/zeit/now/pull/3481
2019-12-30 17:58:19 +00:00
dependabot[bot]
688fcc6a5b Bump handlebars from 4.1.2 to 4.5.3 (#3476)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.2 to 4.5.3.
<details>
<summary>Changelog</summary>

*Sourced from [handlebars's changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md).*

> ## v4.5.3 - November 18th, 2019
> Bugfixes:
> 
> - fix: add "no-prototype-builtins" eslint-rule and fix all occurences - f7f05d7
> - fix: add more properties required to be enumerable - 1988878
> 
> Chores / Build:
> - fix: use !== 0 instead of != 0 - c02b05f
> - add chai and dirty-chai and sinon, for cleaner test-assertions and spies, 
>   deprecate old assertion-methods  - 93e284e, 886ba86, 0817dad, 93516a0
> 
> Security:
> 
> - The properties `__proto__`, `__defineGetter__`, `__defineSetter__` and `__lookupGetter__` 
>   have been added to the list of "properties that must be enumerable".
>   If a property by that name is found and not enumerable on its parent, 
>   it will silently evaluate to `undefined`. This is done in both the compiled template and the "lookup"-helper. 
>   This will prevent new Remote-Code-Execution exploits that have been
>   published recently.
> 
> Compatibility notes: 
> 
> - Due to the security-fixes. The semantics of the templates using
>   `__proto__`, `__defineGetter__`, `__defineSetter__` and `__lookupGetter__` in the respect that those expression now return 
>   `undefined` rather than their actual value from the proto.
> - The semantics have not changed in cases where the properties are  enumerable, as in:
> 
> ```js
> {
>   __proto__: 'some string'
> }
> ```
> 
> - The change may be breaking in that respect, but we still only 
>   increase the patch-version, because the incompatible use-cases
>   are not intended, undocumented and far less important than fixing
>   Remote-Code-Execution exploits on existing systems.
> 
> 
> 
> [Commits](https://github.com/wycats/handlebars.js/compare/v4.5.2...v4.5.3)
> 
> ## v4.5.2 - November 13th, 2019
> # Bugfixes
> 
> - fix: use String(field) in lookup when checking for "constructor" - d541378
> - test: add fluent API for testing Handlebars - c2ac79c
> 
> Compatibility notes:
> - no incompatibility are to be expected
></tr></table> ... (truncated)
</details>
<details>
<summary>Commits</summary>

- [`c819c8b`](c819c8b533) v4.5.3
- [`827c9d0`](827c9d0747) Update release notes
- [`f7f05d7`](f7f05d7558) fix: add "no-prototype-builtins" eslint-rule and fix all occurences
- [`1988878`](1988878087) fix: add more properties required to be enumerable
- [`886ba86`](886ba86c2f) test/chore: add chai/expect and sinon to "runtime"-environment
- [`0817dad`](0817dad7e7) test: add sinon as global variable to eslint in the specs
- [`93516a0`](93516a0b07) test: add sinon.js for spies, deprecate current assertions
- [`93e284e`](93e284ed9b) chore: add chai and dirty-chai for better test assertions
- [`c02b05f`](c02b05fa81) fix: use !== 0 instead of != 0
- [`8de121d`](8de121d21c) v4.5.2
- Additional commits viewable in [compare view](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.5.3)
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=handlebars&package-manager=npm_and_yarn&previous-version=4.1.2&new-version=4.5.3)](https://help.github.com/articles/configuring-automated-security-fixes)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/zeit/now/network/alerts).

</details>
2019-12-30 14:13:42 +00:00
Shu Ding
847102cf62 Publish Canary
- @now/frameworks@0.0.2-canary.2
2019-12-28 07:23:53 +08:00
Shu Ding
25d5b9c9cf add missing logos (#3475) 2019-12-28 07:22:47 +08:00
Nathan Rajlich
271bab786e Publish Canary
- now@16.7.1-canary.4
 - now-client@6.0.1-canary.1
 - @now/routing-utils@1.4.1-canary.3
2019-12-23 17:33:19 -08:00
Nathan Rajlich
028e023aba [now-routing-utils] Make status code be configurable (#3470)
Now v1 static deployments use 301 by default, so this change allows the routing utils to match that behavior.
2019-12-24 00:56:47 +00:00
Steven
39719eed20 [now-client] Fix single file deploy with new routing properties (#3465)
There is an undocumented feature that adds a route when a single file is deployed, for example a single image or a zip file. This was causing the legacy routes to be added even when `rewrites` or `redirects` was defined. This PR fixes that case when the user has a single file, `now.json`, with redirects defined.
2019-12-24 00:07:30 +00:00
Andy Bitz
438339258d Publish Canary
- @now/build-utils@1.2.1-canary.5
2019-12-23 22:21:11 +01:00
Andy
be445c987c [now-build-utils] Only add 404 route when there are no api routes (#3468)
* [now-build-utils] Only add 404 route when there are no api routes

* Fix startWith

* Update packages/now-build-utils/src/detect-routes.ts

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

* Import isHandler

Co-authored-by: Steven <steven@ceriously.com>
2019-12-23 22:20:10 +01:00
Andy Bitz
93fef7885b Publish Canary
- @now/static-build@0.14.1-canary.6
2019-12-23 18:32:49 +01:00
Andy
899c9962ad [now-static-build] Ensure outputDirectory is used when it exists (#3466) 2019-12-23 18:32:29 +01:00
Andy Bitz
2b601d2424 Publish Canary
- @now/static-build@0.14.1-canary.5
2019-12-23 17:15:20 +01:00
Andy
3e36b05434 [now-static-build] Fix outputDirectory and add test (#3464) 2019-12-23 17:14:19 +01:00
Andy Bitz
59c9665c3f Publish Canary
- @now/static-build@0.14.1-canary.4
2019-12-23 15:57:53 +01:00
Andy
901137c7f6 [now-static-build] Add missing frameworks (#3463)
* [now-static-build] Add missing frameworks

* Update test

* Revert "Update test"

This reverts commit 86b2169caf145200b164b0c2f860c22d50b495cb.
2019-12-23 15:55:41 +01:00
Andy Bitz
e594e7bbbb Publish Canary
- @now/build-utils@1.2.1-canary.4
2019-12-23 10:02:00 +01:00
Andy
a477b1c22e [now-build-utils] Add option to ignore build script (#3461) 2019-12-23 10:00:45 +01:00
100 changed files with 2424 additions and 318 deletions

View File

@@ -12,6 +12,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"next\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`next build` or `build` from `package.json`"
},
"devCommand": {
"value": "next dev --port $PORT"
},
"outputDirectory": {
"placeholder": "Next.js default"
}
}
},
{
@@ -27,6 +38,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"gatsby\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`gatsby build` or `build` from `package.json`"
},
"devCommand": {
"value": "gatsby develop --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
@@ -41,6 +63,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"hexo\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`hexo generate` or `build` from `package.json`"
},
"devCommand": {
"value": "hexo server --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
@@ -55,6 +88,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@11ty\\/eleventy\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`npx @11ty/eleventy` or `build` from `package.json`"
},
"devCommand": {
"value": "npx @11ty/eleventy --serve --watch --port $PORT"
},
"outputDirectory": {
"value": "_site"
}
}
},
{
@@ -73,6 +117,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@docusaurus\\/core\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`docusaurus-build` or `build` from `package.json`"
},
"devCommand": {
"value": "docusaurus-start --port $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
@@ -88,6 +143,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"preact-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`preact build` or `build` from `package.json`"
},
"devCommand": {
"value": "preact watch --port $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
@@ -102,6 +168,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"ember-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`ember build` or `build` from `package.json`"
},
"devCommand": {
"value": "ember serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
@@ -117,6 +194,41 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@vue\\/cli-service\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`vue-cli-service build` or `build` from `package.json`"
},
"devCommand": {
"value": "vue-cli-service serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "Scully",
"slug": "scully",
"tagline": "Scully is a static site generator for Angular.",
"detectors": {
"every": [
{
"file": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@scullyio\\/init\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`ng build && scully` or `build` from `package.json`"
},
"devCommand": {
"value": "ng serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
@@ -132,6 +244,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@angular\\/cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`ng build` or `build` from `package.json`"
},
"devCommand": {
"value": "ng serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
@@ -146,6 +269,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"polymer-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`polymer build` or `build` from `package.json`"
},
"devCommand": {
"value": "polymer serve --port $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
@@ -161,6 +295,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"sirv-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`rollup -c` or `build` from `package.json`"
},
"devCommand": {
"value": "sirv public --single --dev --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
@@ -180,6 +325,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"react-dev-utils\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`react-scripts build` or `build` from `package.json`"
},
"devCommand": {
"value": "react-scripts start"
},
"outputDirectory": {
"value": "build"
}
}
},
{
@@ -194,6 +350,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"gridsome\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`gridsome build` or `build` from `package.json`"
},
"devCommand": {
"value": "gridsome develop -p $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
@@ -209,6 +376,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"umi\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`umi build` or `build` from `package.json`"
},
"devCommand": {
"value": "umi dev --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
@@ -224,6 +402,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"sapper\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`sapper export` or `build` from `package.json`"
},
"devCommand": {
"value": "sapper dev --port $PORT"
},
"outputDirectory": {
"value": "__sapper__/export"
}
}
},
{
@@ -238,6 +427,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"saber\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`saber build` or `build` from `package.json`"
},
"devCommand": {
"value": "saber --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
@@ -252,6 +452,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@stencil\\/core\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`stencil build` or `build` from `package.json`"
},
"devCommand": {
"value": "stencil build --dev --watch --serve --port $PORT"
},
"outputDirectory": {
"value": "www"
}
}
},
{
@@ -267,6 +478,17 @@
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"nuxt\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`nuxt build` or `build` from `package.json`"
},
"devCommand": {
"value": "nuxt"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
@@ -287,6 +509,17 @@
"file": "config.json"
}
]
},
"settings": {
"buildCommand": {
"value": "hugo"
},
"devCommand": {
"value": "hugo server -D -w -p $PORT"
},
"outputDirectory": {
"placeholder": "`public` or `publishDir` from the `config` file"
}
}
},
{
@@ -300,6 +533,17 @@
"file": "_config.yml"
}
]
},
"settings": {
"buildCommand": {
"value": "jekyll build"
},
"devCommand": {
"value": "bundle exec jekyll serve --watch --port $PORT"
},
"outputDirectory": {
"placeholder": "`_site` or `destination` from `_config.yml`"
}
}
},
{
@@ -313,6 +557,17 @@
"file": "brunch-config.js"
}
]
},
"settings": {
"buildCommand": {
"placeholder": "`brunch build --production` or `build` from `package.json`"
},
"devCommand": {
"value": "brunch watch --server --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
@@ -326,6 +581,17 @@
"file": "config.rb"
}
]
},
"settings": {
"buildCommand": {
"value": "bundle exec middleman build"
},
"devCommand": {
"value": "bundle exec middleman server -p $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
@@ -351,6 +617,7 @@
{
"name": "mdx-deck",
"slug": "mdx-deck",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/mdx-deck.svg",
"tagline": "MDX Deck allows you to swiftly create React MDX-based presentation decks.",
"website": "https://github.com/jxnblk/mdx-deck"
},
@@ -363,6 +630,7 @@
{
"name": "VuePress",
"slug": "vuepress",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/vuepress.png",
"tagline": "VuePress is the performant way to create static sites with Vue.js."
},
{
@@ -374,11 +642,13 @@
{
"name": "Riot.js",
"slug": "riot",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/riot.svg",
"tagline": "Riot.js lets you build user interfaces with custom tags using simple and enjoyable syntax."
},
{
"name": "Marko.js",
"slug": "marko",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/marko.png",
"tagline": "Marko is a super fast UI library that makes building web apps fun."
},
{
@@ -390,6 +660,7 @@
{
"name": "Metalsmith",
"slug": "metalsmith",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/metalsmith.png",
"tagline": "Metalsmith is an extremely simple, extendable static site generator."
},
{
@@ -407,6 +678,7 @@
{
"name": "Pelican",
"slug": "pelican",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/pelican.png",
"tagline": "Pelican is a versatile static site generator, written in Python."
},
{
@@ -430,6 +702,7 @@
{
"name": "Foundation",
"slug": "foundation",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/foundation.svg",
"tagline": "Foundation is the most advanced responsive front-end framework in the world."
}
]

View File

@@ -3,6 +3,11 @@ export interface FrameworkDetectionItem {
matchContent?: string;
}
interface Setting {
value?: string;
placeholder?: string;
}
export interface Framework {
name: string;
slug: string;
@@ -13,4 +18,9 @@ export interface Framework {
every?: FrameworkDetectionItem[];
some?: FrameworkDetectionItem[];
};
settings?: {
buildCommand?: Setting;
devCommand?: Setting;
outputDirectory?: Setting;
};
}

View File

@@ -0,0 +1,73 @@
<svg xmlns="http://www.w3.org/2000/svg" width="483" height="400">
<path fill="#EAF7FE" d="M316 92l15-18c-10-11-30-9-30-9l12-14-20-8c3-9 13-17 13-17-37-14-64 3-64 3 2-9 14-22 14-22-37 2-65 32-65 32 0-5 4-19 4-19s-20 14-31 35c-4 7-19 61-21 73 0 0-5-2-10-1 0 0 7 10 8 14s-7 33-10 38-7 7-9 8c0 0 3 5 6 6 0 0-1 16-3 20s-8 9-10 11c0 0 6 7 10 7l-6 10c-2 2-5-2-5-2l1 8c1 3 0 7-1 9s-5 15-6 21 1 11 1 11 2-3 4-4c0 0 0 13 3 19l9 13s-1-4 1-6c0 0 3 11 18 13 33 5 56 11 94-11 35-21 31-96 42-144 5-23 17-45 27-62 7-8 18-14 18-14h-9z"/>
<ellipse cx="248" cy="377" fill="#E5E5E5" rx="230" ry="17"/>
<path fill="#A1D4E9" d="M83 369l1-5c0-5 1-11 5-16l-3 1c5-6 10-10 13-11l-3-1c3-2 11-3 18-4l-3-2 13 1 21-4-2-6c8 0 20 2 23 3 2 0 3 5 4 5 0 0 20-11 20 8l-1 36-16-1h-28l-43 1v-2H92v-2-2l-9 1z"/>
<path fill="#85C1CE" d="M92 368h-4c0-24 24-30 24-30-21 8-20 27-20 30zM97 363l6-9c8-11 22-12 22-12-25 5-23 30-23 30h-4l1-9h-2z"/>
<path fill="#4F4E51" d="M162 82c-22-8-34-30-34-30s-6 16 1 33c6 12 19 20 26 24l7-27z"/>
<path fill="#5C5B5D" d="M162 82c-22-8-34-30-34-30s2 30 30 43l4-13z"/>
<g fill="#A1D4E9">
<path d="M128 208l-8-8c-4-7-5-15-5-15-2 2-2 6-2 7a55 55 0 00-13-9l-8 1 3 61 21 10s20-31 12-47zM95 181l-12-5s4 3 3 6c0 2 3 2 6 2v-4l3 1z"/>
<path d="M95 181l-4-1 1 4 8-1-5-2z"/>
</g>
<path fill="#CDEAF5" d="M183 64c13-22 56-42 64-45l9-12c-37 2-65 32-65 32 0-5 4-19 4-19s-20 14-31 35c-4 7-19 61-21 73 0 0-5-2-10-1 0 0 7 10 8 14s-7 33-10 38-7 7-9 8c0 0 3 5 6 6 0 0-1 16-3 20s-8 9-10 11c0 0 5 7 9 8l-5 9c-2 2-5-2-5-2l1 8c1 3 0 7-1 9s-5 15-6 21 1 11 1 11 2-3 4-4c0 0 0 13 3 19l9 13s-1-2 1-4c0 0 4 9 19 14 12 4 22 5 36 1 9-3 15-13 15-13-62 20-80-22-73-49 4-13 19-35 19-35-7 2-11-5-11-5s3 0 9-6c5-5 4-34 4-34l-6 1c1 0 2-1 6-10l-3-1v-1c2-6 1-13 3-19l2-5h6l4-17c12-54 25-85 25-85-2 6 2 16 2 16z"/>
<path fill="#5D5C5E" d="M120 235c1 2 1 5-1 6l-38 28c-2 2-5 2-6 0l-56-74c-1-2-1-5 1-6l38-29c2-1 5-1 6 1l56 74z"/>
<path fill="none" stroke="#738083" stroke-miterlimit="10" stroke-width="2" d="M120 235c1 2 1 5-1 6l-38 28c-2 2-5 2-6 0l-56-74c-1-2-1-5 1-6l38-29c2-1 5-1 6 1l56 74z"/>
<path fill="#747F84" d="M109 229l-38 28-41-54 38-29z"/>
<path fill="#414141" d="M98 247c2 2 1 4 0 5-2 1-4 1-5-1v-5c2-1 4 0 5 1z"/>
<path fill="none" stroke="#414141" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M39 187l12-9"/>
<g fill="#E9F7FE" opacity=".5">
<path d="M91 208l-33 26-18-25 33-25zM75 236l-10 7-5-6 10-7zM95 214l-21 16-2-2 21-16zM99 218l-22 16-2-2 22-16z"/>
<g>
<path d="M82 245l-10 8-5-7 10-7zM102 223l-21 17-2-3 21-16zM105 227l-21 16-2-3 21-16z"/>
</g>
<path d="M70 182l-33 25-3-3 34-26z"/>
</g>
<path fill="#A1D4E9" d="M99 207c-19 15-41 3-41 3 3-6 21-30 34-30 0 0 19 8 7 27zM87 224c-25-7-47-1-47-1-10 2-7 14-2 23l4 14 3-3c5 7 15 12 28 4 16-10 14-37 14-37z"/>
<ellipse cx="214.2" cy="174" fill="#CDEAF5" rx="10.8" ry="13.7"/>
<ellipse cx="160.3" cy="165.8" fill="#CDEAF5" rx="10.8" ry="13.7"/>
<ellipse cx="213.2" cy="170.2" fill="#FFF" rx="10.7" ry="13.5"/>
<ellipse cx="160.2" cy="162.2" fill="#FFF" rx="10.7" ry="13.5"/>
<path fill="#4F4E51" d="M166 156v1c0 2-1 4-3 4l-2-3-2 4c0 4 3 7 5 7 3 0 6-3 6-7 0-3-2-5-4-6z"/>
<path fill="#5C5B5D" d="M255 165c-4-3-9-4-9-4-4-2-13-4-23-5l-31-1h-12a38 38 0 01-4-2l-9-4-13-2-20-1-2 1-2 4v4l2 4 1 2v11c1 3 3 6 10 8l5 1c12 3 17 2 21 0s6-10 8-14v-2l1-1 1-1 3 1 2 1v4l1 4v1c0 3 1 9 4 11 1 2 6 4 11 6l11 2h21c4-1 5-3 7-7l1-1v-1l5-17 9 3 1-5zm-82-4l-4 11c-2 5-2 5-4 6-2 0-9 0-17-2-6-1-10-4-10-6v-18-1h8c6 0 14 3 17 4h2c5 2 8 4 8 6zm66 8l-5 17c-1 2-3 2-10 3h-1a91 91 0 01-24-4c-6-2-7-3-8-6l-1-15c0-2 0-3 2-3h25a143 143 0 0120 4c1 0 3 2 2 4z"/>
<path fill="#4F4E51" d="M216 163v1c0 2-1 4-3 4l-2-3-2 4c0 4 3 7 5 7 3 0 6-3 6-7 0-3-2-5-4-6z"/>
<path fill="#A1D4E9" d="M207 121s19-8 28 1c0 0 0-9-12-9-8 0-12 4-16 8zM211 111s6-6 17-4c0 0-4-4-9-3-4 1-6 3-8 7zM154 127s10 3 14 10c0 0 1-6-4-9-5-2-6-2-10-1zM171 126s-2-4-7-4c0 0 4-2 6 0 0 0 2 2 1 4z"/>
<path fill="#5C5B5D" d="M176 198l25 5 2-4v5s-7 2-13 1l10-1s-13-1-24-6zM280 102l3 2c26 10 52-4 52-4s-4 16-20 27c-15 10-39 5-39 5-2 3-3 10-3 10s-4-9-1-24c2-10 7-16 8-16"/>
<path fill="#747F83" d="M280 102l3 2c26 10 52-4 52-4s-23 31-62 15c0 0 1-7 7-13"/>
<path fill="#CDEAF5" d="M125 231s10 2 16-2c0 0 2 9 10 15 0 0 7-12 14-14l1 11s13 1 25-9c0 0-3 14-30 15v-8s-6 7-10 16c0 0-14-10-16-19h-12l2-5z"/>
<path fill="#85C1CE" d="M76 222s-13 10-22 13c0 0 22-5 29-8 0 0-6 15-24 21 0 0 17-2 24-8 0 0-8 23-25 25 0 0 10 1 19-7 9-9 11-27 10-34l-11-2zM100 207l7-5s0 5-5 8l-2-3z"/>
<path fill="#EAF7FE" d="M331 263c3-2 5-1 4-10-1-8-6-17-6-17M279 221l-5 2 6 2zM334 228s12-3 16-10"/>
<path fill="#85C1CE" d="M130 330s16 10 39 7v-8s-13 1-26-4v3l-4-2v3l-9 1z"/>
<path fill="#CDEAF5" d="M222 347h-3c-11 0-19-2-30-1l-22 1 10-10 1 3 6-10 11-15-17 6-4 6v1c-2-2-4-3-4-1l-1 2c-3 5-17 14-17 14l-2 1 3 1v2l-1 1v5l-1 10c0 2-2 4-4 4 0 0 2 2 3 1v1s-1 8 6 9h21l21 1v-3l12 1 2-4 18 1c3-19-2-26-8-27z"/>
<path fill="#EAF7FE" d="M233 367v-6l1 1-3-8c-2-4-5-7-10-7l1-2c-4-1-7-2-14-1 4-1 8-4 12-7l2 1 2-5h-1l7-8 2 3 6-16-28 9-3 3-11-11-14 19-3 7c-1-1-1-3-2-2-4 4-8 8-12 10h8l-4 2 12-1c12-1 19 4 19 4v-1c1 1 4 3 6 8 0 1 2 8 1 10l2-1v9h8s1-10-1-18h2c-1-4-4-7-7-8l-1-1s16-2 16 23h3l7 1-1-1-2-6zm-27-8l2 4-2-4z"/>
<path fill="#EAF7FE" d="M194 358l2-1c-3-2-7-2-9-2h-2s7 6 6 24h7s3-14-4-21z"/>
<g>
<path fill="#5D5C5E" d="M452 372c0 5-5 9-10 9H237c-6 0-10-4-10-9V227c0-5 4-10 10-10h205c5 0 10 5 10 10v145z"/>
<path fill="none" stroke="#738083" stroke-miterlimit="10" stroke-width="2" d="M452 372c0 5-5 9-10 9H237c-6 0-10-4-10-9V227c0-5 4-10 10-10h205c5 0 10 5 10 10v145h0z"/>
<path fill="#747F84" d="M247 231h185v136H247z"/>
<path fill="#414141" d="M243 299c0 3-2 5-5 5-2 0-4-2-4-5 0-2 2-4 4-4 3 0 5 2 5 4z"/>
<path fill="none" stroke="#414141" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M440 290v18"/>
<g fill="#E9F7FE" opacity=".5">
<path d="M252 237h176v12H252zM252 252h176v58H252zM252 317h28v20h-28zM284 317h53v5h-53zM284 324h53v5h-53zM284 332h53v5h-53z"/>
<g>
<path d="M342 317h28v20h-28zM374 317h53v5h-53zM374 324h53v5h-53zM374 332h53v5h-53z"/>
</g>
<g>
<path d="M252 343h28v20h-28zM284 343h53v5h-53zM284 351h53v5h-53zM284 358h53v5h-53z"/>
</g>
<g>
<path d="M342 343h28v20h-28zM374 343h53v5h-53zM374 351h53v5h-53zM374 358h53v5h-53z"/>
</g>
</g>
</g>
<g>
<path fill="#EAF7FE" d="M382 187l8 3-10-11-15-7 2-2h-10l-30-4 2-3-39 2 1-4-6 3c0-3 5-9 5-9-6 1-11 9-11 9l-1 5-7 47h112c1-9 0-21-2-29h1zm-94-20z"/>
<path fill="#4F4E51" d="M330 260l-4 5-8-7-12 9-7-10c-2-3-2-14-2-14l-3 2 5-15s30-6 31 30z" opacity=".3"/>
<path fill="#EAF7FE" d="M333 246c1-4-1-8-1-12l2-5c1-2 3-2 5-3a385 385 0 017-4c10-5 37-8 37-8-39-15-95 0-95 0-11 7-17 30-17 30 9-7 35-22 35-22-1 11-16 11 25 41 0-6 2-11 3-17h-1z"/>
<path fill="#CDEAF5" d="M289 197c-7 5-13 12-18 19h15l13-10-15-1s21-5 29-9c10-8 17-13 26-16 0 0 1-1-1-1l4-1 10-3s-20-2-63 22z"/>
<path fill="#4F4E51" d="M285 229h-19l-2 6-1 9 22-15z" opacity=".3"/>
<path fill="#EAF7FE" d="M299 206s-26 15-27 29l-1 9 28-21v-17zM305 224c-5 16-5 16-4 24l2 8 11 11s10-7 9-14c-3-23-18-29-18-29z"/>
<path fill="#EAF7FE" d="M293 230s4-5 14-6c7 0 0-5 0-5l-13 1"/>
<path fill="#EAF7FE" d="M302 252l-2-12-2 2c3-15 2-15 5-19 3-7 3 21 3 21"/>
<path fill="#CDEAF5" d="M317 240c1-5 1-13 3-18l-2 3c-2 4-4 10-4 17 0 8 7 17 7 17l2-2-4-8s-2-3-2-9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="138" height="57">
<g fill="none" fill-rule="evenodd">
<rect width="136.5" height="55.5" x=".8" y=".8" fill="#FFF" stroke="#EAEAEA" stroke-width="1.5" rx="4.5"/>
<g stroke="#000" stroke-width="6">
<path stroke-linecap="square" d="M70.5 36V13.8"/>
<path d="M57 27.2L70.6 41 84 27.4"/>
</g>
<path stroke="#000" stroke-width="6" d="M16.4 44V19l13.9 13.8 14-14v25"/>
<path stroke="#F9AC00" stroke-width="6" d="M122.4 41.3L93.2 12m.4 29.3L122.8 12"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,4 @@
<svg width="111" height="116" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 2l2-2h73c20 0 36 16 36 36v6l-2 2H89l-2-2v-6c0-7-5-12-12-12H25l-1 2v80l-2 1C10 106 0 96 0 84V2z" fill="#ED1846"/>
<path d="M45 48l-1 1c1 13 11 23 23 23h16c2 0 4 1 4 4v30l2 1c12-1 22-11 22-23v-8c0-16-12-28-28-28H45z" fill="#ED1846"/>
</svg>

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "@now/frameworks",
"version": "0.0.2-canary.1",
"version": "0.0.3",
"main": "frameworks.json",
"license": "UNLICENSED"
}

View File

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

View File

@@ -10,6 +10,7 @@ interface ErrorResponse {
interface Options {
tag?: 'canary' | 'latest' | string;
functions?: BuilderFunctions;
ignoreBuildScript?: boolean;
projectSettings?: {
framework?: string | null;
devCommand?: string | null;
@@ -407,7 +408,7 @@ export async function detectBuilders(
};
}
} else {
if (pkg && builders.length === 0) {
if (!options.ignoreBuildScript && 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({

View File

@@ -1,5 +1,5 @@
import { parse as parsePath } from 'path';
import { Route } from '@now/routing-utils';
import { Route, Source } from '@now/routing-utils';
import { Builder } from './types';
import { getIgnoreApiFilter, sortFiles } from './detect-builders';
@@ -41,20 +41,26 @@ function getSegmentName(segment: string): string | null {
return null;
}
function createRouteFromPath(filePath: string): Route {
function createRouteFromPath(
filePath: string,
featHandleMiss: boolean,
cleanUrls: boolean
): { route: Source; isDynamic: boolean } {
const parts = filePath.split('/');
let counter = 1;
const query: string[] = [];
let isDynamic = false;
const srcParts = parts.map((segment, index): string => {
const srcParts = parts.map((segment, i): string => {
const name = getSegmentName(segment);
const isLast = index === parts.length - 1;
const isLast = i === parts.length - 1;
if (name !== null) {
// We can't use `URLSearchParams` because `$` would get escaped
query.push(`${name}=$${counter++}`);
return `([^\\/]+)`;
isDynamic = true;
return `([^/]+)`;
} else if (isLast) {
const { name: fileName, ext } = parsePath(segment);
const isIndex = fileName === 'index';
@@ -63,27 +69,43 @@ function createRouteFromPath(filePath: string): Route {
const names = [
isIndex ? prefix : `${fileName}\\/`,
prefix + escapeName(fileName),
prefix + escapeName(fileName) + escapeName(ext),
featHandleMiss && cleanUrls
? ''
: prefix + escapeName(fileName) + escapeName(ext),
].filter(Boolean);
// Either filename with extension, filename without extension
// or nothing when the filename is `index`
// or nothing when the filename is `index`.
// When `cleanUrls: true` then do *not* add the filename with extension.
return `(${names.join('|')})${isIndex ? '?' : ''}`;
}
return segment;
});
const { name: fileName } = parsePath(filePath);
const { name: fileName, ext } = parsePath(filePath);
const isIndex = fileName === 'index';
const queryString = `${query.length ? '?' : ''}${query.join('&')}`;
const src = isIndex
? `^/${srcParts.slice(0, -1).join('/')}${srcParts.slice(-1)[0]}$`
: `^/${srcParts.join('/')}$`;
const dest = `/${filePath}${query.length ? '?' : ''}${query.join('&')}`;
return { src, dest };
let route: Source;
if (featHandleMiss) {
const extensionless = ext ? filePath.slice(0, -ext.length) : filePath;
route = {
src,
dest: `/${extensionless}${queryString}`,
check: true,
};
} else {
route = {
src,
dest: `/${filePath}${queryString}`,
};
}
return { route, isDynamic };
}
// Check if the path partially matches and has the same
@@ -193,18 +215,30 @@ function sortFilesBySegmentCount(fileA: string, fileB: string): number {
return 0;
}
interface ApiRoutesResult {
defaultRoutes: Source[] | null;
dynamicRoutes: Source[] | null;
error: { [key: string]: string } | null;
}
interface RoutesResult {
defaultRoutes: Route[] | null;
redirectRoutes: Route[] | null;
error: { [key: string]: string } | null;
}
async function detectApiRoutes(
files: string[],
builders: Builder[],
featHandleMiss: boolean
): Promise<RoutesResult> {
featHandleMiss: boolean,
cleanUrls: boolean
): Promise<ApiRoutesResult> {
if (!files || files.length === 0) {
return { defaultRoutes: null, error: null };
return {
defaultRoutes: null,
dynamicRoutes: null,
error: null,
};
}
// The deepest routes need to be
@@ -214,7 +248,8 @@ async function detectApiRoutes(
.sort(sortFiles)
.sort(sortFilesBySegmentCount);
let defaultRoutes: Route[] = [];
const defaultRoutes: Source[] = [];
const dynamicRoutes: Source[] = [];
for (const file of sortedFiles) {
// We only consider every file in the api directory
@@ -231,6 +266,7 @@ async function detectApiRoutes(
if (conflictingSegment) {
return {
defaultRoutes: null,
dynamicRoutes: null,
error: {
code: 'conflicting_path_segment',
message:
@@ -252,6 +288,7 @@ async function detectApiRoutes(
return {
defaultRoutes: null,
dynamicRoutes: null,
error: {
code: 'conflicting_file_path',
message:
@@ -262,34 +299,14 @@ async function detectApiRoutes(
};
}
defaultRoutes.push(createRouteFromPath(file));
}
// 404 Route to disable directory listing
if (defaultRoutes.length > 0) {
if (featHandleMiss) {
defaultRoutes = [
{ handle: 'miss' },
{
src: '/api/(.+)\\.\\w+',
dest: '/api/$1',
check: true,
},
{
status: 404,
src: '/api(/.*)?$',
continue: true,
},
];
} else {
defaultRoutes.push({
status: 404,
src: '/api(/.*)?$',
});
const out = createRouteFromPath(file, featHandleMiss, cleanUrls);
if (out.isDynamic) {
dynamicRoutes.push(out.route);
}
defaultRoutes.push(out.route);
}
return { defaultRoutes, error: null };
return { defaultRoutes, dynamicRoutes, error: null };
}
function getPublicBuilder(builders: Builder[]): Builder | null {
@@ -314,17 +331,81 @@ export function detectOutputDirectory(builders: Builder[]): string | null {
export async function detectRoutes(
files: string[],
builders: Builder[],
featHandleMiss = false
featHandleMiss = false,
cleanUrls = false,
trailingSlash?: boolean
): Promise<RoutesResult> {
const routesResult = await detectApiRoutes(files, builders, featHandleMiss);
const result = await detectApiRoutes(
files,
builders,
featHandleMiss,
cleanUrls
);
const { dynamicRoutes, defaultRoutes: allRoutes, error } = result;
if (error) {
return { defaultRoutes: null, redirectRoutes: null, error };
}
const directory = detectOutputDirectory(builders);
const defaultRoutes: Route[] = [];
const redirectRoutes: Route[] = [];
if (allRoutes && allRoutes.length > 0) {
const hasApiRoutes = allRoutes.some(
r => r.dest && r.dest.startsWith('/api/')
);
if (featHandleMiss) {
defaultRoutes.push({ handle: 'miss' });
if (cleanUrls) {
const extensions = builders
.map(b => parsePath(b.src).ext)
.filter(Boolean);
if (extensions.length > 0) {
const exts = extensions.map(ext => ext.slice(1)).join('|');
const group = `(?:\\.(?:${exts}))`;
redirectRoutes.push({
src: `^/(api(?:.+)?)/index${group}?/?$`,
headers: { Location: trailingSlash ? '/$1/' : '/$1' },
status: 308,
});
redirectRoutes.push({
src: `^/api/(.+)${group}/?$`,
headers: { Location: trailingSlash ? '/api/$1/' : '/api/$1' },
status: 308,
});
}
} else {
defaultRoutes.push({
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
});
}
if (dynamicRoutes) {
defaultRoutes.push(...dynamicRoutes);
}
if (hasApiRoutes) {
defaultRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
}
} else {
defaultRoutes.push(...allRoutes);
if (hasApiRoutes) {
defaultRoutes.push({
status: 404,
src: '^/api(/.*)?$',
});
}
}
}
if (routesResult.defaultRoutes && directory && !featHandleMiss) {
routesResult.defaultRoutes.push({
if (!featHandleMiss && directory) {
defaultRoutes.push({
src: '/(.*)',
dest: `/${directory}/$1`,
});
}
return routesResult;
return { defaultRoutes, redirectRoutes, error };
}

View File

@@ -0,0 +1,18 @@
/**
* This error should be thrown from a Builder in
* order to stop the build and print a message.
* This is necessary to avoid printing a stack trace.
*/
export class NowBuildError extends Error {
public code: string;
constructor({ message, code }: Props) {
super(message);
this.code = code;
}
}
interface Props {
message: string;
code: string;
}

View File

@@ -1,6 +1,7 @@
import { intersects } from 'semver';
import boxen from 'boxen';
import { NodeVersion } from '../types';
import { NowBuildError } from '../errors';
import debug from '../debug';
const allOptions: NodeVersion[] = [
@@ -14,87 +15,90 @@ const allOptions: NodeVersion[] = [
},
];
const supportedOptions = allOptions.filter(o => !isDiscontinued(o));
const pleaseSet =
'Please set "engines": { "node": "' +
getLatestNodeVersion().range +
'" } in your `package.json` file to upgrade to Node.js ' +
getLatestNodeVersion().major;
const upstreamProvider =
'This change is the result of a decision made by an upstream infrastructure provider (AWS).' +
'\nRead more: https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html';
// 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 function getOldestNodeVersion(): NodeVersion {
return allOptions[allOptions.length - 1];
}
export function getLatestNodeVersion(): NodeVersion {
return allOptions[0];
}
export async function getSupportedNodeVersion(
engineRange?: string,
silent?: boolean
isAuto?: boolean
): Promise<NodeVersion> {
let selection = defaultSelection;
let selection = getOldestNodeVersion();
if (!engineRange) {
if (!silent) {
debug(
'Missing `engines` in `package.json`, using default range: ' +
selection.range
);
}
} else {
if (engineRange) {
const found = allOptions.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);
});
const discontinued = isDiscontinued(selection);
if (found && !discontinued) {
if (!silent) {
debug(
'Found `engines` in `package.json`, selecting range: ' +
selection.range
);
}
} else {
throw new Error(
'Found `engines` in `package.json` with an unsupported Node.js version range: ' +
engineRange +
'\nPlease use one of the following supported ranges: ' +
JSON.stringify(supportedOptions.map(o => o.range)) +
(discontinued
? '\nThis change is the result of a decision made by an upstream infrastructure provider (AWS).' +
'\nRead more: https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html'
: '')
);
if (!found) {
const intro =
isAuto || !engineRange
? 'This project is using an invalid version of Node.js and must be changed.'
: 'Found `engines` in `package.json` with an invalid Node.js version range: ' +
engineRange;
throw new NowBuildError({
code: 'NOW_BUILD_UTILS_NODE_VERSION_INVALID',
message: intro + '\n' + pleaseSet,
});
}
}
const { range, discontinueDate } = selection;
if (discontinueDate && !isDiscontinued(selection)) {
const d = discontinueDate.toISOString().split('T')[0];
const validRanges = supportedOptions
.filter(o => !o.discontinueDate)
.map(o => o.range);
const prevTerm = process.env.TERM;
if (!prevTerm) {
// workaround for https://github.com/sindresorhus/term-size/issues/13
process.env.TERM = 'xterm';
}
if (isDiscontinued(selection)) {
const intro =
isAuto || !engineRange
? 'This project is using a discontinued version of Node.js and must be upgraded.'
: 'Found `engines` in `package.json` with a discontinued Node.js version range: ' +
engineRange;
throw new NowBuildError({
code: 'NOW_BUILD_UTILS_NODE_VERSION_DISCONTINUED',
message: intro + '\n' + pleaseSet + '\n' + upstreamProvider,
});
}
debug(
isAuto || !engineRange
? 'Using default Node.js range: ' + selection.range
: (engineRange ? 'Found' : 'Missing') +
' `engines` in `package.json`, selecting range: ' +
selection.range
);
if (selection.discontinueDate) {
const d = selection.discontinueDate.toISOString().split('T')[0];
console.warn(
boxen(
'NOTICE' +
'\n' +
`\nNode.js version ${range} has reached end-of-life.` +
`\nNode.js version ${selection.range} has reached end-of-life.` +
`\nAs a result, deployments created on or after ${d} will fail to build.` +
'\nPlease use one of the following supported `engines` in `package.json`: ' +
JSON.stringify(validRanges) +
'\nThis change is the result of a decision made by an upstream infrastructure provider (AWS).' +
'\nRead more: https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html',
'\n' +
pleaseSet +
'\n' +
upstreamProvider,
{ padding: 1 }
)
);
process.env.TERM = prevTerm;
}
return selection;
}
function isDiscontinued({ discontinueDate }: NodeVersion): boolean {
const today = new Date();
return discontinueDate !== undefined && discontinueDate <= today;
const today = Date.now();
return discontinueDate !== undefined && discontinueDate.getTime() <= today;
}

View File

@@ -7,7 +7,7 @@ import { SpawnOptions } from 'child_process';
import { deprecate } from 'util';
import { cpus } from 'os';
import { Meta, PackageJson, NodeVersion, Config } from '../types';
import { getSupportedNodeVersion } from './node-version';
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
export function spawnAsync(
command: string,
@@ -140,22 +140,30 @@ export function getSpawnOptions(
export async function getNodeVersion(
destPath: string,
minNodeVersion?: string,
config?: Config
config?: Config,
meta?: Meta
): Promise<NodeVersion> {
if (meta && meta.isDev) {
// Use the system-installed version of `node` in PATH for `now dev`
const latest = getLatestNodeVersion();
return { ...latest, runtime: 'nodejs' };
}
const { packageJson } = await scanParentDirs(destPath, true);
let range: string | undefined;
let silent = false;
let isAuto = false;
if (packageJson && packageJson.engines && packageJson.engines.node) {
range = packageJson.engines.node;
} else if (minNodeVersion) {
range = minNodeVersion;
silent = true;
isAuto = true;
} else if (config && config.nodeVersion) {
range = config.nodeVersion;
isAuto = true;
} else if (config && config.zeroConfig) {
// Use latest node version zero config detected
range = '10.x';
silent = true;
isAuto = true;
}
return getSupportedNodeVersion(range, silent);
return getSupportedNodeVersion(range, isAuto);
}
async function scanParentDirs(destPath: string, readPackageJson = false) {

View File

@@ -21,6 +21,7 @@ import {
getNodeVersion,
getSpawnOptions,
} from './fs/run-user-scripts';
import { getLatestNodeVersion } from './fs/node-version';
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
import debug from './debug';
@@ -48,6 +49,7 @@ export {
runPipInstall,
runShellScript,
getNodeVersion,
getLatestNodeVersion,
getSpawnOptions,
streamToBuffer,
shouldServe,
@@ -64,3 +66,4 @@ export { readConfigFile } from './fs/read-config-file';
export * from './schemas';
export * from './types';
export * from './errors';

View File

@@ -43,6 +43,7 @@ export interface Config {
buildCommand?: string;
devCommand?: string;
framework?: string;
nodeVersion?: string;
}
export interface Meta {

View File

@@ -1,3 +1,4 @@
import { Source, Route } from '@now/routing-utils';
import { detectBuilders, detectRoutes } from '../src';
describe('Test `detectBuilders`', () => {
@@ -700,7 +701,7 @@ describe('Test `detectBuilders`', () => {
const { defaultRoutes } = await detectRoutes(files, builders!);
expect(defaultRoutes!.length).toBe(3);
expect(defaultRoutes!.length).toBe(2);
expect((defaultRoutes![0] as any).dest).toBe('/server/team.ts');
expect((defaultRoutes![0] as any).src).toBe(
'^/server/(team\\/|team|team\\.ts)$'
@@ -709,7 +710,6 @@ describe('Test `detectBuilders`', () => {
expect((defaultRoutes![1] as any).src).toBe(
'^/server/(user\\/|user|user\\.ts)$'
);
expect((defaultRoutes![2] as any).status).toBe(404);
});
it('Custom directory for Serverless Functions + Next.js', async () => {
@@ -755,12 +755,11 @@ describe('Test `detectBuilders`', () => {
const { defaultRoutes } = await detectRoutes(files, builders!);
expect(defaultRoutes!.length).toBe(2);
expect(defaultRoutes!.length).toBe(1);
expect((defaultRoutes![0] as any).dest).toBe('/server/user.ts');
expect((defaultRoutes![0] as any).src).toBe(
'^/server/(user\\/|user|user\\.ts)$'
);
expect((defaultRoutes![1] as any).status).toBe(404);
});
it('Framework with non-package.json entrypoint', async () => {
@@ -894,7 +893,7 @@ it('Test `detectRoutes`', async () => {
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(files, builders!);
expect((defaultRoutes![2] as any).status).toBe(404);
expect((defaultRoutes![2] as any).src).toBe('/api(/.*)?$');
expect((defaultRoutes![2] as any).src).toBe('^/api(/.*)?$');
expect((defaultRoutes![3] as any).src).toBe('/(.*)');
expect((defaultRoutes![3] as any).dest).toBe('/public/$1');
expect(defaultRoutes!.length).toBe(4);
@@ -910,7 +909,7 @@ it('Test `detectRoutes`', async () => {
const { builders } = await detectBuilders(files, pkg);
const { defaultRoutes } = await detectRoutes(files, builders!);
expect((defaultRoutes![1] as any).status).toBe(404);
expect((defaultRoutes![1] as any).src).toBe('/api(/.*)?$');
expect((defaultRoutes![1] as any).src).toBe('^/api(/.*)?$');
expect(defaultRoutes!.length).toBe(2);
}
@@ -948,7 +947,7 @@ it('Test `detectRoutes`', async () => {
expect(defaultRoutes!.length).toBe(3);
expect((defaultRoutes![0] as any).src).toBe(
'^/api/([^\\/]+)(\\/|\\/index|\\/index\\.js)?$'
'^/api/([^/]+)(\\/|\\/index|\\/index\\.js)?$'
);
expect((defaultRoutes![0] as any).dest).toBe(
'/api/[date]/index.js?date=$1'
@@ -995,20 +994,6 @@ it('Test `detectRoutes`', async () => {
it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
const featHandleMiss = true;
const expectedRoutes = [
{ handle: 'miss' },
{
src: '/api/(.+)\\.\\w+',
dest: '/api/$1',
check: true,
},
{
status: 404,
src: '/api(/.*)?$',
continue: true,
},
];
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
@@ -1018,7 +1003,19 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
@@ -1067,7 +1064,29 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{
src: '^/api/([^/]+)/([^/]+)$',
dest: '/api/[endpoint]/[id]?endpoint=$1&id=$2',
check: true,
},
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
@@ -1083,7 +1102,29 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{
src: '^/api/([^/]+)/([^/]+)$',
dest: '/api/[endpoint]/[id]?endpoint=$1&id=$2',
check: true,
},
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
@@ -1105,7 +1146,24 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
@@ -1129,7 +1187,19 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
@@ -1141,7 +1211,24 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{
src: '^/api/([^/]+)(\\/|\\/index|\\/index\\.js)?$',
dest: '/api/[date]/index?date=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
@@ -1159,7 +1246,16 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{ status: 404, src: '^/api(/.*)?$', continue: true },
]);
}
{
@@ -1173,6 +1269,611 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
builders!,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)\\.\\w+$',
dest: '/api/$1',
check: true,
},
{ status: 404, src: '^/api(/.*)?$', continue: true },
]);
}
});
it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async () => {
const featHandleMiss = true;
const cleanUrls = true;
const testHeaders = (redirectRoutes: Route[] | null) => {
if (!redirectRoutes || redirectRoutes.length === 0) {
throw new Error('Expected one redirect but found none');
}
expect(redirectRoutes).toBeDefined();
expect(redirectRoutes.length).toBe(2);
};
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
// expected redirect should match inputs
const getLocation = createReplaceLocation(redirectRoutes);
expect(getLocation('/api/index')).toBe('/api');
expect(getLocation('/api/index.js')).toBe('/api');
expect(getLocation('/api/user.js')).toBe('/api/user');
expect(getLocation('/api/user.prod.js')).toBe('/api/user.prod');
expect(getLocation('/api/user/index.js')).toBe('/api/user');
expect(getLocation('/api/index.go')).toBe('/api');
expect(getLocation('/api/user.go')).toBe('/api/user');
expect(getLocation('/api/user.prod.go')).toBe('/api/user.prod');
expect(getLocation('/api/user/index.go')).toBe('/api/user');
expect(getLocation('/api/index.cpp')).toBe(null);
expect(getLocation('/api/user.cpp')).toBe(null);
expect(getLocation('/api/user.prod.cpp')).toBe(null);
expect(getLocation('/api/user/index.cpp')).toBe(null);
expect(getLocation('/api/user')).toBe(null);
expect(getLocation('/api/user/get')).toBe(null);
expect(getLocation('/apiindex')).toBe(null);
expect(getLocation('/api-index')).toBe(null);
expect(getLocation('/apiuserindex')).toBe(null);
expect(getLocation('/apiuser-index')).toBe(null);
}
{
const files = ['api/user.go', 'api/user.js'];
const { builders } = await detectBuilders(files);
const { error } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
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!,
featHandleMiss,
cleanUrls
);
expect(error!.code).toBe('conflicting_file_path');
}
{
const files = ['api/[team]/[team].js'];
const { builders } = await detectBuilders(files);
const { error } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
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!,
featHandleMiss,
cleanUrls
);
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, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)/([^/]+)$',
dest: '/api/[endpoint]/[id]?endpoint=$1&id=$2',
check: true,
},
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const files = [
'public/index.html',
'api/[endpoint].js',
'api/[endpoint]/[id].js',
];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)/([^/]+)$',
dest: '/api/[endpoint]/[id]?endpoint=$1&id=$2',
check: true,
},
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const pkg = {
scripts: {
build: 'next build',
},
framework: {
slug: 'next',
version: '9.0.0',
},
};
const files = ['public/index.html', 'api/[endpoint].js'];
const { builders } = await detectBuilders(files, pkg);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const files = ['public/index.html'];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
expect(defaultRoutes).toStrictEqual([]);
}
{
const files = ['api/date/index.js', 'api/date.js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const files = ['api/date.js', 'api/[date]/index.js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)(\\/|\\/index)?$',
dest: '/api/[date]/index?date=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
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, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{ status: 404, src: '^/api(/.*)?$', continue: true },
]);
}
{
// use a custom runtime
const functions = { 'api/user.php': { runtime: 'now-php@0.0.5' } };
const files = ['api/user.php'];
const { builders } = await detectBuilders(files, null, { functions });
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{ status: 404, src: '^/api(/.*)?$', continue: true },
]);
}
});
it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingSlash=true`', async () => {
const featHandleMiss = true;
const cleanUrls = true;
const trailingSlash = true;
const testHeaders = (redirectRoutes: Route[] | null) => {
if (!redirectRoutes || redirectRoutes.length === 0) {
throw new Error('Expected one redirect but found none');
}
expect(redirectRoutes).toBeDefined();
expect(redirectRoutes.length).toBe(2);
};
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
// expected redirect should match inputs
const getLocation = createReplaceLocation(redirectRoutes);
expect(getLocation('/api/index')).toBe('/api/');
expect(getLocation('/api/index.js')).toBe('/api/');
expect(getLocation('/api/user.js')).toBe('/api/user/');
expect(getLocation('/api/user.prod.js')).toBe('/api/user.prod/');
expect(getLocation('/api/user/index.js')).toBe('/api/user/');
expect(getLocation('/api/index.go')).toBe('/api/');
expect(getLocation('/api/user.go')).toBe('/api/user/');
expect(getLocation('/api/user.prod.go')).toBe('/api/user.prod/');
expect(getLocation('/api/user/index.go')).toBe('/api/user/');
expect(getLocation('/api/index.cpp')).toBe(null);
expect(getLocation('/api/user.cpp')).toBe(null);
expect(getLocation('/api/user.prod.cpp')).toBe(null);
expect(getLocation('/api/user/index.cpp')).toBe(null);
expect(getLocation('/api/user')).toBe(null);
expect(getLocation('/api/user/get')).toBe(null);
expect(getLocation('/apiindex')).toBe(null);
expect(getLocation('/api.index')).toBe(null);
expect(getLocation('/api.index.js')).toBe(null);
expect(getLocation('/api-index')).toBe(null);
expect(getLocation('/apiuser.index')).toBe(null);
expect(getLocation('/apiuser-index')).toBe(null);
expect(getLocation('/apiuser-index')).toBe(null);
}
{
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)/([^/]+)$',
dest: '/api/[endpoint]/[id]?endpoint=$1&id=$2',
check: true,
},
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const files = [
'public/index.html',
'api/[endpoint].js',
'api/[endpoint]/[id].js',
];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)/([^/]+)$',
dest: '/api/[endpoint]/[id]?endpoint=$1&id=$2',
check: true,
},
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const pkg = {
scripts: {
build: 'next build',
},
framework: {
slug: 'next',
version: '9.0.0',
},
};
const files = ['public/index.html', 'api/[endpoint].js'];
const { builders } = await detectBuilders(files, pkg);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)$',
dest: '/api/[endpoint]?endpoint=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const files = ['api/date/index.js', 'api/date.js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
const files = ['api/date.js', 'api/[date]/index.js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/([^/]+)(\\/|\\/index)?$',
dest: '/api/[date]/index?date=$1',
check: true,
},
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
{
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, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{ status: 404, src: '^/api(/.*)?$', continue: true },
]);
}
{
// use a custom runtime
const functions = { 'api/user.php': { runtime: 'now-php@0.0.5' } };
const files = ['api/user.php'];
const { builders } = await detectBuilders(files, null, { functions });
const { defaultRoutes, redirectRoutes } = await detectRoutes(
files,
builders!,
featHandleMiss,
cleanUrls,
trailingSlash
);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{ status: 404, src: '^/api(/.*)?$', continue: true },
]);
}
});
/**
* Create a function that will replace matched redirects
* similar to how it works with `now-proxy` in production.
*/
function createReplaceLocation(redirectRoutes: Route[] | null) {
const redirectSources = (redirectRoutes || []) as Source[];
return (filePath: string): string | null => {
for (const r of redirectSources) {
const m = new RegExp(r.src).exec(filePath);
console.log({ filePath, r, m });
if (m && r.headers) {
const match = m[1] || '';
return r.headers['Location'].replace('$1', match);
}
}
return null;
};
}

View File

@@ -125,4 +125,17 @@ describe('#detectFramework', () => {
expect(await detectFramework({ fs, frameworkList })).toBe('middleman');
});
it('Detect Scully', async () => {
const fs = new VirtualFilesystem({
'package.json': JSON.stringify({
dependencies: {
'@angular/cli': 'latest',
'@scullyio/init': 'latest',
},
}),
});
expect(await detectFramework({ fs, frameworkList })).toBe('scully');
});
});

View File

@@ -3,10 +3,8 @@ const fs = require('fs-extra');
const assert = require('assert');
const { createZip } = require('../dist/lambda');
const { glob, spawnAsync, download } = require('../');
const {
getSupportedNodeVersion,
defaultSelection,
} = require('../dist/fs/node-version');
const { getSupportedNodeVersion } = require('../dist/fs/node-version');
const { getNodeVersion, getLatestNodeVersion } = require('../dist');
it('should re-create symlinks properly', async () => {
const files = await glob('**', path.join(__dirname, 'symlinks'));
@@ -49,15 +47,31 @@ it('should create zip files with symlinks properly', async () => {
});
it('should only match supported node versions', async () => {
expect(await getSupportedNodeVersion('10.x')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('8.10.x')).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(await getSupportedNodeVersion('')).toBe(defaultSelection);
expect(await getSupportedNodeVersion(null)).toBe(defaultSelection);
expect(await getSupportedNodeVersion(undefined)).toBe(defaultSelection);
expect(await getSupportedNodeVersion('10.x', false)).toHaveProperty(
'major',
10
);
expect(await getSupportedNodeVersion('12.x', false)).toHaveProperty(
'major',
12
);
expect(getSupportedNodeVersion('8.11.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('6.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('999.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('foo', false)).rejects.toThrow();
expect(await getSupportedNodeVersion('10.x', true)).toHaveProperty(
'major',
10
);
expect(await getSupportedNodeVersion('12.x', true)).toHaveProperty(
'major',
12
);
expect(getSupportedNodeVersion('8.11.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('6.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('999.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('foo', true)).rejects.toThrow();
});
it('should match all semver ranges', async () => {
@@ -78,6 +92,47 @@ it('should match all semver ranges', async () => {
expect(await getSupportedNodeVersion('^10.5.0')).toHaveProperty('major', 10);
});
it('should select correct node version in getNodeVersion()', async () => {
expect(
await getNodeVersion('/tmp', undefined, { nodeVersion: '12.x' })
).toHaveProperty('major', 12);
expect(
await getNodeVersion('/tmp', undefined, { nodeVersion: '10.x' })
).toHaveProperty('major', 10);
expect(
await getNodeVersion('/tmp', '10.x', { nodeVersion: '12.x' })
).toHaveProperty('major', 10);
});
it('should ignore node version in now dev getNodeVersion()', async () => {
expect(
await getNodeVersion(
'/tmp',
undefined,
{ nodeVersion: '1' },
{ isDev: true }
)
).toHaveProperty('runtime', 'nodejs');
});
it('should get latest node version', async () => {
expect(await getLatestNodeVersion()).toHaveProperty('major', 12);
});
it('should throw for discontinued versions', async () => {
// Mock a future date so that Node 8 becomes discontinued
const realDateNow = Date.now.bind(global.Date);
global.Date.now = () => new Date('2020-02-14').getTime();
expect(getSupportedNodeVersion('', false)).rejects.toThrow();
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('', true)).rejects.toThrow();
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
global.Date.now = realDateNow;
});
it('should support require by path for legacy builders', () => {
const index = require('@now/build-utils');

View File

@@ -6,7 +6,7 @@
To install the latest version of Now CLI, visit [zeit.co/download](https://zeit.co/download) or run this command:
```bash
```sh
npm i -g now
```

View File

@@ -1,6 +1,6 @@
{
"name": "now",
"version": "16.7.1-canary.3",
"version": "16.7.2-canary.3",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Now",

View File

@@ -17,9 +17,7 @@ export default async function dev(
args: string[],
output: Output
) {
output.dim(
`Now CLI ${pkg.version} dev (beta) — https://zeit.co/feedback`
);
output.dim(`Now CLI ${pkg.version} dev (beta) — https://zeit.co/feedback`);
const [dir = '.'] = args;
const cwd = path.resolve(dir);

View File

@@ -18,7 +18,10 @@ import getMinFromArgs from '../util/scale/get-min-from-args';
import patchDeploymentScale from '../util/scale/patch-deployment-scale';
import waitVerifyDeploymentScale from '../util/scale/wait-verify-deployment-scale';
import { handleError } from '../util/error';
import { VerifyScaleTimeout, DeploymentTypeUnsupported } from '../util/errors-ts';
import {
VerifyScaleTimeout,
DeploymentTypeUnsupported,
} from '../util/errors-ts';
import {
DeploymentNotFound,
DeploymentPermissionDenied,
@@ -28,7 +31,7 @@ import {
InvalidMaxForScale,
InvalidMinForScale,
InvalidScaleMinMaxRelation,
NotSupportedMinScaleSlots
NotSupportedMinScaleSlots,
} from '../util/errors-ts';
import { InvalidAllForScale, InvalidRegionOrDCForScale } from '../util/errors';
import handleCertError from '../util/certs/handle-cert-error';
@@ -56,7 +59,9 @@ const help = () => {
${chalk.dim('Examples:')}
${chalk.gray('')} Enable your deployment in all datacenters (min: 0, max: auto)
${chalk.gray(
''
)} Enable your deployment in all datacenters (min: 0, max: auto)
${chalk.cyan('$ now scale my-deployment-123.now.sh all')}
@@ -87,7 +92,7 @@ export default async function main(ctx) {
argv = getArgs(ctx.argv.slice(2), {
'--verify-timeout': Number,
'--no-verify': Boolean,
'-n': '--no-verify'
'-n': '--no-verify',
});
} catch (err) {
handleError(err);
@@ -100,7 +105,10 @@ export default async function main(ctx) {
}
// Prepare the context
const { authConfig: { token }, config } = ctx;
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = argv['--debug'];
@@ -155,8 +163,7 @@ export default async function main(ctx) {
}
if (dcs instanceof InvalidRegionOrDCForScale) {
output.error(
`The value "${dcs.meta
.regionOrDC}" is not a valid region or DC identifier`
`The value "${dcs.meta.regionOrDC}" is not a valid region or DC identifier`
);
now.close();
return 1;
@@ -165,8 +172,7 @@ export default async function main(ctx) {
const min = getMinFromArgs(argv._);
if (min instanceof InvalidMinForScale) {
output.error(
`Invalid <min> parameter "${min.meta
.value}". A number or "auto" were expected`
`Invalid <min> parameter "${min.meta.value}". A number or "auto" were expected`
);
now.close();
return 1;
@@ -175,24 +181,21 @@ export default async function main(ctx) {
const max = getMaxFromArgs(argv._);
if (max instanceof InvalidMinForScale) {
output.error(
`Invalid <min> parameter "${max.meta
.value}". A number or "auto" were expected`
`Invalid <min> parameter "${max.meta.value}". A number or "auto" were expected`
);
now.close();
return 1;
}
if (max instanceof InvalidArgsForMinMaxScale) {
output.error(
`Invalid number of arguments: expected <min> ("${max.meta
.min}") and [max]`
`Invalid number of arguments: expected <min> ("${max.meta.min}") and [max]`
);
now.close();
return 1;
}
if (max instanceof InvalidMaxForScale) {
output.error(
`Invalid <max> parameter "${max.meta
.value}". A number or "auto" were expected`
`Invalid <max> parameter "${max.meta.value}". A number or "auto" were expected`
);
now.close();
return 1;
@@ -262,14 +265,15 @@ export default async function main(ctx) {
deployment.url
);
if (result instanceof ForbiddenScaleMinInstances) {
output.error(`You can't scale to more than ${result.meta.max} min instances with your current plan.`);
output.error(
`You can't scale to more than ${result.meta.max} min instances with your current plan.`
);
now.close();
return 1;
}
if (result instanceof ForbiddenScaleMaxInstances) {
output.error(
`You can't scale to more than ${result.meta
.max} max instances with your current plan.`
`You can't scale to more than ${result.meta.max} max instances with your current plan.`
);
now.close();
return 1;

View File

@@ -82,16 +82,6 @@ export default async function processDeployment({
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
);
if (!quiet) {
log(
`Synced ${pluralize(
'file',
event.payload.missing.length,
true
)} ${uploadStamp()}`
);
}
const missingSize = event.payload.missing
.map((sha: string) => event.payload.total.get(sha).data.length)
.reduce((a: number, b: number) => a + b, 0);
@@ -121,6 +111,13 @@ export default async function processDeployment({
now._host = event.payload.url;
if (!quiet) {
log(
`Synced ${pluralize(
'file',
event.payload.missing.length,
true
)} ${uploadStamp()}`
);
const version = isLegacy ? `${chalk.grey('[v1]')} ` : '';
log(`https://${event.payload.url} ${version}${deployStamp()}`);
} else {
@@ -128,7 +125,10 @@ export default async function processDeployment({
}
if (queuedSpinner === null) {
queuedSpinner = wait('Queued...');
queuedSpinner =
event.payload.readyState === 'QUEUED'
? wait('Queued...')
: wait('Building...');
}
}
@@ -200,15 +200,6 @@ export default async function processDeployment({
debug(
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
);
if (!quiet) {
log(
`Synced ${pluralize(
'file',
event.payload.missing.length,
true
)} ${uploadStamp()}`
);
}
const missingSize = event.payload.missing
.map((sha: string) => event.payload.total.get(sha).data.length)
@@ -239,6 +230,13 @@ export default async function processDeployment({
now._host = event.payload.url;
if (!quiet) {
log(
`Synced ${pluralize(
'file',
event.payload.missing.length,
true
)} ${uploadStamp()}`
);
const version = isLegacy ? `${chalk.grey('[v1]')} ` : '';
log(`${event.payload.url} ${version}${deployStamp()}`);
} else {

View File

@@ -544,10 +544,11 @@ export default class DevServer {
}
if (builders) {
const { defaultRoutes, error: routesError } = await detectRoutes(
files,
builders
);
const {
defaultRoutes,
redirectRoutes,
error: routesError,
} = await detectRoutes(files, builders);
config.builds = config.builds || [];
config.builds.push(...builders);
@@ -556,8 +557,12 @@ export default class DevServer {
this.output.error(routesError.message);
await this.exit();
} else {
config.routes = config.routes || [];
config.routes.push(...(defaultRoutes as RouteConfig[]));
const routes: RouteConfig[] = [];
const { routes: nowConfigRoutes } = config;
routes.push(...(redirectRoutes || []));
routes.push(...(nowConfigRoutes || []));
routes.push(...(defaultRoutes || []));
config.routes = routes;
}
}
}

View File

@@ -15,7 +15,7 @@ export default `.hg
.wafpicke-*
.lock-wscript
.env
.env.build
.env.*
.venv
npm-debug.log
config.gypi

View File

@@ -311,6 +311,13 @@ CMD ["node", "index.js"]`,
files: ['.gitignore', 'folder', 'index.js', 'test.html'],
}),
},
'redirects-v2': {
'now.json': JSON.stringify({
version: 2,
name: 'redirects-v2',
redirects: [{ source: `/(.*)`, destination: 'https://example.com/$1' }],
}),
},
'local-config-v2': {
[`main-${session}.html`]: '<h1>hello main</h1>',
[`test-${session}.html`]: '<h1>hello test</h1>',

View File

@@ -211,6 +211,26 @@ test('login', async t => {
t.is(typeof token, 'string');
});
test('deploy using only now.json with `redirects` defined', async t => {
const target = fixture('redirects-v2');
const { exitCode, stderr, stdout } = await execa(
binaryPath,
[target, ...defaultArgs],
{
reject: false,
}
);
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
const i = stdout.lastIndexOf('https://');
const url = stdout.slice(i);
const res = await fetch(`${url}/foo/bar`, { redirect: 'manual' });
const location = res.headers.get('location');
t.is(location, 'https://example.com/foo/bar');
});
test('deploy using --local-config flag v2', async t => {
const target = fixture('local-config-v2');
const configPath = path.join(target, 'now-test.json');

View File

@@ -1,6 +1,6 @@
{
"name": "now-client",
"version": "6.0.1-canary.0",
"version": "6.0.2-canary.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://zeit.co",
@@ -38,7 +38,7 @@
]
},
"dependencies": {
"@zeit/fetch": "5.1.0",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",
"fs-extra": "8.0.1",

View File

@@ -115,8 +115,13 @@ export async function* deploy(
if (
files.size === 1 &&
!deploymentOptions.builds &&
!deploymentOptions.routes
deploymentOptions.builds === undefined &&
deploymentOptions.routes === undefined &&
deploymentOptions.cleanUrls === undefined &&
deploymentOptions.rewrites === undefined &&
deploymentOptions.redirects === undefined &&
deploymentOptions.headers === undefined &&
deploymentOptions.trailingSlash === undefined
) {
debug(`Assigning '/' route for single file deployment`);
const filePath = Array.from(files.values())[0].names[0];

View File

@@ -153,6 +153,11 @@ export interface DeploymentOptions extends LegacyDeploymentOptions {
version?: number;
regions?: string[];
routes?: Route[];
cleanUrls?: boolean;
rewrites?: NowRewrite[];
redirects?: NowRedirect[];
headers?: NowHeader[];
trailingSlash?: boolean;
builds?: Builder[];
functions?: BuilderFunctions;
env?: Dictionary<string>;

View File

@@ -115,7 +115,8 @@ export async function* upload(
apiUrl,
userAgent,
},
clientOptions.debug
clientOptions.debug,
true
);
if (res.status === 200) {
@@ -168,8 +169,9 @@ export async function* upload(
return result;
},
{
retries: 3,
factor: 2,
retries: 5,
factor: 6,
minTimeout: 10,
}
);
});

View File

@@ -0,0 +1,6 @@
import nodeFetch from 'node-fetch';
import setupZeitFetch from '@zeit/fetch';
const zeitFetch = setupZeitFetch(nodeFetch);
export { zeitFetch, nodeFetch };

View File

@@ -1,6 +1,7 @@
import { DeploymentFile } from './hashes';
import { parse as parseUrl } from 'url';
import fetch_, { RequestInit } from 'node-fetch';
import { RequestInit } from 'node-fetch';
import { nodeFetch, zeitFetch } from './fetch';
import { join, sep } from 'path';
import qs from 'querystring';
import ignore from 'ignore';
@@ -85,7 +86,7 @@ export async function getNowIgnore(path: string | string[]): Promise<any> {
'.wafpicke-*',
'.lock-wscript',
'.env',
'.env.build',
'.env.*',
'.venv',
'npm-debug.log',
'config.gypi',
@@ -122,7 +123,8 @@ export const fetch = async (
url: string,
token: string,
opts: FetchOpts = {},
debugEnabled?: boolean
debugEnabled?: boolean,
useNodeFetch?: boolean
): Promise<any> => {
semaphore.acquire();
const debug = createDebug(debugEnabled);
@@ -152,7 +154,9 @@ export const fetch = async (
debug(`${opts.method || 'GET'} ${url}`);
time = Date.now();
const res = await fetch_(url, opts);
const res = useNodeFetch
? await nodeFetch(url, opts)
: await zeitFetch(url, opts);
debug(`DONE in ${Date.now() - time}ms: ${opts.method || 'GET'} ${url}`);
semaphore.release();

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "2.3.1",
"version": "2.3.7",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/next-js",
@@ -25,7 +25,7 @@
"@types/resolve-from": "5.0.1",
"@types/semver": "6.0.0",
"@types/yazl": "2.4.1",
"@zeit/node-file-trace": "0.4.0",
"@zeit/node-file-trace": "0.4.1",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",
"execa": "2.0.4",

View File

@@ -202,7 +202,7 @@ export const build = async ({
const pkg = await readPackageJson(entryPath);
const nextVersion = getNextVersion(pkg);
const nodeVersion = await getNodeVersion(entryPath, undefined, config);
const nodeVersion = await getNodeVersion(entryPath, undefined, config, meta);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
if (!nextVersion) {
@@ -336,12 +336,35 @@ export const build = async ({
const routesManifest = await getRoutesManifest(entryPath, realNextVersion);
const rewrites: Route[] = [];
const redirects: Route[] = [];
const nextBasePathRoute: Route[] = [];
let nextBasePath: string | undefined;
if (routesManifest) {
switch (routesManifest.version) {
case 1: {
case 1:
case 2: {
redirects.push(...convertRedirects(routesManifest.redirects));
rewrites.push(...convertRewrites(routesManifest.rewrites));
if (routesManifest.basePath && routesManifest.basePath !== '/') {
nextBasePath = routesManifest.basePath;
if (!nextBasePath.startsWith('/')) {
throw new Error(
'basePath must start with `/`. Please upgrade your `@now/next` builder and try again. Contact support if this continues to happen.'
);
}
if (nextBasePath.endsWith('/')) {
throw new Error(
'basePath must not end with `/`. Please upgrade your `@now/next` builder and try again. Contact support if this continues to happen.'
);
}
nextBasePathRoute.push({
src: `^${nextBasePath}(?:$|/(.*))$`,
dest: `/$1`,
continue: true,
});
}
break;
}
default: {
@@ -399,6 +422,9 @@ export const build = async ({
routes: [
// TODO: low priority: handle trailingSlash
// Add top level rewrite for basePath if provided
...nextBasePathRoute,
// redirects take the highest priority
...redirects,
// Before we handle static files we need to set proper caching headers
@@ -940,6 +966,9 @@ export const build = async ({
...staticDirectoryFiles,
},
routes: [
// Add top level rewrite for basePath if provided
...nextBasePathRoute,
// redirects take the highest priority
...redirects,
// Before we handle static files we need to set proper caching headers

View File

@@ -308,6 +308,7 @@ type RoutesManifestRegex = {
};
export type RoutesManifest = {
basePath: string | undefined;
redirects: (Redirect & RoutesManifestRegex)[];
rewrites: (Rewrite & RoutesManifestRegex)[];
dynamicRoutes: {
@@ -360,7 +361,8 @@ export async function getDynamicRoutes(
if (routesManifest) {
switch (routesManifest.version) {
case 1: {
case 1:
case 2: {
return routesManifest.dynamicRoutes.map(
({ page, regex }: { page: string; regex: string }) => {
return {

View File

@@ -0,0 +1,8 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
experimental: {
basePath: '/docs',
},
};

View File

@@ -0,0 +1,24 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/docs/_next/static/testing-build-id/pages/index.js",
"responseHeaders": {
"cache-control": "public,max-age=31536000,immutable"
}
},
{
"path": "/docs/",
"mustContain": "hello from index"
},
{
"path": "/docs",
"mustContain": "hello from index"
},
{
"path": "/docs/another",
"mustContain": "hello from another"
}
]
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,5 @@
const Page = () => 'hello from another';
Page.getInitialProps = () => ({ hello: 'world' });
export default Page;

View File

@@ -0,0 +1 @@
export default () => 'hello from index';

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node",
"version": "1.3.0",
"version": "1.3.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/node-js",
@@ -30,7 +30,7 @@
"@types/etag": "1.8.0",
"@types/test-listen": "1.1.0",
"@zeit/ncc": "0.20.4",
"@zeit/node-file-trace": "0.4.0",
"@zeit/node-file-trace": "0.4.1",
"content-type": "1.0.4",
"cookie": "0.4.0",
"etag": "1.8.1",

View File

@@ -71,7 +71,8 @@ async function downloadInstallAndBundle({
const nodeVersion = await getNodeVersion(
entrypointFsDirname,
undefined,
config
config,
meta
);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
await runNpmInstall(
@@ -369,16 +370,13 @@ export async function build({
});
}
// Use the system-installed version of `node` when running via `now dev`
const runtime = meta.isDev ? 'nodejs' : nodeVersion.runtime;
const lambda = await createLambda({
files: {
...preparedFiles,
...launcherFiles,
},
handler: `${LAUNCHER_FILENAME}.launcher`,
runtime,
runtime: nodeVersion.runtime,
});
return { output: lambda, watch };

View File

@@ -1,5 +1,6 @@
import { relative, basename, resolve, dirname } from 'path';
import _ts from 'typescript';
import { NowBuildError } from '@now/build-utils';
/*
* Fork of TS-Node - https://github.com/TypeStrong/ts-node
@@ -180,8 +181,8 @@ export function register(opts: Options = {}): Register {
};
function createTSError(diagnostics: ReadonlyArray<_ts.Diagnostic>) {
const diagnosticText = formatDiagnostics(diagnostics, diagnosticHost);
return new Error(diagnosticText);
const message = formatDiagnostics(diagnostics, diagnosticHost);
return new NowBuildError({ code: 'NOW_NODE_TYPESCRIPT_ERROR', message });
}
function reportTSError(

View File

@@ -7,7 +7,7 @@
}
],
"probes": [
{ "path": "/empty", "mustContain": "RANDOMNESS_PLACEHOLDER:8" },
{ "path": "/empty", "mustContain": "RANDOMNESS_PLACEHOLDER:12" },
{ "path": "/exact", "mustContain": "RANDOMNESS_PLACEHOLDER:10" },
{ "path": "/greater", "mustContain": "RANDOMNESS_PLACEHOLDER:12" },
{ "path": "/major", "mustContain": "RANDOMNESS_PLACEHOLDER:10" },

View File

@@ -1,7 +1,10 @@
{
"engines": {
"node": "10.x"
},
"dependencies": {
"chrome-aws-lambda": "1.11.1",
"lighthouse": "4.3.1",
"puppeteer-core": "1.11.0"
"chrome-aws-lambda": "1.20.4",
"lighthouse": "5.6.0",
"puppeteer-core": "1.20.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "1.0.2-canary.0",
"version": "1.1.1-canary.0",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/python",

View File

@@ -36,11 +36,10 @@ export async function downloadFilesInWorkPath({
files,
meta,
config,
}: BuildOptions) {
debug('Downloading user files...');
let downloadedFiles = await download(files, workPath, meta);
if (meta && meta.isDev) {
debug('Downloading user files...');
let downloadedFiles = await download(files, workPath, meta);
if (meta && meta.isDev) {
let base = null;
if (config && config.zeroConfig) {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/routing-utils",
"version": "1.4.1-canary.2",
"version": "1.5.0",
"description": "ZEIT Now routing utilities",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@@ -2,6 +2,7 @@
* This converts Superstatic configuration to Now.json Routes
* See https://github.com/firebase/superstatic#configuration
*/
import { isString } from 'util';
import { parse as parseUrl, format as formatUrl } from 'url';
import { pathToRegexp, compile, Key } from 'path-to-regexp';
import { Route, NowRedirect, NowRewrite, NowHeader } from './types';
@@ -22,7 +23,8 @@ export function getCleanUrls(
export function convertCleanUrls(
cleanUrls: boolean,
trailingSlash: boolean | undefined
trailingSlash?: boolean,
status = 308
): Route[] {
const routes: Route[] = [];
if (cleanUrls) {
@@ -30,25 +32,28 @@ export function convertCleanUrls(
routes.push({
src: '^/(?:(.+)/)?index(?:\\.html)?/?$',
headers: { Location: loc },
status: 308,
status,
});
routes.push({
src: '^/(.*)\\.html/?$',
headers: { Location: loc },
status: 308,
status,
});
}
return routes;
}
export function convertRedirects(redirects: NowRedirect[]): Route[] {
export function convertRedirects(
redirects: NowRedirect[],
defaultStatus = 308
): Route[] {
return redirects.map(r => {
const { src, segments } = sourceToRegex(r.source);
const loc = replaceSegments(segments, r.destination);
const route: Route = {
src,
headers: { Location: loc },
status: r.statusCode || 308,
status: r.statusCode || defaultStatus,
};
return route;
});
@@ -78,19 +83,19 @@ export function convertHeaders(headers: NowHeader[]): Route[] {
});
}
export function convertTrailingSlash(enable: boolean): Route[] {
export function convertTrailingSlash(enable: boolean, status = 308): Route[] {
const routes: Route[] = [];
if (enable) {
routes.push({
src: '^/(.*[^\\/])$',
headers: { Location: '/$1/' },
status: 308,
status,
});
} else {
routes.push({
src: '^/(.*)\\/$',
headers: { Location: '/$1' },
status: 308,
status,
});
}
return routes;
@@ -103,10 +108,6 @@ function sourceToRegex(source: string): { src: string; segments: string[] } {
return { src: r.source, segments };
}
function isString(key: any): key is string {
return typeof key === 'string';
}
function replaceSegments(segments: string[], destination: string): string {
const parsedDestination = parseUrl(destination, true);
let { pathname } = parsedDestination;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.14.1-canary.3",
"version": "0.14.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/static-builds",

View File

@@ -121,6 +121,14 @@ export const frameworks: Framework[] = [
},
],
},
{
name: 'Scully',
slug: 'scully',
dependency: '@scullyio/init',
minNodeRange: '10.x',
buildCommand: 'ng build && scully',
getOutputDirName: async () => 'dist/static',
},
{
name: 'Angular',
slug: 'angular',
@@ -328,7 +336,8 @@ export const frameworks: Framework[] = [
name: 'Nuxt.js',
slug: 'nuxtjs',
dependency: 'nuxt',
getOutputDirName: async () => 'public',
buildCommand: 'nuxt generate',
getOutputDirName: async () => 'dist',
},
{
name: 'Hugo',
@@ -342,9 +351,6 @@ export const frameworks: Framework[] = [
})
);
console.log(`trying to read file at ${dirPrefix}`);
console.log('got config', config);
return (config && config.publishDir) || 'public';
},
},
@@ -358,6 +364,18 @@ export const frameworks: Framework[] = [
return (config && config.destination) || '_site';
},
},
{
name: 'Brunch',
slug: 'brunch',
getOutputDirName: async () => 'public',
},
{
name: 'Middleman',
slug: 'middleman',
buildCommand: 'bundle exec middleman build',
devCommand: 'bundle exec middleman server -p $PORT',
getOutputDirName: async () => 'build',
},
];
export interface Framework {

View File

@@ -162,23 +162,19 @@ function getPkg(entrypoint: string, workPath: string) {
return pkg;
}
function getFramework(config: Config | null, pkg?: PackageJson | null) {
const { framework: configFramework = null, outputDirectory = null } =
config || {};
function getFramework(
config: Config | null,
pkg?: PackageJson | null
): Framework | undefined {
if (!config || !config.zeroConfig) {
return;
}
const { framework: configFramework = null } = config || {};
if (configFramework) {
const framework = frameworks.find(({ slug }) => slug === configFramework);
if (framework) {
if (!framework.getOutputDirName && outputDirectory) {
return {
...framework,
getOutputDirName(prefix: string) {
return Promise.resolve(path.join(prefix, outputDirectory));
},
};
}
return framework;
}
}
@@ -304,7 +300,8 @@ export async function build({
const nodeVersion = await getNodeVersion(
entrypointDir,
minNodeRange,
config
config,
meta
);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
@@ -420,7 +417,9 @@ export async function build({
const outputDirPrefix = path.join(workPath, path.dirname(entrypoint));
if (framework) {
const outputDirName = await framework.getOutputDirName(outputDirPrefix);
const outputDirName = config.outputDirectory
? config.outputDirectory
: await framework.getOutputDirName(outputDirPrefix);
distPath = path.join(outputDirPrefix, outputDirName);
} else if (!config || !config.distDir) {
@@ -455,7 +454,12 @@ export async function build({
if (!config.zeroConfig && entrypoint.endsWith('.sh')) {
debug(`Running build script "${entrypoint}"`);
const nodeVersion = await getNodeVersion(entrypointDir, undefined, config);
const nodeVersion = await getNodeVersion(
entrypointDir,
undefined,
config,
meta
);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
await runShellScript(path.join(workPath, entrypoint), [], spawnOpts);
validateDistDir(distPath, meta.isDev, config);

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@now/static-build",
"config": { "distDir": "out" }
}
],
"probes": [{ "path": "/", "mustContain": "output:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,9 @@
{
"private": true,
"scripts": {
"now-build": "mkdir out && echo 'output:RANDOMNESS_PLACEHOLDER' > out/index.txt"
},
"dependencies": {
"@vue/cli-service": "3.11.0"
}
}

View File

@@ -0,0 +1 @@
Should not use this file

View File

@@ -0,0 +1,5 @@
node_modules
dist
custom-output
yarn.lock
node_modules

View File

@@ -0,0 +1,5 @@
node_modules
dist
custom-output
yarn.lock
node_modules

View File

@@ -0,0 +1,14 @@
{
"builds": [
{
"use": "@now/static-build",
"src": "package.json",
"config": {
"zeroConfig": true,
"framework": "nuxtjs",
"buildCommand": "yarn generate && mv dist custom-output",
"outputDirectory": "custom-output"
}
}
]
}

View File

@@ -0,0 +1,14 @@
{
"name": "47-nuxt-with-custom-output",
"version": "1.0.0",
"main": "pages/index.js",
"scripts": {
"dev": "nuxt dev",
"generate": "nuxt generate"
},
"keywords": [],
"private": true,
"dependencies": {
"nuxt": "2.11.0"
}
}

View File

@@ -0,0 +1 @@
export default () => <h1>hello nuxt</h1>;

View File

@@ -0,0 +1,5 @@
node_modules
dist
custom-output
yarn.lock
node_modules

View File

@@ -0,0 +1,5 @@
node_modules
dist
custom-output
yarn.lock
node_modules

View File

@@ -0,0 +1,13 @@
{
"builds": [
{
"use": "@now/static-build",
"src": "package.json",
"config": {
"zeroConfig": true,
"buildCommand": "yarn generate && mv dist custom-output",
"outputDirectory": "custom-output"
}
}
]
}

View File

@@ -0,0 +1,14 @@
{
"name": "47-nuxt-with-custom-output",
"version": "1.0.0",
"main": "pages/index.js",
"scripts": {
"dev": "nuxt dev",
"generate": "nuxt generate"
},
"keywords": [],
"private": true,
"dependencies": {
"nuxt": "2.11.0"
}
}

View File

@@ -0,0 +1 @@
export default () => <h1>hello nuxt</h1>;

View File

@@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@@ -0,0 +1,46 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,27 @@
# Scully
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.21.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@@ -0,0 +1,114 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"scully": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/scully",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "scully:build"
},
"configurations": {
"production": {
"browserTarget": "scully:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "scully:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "scully:serve"
},
"configurations": {
"production": {
"devServerTarget": "scully:serve:production"
}
}
}
}
}
},
"defaultProject": "scully"
}

View File

@@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

View File

@@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/scully'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true,
});
};

View File

@@ -0,0 +1,12 @@
{
"builds": [
{
"src": "package.json",
"use": "@now/static-build",
"config": {
"zeroConfig": true
}
}
],
"probes": [{ "path": "/", "mustContain": "Scully" }]
}

View File

@@ -0,0 +1,51 @@
{
"name": "scully",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"scully": "scully",
"scully:serve": "scully serve"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.2.14",
"@angular/common": "~8.2.14",
"@angular/compiler": "~8.2.14",
"@angular/core": "~8.2.14",
"@angular/forms": "~8.2.14",
"@angular/platform-browser": "~8.2.14",
"@angular/platform-browser-dynamic": "~8.2.14",
"@angular/router": "~8.2.14",
"@scullyio/init": "0.0.9",
"@scullyio/ng-lib": "latest",
"@scullyio/scully": "latest",
"rxjs": "~6.4.0",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.803.21",
"@angular/cli": "~8.3.21",
"@angular/compiler-cli": "~8.2.14",
"@angular/language-service": "~8.2.14",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
}
}

View File

@@ -0,0 +1,4 @@
exports.config = {
projectRoot: './src/app',
routes: {},
};

View File

@@ -0,0 +1,3 @@
<div>
Scully on ZEIT Now.
</div>

View File

@@ -0,0 +1,31 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'scully'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('scully');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain(
'scully app is running!'
);
});
});

View File

@@ -0,0 +1,12 @@
import { IdleMonitorService } from '@scullyio/ng-lib';
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
constructor() {}
title = 'scully';
}

View File

@@ -0,0 +1,13 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,3 @@
export const environment = {
production: true,
};

View File

@@ -0,0 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Scully</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -0,0 +1,13 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@@ -0,0 +1,71 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/***************************************************************************************************
* SCULLY IMPORTS
*/
// tslint:disable-next-line: align
import 'zone.js/dist/task-tracking';

View File

@@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": ["src/main.ts", "src/polyfills.ts"],
"include": ["src/**/*.ts"],
"exclude": ["src/test.ts", "src/**/*.spec.ts"]
}

View File

@@ -0,0 +1,21 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": ["node_modules/@types"],
"lib": ["es2018", "dom"]
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": ["jasmine", "node"]
},
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

View File

@@ -0,0 +1,57 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warning"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [true, "attribute", "app", "camelCase"],
"component-selector": [true, "element", "app", "kebab-case"],
"import-blacklist": [true, "rxjs/Rx"],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [true, 140],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
"no-empty": false,
"no-inferrable-types": [true, "ignore-params"],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [true, "as-needed"],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [true, "single"],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": ["codelyzer"]
}

View File

@@ -4,6 +4,7 @@ const path = require('path');
describe('prepareCache', () => {
test('should cache node_modules', async () => {
const files = await prepareCache({
config: { zeroConfig: true },
workPath: path.resolve(__dirname, './cache-fixtures/default'),
entrypoint: 'index.js',
});
@@ -14,6 +15,7 @@ describe('prepareCache', () => {
test('should cache node_modules and `.cache` folder for gatsby deployments', async () => {
const files = await prepareCache({
config: { zeroConfig: true },
workPath: path.resolve(__dirname, './cache-fixtures/gatsby'),
entrypoint: 'package.json',
});

View File

@@ -1,6 +1,5 @@
const assert = require('assert');
const { createHash } = require('crypto');
const { homedir } = require('os');
const path = require('path');
const fetch = require('./fetch-retry.js');
@@ -28,7 +27,7 @@ async function nowDeploy (bodies, randomness) {
RANDOMNESS_BUILD_ENV_VAR: randomness,
},
},
name: 'test',
name: 'test2020',
files,
builds: nowJson.builds,
routes: nowJson.routes || [],
@@ -133,20 +132,12 @@ async function fetchWithAuth (url, opts = {}) {
if (!opts.headers) opts.headers = {};
if (!opts.headers.Authorization) {
const { NOW_TOKEN, CIRCLECI } = process.env;
currentCount += 1;
if (!token || currentCount === MAX_COUNT) {
currentCount = 0;
if (NOW_TOKEN) {
token = NOW_TOKEN;
} else if (CIRCLECI) {
token = await fetchTokenWithRetry(
Buffer.from(str, 'base64').toString()
);
} else {
const authJsonPath = path.join(homedir(), '.now/auth.json');
token = require(authJsonPath).token;
}
token = await fetchTokenWithRetry(
Buffer.from(str, 'base64').toString()
);
}
opts.headers.Authorization = `Bearer ${token}`;

View File

@@ -1,5 +1,8 @@
const getWritableDirectory = require('../../packages/now-build-utils/fs/get-writable-directory.js');
const glob = require('../../packages/now-build-utils/fs/glob.js');
const {
getLatestNodeVersion,
glob,
getWriteableDirectory,
} = require('@now/build-utils');
function runAnalyze(wrapper, context) {
if (wrapper.analyze) {
@@ -16,6 +19,11 @@ async function runBuildLambda(inputPath) {
const nowJson = require(nowJsonRef.fsPath);
expect(nowJson.builds.length).toBe(1);
const build = nowJson.builds[0];
if (!build.config || !build.config.nodeVersion) {
// Mimic api-deployments when a new project is created
const nodeVersion = getLatestNodeVersion().range;
build.config = { ...build.config, nodeVersion };
}
expect(build.src.includes('*')).toBeFalsy();
const entrypoint = build.src.replace(/^\//, ''); // strip leftmost slash
expect(inputFiles[entrypoint]).toBeDefined();
@@ -26,15 +34,15 @@ async function runBuildLambda(inputPath) {
const analyzeResult = runAnalyze(wrapper, {
files: inputFiles,
entrypoint,
config: build.config
config: build.config,
});
const workPath = await getWritableDirectory();
const workPath = await getWriteableDirectory();
const buildResult = await wrapper.build({
files: inputFiles,
entrypoint,
config: build.config,
workPath
workPath,
});
const { output } = buildResult;
@@ -43,7 +51,7 @@ async function runBuildLambda(inputPath) {
buildResult.output = Object.keys(output).reduce(
(result, path) => ({
...result,
[path.replace(/\\/g, '/')]: output[path]
[path.replace(/\\/g, '/')]: output[path],
}),
{}
);
@@ -52,7 +60,7 @@ async function runBuildLambda(inputPath) {
return {
analyzeResult,
buildResult,
workPath
workPath,
};
}

View File

@@ -2023,22 +2023,22 @@
dependencies:
"@zeit/dns-cached-resolve" "2.1.0"
"@zeit/fetch-retry@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@zeit/fetch-retry/-/fetch-retry-4.0.0.tgz#ad7fe06c4ceb3dcbd76c04db95b1b624ed6fcf3f"
integrity sha512-ALXnrCPpiVWha/L3Mm1klPhqmVTKmPQ2dmb5YIsSCrMBJugfhDb42kacVsvQ11vAFRE1LRaJ9Pmw16zEMvQnbw==
"@zeit/fetch-retry@^4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@zeit/fetch-retry/-/fetch-retry-4.1.0.tgz#a960e335b3ecf01f219e045a4b8819b6ef34b1f4"
integrity sha512-2/vtTzIs3/Bp5tNOmCpICCRooa+v0wL4e+3Afl4etyqZrjOmCMfKshWPSHTxLe20qfsn010qXy/hK6yqD4t0zg==
dependencies:
async-retry "^1.1.3"
async-retry "^1.3.1"
debug "^3.1.0"
"@zeit/fetch@5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@zeit/fetch/-/fetch-5.1.0.tgz#5a24c03ce690f63a81543a016e54aa59f8ca71a0"
integrity sha512-e+ZClpgyP8AlOcewSNrpJzXLjPLG+dXBnBg3vYXPYdYItj2dWaI1mRjiyBriH/U9Gt48wZJukz9Q3uhMYS4X6w==
"@zeit/fetch@5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@zeit/fetch/-/fetch-5.2.0.tgz#7a8bfa31284e33a4e3fe7940351b0d48264d167d"
integrity sha512-S4TFaT210j/lgzHjaxN2SiaMxXGeoB4hghNlhlvSqwTFSDNIqyJIDKFZQy+r3NKJOQU+RB5yGKJt8LS1pPHUrw==
dependencies:
"@types/async-retry" "1.2.1"
"@zeit/fetch-cached-dns" "1.2.0"
"@zeit/fetch-retry" "4.0.0"
"@zeit/fetch-retry" "^4.1.0"
agentkeepalive "3.4.1"
debug "3.1.0"
@@ -2076,10 +2076,10 @@
resolved "https://registry.yarnpkg.com/@zeit/ncc/-/ncc-0.20.4.tgz#00f0a25a88cac3712af4ba66561d9e281c6f05c9"
integrity sha512-fmq+F/QxPec+k/zvT7HiVpk7oiGFseS6brfT/AYqmCUp6QFRK7vZf2Ref46MImsg/g2W3g5X6SRvGRmOAvEfdA==
"@zeit/node-file-trace@0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@zeit/node-file-trace/-/node-file-trace-0.4.0.tgz#67dbd5d523cf05e7bc5bad017b8c7bab163e256c"
integrity sha512-XBpMaxmgQr27Vt1Xfpd+pOg6zNHxc6+7/tOnOkSl78EpMeKTcPhARllcjdbN1ItCSskfxkfh/zHDnaPWG7Wc8Q==
"@zeit/node-file-trace@0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@zeit/node-file-trace/-/node-file-trace-0.4.1.tgz#37cb1382fa0d31c2083b634201fdad7a01849dda"
integrity sha512-jrRdmyYzMZ9w00EoNM3dLgFD+AvERlM37QGtXn9wZ6WZMPfVa/VSd2HrdVaYUI5gg2cTcwtkJenz51UHUPfmOg==
dependencies:
acorn "^6.1.1"
acorn-stage3 "^2.0.0"
@@ -2341,9 +2341,9 @@ ansi-styles@^4.0.0:
color-convert "^2.0.1"
ansi-styles@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.0.tgz#5681f0dcf7ae5880a7841d8831c4724ed9cc0172"
integrity sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
dependencies:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"
@@ -2590,13 +2590,20 @@ async-retry@1.1.3:
dependencies:
retry "0.10.1"
async-retry@1.2.3, async-retry@^1.1.3:
async-retry@1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0"
integrity sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==
dependencies:
retry "0.12.0"
async-retry@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55"
integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==
dependencies:
retry "0.12.0"
async-sema@2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-2.1.4.tgz#3f5aa091d0a763354045ee899a5d17ffb69251af"
@@ -3468,11 +3475,16 @@ commander@2.15.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
commander@^2.20.0, commander@~2.20.0:
commander@^2.20.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
commander@~2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@~2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
@@ -5497,9 +5509,9 @@ growly@^1.3.0:
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
handlebars@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==
version "4.5.3"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==
dependencies:
neo-async "^2.6.0"
optimist "^0.6.1"
@@ -9726,7 +9738,7 @@ shellwords@^0.1.1:
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
signal-exit@3.0.2, signal-exit@TooTallNate/signal-exit#update/sighub-to-sigint-on-windows, signal-exit@^3.0.0, signal-exit@^3.0.2:
signal-exit@3.0.2, signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://codeload.github.com/TooTallNate/signal-exit/tar.gz/58088fa7f715149f8411e089a4a6e3fe6ed265ec"
@@ -10406,9 +10418,9 @@ term-size@^1.2.0:
execa "^0.7.0"
term-size@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.1.0.tgz#3aec444c07a7cf936e157c1dc224b590c3c7eef2"
integrity sha512-I42EWhJ+2aeNQawGx1VtpO0DFI9YcfuvAMNIdKyf/6sRbHJ4P+ZQ/zIT87tE+ln1ymAGcCJds4dolfSAS0AcNg==
version "2.1.1"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.1.1.tgz#f81ec25854af91a480d2f9d0c77ffcb26594ed1a"
integrity sha512-UqvQSch04R+69g4RDhrslmGvGL3ucDRX/U+snYW0Mab4uCAyKSndUksaoqlJ81QKSpRnIsuOYQCbC2ZWx2896A==
test-exclude@^5.1.0, test-exclude@^5.2.3:
version "5.2.3"
@@ -10759,11 +10771,11 @@ typescript@3.6.4:
integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==
uglify-js@^3.1.4:
version "3.6.0"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
version "3.7.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==
dependencies:
commander "~2.20.0"
commander "~2.20.3"
source-map "~0.6.1"
uid-number@0.0.6: