Compare commits

..

29 Commits

Author SHA1 Message Date
Andy Bitz
92a40db048 Publish Stable
- @now/next@2.3.1
2019-12-20 14:28:26 +01:00
Steven
502aad7c2b Publish Canary
- @now/build-utils@1.2.1-canary.2
 - now@16.7.1-canary.2
 - @now/next@2.3.1-canary.3
 - @now/python@1.0.2-canary.0
 - @now/static-build@0.14.1-canary.2
2019-12-19 13:33:02 -05:00
Steven
b49afb61a6 [now-build-utils] Add handle: miss route to zero config (#3403)
This PR adds the route `handle: miss` and a catch-all route for the api directory.

The plan is to rename files in `/api` and `/public` (in a future PR) to be extension-less and then the route is only used to rewrite the extension to the extension-less file (for example, `/api/user.go` => `/api/user`)

This reduces the routes needed for zero config (previously N routes for N files down to 1 route for N files).
2019-12-19 18:30:48 +00:00
Steven
d380902ad3 [all] Fix types for Route vs Source (#3450)
This PR deletes the incorrect `Route` type defined in `@now/build-utils` and instead relies on the correct type defined in `@now/routing-utils`. There is no change to runtime code since this is strictly a change to the typescript types. The one exception to this is I had to change the sort order for our build script so that `@now/routing-utils` is built first.

This is necessary for PR #3403
2019-12-18 23:10:26 +00:00
Andy
ffaed62094 [now-dev] Update tests with verbose logging (#3447)
Updates the tests for `now dev` to log more and make the hopefully more reliable.
2019-12-18 21:57:59 +00:00
Tommaso De Rossi
b0adeb68fe [now-python] Skip reinstalling user dependencies for now dev (#3352)
Same as #2926 but for python
Fixes #3351
2019-12-18 21:04:06 +00:00
JJ Kasper
2372832654 Publish Canary
- @now/next@2.3.1-canary.2
 - @now/routing-utils@1.4.1-canary.2
2019-12-18 13:57:42 -06:00
JJ Kasper
e6a9586b7e [now-routing-utils] Add replacing of multi-match characters (#3446)
This makes sure to replace multi-match characters used in `path-to-regexp` when converting redirects.

Fixes `/:path*/` being converted to `/$1*/` and now converts it to `/$1/`
2019-12-18 19:43:58 +00:00
Tim Neutkens
9687415eed Publish Canary
- @now/next@2.3.1-canary.1
2019-12-18 10:54:34 +01:00
JJ Kasper
49b375ed6a [now-next] Disable running next export from export-intent (#3448)
As requested by @timneutkens after investigating we're disabling auto running `next export` for the user during a build since most users who want their app exported will define `next export` in their `build` script
2019-12-18 03:01:33 +00:00
Shu Ding
906dea096e Publish Canary
- @now/frameworks@0.0.2-canary.0
 - @now/next@2.3.1-canary.0
2019-12-18 03:04:00 +08:00
Shu Ding
3225a83084 Add @now/frameworks (#3445)
* add @now/frameworks

* add newlines

* add websites

* add logos
2019-12-18 02:35:45 +08:00
JJ Kasper
87794cfcc5 [now-next] Remove node 8 test (#3426)
This removes the `node 8` firebase test since `node 8` is being deprecated and we have the same test running against `node 10` also
2019-12-17 16:08:56 +00:00
Steven
79ad0ce0c4 Publish Canary
- @now/routing-utils@1.4.1-canary.1
2019-12-17 10:24:05 -05:00
Steven
fda5987465 [now-routing-utils] Merge routes with multiple handles (#3442)
This PR adds support for `handle: miss` and `handle: hit` and really any arbitrary `handle` when merging routes from the builder and from the user.
2019-12-17 15:16:07 +00:00
Andy Bitz
5cfdd5a6b2 Publish Canary
- @now/build-utils@1.2.1-canary.1
 - now@16.7.1-canary.1
 - @now/static-build@0.14.1-canary.1
2019-12-17 15:32:03 +01:00
Andy Bitz
bd6e0f9f93 Publish Stable
- @now/next@2.3.0
2019-12-17 15:30:37 +01:00
Andy
aa8d002309 [now-cli][now-build-utils] Adjust tests for new detectors (#3443)
* Adjust README

* Fix now-dev test

* Add hugo to the PATH

* Fix Hugo build

* Add more logging

* Resolve path

* Do not use the build script as dev command

* Update yarn.lock file

* fetch with retry

* Remove sh from README

* Use Set for Hugo config files
2019-12-17 15:27:12 +01:00
Andy Bitz
42ce9aca86 Publish Canary
- @now/build-utils@1.2.1-canary.0
 - now@16.7.1-canary.0
 - @now/next@2.2.1-canary.1
 - @now/routing-utils@1.4.1-canary.0
 - @now/static-build@0.14.1-canary.0
2019-12-17 00:25:35 +01:00
Andy
156f596189 [now-cli][now-static-build] Add support for buildCommand, devCommand and outputDirectory (#3434)
* Revert "Revert "[now-static-build] Add support `buildCommand`, `devCommand` and `outputDirectory` (#3422)" (#3428)"

This reverts commit f7b4dd4458.

* Handle generic node projects with /public

* Remove .only

* Ensure node_modules/.bin is also available during `now dev`

* Remove config log

* Adjust test

* Fix integration tests

* Fix public check

* Remove build + public

* Remove _scan

* Remove any casting

* Use `spawnCommand` for dev

* Remove unused import

* Remove cross-spawn

* Fix null config

* Fix build

* Only do a single Buffer.concat
2019-12-17 00:21:58 +01:00
JJ Kasper
8acfd5bf71 [now-next] Add testing for export handling (#3437)
Follow up to #3431 adding tests for this behavior

Tests to add:
- [x] custom routes: redirects
- [x] custom routes: rewrites
- [x] confirming each test is actually a `next export` deploy somehow
2019-12-16 22:47:36 +00:00
Andy
76c99ebb28 [now-build-utils] Restore previous detectors (#3441) 2019-12-16 23:20:48 +01:00
Alejandro Pacheco
5fb119b99c [now-cli] Changed scale error message (#3394)
The error message does not give a clear reason of why it failed
2019-12-16 21:44:40 +00:00
Steven
99b766d9cb [now-routing-utils] Add validation for handle:hit and handle:miss (#3438)
This PR adds initial support for `handle: hit` and `handle: miss` routes.
2019-12-16 20:56:48 +00:00
Andy
c207cf9b40 [now-cli] Change --team warning style (#3439)
* [now-cli] Change warning style

* Adjust test

* Fix screen error
2019-12-16 19:38:43 +01:00
Andy
dd00ac4621 [now-cli] Change automatic version detection message (#3440)
* [now-cli] Change automatic version detection message

* Fix unit test
2019-12-16 19:16:23 +01:00
Allen Hai
3d18a067a0 remove unused vars from builder-cache file (#3411) 2019-12-16 09:03:58 -05:00
Tim Neutkens
c48571a799 Publish Canary
- @now/next@2.2.1-canary.0
2019-12-16 12:49:02 +01:00
Joe Haddad
6eeb6983d9 [now-next] Support for next export (#3431)
* Add Support for `next export`

* Add some test cases

* tests require canary next.js

* bump

* fix test cases

* Update packages/now-next/src/index.ts

Co-Authored-By: Joe Haddad <joe.haddad@zeit.co>

* Update packages/now-next/src/index.ts

Co-Authored-By: Joe Haddad <joe.haddad@zeit.co>
2019-12-16 12:46:39 +01:00
120 changed files with 3446 additions and 830 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ packages/now-cli/test/dev/fixtures/08-hugo/hugo
packages/now-cli/test/dev/fixtures/**/dist
packages/now-cli/test/dev/fixtures/**/public
packages/now-cli/test/fixtures/integration
.DS_Store

View File

@@ -0,0 +1,249 @@
[
{
"name": "Next.js",
"slug": "nextjs",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/next.svg",
"tagline": "Next.js makes you productive with React instantly — whether you want to build static or dynamic sites. ",
"website": "https://nextjs.org"
},
{
"name": "Gatsby",
"slug": "gatsby",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/gatsby.svg",
"tagline": "Gatsby helps developers build blazing fast websites and apps with React.",
"website": "https://gatsbyjs.org"
},
{
"name": "Nuxt.js",
"slug": "nuxtjs",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/nuxt.svg",
"tagline": "Nuxt.js is the web comprehensive framework that lets you dream big with Vue.js.",
"website": "https://nuxtjs.org"
},
{
"name": "Create-React-App",
"slug": "create-react-app",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/react.svg",
"tagline": "Create React App allows you to get going with React in no time.",
"website": "https://create-react-app.dev"
},
{
"name": "Svelte",
"slug": "svelte",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/svelte.svg",
"tagline": "Svelte lets you write high performance reactive apps with significantly less boilerplate. ",
"website": "https://svelte.dev"
},
{
"name": "Vue.js",
"slug": "vue",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/vue.svg",
"tagline": "Vue.js is a versatile JavaScript framework that is as approachable as it is performant.",
"website": "https://vuejs.org"
},
{
"name": "Angular",
"slug": "angular",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/angular.svg",
"tagline": "Angular is a TypeScript-based cross-platform framework from Google.",
"website": "https://angular.io"
},
{
"name": "Hugo",
"slug": "hugo",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/hugo.svg",
"tagline": "Hugo is the worlds fastest framework for building websites, written in Go.",
"website": "https://gohugo.io"
},
{
"name": "Sapper",
"slug": "sapper",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/svelte.svg",
"tagline": "Sapper is a framework for building high-performance universal web apps with Svelte.",
"website": "https://sapper.svelte.dev"
},
{
"name": "UmiJS",
"slug": "umijs",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/umi.svg",
"tagline": "UmiJS is an extensible enterprise-level React application framework.",
"website": "https://umijs.org"
},
{
"name": "Vanilla",
"slug": "vanilla",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/vanilla.svg",
"tagline": "Love the original way of making websites?"
},
{
"name": "Storybook",
"slug": "storybook",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/storybook.svg",
"tagline": "Storybook is an open source tool for developing UI components in isolation for React, Vue, and Angular.",
"website": "https://storybook.js.org"
},
{
"name": "Preact",
"slug": "preact",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/preact.svg",
"tagline": "Preact is a fast 3kB alternative to React with the same modern API.",
"website": "https://preactjs.com"
},
{
"name": "Docz",
"slug": "docz",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/docz.svg",
"tagline": "Docz makes it easy to write and publish beautiful interactive documentation for your code.",
"website": "https://docz.site"
},
{
"name": "mdx-deck",
"slug": "mdx-deck",
"tagline": "MDX Deck allows you to swiftly create React MDX-based presentation decks.",
"website": "https://github.com/jxnblk/mdx-deck"
},
{
"name": "Gridsome",
"slug": "gridsome",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/gridsome.svg",
"tagline": "Gridsome is a Vue.js-powered framework for building websites & apps that are fast by default."
},
{
"name": "Aurelia",
"slug": "aurelia",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/aurelia.svg",
"tagline": "Aurelia is an all-in-one framework for building web, desktop, and mobile applications."
},
{
"name": "Ember",
"slug": "ember",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/ember.svg",
"tagline": "Ember.js helps webapp developers be more productive out of the box."
},
{
"name": "Docusaurus",
"slug": "docusaurus",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/docusaurus.svg",
"tagline": "Docusaurus makes it easy to maintain Open Source documentation websites."
},
{
"name": "VuePress",
"slug": "vuepress",
"tagline": "VuePress is the performant way to create static sites with Vue.js."
},
{
"name": "Charge.js",
"slug": "charge",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/chargejs.svg",
"tagline": "Charge is an opinionated, zero-config static site generator written in JavaScript."
},
{
"name": "Riot.js",
"slug": "riot",
"tagline": "Riot.js lets you build user interfaces with custom tags using simple and enjoyable syntax."
},
{
"name": "Jekyll",
"slug": "jekyll",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/jekyll.svg",
"tagline": "Jekyll makes it super easy to transform your plain text into static websites and blogs."
},
{
"name": "Marko.js",
"slug": "marko",
"tagline": "Marko is a super fast UI library that makes building web apps fun."
},
{
"name": "Hexo",
"slug": "hexo",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/hexo.svg",
"tagline": "Hexo is a fast, simple & powerful blog framework powered by Node.js."
},
{
"name": "Mithril.js",
"slug": "mithril",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/mithriljs.svg",
"tagline": "Mithril is a lightweight modern web framework for that makes it easy to build SPAs."
},
{
"name": "Metalsmith",
"slug": "metalsmith",
"tagline": "Metalsmith is an extremely simple, extendable static site generator."
},
{
"name": "HyperApp",
"slug": "hyperapp",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/hyperapp.svg",
"tagline": "HyperApp is a low-footprint framework for building web interfaces without a learning curve."
},
{
"name": "Polymer",
"slug": "polymer",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/polymer.svg",
"tagline": "Polymer is an open-source webapps library from Google, for building using Web Components."
},
{
"name": "Brunch",
"slug": "brunch",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/brunch.svg",
"tagline": "Brunch is a fast and simple webapp build tool with seamless incremental compilation for rapid development."
},
{
"name": "Saber",
"slug": "saber",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/saber.svg",
"tagline": "Saber is a framework for building static sites in Vue.js that supports data from any source."
},
{
"name": "Eleventy",
"slug": "eleventy",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/eleventy.svg",
"tagline": "11ty is a simpler static site generator written in JavaScript, created to be an alternative to Jekyll."
},
{
"name": "Zola",
"slug": "zola",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/zola.svg",
"tagline": "Zola is a one-stop static site engine for all of your static needs. "
},
{
"name": "Pelican",
"slug": "pelican",
"tagline": "Pelican is a versatile static site generator, written in Python."
},
{
"name": "MkDocs",
"slug": "mkdocs",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/mkdocs.svg",
"tagline": "MkDocs is a fast, simple and downright gorgeous static site generator that's geared towards building project documentation."
},
{
"name": "Middleman",
"slug": "middleman",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/middleman.svg",
"tagline": "Middleman is a static site generator that uses all the shortcuts and tools in modern web development."
},
{
"name": "Assemble",
"slug": "assemble",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/assemble.svg",
"tagline": "A static site generator for Grunt.js and Yeoman, Assemble makes it dead simple to build modular sites and blogs."
},
{
"name": "Ionic React",
"slug": "ionic-react",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/ionic-react.svg",
"tagline": "Ionic React allows you to build mobile PWAs with React and the Ionic Framework."
},
{
"name": "Stencil",
"slug": "stencil",
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/stencil.svg",
"tagline": "Stencil is a powerful toolchain for building Progressive Web Apps and Design Systems."
},
{
"name": "Foundation",
"slug": "foundation",
"tagline": "Foundation is the most advanced responsive front-end framework in the world."
}
]

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M24 1L3 8.636l3.203 28.313L24 47l17.797-10.051L45 8.636 24 1z" fill="#DA0B36"/><path d="M24 1v5.106-.023V47l17.797-10.051L45 8.636 24 1z" fill="#C10933"/><path d="M24.022 6L11 36h4.855l2.618-6.713h11.054L32.145 36H37L24.022 6zm3.804 19.15H20.22l3.803-9.403 3.804 9.402z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 374 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 52 48">
<path fill="#111" stroke="#190000" stroke-linecap="square" stroke-miterlimit="10" stroke-width=".5" d="M22.32 4.48h7.5l16.5 38.97h-8.55l-3.53-8.9H17.71l-3.63 8.75h-8.4L22.33 4.48h0zm3.53 9.68c.05.15 5.45 13.13 5.45 13.13H20.55s5.27-13.24 5.3-13.13z"/>
<path fill="#fff" stroke="#190000" stroke-linecap="square" stroke-miterlimit="10" stroke-width=".5" d="M25.77 14l5.44 13.13H20.47S25.73 13.9 25.77 14z"/>
</svg>

After

Width:  |  Height:  |  Size: 490 B

View File

@@ -0,0 +1,61 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<path fill="url(#paint0_linear)" d="M31.48 8.75l-4.44 2.99-4.58-6.9 4.44-2.99 4.58 6.9z"/>
<path fill="url(#paint1_linear)" d="M35.09 29.48l7.58 11.45L33.64 47l-7.6-11.45-1.31-2 9.03-6.06 1.33 2z"/>
<path fill="url(#paint2_linear)" d="M23.7 37.13l1.67 2.52-6.87 4.62-3-4.52 1.5-1 5.38-3.62 1.32 2z"/>
<path fill="url(#paint3_linear)" d="M38.84 24.07l1.93-1.3 3 4.52-4.44 2.99-1.67-2.52 2.5-1.7-1.32-1.99zm-1.18 3.69l-1.33-2 2.51-1.69 1.33 2-2.51 1.69z"/>
<path fill="url(#paint4_linear)" d="M7.7 24.72l-1.5 1.01-4.57-6.9 6.88-4.62 3.2 4.84-5.37 3.6 5.38-3.6 1.36 2.06-5.37 3.61z"/>
<path fill="url(#paint5_linear)" d="M24.47 13.46l-9.04 6.07-1.37-2.06L6.55 6.13 15.58.06 23.1 11.4l1.37 2.06z"/>
<path fill="url(#paint6_linear)" d="M29.55 10.05l-2.51 1.69-1.37-2.07-3.2-4.83 4.43-2.99 4.58 6.9-1.93 1.3z"/>
<path fill="#714896" d="M18.33 40.74l-1.33-2 5.38-3.6 1.32 1.99-5.37 3.61z"/>
<path fill="#6F4795" d="M37.66 27.76l-1.33-2 2.51-1.69 1.33 2-2.51 1.69z"/>
<path fill="#88519F" d="M7.7 24.72l-1.36-2.06 5.38-3.61 1.36 2.06-5.37 3.61z"/>
<path fill="#85509E" d="M27.04 11.74l-1.37-2.07L28.18 8l1.37 2.06-2.51 1.69z"/>
<path fill="#8D166A" d="M35.09 29.48l-9.04 6.07-1.32-2 9.03-6.06 1.33 2z"/>
<path fill="#A70D6F" d="M23.1 11.4l1.37 2.06-9.04 6.07-1.37-2.06 9.04-6.07z"/>
<path fill="#9E61AD" d="M5.14 9.75l1.71 2.57-2.56 1.72-1.7-2.57 2.55-1.72z"/>
<path fill="#8053A3" d="M14.36 40.6l1.7 2.57-2.55 1.72-1.71-2.57 2.56-1.72z"/>
<path fill="url(#paint7_linear)" d="M7.63 43.19L.15 31.8l40-26.93 7.84 11.2L7.63 43.19z"/>
<defs>
<linearGradient id="paint0_linear" x1="-12.53" x2="47.68" y1="-9.38" y2="33.11" gradientUnits="userSpaceOnUse">
<stop stop-color="#C06FBB"/>
<stop offset="1" stop-color="#6E4D9B"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="37.82" x2="4.33" y1="43" y2="6.94" gradientUnits="userSpaceOnUse">
<stop stop-color="#6E4D9B"/>
<stop offset=".14" stop-color="#77327A"/>
<stop offset=".29" stop-color="#B31777"/>
<stop offset=".84" stop-color="#CD0F7E"/>
<stop offset="1" stop-color="#ED2C89"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="-7.42" x2="35.06" y1="-28.24" y2="47.35" gradientUnits="userSpaceOnUse">
<stop stop-color="#C06FBB"/>
<stop offset="1" stop-color="#6E4D9B"/>
</linearGradient>
<linearGradient id="paint3_linear" x1="-24.72" x2="44.39" y1="-12.24" y2="43.73" gradientUnits="userSpaceOnUse">
<stop stop-color="#C06FBB"/>
<stop offset="1" stop-color="#6E4D9B"/>
</linearGradient>
<linearGradient id="paint4_linear" x1="-14.41" x2="49.9" y1="-12.4" y2="44.01" gradientUnits="userSpaceOnUse">
<stop stop-color="#C06FBB"/>
<stop offset="1" stop-color="#6E4D9B"/>
</linearGradient>
<linearGradient id="paint5_linear" x1="40.79" x2="7.22" y1="43.89" y2="7.61" gradientUnits="userSpaceOnUse">
<stop stop-color="#6E4D9B"/>
<stop offset=".14" stop-color="#77327A"/>
<stop offset=".29" stop-color="#B31777"/>
<stop offset=".84" stop-color="#CD0F7E"/>
<stop offset="1" stop-color="#ED2C89"/>
</linearGradient>
<linearGradient id="paint6_linear" x1="-12.52" x2="48.68" y1="-11.86" y2="40.25" gradientUnits="userSpaceOnUse">
<stop stop-color="#C06FBB"/>
<stop offset="1" stop-color="#6E4D9B"/>
</linearGradient>
<linearGradient id="paint7_linear" x1="5.65" x2="34.95" y1="39.23" y2="2.75" gradientUnits="userSpaceOnUse">
<stop stop-color="#6E4D9B"/>
<stop offset=".14" stop-color="#77327A"/>
<stop offset=".53" stop-color="#B31777"/>
<stop offset=".79" stop-color="#CD0F7E"/>
<stop offset="1" stop-color="#ED2C89"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="49" height="48" fill="none">
<path fill="#3F894A" d="M14.52 35.93a3.39 3.39 0 0 0-1.14-2.41 4.65 4.65 0 0 0-.1-.1l-.02-.01a7.6 7.6 0 0 1-2.14-5.4c0-3.08 1.37-11.87 1.67-11.87.23 0 .23 4.24.08 8.22-.1 2.77.3 3.86.96 3.86.88 0 1.02-2.79 1.11-5.92.08-2.57.3-6.14.48-6.16.28 0 .51 3.55.59 6.13.1 3.2.32 5.87 1.05 5.87.77 0 1-1.23.95-3.97-.07-3.34-.07-8.03.17-8.03.3 0 1.3 3.09 1.54 11.88a7.36 7.36 0 0 1-2.14 5.39h-.01l-.11.1a3.39 3.39 0 0 0-1.14 2.42 62.5 62.5 0 0 0-.13 4.62l6.78 6.78c.1-2.95.08-5.74-.05-7.84-.05-.79-.17-1.7-.3-2.43-.05-.23-.2-.44-.2-.63v-25.6c0-2.18 2.05-3.2 3.93 1.58 1.88 4.8 1.35 21.84 1.35 23.05 0 .26-.22.56-.59.86l-.4.29c-1.12.9-1.28 1.95-1.34 2.88-.14 2.26-.25 5.31-.28 8.51l9.34-9.34c-.02-1.1-.05-2.08-.1-2.9-.02-.32-.1-.64-.16-.98-.16-.95-1.35-1.74-2.03-2.43-1.33-1.33-2.19-3.58-2.19-6.11 0-4.11 2.28-9.32 5.1-9.32 2.8 0 5.08 5.2 5.08 9.32 0 2.53-.85 4.78-2.18 6.11-.69.7-1.87 1.48-2.04 2.43-.06.34-.14.66-.16.98l-.07 1.65 13.05-13.05L24.36 0 0 24.36 14.64 39c-.02-1.16-.06-2.2-.12-3.07z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<path fill="url(#paint0_linear)" fill-rule="evenodd" d="M17.1 48l-16-9.4a1.87 1.87 0 0 1-.92-1.61l-.1-25.7A1.89 1.89 0 0 1 1 9.64L17.32.16l7.19 6.76 6.1-6.59 16.2 9.53a1.87 1.87 0 0 1 .93 1.6l.1 25.71a1.89 1.89 0 0 1-.92 1.63l-15.7 9.13-7.52-7.07-6.6 7.13V48z" clip-rule="evenodd" opacity=".9"/>
<path fill="url(#paint1_linear)" fill-rule="evenodd" d="M44.44 13.39l.1 21.59a1.89 1.89 0 0 1-.92 1.63L25.27 47.29a1.85 1.85 0 0 1-1.87 0L4.9 36.38a1.87 1.87 0 0 1-.92-1.6l-.11-21.6a1.89 1.89 0 0 1 .93-1.63L23.15.88a1.85 1.85 0 0 1 1.87.01l18.5 10.89a1.87 1.87 0 0 1 .92 1.6v.01z" clip-rule="evenodd"/>
<path fill="#fff" d="M32.14 19.09l-7.1 1.92.1-10.96-9.5 19.41 7.09-1.92-.11 10.96 9.52-19.41z"/>
<defs>
<linearGradient id="paint0_linear" x1="23.17" x2="31.2" y1="-3.24" y2="50.11" gradientUnits="userSpaceOnUse">
<stop stop-color="#FE4386"/>
<stop offset="1" stop-color="#FFBB94"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="24.59" x2="28.79" y1="2.21" y2="47.42" gradientUnits="userSpaceOnUse">
<stop stop-color="#F8016B"/>
<stop offset="1" stop-color="#FC716A"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="53" height="42" fill="none">
<path fill="#E2D9C5" d="M17.92 4.81h31.96A4.3 4.3 0 0 0 45.52.44H13.56a4.3 4.3 0 0 1 4.36 4.37z"/>
<path fill="#F2EBDA" d="M41.14 4.76A4.34 4.34 0 0 1 45.5.44H13.55a4.34 4.34 0 0 0-4.36 4.32v35.49h27.6c2.4 0 4.35-1.93 4.35-4.32V4.76z"/>
<path fill="#E2D9C5" d="M40.44.44a4.36 4.36 0 0 0-4.37 4.35v31.38c0 2.4-1.96 4.35-4.38 4.35H37a4.36 4.36 0 0 0 4.37-4.35V4.8c0-2.4 1.96-4.35 4.38-4.35h-5.31z"/>
<path fill="#E2D9C5" d="M32.53 35.92H.5c.03 2.68 1.97 4.6 4.37 4.6H36.9c-2.4 0-4.34-1.92-4.37-4.6z"/>
<path fill="#DDA064" d="M30.19 28c.88-2.74 1.68-5.53 3.56-7.67 4.1-4.66 12.34-6.03 18.31-5.88a53 53 0 0 0-5.38 5.12c-2.12 2.28-6.74 5.3-8.96 5.99-3.46 1.06-5.06 1.24-7.53 2.44z"/>
<path fill="#B57947" d="M30.19 28c.88-2.74 15.9-13.71 21.87-13.56a53.02 53.02 0 0 0-5.38 5.12c-2.12 2.28-6.74 5.32-8.96 6-3.46 1.06-5.06 1.23-7.53 2.44z"/>
<path fill="#1F2D3D" d="M38.06 8.1c0-.37-.34-.66-.77-.66H20.9c-.42 0-.76.3-.76.65 0 .37.34.66.77.66h16.4c.42 0 .76-.3.76-.66zM13.94 8.75h2.75c.45 0 .81-.3.81-.66 0-.36-.36-.65-.81-.65h-2.75c-.45 0-.81.3-.81.65 0 .37.36.66.81.66zM13.9 31.06c-.43 0-.77.3-.77.66 0 .36.34.66.77.66h8.08c.42 0 .77-.3.77-.66 0-.36-.35-.66-.77-.66H13.9zM26.25 24.72c0-.36-.35-.66-.79-.66H13.91c-.43 0-.78.3-.78.66 0 .36.35.66.78.66h11.55c.44 0 .79-.3.79-.66zM31.94 16.84c0-.36-.36-.65-.8-.65H13.93c-.44 0-.8.3-.8.65 0 .37.36.66.8.66h17.23c.43 0 .79-.3.79-.66z"/>
<path fill="#1F2D3D" d="M41.3 26.62a.77.77 0 0 0-.76.78v9.43c0 2-1.6 3.61-3.57 3.61h-.1c-2 0-3.54-1.66-3.56-3.88a.77.77 0 0 0-.77-.77H10.3V5.17c0-2 1.6-3.61 3.57-3.61h28.12l-.06.07-.08.09-.09.1-.07.08-.08.1-.07.1-.07.1-.07.1-.07.1-.06.1-.07.12-.06.1-.05.11-.06.12-.05.1c-.01.05-.03.08-.05.12l-.04.12-.05.12-.04.12-.04.11-.03.13-.04.12-.03.13-.02.12-.03.13-.02.12-.01.15-.02.1-.01.17v.1l-.01.26v6.91c0 .43.34.77.76.77.43 0 .77-.34.77-.77V6.2h7.91c.2 0 .4-.09.55-.23a.78.78 0 0 0 .22-.56C50.72 2.33 48.53 0 45.64 0H13.87a5.14 5.14 0 0 0-5.1 5.17v30.62h-8c-.2 0-.4.09-.55.23a.78.78 0 0 0-.22.56C.03 39.67 2.22 42 5.1 42h31.87c2.81 0 5.1-2.32 5.1-5.17V27.4a.77.77 0 0 0-.77-.78zm.81-21.99l.01-.07.02-.1.02-.07a3.53 3.53 0 0 1 .2-.65l.04-.07.04-.09.03-.06.04-.09.02-.03a3.72 3.72 0 0 1 .32-.48l.02-.02.08-.1.02-.02.1-.1v-.02l.1-.1.02-.01.1-.1h.01a3.46 3.46 0 0 1 1.85-.85v-.01l.16-.02H45.48l.08-.01h.09c1.76 0 3.16 1.28 3.49 3.09H42.1v-.02zm-37 35.81c-1.77 0-3.17-1.28-3.5-3.09h30.22a6 6 0 0 0 .03.24l.02.08a6 6 0 0 0 .03.15l.02.1.03.14.03.1a6.14 6.14 0 0 0 .18.56l.04.1.05.12.04.09.06.12.04.08.08.15.03.05.11.19c0 .02.02.03.03.04l.1.15a4.63 4.63 0 0 0 .13.18l.04.07.08.1.06.07a5.16 5.16 0 0 0 .15.17l.04.04H5.1z"/>
<path fill="#1F2D3D" d="M52.94 14.35v-.03a.77.77 0 0 0-.04-.21v-.01-.01a.77.77 0 0 0-.05-.1v-.01a.78.78 0 0 0-.06-.08l-.01-.02a.78.78 0 0 0-.07-.07l-.01-.02a.8.8 0 0 0-.17-.12l-.02-.01a.79.79 0 0 0-.33-.08c-6.56-.17-15.11 1.4-19.44 6.29-1.89 2.14-2.76 4.86-3.6 7.49l-.22.64v.03l-1.33 4.67a.78.78 0 0 0 .76.99c.34 0 .65-.23.75-.57l1.23-4.33c1.5-.7 2.71-1.02 4.34-1.46.85-.23 1.81-.5 2.97-.85 2.52-.76 7.34-3.97 9.55-6.35a53.37 53.37 0 0 1 5.46-5.18.78.78 0 0 0 .15-.16l.02-.02a.77.77 0 0 0 .11-.32v-.08-.02zm-19.02 6.54c1.83-2.06 4.62-3.62 8.31-4.63 1-.27 2.04-.49 3.12-.67a60.73 60.73 0 0 0-6.83 3.95 62.64 62.64 0 0 0-6.81 5.16c.56-1.4 1.23-2.7 2.21-3.8zm12.12-1.82c-2.16 2.32-6.82 5.3-8.86 5.93-1.13.34-2.08.6-2.92.83-.75.2-1.44.38-2.12.6.3-.29.66-.61 1.08-.98 1.7-1.46 3.88-3.1 6.17-4.62 3.95-2.62 7.53-4.46 10.16-5.26a60.6 60.6 0 0 0-3.5 3.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M42.857 24h-12v3.428h8.229c-1.2 5.143-4.972 9.43-9.943 11.143L9.429 18.857c2.057-6 7.885-10.286 14.571-10.286 5.143 0 9.771 2.572 12.686 6.515l2.571-2.229C35.83 8.23 30.343 5.143 24 5.143c-8.914 0-16.457 6.343-18.343 14.743l22.629 22.628c8.228-2.057 14.571-9.6 14.571-18.514zm-37.714.171c0 4.8 1.886 9.429 5.486 13.029 3.6 3.6 8.4 5.486 13.028 5.486L5.143 24.17z" fill="#fff"/><path d="M24 0C10.8 0 0 10.8 0 24s10.8 24 24 24 24-10.8 24-24S37.2 0 24 0zM10.629 37.371c-3.6-3.6-5.486-8.4-5.486-13.028l18.686 18.514c-4.8-.171-9.6-1.886-13.2-5.486zm17.485 4.972L5.657 19.886C7.543 11.486 15.086 5.143 24 5.143c6.343 0 11.829 3.086 15.257 7.714l-2.571 2.229C33.77 11.143 29.143 8.57 24 8.57c-6.686 0-12.343 4.286-14.571 10.286l19.714 19.714c4.971-1.714 8.743-6 9.943-11.142h-8.229V24h12c0 8.914-6.343 16.457-14.743 18.343z" fill="#639"/></svg>

After

Width:  |  Height:  |  Size: 921 B

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M43.395 19.862c2.308-.11 4.35 1.68 4.55 3.99.863 11.174-9.364 23.692-23.775 24.092C12.025 48.244.013 38.521.013 23.82c0-2.317 1.915-4.195 4.225-4.195a4.19 4.19 0 0 1 4.184 4.195c0 9.67 7.776 15.928 15.542 15.737 9.56-.266 15.776-8.544 15.453-15.303a4.19 4.19 0 0 1 3.978-4.392z" fill="url(#paint0_linear)"/><path d="M32.944 24.145c0-2.372 1.935-4.295 4.321-4.295h6.298c2.387 0 4.382 1.923 4.382 4.295s-1.995 4.295-4.382 4.295h-6.298c-2.386 0-4.321-1.923-4.321-4.295zm-13.215.006a4.297 4.297 0 0 1 4.291-4.301 4.297 4.297 0 0 1 4.292 4.301 4.297 4.297 0 0 1-4.292 4.301 4.297 4.297 0 0 1-4.291-4.301z" fill="#00A672"/><path fill-rule="evenodd" clip-rule="evenodd" d="M28.193 4.055a4.19 4.19 0 0 1-4.006 4.365c-9.966.415-16.05 8.248-15.753 15.685.092 2.315-1.668 4.267-3.976 4.36-2.309.092-4.34-1.777-4.431-4.092C-.456 12.253 9.635.273 23.84.037a4.188 4.188 0 0 1 4.353 4.018z" fill="#00A672"/><defs><linearGradient id="paint0_linear" x1="24.005" y1="19.625" x2="24.005" y2="47.951" gradientUnits="userSpaceOnUse"><stop stop-color="#00583E"/><stop offset="1" stop-color="#00835C"/></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M23.829.005l-20.735 12 .002 23.964L23.878 47.92l20.736-12V11.97L23.829.004z" fill="#0E83CD"/><path d="M14.101 34.338l.002-10.362.002-10.363 1.882-1.101 1.804 1.067.019 4.235.02 4.234h12.05l.019-4.231.02-4.232 1.83-1.059 1.857 1.087V23.97l-.002 10.355-1.837 1.027-1.849-1.04-.02-4.21-.019-4.212H17.83l-.019 4.21.006 4.23-1.848 1.085-1.866-1.076z" fill="#fff"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(3)" d="M0 0H41.645V48H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 591 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<rect width="48" height="48" fill="#0080FF" rx="24"/>
<path fill="#fff" d="M18.5386 21.6899h7.8153l1.8771 3.8487h-9.6924v9.6902h-3.8481v-9.6902H5v-3.8487h9.6905V12h3.8481v9.6899zm24.1057 13.5389h-4.5342L26.7809 12h4.5341l11.3293 23.2288z"/>
</svg>

After

Width:  |  Height:  |  Size: 328 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 48 48">
<rect width="48" height="48" fill="#4180FC" rx="24"/>
<path fill="#fff" fill-rule="evenodd" d="M38.4327 17.0558l.1306.3047C39.5211 19.4721 40 21.6925 40 24c0 8.8163-7.1837 16-16 16S8 32.8163 8 24 15.1837 8 24 8c2.5905 0 5.0503.6095 7.3143 1.7415l.3047.1524-.2612.2177c-.653.5224-1.1537 1.1755-1.4802 1.9374l-.0871.2177-.1959-.0871C27.8313 11.3524 25.9592 10.917 24 10.917c-7.2272 0-13.083 5.8558-13.083 13.083S16.7728 37.083 24 37.083 37.083 31.2054 37.083 24c0-1.7197-.3265-3.4177-1.0014-5.0068l-.087-.2177.2176-.0871c.7619-.283 1.4585-.7619 2.0028-1.3714l.2177-.2612zm-3.962.3048c1.8395 0 3.3307-1.4912 3.3307-3.3306 0-1.8395-1.4912-3.3307-3.3307-3.3307-1.8394 0-3.3306 1.4912-3.3306 3.3307 0 1.8394 1.4912 3.3306 3.3306 3.3306zm-10.4708-.6531c-4.0272 0-7.2925 3.2653-7.2925 7.2926 0 4.0272 3.2653 7.2925 7.2925 7.2925 4.0272 0 7.2925-3.2653 7.2925-7.2925 0-4.0273-3.2653-7.2926-7.2925-7.2926z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1003 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<path fill="#333" d="M0 24a24 24 0 1 0 48 0 24 24 0 0 0-48 0z"/>
<path fill="#000" d="M25.22 5.55a.52.52 0 0 0 0 .22s.23 1.56-.03 2.25l-11.35 29.5a4.22 4.22 0 0 0 7.88 3.03l11.35-29.5c.22-.57 1.48-1.7 1.48-1.7.07-.04.12-.1.15-.16v-.01l.02-.04c.3-.76-1.6-2.2-4.21-3.2-2.62-1-4.98-1.2-5.27-.45l-.02.04v.02z" opacity=".3"/>
<path fill="url(#paint0_linear)" d="M24.9 5.3a.51.51 0 0 0-.01.22s.23 1.56-.03 2.25L13.5 37.27a4.22 4.22 0 0 0 7.88 3.03l11.35-29.5c.22-.58 1.49-1.7 1.49-1.7.06-.04.1-.1.14-.16v-.01l.03-.04c.29-.76-1.6-2.2-4.22-3.2-2.62-1-4.98-1.2-5.27-.45l-.01.04v.01z"/>
<path fill="url(#paint1_linear)" d="M28.45 18.64L20.27 39.9a3.06 3.06 0 0 1-3.91 1.72 3.12 3.12 0 0 1-1.83-3.93l5.23-13.59s.65-1.05 1.93-1.8c1.29-.75 2.34-.6 3.77-1.22 1.42-.64 3-2.45 3-2.45z"/>
<path fill="#333" d="M29.33 7.92c1.62.63 3.07.79 3.24.36.16-.43-1.02-1.28-2.65-1.9-1.62-.63-3.08-.8-3.24-.37-.17.43 1.02 1.28 2.65 1.91z"/>
<path fill="#fff" d="M24.89 5.52s.23 1.57-.03 2.25L13.5 37.27a4.22 4.22 0 0 0 3.58 5.68 4.22 4.22 0 0 1-1.41-4.85L26.8 9.19s-1.52-1.42-1.91-3.67z" opacity=".3"/>
<path fill="#fff" d="M21.24 24.87a.33.33 0 1 0 0-.65.33.33 0 0 0 0 .65zM23.4 27.42a.5.5 0 1 0 0-.99.5.5 0 0 0 0 1zM20.1 30.7a.93.93 0 1 0 0-1.87.93.93 0 0 0 0 1.86zM18.6 31.98a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1zM20.57 35.49a.33.33 0 1 0 0-.66.33.33 0 0 0 0 .66z" opacity=".5"/>
<defs>
<linearGradient id="paint0_linear" x1="18.32" x2="27.81" y1="22.36" y2="26.01" gradientUnits="userSpaceOnUse">
<stop stop-color="#919191"/>
<stop offset="1" stop-color="#fff"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="18.08" x2="23.83" y1="28.47" y2="30.68" gradientUnits="userSpaceOnUse">
<stop stop-color="#900"/>
<stop offset="1" stop-color="#E80000"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 48 48">
<rect width="48" height="48" fill="#FBC547" rx="24"/>
<path fill="#000" fill-opacity=".45" d="M6 12.53L23.65 9v2.65L6 14.29v-1.76z"/>
<path fill="#fff" d="M23.65 9l17.64 3.53v1.76l-17.64-2.64V9z"/>
<path fill="#000" fill-opacity=".45" d="M6 15.88v16.24l3.97.44V22.4l3.97 7.33 4.15-7.33V33.8l4.67.62V13.6l-5.02.7-3.8 7.33-3.8-6.18-4.14.44z"/>
<path fill="#fff" d="M41.3 15.88v16.24l-3.98.44V22.4l-3.97 7.33-4.14-7.33V33.8l-4.68.62V13.6l5.03.7 3.8 7.33 3.79-6.18 4.14.44z"/>
<path fill="#000" fill-opacity=".45" d="M6 35.47L23.65 39v-2.65L6 33.71v1.76z"/>
<path fill="#fff" d="M23.65 39l17.64-3.53v-1.76l-17.64 2.64V39z"/>
</svg>

After

Width:  |  Height:  |  Size: 715 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<rect width="48" height="48" fill="#000" rx="24"/>
<path fill="#fff" d="M36.109 17.5974C36.0934 11.1453 30.839 5.9 24.3831 5.9c-6.4659 0-11.7267 5.2609-11.7267 11.7268 0 .0202.0037.0394.0037.0596C9.2734 19.745 7 23.4594 7 27.7033 7 34.1701 12.2599 39.43 18.7267 39.43c2.0816 0 4.0338-.5516 5.7308-1.507 1.697.9554 3.6501 1.507 5.7316 1.507 6.4669 0 11.7268-5.2608 11.7268-11.7267 0-4.3054-2.3386-8.0666-5.8069-10.1059zM24.4575 35.4587c-2.122-1.5722-3.5849-3.9842-3.8731-6.7477 1.1931.4112 2.4679.6443 3.7987.6443 1.3896 0 2.7167-.2551 3.953-.7012-.2735 2.7874-1.742 5.2214-3.8786 6.8046zm-9.6406-16.5782c1.1959-.5314 2.5175-.8343 3.9098-.8343a9.5872 9.5872 0 0 1 3.8126.7875c-1.9403 1.6759-3.3307 3.9695-3.8502 6.5778-2.077-1.5253-3.5225-3.8547-3.8722-6.531zm9.5662 8.4043c-1.3372 0-2.612-.2735-3.7712-.7682.3313-2.6873 1.7704-5.0314 3.8456-6.5687 2.0586 1.5254 3.4895 3.8428 3.8364 6.5026-1.1968.5332-2.5185.8343-3.9108.8343zm1.9926-8.4521a9.6043 9.6043 0 0 1 3.8125-.7874c1.3373 0 2.6121.2735 3.7722.7682-.3267 2.6478-1.7282 4.9598-3.7529 6.4998-.5342-2.5689-1.9154-4.8248-3.8318-6.4806zm-1.9926-10.863c4.9855 0 9.1 3.7969 9.6057 8.6502-1.1931-.4102-2.4679-.6443-3.7997-.6443-2.0815 0-4.0346.5516-5.7316 1.5071-1.6961-.9555-3.6483-1.5071-5.7308-1.5071-1.3895 0-2.7167.2552-3.9529.7003.48-4.8799 4.6055-8.7062 9.6093-8.7062zm-5.6564 29.3916c-5.326 0-9.658-4.332-9.658-9.658 0-3.1371 1.5098-5.9207 3.8337-7.6866.6828 3.2867 2.7525 6.0639 5.5609 7.7169.0101 3.5308 1.5924 6.6935 4.0769 8.8393a9.5766 9.5766 0 0 1-3.8135.7884zm11.4624 0a9.587 9.587 0 0 1-3.8125-.7875c2.4918-2.1522 4.0787-5.3269 4.0787-8.8696 0-.0202-.0037-.0404-.0037-.0596 2.7599-1.6787 4.7809-4.4532 5.4316-7.7243 2.3982 1.7595 3.964 4.5881 3.964 7.783-.001 5.3251-4.333 9.658-9.6581 9.658z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<circle cx="24" cy="24" r="24" fill="#2FA4E7"/>
<path fill="#fff" d="M32.55 32.91V16h-3.07L24.1 29.23h-.2L18.52 16h-3.07v16.91h2.43V20.52h.17L23 32.61h2l4.95-12.09h.17v12.39h2.43z"/>
</svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#000"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,5 @@
<svg width="48" height="38" viewBox="0 0 48 38" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.7599 35.9199L14.6399 35.7999C14.3999 35.3199 14.3999 34.8399 14.3999 34.3599H2.99992L20.0399 4.11988L27.1199 16.9599L29.3999 15.2799L22.3199 2.43988C22.1999 2.19988 21.3599 0.879883 19.9199 0.879883C19.3199 0.879883 18.3599 1.11988 17.6399 2.43988L0.479919 33.0399C0.359919 33.2799 -0.360081 34.7199 0.359919 35.9199C0.599919 36.5199 1.31992 37.1199 2.87992 37.1199H17.2799C15.7199 37.1199 14.9999 36.5199 14.7599 35.9199Z" fill="#00C58E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M47.3999 33.1598L33.5999 8.31977C33.3599 8.07977 32.6399 6.75977 31.1999 6.75977C30.5999 6.75977 29.7599 6.99977 28.9199 8.31977L27.1199 11.1998V16.9598L31.1999 9.87977L44.8799 34.3598H39.7199C39.7913 34.853 39.7075 35.3563 39.4799 35.7998V35.9198C38.7599 37.1198 37.1999 37.1198 36.9599 37.1198H45.1199C45.3599 37.1198 46.9199 37.1198 47.6399 35.9198C47.8799 35.3198 48.1199 34.3598 47.3999 33.1598Z" fill="#108775"/>
<path d="M39.84 35.9199V35.7999L39.96 35.5599C40.08 35.1999 40.2 34.7199 40.08 34.3599L39.6 33.0399L28.8 14.08L27.24 11.2H27.12L25.56 14.08L14.64 33.0399L14.28 34.3599C14.1908 34.8922 14.275 35.4391 14.52 35.9199C14.88 36.5199 15.6 37.1199 17.04 37.1199H37.2C37.56 37.1199 39.12 37.1199 39.84 35.9199ZM27.12 16.96L37.08 34.3599H17.28L27.12 16.96Z" fill="#2F495E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M28.8 40.371L9.6 7.113H19.2l19.201 33.258h-9.6z" fill="#FF4081"/><path d="M28.8 40.371l4.8-8.314 4.802 8.314H28.8z" fill="#fff" fill-opacity=".2"/><path d="M24 32.057l4.8 8.314 4.8-8.314H24z" fill="#fff" fill-opacity=".1"/><path d="M19.2 23.742l4.8 8.315 4.8-8.315h-9.6z" fill="#000" fill-opacity=".1"/><path d="M19.2 23.742l4.8-8.314 4.8 8.314h-9.6z" fill="#000" fill-opacity=".2"/><path d="M14.4 15.428l4.8 8.314 4.8-8.314h-9.6z" fill="#000" fill-opacity=".3"/><path d="M14.4 15.428l4.8-8.315 4.8 8.315h-9.6z" fill="#000" fill-opacity=".4"/><path d="M9.599 7.113l4.801 8.315 4.8-8.315H9.6z" fill="#000" fill-opacity=".5"/><path d="M9.599 40.371L-.002 23.742l4.8-8.314L19.2 40.37H9.599z" fill="#536DFE"/><path d="M9.599 40.371l4.801-8.314 4.8 8.314H9.6z" fill="#fff" fill-opacity=".2"/><path d="M4.799 32.057l4.8 8.314 4.801-8.314H4.8z" fill="#fff" fill-opacity=".1"/><path d="M-.002 23.742l4.8 8.315 4.8-8.315h-9.6z" fill="#000" fill-opacity=".1"/><path d="M-.002 23.742l4.8-8.314 4.8 8.314h-9.6z" fill="#000" fill-opacity=".2"/><path d="M9.599 23.742l-4.8-8.314 4.8-8.315 4.801 8.315L9.6 23.742z" fill="#303F9F"/><path d="M14.4 15.428L9.6 7.113l-4.8 8.315H14.4z" fill="#000" fill-opacity=".2"/><path d="M38.401 40.371l-4.8-8.314 4.8-8.315 4.8 8.315-4.8 8.314z" fill="#3F51B5"/><path d="M43.201 32.057l-4.8 8.314-4.8-8.314h9.6z" fill="#000" fill-opacity=".2"/><path d="M43.201 32.057L28.8 7.113h9.601l9.602 16.63-4.802 8.314z" fill="#7986CB"/><path d="M38.401 23.742l4.8 8.315 4.801-8.315h-9.6z" fill="#fff" fill-opacity=".2"/><path d="M38.401 23.742l4.8-8.314 4.801 8.314h-9.6z" fill="#fff" fill-opacity=".1"/><path d="M33.6 15.428l4.801-8.315 4.8 8.315h-9.6z" fill="#000" fill-opacity=".1"/><path d="M28.8 7.113l4.8 8.315 4.801-8.315H28.8z" fill="#000" fill-opacity=".2"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(0 7)" d="M0 0H48V33.375H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M23.757 0l20.756 11.984V35.95L23.758 47.935 3 35.951V11.984L23.757 0z" fill="#673AB8"/><path d="M8.654 35.753c2.76 3.532 11.529.84 19.808-5.628S41.474 15.8 38.714 12.267s-11.529-.84-19.808 5.628S5.894 32.22 8.654 35.753zm1.178-.92c-.915-1.172-.515-3.472 1.229-6.366 1.836-3.048 4.952-6.413 8.766-9.393 3.814-2.98 7.833-5.19 11.235-6.234 3.23-.991 5.558-.823 6.473.348.916 1.172.516 3.471-1.228 6.366-1.836 3.048-4.952 6.413-8.766 9.393-3.814 2.98-7.833 5.19-11.235 6.233-3.23.992-5.558.824-6.474-.348z" fill="#fff"/><path d="M38.714 35.753c2.76-3.532-1.973-11.39-10.252-17.858-8.28-6.468-17.049-9.16-19.808-5.628-2.76 3.532 1.973 11.39 10.252 17.858 8.28 6.469 17.049 9.16 19.808 5.628zm-1.178-.92c-.916 1.17-3.244 1.339-6.474.347-3.402-1.044-7.42-3.254-11.235-6.233-3.814-2.98-6.93-6.345-8.766-9.393-1.744-2.895-2.144-5.194-1.229-6.366.916-1.171 3.243-1.34 6.474-.348 3.402 1.044 7.42 3.254 11.235 6.234 3.814 2.98 6.93 6.345 8.766 9.393 1.744 2.894 2.144 5.194 1.228 6.365z" fill="#fff"/><path d="M23.684 27.19a3.179 3.179 0 1 0 0-6.359 3.179 3.179 0 0 0 0 6.358z" fill="#fff"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(3)" d="M0 0H41.514V48H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M24 28.631a4.278 4.278 0 1 0 0-8.556 4.278 4.278 0 0 0 0 8.556z" fill="#61DAFB"/><path d="M24 33.118c12.678 0 22.956-3.924 22.956-8.765 0-4.84-10.278-8.765-22.956-8.765-12.679 0-22.957 3.924-22.957 8.765 0 4.841 10.278 8.765 22.957 8.765z" stroke="#61DAFB"/><path d="M16.409 28.736c6.34 10.98 14.877 17.918 19.07 15.498 4.191-2.42 2.451-13.284-3.888-24.264C25.25 8.99 16.714 2.053 12.52 4.473 8.33 6.892 10.07 17.756 16.41 28.736z" stroke="#61DAFB"/><path d="M16.409 19.97c-6.34 10.98-8.08 21.843-3.887 24.264 4.192 2.42 12.73-4.518 19.069-15.498 6.34-10.98 8.08-21.843 3.887-24.264-4.192-2.42-12.73 4.519-19.069 15.498z" stroke="#61DAFB"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(0 3)" d="M0 0H48V42.706H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 874 B

View File

@@ -0,0 +1 @@
<svg width="50" height="50" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M25 49c13.255 0 24-10.745 24-24S38.255 1 25 1 1 11.745 1 25s10.745 24 24 24z" fill="#00838F" stroke="#00838F" stroke-width=".308"/><path fill-rule="evenodd" clip-rule="evenodd" d="M19.355 28.86l6.064-.508 1.14 2.633-3.913 3.882-3.29 4.626.433 5.617c-3.218-.786-5.361-1.56-6.43-2.324-1.068-.763-2.215-1.861-3.441-3.293l5.745-6.843h2.514l1.178-3.79z" fill="#FBB526"/><path fill-rule="evenodd" clip-rule="evenodd" d="M25.36 7.404a2.993 2.993 0 0 0-1.916 5.292v4.045h3.95v-4.15a2.993 2.993 0 0 0-2.035-5.187zm-1.318 2.992a1.317 1.317 0 1 1 2.634 0 1.317 1.317 0 0 1-2.634 0z" fill="#fff"/><path d="M27.373 37.265l-2.953 2.83 1.16 1.21 2.083-1.997 1.866.978 5.01.68.225-1.661-4.718-.64-2.673-1.4z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M2.736 25C2.736 12.704 12.704 2.736 25 2.736S47.264 12.704 47.264 25 37.296 47.264 25 47.264 2.736 37.296 2.736 25zM25 4.41C13.63 4.411 4.41 13.63 4.41 25a20.51 20.51 0 0 0 5.1 13.565l5.418-7.22h3.086l.255-2.69 3.259-.618v-9.62h-3.352V16.74h14.364v1.675H29.31V30.52l2.17.222 1.761 2.328h1.629l5.653 5.457A20.51 20.51 0 0 0 45.59 25c0-11.37-9.218-20.589-20.59-20.589zm7.43 30.364l-.023-.03h-2.293v-1.676h1.024l-.556-.735-4.404-.45-.974 2.587-2.52 1.545-.875-1.43 2.03-1.243 1.225-3.257.847.086-.694-1.129-5.398 1.024-.63 6.639-2.977 2.581-1.098-1.266 2.47-2.142.271-2.86h-2.088l-5.083 6.777A20.52 20.52 0 0 0 25 45.59a20.52 20.52 0 0 0 14.37-5.846l-5.178-4.998h-1.723l-.04.03zm-7.909-7.306l-1.556.296v-9.349h4.908v11.748l-1.834-2.983-.082.016v-3.993h-1.436v4.265z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="24" cy="24" r="24" fill="#2A4660"/><g clip-path="url(#clip0)"><path d="M28.187 28.006h-7.993V36h7.993v-7.994zm-7.993-7.994H12.2v7.994h7.994v-7.994z" fill="#99E1F4"/><path d="M20.194 28.006H12.2V36h7.994v-7.994z" fill="#1A82E2"/><path d="M36.2 20.012h-7.994v7.994H36.2v-7.994zM28.187 12h-7.993v7.994h7.993V12z" fill="#00B2E3"/><path d="M20.194 20.012v7.994h8.012v-7.994h-8.012z" fill="#009DD9"/><path d="M36.2 12h-7.994v7.994H36.2V12z" fill="#1A82E2"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(12 12)" d="M0 0H24V24H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 659 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<circle cx="24" cy="24" r="24" fill="#000"/>
<path fill="#fff" d="M20.27 29H32l-6.34 7H14l6.27-7zM40 21H15.45L9 27h24.55L40 21zM23.32 12H35l-6.3 7H17l6.32-7z"/>
</svg>

After

Width:  |  Height:  |  Size: 248 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="39" height="48" fill="none">
<mask id="a" width="39" height="48" x="0" y="0" maskUnits="userSpaceOnUse">
<path fill="#fff" d="M1.48 43.97L0 4.58A2.4 2.4 0 0 1 2.25 2.1L35.75 0a2.4 2.4 0 0 1 2.55 2.4v42.92a2.4 2.4 0 0 1-2.5 2.4L3.76 46.28a2.4 2.4 0 0 1-2.29-2.3z"/>
</mask>
<g mask="url(#a)">
<path fill="#FF4785" d="M1.47 43.97L-.01 4.58A2.4 2.4 0 0 1 2.24 2.1L35.74 0a2.4 2.4 0 0 1 2.55 2.4v42.92a2.4 2.4 0 0 1-2.5 2.4L3.75 46.28a2.4 2.4 0 0 1-2.3-2.3z"/>
<path fill="#fff" d="M28.26 5.87l.23-5.5L33.1 0l.2 5.68a.36.36 0 0 1-.58.29l-1.77-1.4-2.1 1.6a.36.36 0 0 1-.58-.3zm-5.88 12.12c0 .93 6.29.48 7.13-.17 0-6.36-3.41-9.7-9.66-9.7-6.24 0-9.74 3.4-9.74 8.48 0 8.86 11.96 9.03 11.96 13.87 0 1.35-.67 2.16-2.13 2.16-1.9 0-2.66-.97-2.57-4.28 0-.72-7.26-.94-7.48 0-.57 8.01 4.43 10.33 10.14 10.33 5.54 0 9.88-2.96 9.88-8.3 0-9.5-12.14-9.24-12.14-13.95 0-1.9 1.42-2.16 2.26-2.16.89 0 2.48.15 2.35 3.72z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 980 B

View File

@@ -0,0 +1 @@
<svg width="47" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M39.792 6.09C35.507-.027 27.044-1.84 20.924 2.048L10.177 8.88a12.297 12.297 0 0 0-5.57 8.237 12.922 12.922 0 0 0 1.28 8.315 12.313 12.313 0 0 0-1.844 4.596c-.619 3.47.19 7.043 2.244 9.91 4.287 6.119 12.75 7.931 18.869 4.042l10.747-6.831a12.295 12.295 0 0 0 5.57-8.237 12.927 12.927 0 0 0-1.28-8.315A12.31 12.31 0 0 0 42.037 16c.619-3.47-.19-7.043-2.245-9.91" fill="#FF3E00"/><path d="M19.873 40.52a8.55 8.55 0 0 1-9.165-3.388 7.866 7.866 0 0 1-1.35-5.962c.062-.339.148-.674.257-1.001l.202-.616.55.404a13.881 13.881 0 0 0 4.206 2.095l.4.121-.037.398a2.4 2.4 0 0 0 .433 1.594 2.575 2.575 0 0 0 2.76 1.022 2.37 2.37 0 0 0 .66-.29l10.75-6.833a2.233 2.233 0 0 0 1.011-1.492 2.377 2.377 0 0 0-.407-1.797 2.577 2.577 0 0 0-2.76-1.022 2.37 2.37 0 0 0-.66.289L22.62 26.65c-.675.428-1.411.75-2.183.956a8.55 8.55 0 0 1-9.166-3.388 7.866 7.866 0 0 1-1.35-5.962 7.394 7.394 0 0 1 3.35-4.954l10.75-6.834a7.844 7.844 0 0 1 2.185-.957 8.55 8.55 0 0 1 9.165 3.388 7.866 7.866 0 0 1 1.35 5.962c-.062.34-.148.674-.256 1.001l-.203.616-.55-.403a13.87 13.87 0 0 0-4.206-2.096l-.4-.121.037-.398a2.404 2.404 0 0 0-.433-1.594 2.575 2.575 0 0 0-2.76-1.022 2.37 2.37 0 0 0-.66.29l-10.75 6.833a2.229 2.229 0 0 0-1.01 1.492c-.112.63.034 1.277.406 1.797a2.577 2.577 0 0 0 2.76 1.023c.234-.063.457-.16.661-.29l4.102-2.607a7.834 7.834 0 0 1 2.183-.957 8.55 8.55 0 0 1 9.165 3.388 7.865 7.865 0 0 1 1.35 5.962 7.398 7.398 0 0 1-3.35 4.955l-10.75 6.833a7.842 7.842 0 0 1-2.185.958" fill="#fff"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(3.84)" d="M0 0H38.4V46.08H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="24" cy="24" r="24" fill="#1CA1FA"/><path d="M19.77 16.09h-2.625v11.086c0 3.656 2.613 6.234 6.843 6.234 4.254 0 6.856-2.578 6.856-6.234V16.09h-2.625v10.875c0 2.414-1.535 4.113-4.23 4.113-2.684 0-4.22-1.7-4.22-4.113V16.09z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 328 B

View File

@@ -0,0 +1,4 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 24C0 37.2548 10.7452 48 24 48C37.2548 48 48 37.2548 48 24C48 10.7452 37.2548 0 24 0C10.7452 0 0 10.7452 0 24Z" fill="#F7DF1E"/>
<path d="M27.6053 28.7181C28.5722 30.2968 29.8301 31.4571 32.0549 31.4571C33.9238 31.4571 35.1177 30.523 35.1177 29.2324C35.1177 27.6857 33.8911 27.1379 31.8339 26.2381L30.7063 25.7543C27.4514 24.3676 25.2891 22.6305 25.2891 18.9581C25.2891 15.5752 27.8667 13 31.8949 13C34.7627 13 36.8244 13.9981 38.3101 16.6114L34.7977 18.8667C34.0244 17.48 33.1901 16.9337 31.8949 16.9337C30.5737 16.9337 29.7364 17.7718 29.7364 18.8667C29.7364 20.2198 30.5745 20.7676 32.5097 21.6057L33.6373 22.0888C37.4697 23.7322 39.6335 25.4076 39.6335 29.1745C39.6335 33.2354 36.4434 35.4602 32.1592 35.4602C27.9703 35.4602 25.264 33.464 23.9398 30.8476L27.6053 28.7181ZM11.6716 29.109C12.3802 30.3661 13.0248 31.429 14.5745 31.429C16.0564 31.429 16.9912 30.8491 16.9912 28.5947V13.2575H21.5017V28.6556C21.5017 33.3261 18.7634 35.4518 14.7665 35.4518C11.155 35.4518 9.06362 33.5829 8 31.3318L11.6716 29.109Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M29.54 3L24 12.7 18.456 3H-.001l24 42L47.998 3H29.54z" fill="#41B883"/><path d="M29.54 3L24 12.7 18.456 3H9.599l14.4 25.2L38.398 3H29.54z" fill="#34495E"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(0 3)" d="M0 0H48V42H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none">
<circle cx="24" cy="24" r="24" fill="#191919"/>
<path fill="#fff" d="M17.65 33h12.7v-2.26H21v-.2l9.14-12.66V16.1h-12.2v2.26h8.9v.2l-9.18 12.66V33z"/>
</svg>

After

Width:  |  Height:  |  Size: 237 B

View File

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

View File

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

View File

@@ -0,0 +1,432 @@
import minimatch from 'minimatch';
import { valid as validSemver } from 'semver';
import { PackageJson, Builder, Config, BuilderFunctions } from './types';
interface ErrorResponse {
code: string;
message: string;
}
interface Options {
tag?: 'canary' | 'latest' | string;
functions?: BuilderFunctions;
}
const src = 'package.json';
const config: Config = { zeroConfig: true };
const MISSING_BUILD_SCRIPT_ERROR: ErrorResponse = {
code: 'missing_build_script',
message:
'Your `package.json` file is missing a `build` property inside the `scripts` property.' +
'\nMore details: https://zeit.co/docs/v2/platform/frequently-asked-questions#missing-build-script',
};
// Static builders are special cased in `@now/static-build`
function getBuilders({ tag }: Options = {}): Map<string, Builder> {
const withTag = tag ? `@${tag}` : '';
const config = { zeroConfig: true };
return new Map<string, Builder>([
['next', { src, use: `@now/next${withTag}`, config }],
]);
}
// Must be a function to ensure that the returned
// object won't be a reference
function getApiBuilders({ tag }: Pick<Options, 'tag'> = {}): Builder[] {
const withTag = tag ? `@${tag}` : '';
const config = { zeroConfig: true };
return [
{ src: 'api/**/*.js', use: `@now/node${withTag}`, config },
{ src: 'api/**/*.ts', use: `@now/node${withTag}`, config },
{ src: 'api/**/*.go', use: `@now/go${withTag}`, config },
{ src: 'api/**/*.py', use: `@now/python${withTag}`, config },
{ src: 'api/**/*.rb', use: `@now/ruby${withTag}`, config },
];
}
function hasPublicDirectory(files: string[]) {
return files.some(name => name.startsWith('public/'));
}
function hasBuildScript(pkg: PackageJson | undefined) {
const { scripts = {} } = pkg || {};
return Boolean(scripts && scripts['build']);
}
function getApiFunctionBuilder(
file: string,
prevBuilder: Builder | undefined,
{ functions = {} }: Pick<Options, 'functions'>
) {
const key = Object.keys(functions).find(
k => file === k || minimatch(file, k)
);
const fn = key ? functions[key] : undefined;
if (!fn || (!fn.runtime && !prevBuilder)) {
return prevBuilder;
}
const src = (prevBuilder && prevBuilder.src) || file;
const use = fn.runtime || (prevBuilder && prevBuilder.use);
const config: Config = { zeroConfig: true };
if (key) {
Object.assign(config, {
functions: {
[key]: fn,
},
});
}
const { includeFiles, excludeFiles } = fn;
if (includeFiles) Object.assign(config, { includeFiles });
if (excludeFiles) Object.assign(config, { excludeFiles });
return use ? { use, src, config } : prevBuilder;
}
async function detectFrontBuilder(
pkg: PackageJson,
builders: Builder[],
options: Options
): Promise<Builder> {
const { tag } = options;
const withTag = tag ? `@${tag}` : '';
for (const [dependency, builder] of getBuilders(options)) {
const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
// Return the builder when a dependency matches
if (deps[dependency]) {
if (options.functions) {
Object.entries(options.functions).forEach(([key, func]) => {
// When the builder is not used yet we'll use it for the frontend
if (
builders.every(
b => !(b.config && b.config.functions && b.config.functions[key])
)
) {
if (!builder.config) builder.config = {};
if (!builder.config.functions) builder.config.functions = {};
builder.config.functions[key] = { ...func };
}
});
}
return builder;
}
}
// By default we'll choose the `static-build` builder
return { src, use: `@now/static-build${withTag}`, config };
}
// Files that match a specific pattern will get ignored
export function getIgnoreApiFilter(optionsOrBuilders: Options | Builder[]) {
const possiblePatterns: string[] = getApiBuilders().map(b => b.src);
if (Array.isArray(optionsOrBuilders)) {
optionsOrBuilders.forEach(({ src }) => possiblePatterns.push(src));
} else if (optionsOrBuilders.functions) {
Object.keys(optionsOrBuilders.functions).forEach(p =>
possiblePatterns.push(p)
);
}
return (file: string) => {
if (!file.startsWith('api/')) {
return false;
}
if (file.includes('/.')) {
return false;
}
if (file.includes('/_')) {
return false;
}
if (file.endsWith('.d.ts')) {
return false;
}
if (possiblePatterns.every(p => !(file === p || minimatch(file, p)))) {
return false;
}
return true;
};
}
// We need to sort the file paths by alphabet to make
// sure the routes stay in the same order e.g. for deduping
export function sortFiles(fileA: string, fileB: string) {
return fileA.localeCompare(fileB);
}
async function detectApiBuilders(
files: string[],
options: Options
): Promise<Builder[]> {
const builds = files
.sort(sortFiles)
.filter(getIgnoreApiFilter(options))
.map(file => {
const apiBuilders = getApiBuilders(options);
const apiBuilder = apiBuilders.find(b => minimatch(file, b.src));
const fnBuilder = getApiFunctionBuilder(file, apiBuilder, options);
return fnBuilder ? { ...fnBuilder, src: file } : null;
});
return builds.filter(Boolean) as Builder[];
}
// When a package has files that conflict with `/api` routes
// e.g. Next.js pages/api we'll check it here and return an error.
async function checkConflictingFiles(
files: string[],
builders: Builder[]
): Promise<ErrorResponse | null> {
// For Next.js
if (builders.some(b => b.use.startsWith('@now/next'))) {
const hasApiPages = files.some(file => file.startsWith('pages/api/'));
const hasApiBuilders = builders.some(b => b.src.startsWith('api/'));
if (hasApiPages && hasApiBuilders) {
return {
code: 'conflicting_files',
message:
'It is not possible to use `api` and `pages/api` at the same time, please only use one option',
};
}
}
return null;
}
// When e.g. Next.js receives a `functions` property it has to make sure,
// that it can handle those files, otherwise there are unused functions.
async function checkUnusedFunctionsOnFrontendBuilder(
files: string[],
builder: Builder
): Promise<ErrorResponse | null> {
const { config: { functions = undefined } = {} } = builder;
if (!functions) return null;
if (builder.use.startsWith('@now/next')) {
const matchingFiles = files.filter(file =>
Object.keys(functions).some(key => file === key || minimatch(file, key))
);
for (const matchedFile of matchingFiles) {
if (
!matchedFile.startsWith('src/pages/') &&
!matchedFile.startsWith('pages/')
) {
return {
code: 'unused_function',
message: `The function for ${matchedFile} can't be handled by any builder`,
};
}
}
}
return null;
}
function validateFunctions(files: string[], { functions = {} }: Options) {
const apiBuilders = getApiBuilders();
for (const [path, func] of Object.entries(functions)) {
if (path.length > 256) {
return {
code: 'invalid_function_glob',
message: 'Function globs must be less than 256 characters long.',
};
}
if (!func || typeof func !== 'object') {
return {
code: 'invalid_function',
message: 'Function must be an object.',
};
}
if (Object.keys(func).length === 0) {
return {
code: 'invalid_function',
message: 'Function must contain at least one property.',
};
}
if (
func.maxDuration !== undefined &&
(func.maxDuration < 1 ||
func.maxDuration > 900 ||
!Number.isInteger(func.maxDuration))
) {
return {
code: 'invalid_function_duration',
message: 'Functions must have a duration between 1 and 900.',
};
}
if (
func.memory !== undefined &&
(func.memory < 128 || func.memory > 3008 || func.memory % 64 !== 0)
) {
return {
code: 'invalid_function_memory',
message:
'Functions must have a memory value between 128 and 3008 in steps of 64.',
};
}
if (path.startsWith('/')) {
return {
code: 'invalid_function_source',
message: `The function path "${path}" is invalid. The path must be relative to your project root and therefore cannot start with a slash.`,
};
}
if (files.some(f => f === path || minimatch(f, path)) === false) {
return {
code: 'invalid_function_source',
message: `No source file matched the function for ${path}.`,
};
}
if (func.runtime !== undefined) {
const tag = `${func.runtime}`.split('@').pop();
if (!tag || !validSemver(tag)) {
return {
code: 'invalid_function_runtime',
message:
'Function Runtimes must have a valid version, for example `now-php@1.0.0`.',
};
}
if (
apiBuilders.some(b => func.runtime && func.runtime.startsWith(b.use))
) {
return {
code: 'invalid_function_runtime',
message: `The function Runtime ${func.runtime} is not a Community Runtime and must not be specified.`,
};
}
}
if (func.includeFiles !== undefined) {
if (typeof func.includeFiles !== 'string') {
return {
code: 'invalid_function_property',
message: `The property \`includeFiles\` must be a string.`,
};
}
}
if (func.excludeFiles !== undefined) {
if (typeof func.excludeFiles !== 'string') {
return {
code: 'invalid_function_property',
message: `The property \`excludeFiles\` must be a string.`,
};
}
}
}
return null;
}
// When zero config is used we can call this function
// to determine what builders to use
export async function detectBuildersLegacy(
files: string[],
pkg?: PackageJson | undefined | null,
options: Options = {}
): Promise<{
builders: Builder[] | null;
errors: ErrorResponse[] | null;
warnings: ErrorResponse[];
}> {
const errors: ErrorResponse[] = [];
const warnings: ErrorResponse[] = [];
const functionError = validateFunctions(files, options);
if (functionError) {
return {
builders: null,
errors: [functionError],
warnings,
};
}
// Detect all builders for the `api` directory before anything else
const builders = await detectApiBuilders(files, options);
if (pkg && hasBuildScript(pkg)) {
const frontendBuilder = await detectFrontBuilder(pkg, builders, options);
builders.push(frontendBuilder);
const conflictError = await checkConflictingFiles(files, builders);
if (conflictError) {
warnings.push(conflictError);
}
const unusedFunctionError = await checkUnusedFunctionsOnFrontendBuilder(
files,
frontendBuilder
);
if (unusedFunctionError) {
return {
builders: null,
errors: [unusedFunctionError],
warnings,
};
}
} else {
if (pkg && builders.length === 0) {
// We only show this error when there are no api builders
// since the dependencies of the pkg could be used for those
errors.push(MISSING_BUILD_SCRIPT_ERROR);
return { errors, warnings, builders: null };
}
// We allow a `public` directory
// when there are no build steps
if (hasPublicDirectory(files)) {
builders.push({
use: '@now/static',
src: 'public/**/*',
config,
});
} else if (
builders.length > 0 &&
files.some(f => !f.startsWith('api/') && f !== 'package.json')
) {
// Everything besides the api directory
// and package.json can be served as static files
builders.push({
use: '@now/static',
src: '!{api/**,package.json}',
config,
});
}
}
return {
builders: builders.length ? builders : null,
errors: errors.length ? errors : null,
warnings,
};
}

View File

@@ -75,12 +75,18 @@ function getApiFunctionBuilder(
function detectFrontBuilder(
detectorResult: Partial<DetectorOutput>,
builders: Builder[],
files: string[],
options: Options
): Builder {
const { tag } = options;
const withTag = tag ? `@${tag}` : '';
const { framework, buildCommand, outputDirectory } = detectorResult;
const {
framework,
buildCommand,
outputDirectory,
devCommand,
} = detectorResult;
const frameworkSlug = framework ? framework.slug : null;
@@ -88,6 +94,14 @@ function detectFrontBuilder(
zeroConfig: true,
};
if (framework) {
config.framework = framework;
}
if (devCommand) {
config.devCommand = devCommand;
}
if (buildCommand) {
config.buildCommand = buildCommand;
}
@@ -114,6 +128,14 @@ function detectFrontBuilder(
return { src: 'package.json', use: `@now/next${withTag}`, config };
}
if (frameworkSlug === 'hugo') {
const configFiles = new Set(['config.yaml', 'config.toml', 'config.json']);
const source = files.find(file => configFiles.has(file));
if (source) {
return { src: source, use: `@now/static-build${withTag}`, config };
}
}
return { src: 'package.json', use: `@now/static-build${withTag}`, config };
}
@@ -351,6 +373,7 @@ export async function detectBuilders(
const frontendBuilder = detectFrontBuilder(
detectorResult,
builders,
files,
options
);
builders.push(frontendBuilder);

View File

@@ -0,0 +1,299 @@
import { parse as parsePath } from 'path';
import { Route } from '@now/routing-utils';
import { Builder } from './types';
import { getIgnoreApiFilter, sortFiles } from './detect-builders-legacy';
function escapeName(name: string) {
const special = '[]^$.|?*+()'.split('');
for (const char of special) {
name = name.replace(new RegExp(`\\${char}`, 'g'), `\\${char}`);
}
return name;
}
function joinPath(...segments: string[]) {
const joinedPath = segments.join('/');
return joinedPath.replace(/\/{2,}/g, '/');
}
function concatArrayOfText(texts: string[]): string {
if (texts.length <= 2) {
return texts.join(' and ');
}
const last = texts.pop();
return `${texts.join(', ')}, and ${last}`;
}
// Takes a filename or foldername, strips the extension
// gets the part between the "[]" brackets.
// It will return `null` if there are no brackets
// and therefore no segment.
function getSegmentName(segment: string): string | null {
const { name } = parsePath(segment);
if (name.startsWith('[') && name.endsWith(']')) {
return name.slice(1, -1);
}
return null;
}
function createRouteFromPath(filePath: string): Route {
const parts = filePath.split('/');
let counter = 1;
const query: string[] = [];
const srcParts = parts.map((segment, index): string => {
const name = getSegmentName(segment);
const isLast = index === parts.length - 1;
if (name !== null) {
// We can't use `URLSearchParams` because `$` would get escaped
query.push(`${name}=$${counter++}`);
return `([^\\/]+)`;
} else if (isLast) {
const { name: fileName, ext } = parsePath(segment);
const isIndex = fileName === 'index';
const prefix = isIndex ? '\\/' : '';
const names = [
isIndex ? prefix : `${fileName}\\/`,
prefix + escapeName(fileName),
prefix + escapeName(fileName) + escapeName(ext),
].filter(Boolean);
// Either filename with extension, filename without extension
// or nothing when the filename is `index`
return `(${names.join('|')})${isIndex ? '?' : ''}`;
}
return segment;
});
const { name: fileName } = parsePath(filePath);
const isIndex = fileName === 'index';
const src = isIndex
? `^/${srcParts.slice(0, -1).join('/')}${srcParts.slice(-1)[0]}$`
: `^/${srcParts.join('/')}$`;
const dest = `/${filePath}${query.length ? '?' : ''}${query.join('&')}`;
return { src, dest };
}
// Check if the path partially matches and has the same
// name for the path segment at the same position
function partiallyMatches(pathA: string, pathB: string): boolean {
const partsA = pathA.split('/');
const partsB = pathB.split('/');
const long = partsA.length > partsB.length ? partsA : partsB;
const short = long === partsA ? partsB : partsA;
let index = 0;
for (const segmentShort of short) {
const segmentLong = long[index];
const nameLong = getSegmentName(segmentLong);
const nameShort = getSegmentName(segmentShort);
// If there are no segments or the paths differ we
// return as they are not matching
if (segmentShort !== segmentLong && (!nameLong || !nameShort)) {
return false;
}
if (nameLong !== nameShort) {
return true;
}
index += 1;
}
return false;
}
// Counts how often a path occurs when all placeholders
// got resolved, so we can check if they have conflicts
function pathOccurrences(filePath: string, files: string[]): string[] {
const getAbsolutePath = (unresolvedPath: string): string => {
const { dir, name } = parsePath(unresolvedPath);
const parts = joinPath(dir, name).split('/');
return parts.map(part => part.replace(/\[.*\]/, '1')).join('/');
};
const currentAbsolutePath = getAbsolutePath(filePath);
return files.reduce((prev: string[], file: string): string[] => {
const absolutePath = getAbsolutePath(file);
if (absolutePath === currentAbsolutePath) {
prev.push(file);
} else if (partiallyMatches(filePath, file)) {
prev.push(file);
}
return prev;
}, []);
}
// Checks if a placeholder with the same name is used
// multiple times inside the same path
function getConflictingSegment(filePath: string): string | null {
const segments = new Set<string>();
for (const segment of filePath.split('/')) {
const name = getSegmentName(segment);
if (name !== null && segments.has(name)) {
return name;
}
if (name) {
segments.add(name);
}
}
return null;
}
function sortFilesBySegmentCount(fileA: string, fileB: string): number {
const lengthA = fileA.split('/').length;
const lengthB = fileB.split('/').length;
if (lengthA > lengthB) {
return -1;
}
if (lengthA < lengthB) {
return 1;
}
// Paths that have the same segment length but
// less placeholders are preferred
const countSegments = (prev: number, segment: string) =>
getSegmentName(segment) ? prev + 1 : 0;
const segmentLengthA = fileA.split('/').reduce(countSegments, 0);
const segmentLengthB = fileB.split('/').reduce(countSegments, 0);
if (segmentLengthA > segmentLengthB) {
return 1;
}
if (segmentLengthA < segmentLengthB) {
return -1;
}
return 0;
}
interface RoutesResult {
defaultRoutes: Route[] | null;
error: { [key: string]: string } | null;
}
async function detectApiRoutes(
files: string[],
builders: Builder[]
): Promise<RoutesResult> {
if (!files || files.length === 0) {
return { defaultRoutes: null, error: null };
}
// The deepest routes need to be
// the first ones to get handled
const sortedFiles = files
.filter(getIgnoreApiFilter(builders))
.sort(sortFiles)
.sort(sortFilesBySegmentCount);
const defaultRoutes: Route[] = [];
for (const file of sortedFiles) {
// We only consider every file in the api directory
// as we will strip extensions as well as resolving "[segments]"
if (!file.startsWith('api/')) {
continue;
}
const conflictingSegment = getConflictingSegment(file);
if (conflictingSegment) {
return {
defaultRoutes: null,
error: {
code: 'conflicting_path_segment',
message:
`The segment "${conflictingSegment}" occurs more than ` +
`one time in your path "${file}". Please make sure that ` +
`every segment in a path is unique`,
},
};
}
const occurrences = pathOccurrences(file, sortedFiles).filter(
name => name !== file
);
if (occurrences.length > 0) {
const messagePaths = concatArrayOfText(
occurrences.map(name => `"${name}"`)
);
return {
defaultRoutes: null,
error: {
code: 'conflicting_file_path',
message:
`Two or more files have conflicting paths or names. ` +
`Please make sure path segments and filenames, without their extension, are unique. ` +
`The path "${file}" has conflicts with ${messagePaths}`,
},
};
}
defaultRoutes.push(createRouteFromPath(file));
}
// 404 Route to disable directory listing
if (defaultRoutes.length) {
defaultRoutes.push({
status: 404,
src: '/api(\\/.*)?$',
});
}
return { defaultRoutes, error: null };
}
function hasPublicBuilder(builders: Builder[]): boolean {
return builders.some(
builder =>
builder.use === '@now/static' &&
builder.src === 'public/**/*' &&
builder.config &&
builder.config.zeroConfig === true
);
}
export async function detectRoutesLegacy(
files: string[],
builders: Builder[]
): Promise<RoutesResult> {
const routesResult = await detectApiRoutes(files, builders);
if (routesResult.defaultRoutes && hasPublicBuilder(builders)) {
routesResult.defaultRoutes.push({
src: '/(.*)',
dest: '/public/$1',
});
}
return routesResult;
}

View File

@@ -1,5 +1,6 @@
import { parse as parsePath } from 'path';
import { Route, Builder } from './types';
import { Route } from '@now/routing-utils';
import { Builder } from './types';
import { getIgnoreApiFilter, sortFiles } from './detect-builders';
function escapeName(name: string) {
@@ -199,7 +200,8 @@ interface RoutesResult {
async function detectApiRoutes(
files: string[],
builders: Builder[]
builders: Builder[],
featHandleMiss: boolean
): Promise<RoutesResult> {
if (!files || files.length === 0) {
return { defaultRoutes: null, error: null };
@@ -212,7 +214,7 @@ async function detectApiRoutes(
.sort(sortFiles)
.sort(sortFilesBySegmentCount);
const defaultRoutes: Route[] = [];
let defaultRoutes: Route[] = [];
for (const file of sortedFiles) {
// We only consider every file in the api directory
@@ -264,11 +266,27 @@ async function detectApiRoutes(
}
// 404 Route to disable directory listing
if (defaultRoutes.length) {
defaultRoutes.push({
status: 404,
src: '/api(\\/.*)?$',
});
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(/.*)?$',
});
}
}
return { defaultRoutes, error: null };
@@ -286,16 +304,22 @@ function getPublicBuilder(builders: Builder[]): Builder | null {
return builder || null;
}
export function detectOutputDirectory(builders: Builder[]): string | null {
// TODO: We eventually want to save the output directory to
// builder.config.outputDirectory so it is only detected once
const publicBuilder = getPublicBuilder(builders);
return publicBuilder ? publicBuilder.src.replace('/**/*', '') : null;
}
export async function detectRoutes(
files: string[],
builders: Builder[]
builders: Builder[],
featHandleMiss = false
): Promise<RoutesResult> {
const routesResult = await detectApiRoutes(files, builders);
const publicBuilder = getPublicBuilder(builders);
if (routesResult.defaultRoutes && publicBuilder) {
const directory = publicBuilder.src.replace('/**/*', '');
const routesResult = await detectApiRoutes(files, builders, featHandleMiss);
const directory = detectOutputDirectory(builders);
if (routesResult.defaultRoutes && directory && !featHandleMiss) {
routesResult.defaultRoutes.push({
src: '/(.*)',
dest: `/${directory}/$1`,

View File

@@ -13,9 +13,7 @@ export default async function detectGenericNodeProject({
return {
buildCommand: `${useNpm ? 'npm' : 'yarn'} run build`,
devCommand: `${useNpm ? 'npm' : 'yarn'} run ${
devCommand ? 'dev' : 'build'
}`,
devCommand: useNpm && devCommand ? `yarn run ${devCommand}` : undefined,
outputDirectory: 'public',
};
}

View File

@@ -39,6 +39,66 @@ export function spawnAsync(
});
}
export function execAsync(
command: string,
args: string[],
opts: SpawnOptions = {}
) {
return new Promise<{ stdout: string; stderr: string; code: number }>(
(resolve, reject) => {
opts.stdio = 'pipe';
const stdoutList: Buffer[] = [];
const stderrList: Buffer[] = [];
const child = spawn(command, args, opts);
child.stderr!.on('data', data => {
stderrList.push(data);
});
child.stdout!.on('data', data => {
stdoutList.push(data);
});
child.on('error', reject);
child.on('close', (code, signal) => {
if (code !== 0) {
return reject(
new Error(
`Program "${command}" exited with non-zero exit code ${code} ${signal}.`
)
);
}
return resolve({
code,
stdout: Buffer.concat(stdoutList).toString(),
stderr: Buffer.concat(stderrList).toString(),
});
});
}
);
}
export function spawnCommand(command: string, options: SpawnOptions = {}) {
if (process.platform === 'win32') {
return spawn('cmd.exe', ['/C', command], options);
}
return spawn('sh', ['-c', command], options);
}
export async function execCommand(command: string, options: SpawnOptions = {}) {
if (process.platform === 'win32') {
await spawnAsync('cmd.exe', ['/C', command], options);
} else {
await spawnAsync('sh', ['-c', command], options);
}
return true;
}
async function chmodPlusX(fsPath: string) {
const s = await fs.stat(fsPath);
const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise

View File

@@ -8,7 +8,10 @@ import getWriteableDirectory from './fs/get-writable-directory';
import glob from './fs/glob';
import rename from './fs/rename';
import {
execAsync,
spawnAsync,
execCommand,
spawnCommand,
installDependencies,
runPackageJsonScript,
runNpmInstall,
@@ -21,7 +24,7 @@ import {
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
import { detectBuilders } from './detect-builders';
import { detectRoutes } from './detect-routes';
import { detectRoutes, detectOutputDirectory } from './detect-routes';
import DetectorFilesystem from './detectors/filesystem';
import debug from './debug';
@@ -38,9 +41,12 @@ export {
getWriteableDirectory,
glob,
rename,
execAsync,
spawnAsync,
installDependencies,
runPackageJsonScript,
execCommand,
spawnCommand,
runNpmInstall,
runBundleInstall,
runPipInstall,
@@ -51,11 +57,15 @@ export {
shouldServe,
detectBuilders,
detectRoutes,
detectOutputDirectory,
debug,
isSymbolicLink,
getLambdaOptionsFromFunction,
};
export { detectBuildersLegacy } from './detect-builders-legacy';
export { detectRoutesLegacy } from './detect-routes-legacy';
export { detectDefaults } from './detectors';
export * from './schemas';
export * from './types';

View File

@@ -1,6 +1,7 @@
import FileRef from './file-ref';
import FileFsRef from './file-fs-ref';
import DetectorFilesystem from './detectors/filesystem';
import { Route } from '@now/routing-utils';
export interface Env {
[name: string]: string | undefined;
@@ -21,18 +22,6 @@ export interface Files {
[filePath: string]: File;
}
export interface Route {
src?: string;
dest?: string;
handle?: string;
type?: string;
headers?: {
[key: string]: string;
};
continue?: boolean;
status?: number;
}
export interface Config {
[key: string]:
| string
@@ -52,6 +41,13 @@ export interface Config {
zeroConfig?: boolean;
import?: { [key: string]: string };
functions?: BuilderFunctions;
outputDirectory?: string;
buildCommand?: string;
devCommand?: string;
framework?: {
slug: string;
version: string;
};
}
export interface Meta {

View File

@@ -502,6 +502,10 @@ describe('Test `detectBuilders`', () => {
config: {
zeroConfig: true,
buildCommand: 'yarn build',
framework: {
slug: 'next',
version: '9.0.0',
},
functions: {
'pages/api/teams/**': {
memory: 128,
@@ -569,6 +573,10 @@ describe('Test `detectBuilders`', () => {
config: {
zeroConfig: true,
buildCommand: 'yarn build',
framework: {
slug: 'next',
version: '9.0.0',
},
},
});
});
@@ -910,8 +918,12 @@ describe('Test `detectBuilders`', () => {
use: '@now/next',
src: 'package.json',
config: {
buildCommand: 'yarn build',
zeroConfig: true,
buildCommand: 'yarn build',
framework: {
slug: 'next',
version: '9.0.0',
},
},
});
@@ -924,6 +936,189 @@ describe('Test `detectBuilders`', () => {
});
});
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'];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
{
const files = ['api/user.go', 'api/user.js'];
const { builders } = await detectBuilders(files);
const { error } = await detectRoutes(files, builders, featHandleMiss);
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);
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);
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
);
expect(defaultRoutes).toBe(null);
expect(error.code).toBe('conflicting_file_path');
}
{
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
{
const files = [
'public/index.html',
'api/[endpoint].js',
'api/[endpoint]/[id].js',
];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
{
const detected = {
buildCommand: 'yarn build',
framework: {
slug: 'next',
version: '9.0.0',
},
};
const files = ['public/index.html', 'api/[endpoint].js'];
const { builders } = await detectBuilders(files, detected);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
{
const files = ['public/index.html'];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual([]);
}
{
const files = ['api/date/index.js', 'api/date.js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
{
const files = ['api/date.js', 'api/[date]/index.js'];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
{
const files = [
'api/index.ts',
'api/index.d.ts',
'api/users/index.ts',
'api/users/index.d.ts',
'api/food.ts',
'api/ts/gold.ts',
];
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
{
// 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 } = await detectRoutes(
files,
builders,
featHandleMiss
);
expect(defaultRoutes).toStrictEqual(expectedRoutes);
}
});
it('Test `detectRoutes`', async () => {
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
@@ -988,7 +1183,7 @@ it('Test `detectRoutes`', async () => {
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(files, builders);
expect(defaultRoutes[2].status).toBe(404);
expect(defaultRoutes[2].src).toBe('/api(\\/.*)?$');
expect(defaultRoutes[2].src).toBe('/api(/.*)?$');
expect(defaultRoutes[3].src).toBe('/(.*)');
expect(defaultRoutes[3].dest).toBe('/public/$1');
expect(defaultRoutes.length).toBe(4);
@@ -1008,7 +1203,7 @@ it('Test `detectRoutes`', async () => {
const { builders } = await detectBuilders(files, detected);
const { defaultRoutes } = await detectRoutes(files, builders);
expect(defaultRoutes[1].status).toBe(404);
expect(defaultRoutes[1].src).toBe('/api(\\/.*)?$');
expect(defaultRoutes[1].src).toBe('/api(/.*)?$');
expect(defaultRoutes.length).toBe(2);
}

View File

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

View File

@@ -239,7 +239,7 @@ export default async function main(ctx) {
return 1;
}
if (deployment.version === 2) {
output.error('Cannot scale a deployment containing builds');
output.error('Cannot scale a Now 2.0 deployment');
now.close();
return 1;
}

View File

@@ -1,4 +1,3 @@
import chalk from 'chalk';
import execa from 'execa';
import semver from 'semver';
import pipe from 'promisepipe';
@@ -8,7 +7,6 @@ import { extract } from 'tar-fs';
import { createHash } from 'crypto';
import { createGunzip } from 'zlib';
import { join, resolve } from 'path';
import { funCacheDir } from '@zeit/fun';
import { PackageJson } from '@now/build-utils';
import XDGAppPaths from 'xdg-app-paths';
import {
@@ -17,7 +15,6 @@ import {
readFile,
readJSON,
writeFile,
remove,
} from 'fs-extra';
import pkg from '../../../package.json';
@@ -104,11 +101,7 @@ export async function prepareBuilderDir() {
if (!hasBundledBuilders(dependencies)) {
const extractor = extract(builderDir);
await pipe(
createReadStream(bundledTarballPath),
createGunzip(),
extractor
);
await pipe(createReadStream(bundledTarballPath), createGunzip(), extractor);
}
return builderDir;

View File

@@ -1,4 +1,5 @@
import chalk from 'chalk';
import boxen from 'boxen';
import { format } from 'util';
import { Console } from 'console';
@@ -18,10 +19,32 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
}
function warn(str: string, slug: string | null = null) {
log(chalk`{yellow.bold WARN!} ${str}`);
if (slug !== null) {
log(`More details: https://err.sh/now/${slug}`);
const prevTerm = process.env.TERM;
if (!prevTerm) {
// workaround for https://github.com/sindresorhus/term-size/issues/13
process.env.TERM = 'xterm';
}
print(
boxen(
chalk.bold.yellow('WARN! ') +
str +
(slug ? `\nMore details: https://err.sh/now/${slug}` : ''),
{
padding: {
top: 0,
bottom: 0,
left: 1,
right: 1,
},
borderColor: 'yellow',
}
)
);
print('\n');
process.env.TERM = prevTerm;
}
function note(str: string) {
@@ -59,7 +82,7 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
_times: new Map(),
log(a: string, ...args: string[]) {
debug(format(a, ...args));
}
},
};
async function time(label: string, fn: Promise<any> | (() => Promise<any>)) {
@@ -85,6 +108,6 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
debug,
dim,
time,
note
note,
};
}

View File

@@ -58,9 +58,7 @@ export default async function preferV2Deployment({
)} is missing a ${cmd('start')} script. ${INFO}`;
}
} else if (!pkg && !hasDockerfile) {
return `Deploying to Now 2.0, because no ${highlight(
'Dockerfile'
)} was found. ${INFO}`;
return `Deploying to Now 2.0 automatically. ${INFO}`;
}
if (client && projectName) {

View File

@@ -1,5 +0,0 @@
{
"scripts": {
"build": "./hugo"
}
}

View File

@@ -1,4 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

View File

@@ -6698,21 +6698,21 @@ string.prototype.padstart@^3.0.0:
es-abstract "^1.4.3"
function-bind "^1.0.2"
string.prototype.trimleft@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz#68b6aa8e162c6a80e76e3a8a0c2e747186e271ff"
integrity sha1-aLaqjhYsaoDnbjqKDC50cYbicf8=
string.prototype.trimleft@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string.prototype.trimright@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz#ab4a56d802a01fbe7293e11e84f24dc8164661dd"
integrity sha1-q0pW2AKgH75yk+EehPJNyBZGYd0=
string.prototype.trimright@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string_decoder@^1.0.0, string_decoder@^1.1.1:
version "1.3.0"

View File

@@ -7307,21 +7307,21 @@ string.prototype.padstart@^3.0.0:
es-abstract "^1.4.3"
function-bind "^1.0.2"
string.prototype.trimleft@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz#68b6aa8e162c6a80e76e3a8a0c2e747186e271ff"
integrity sha1-aLaqjhYsaoDnbjqKDC50cYbicf8=
string.prototype.trimleft@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string.prototype.trimright@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz#ab4a56d802a01fbe7293e11e84f24dc8164661dd"
integrity sha1-q0pW2AKgH75yk+EehPJNyBZGYd0=
string.prototype.trimright@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string_decoder@^1.0.0, string_decoder@^1.1.1:
version "1.3.0"

View File

@@ -1238,7 +1238,7 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
define-properties@^1.1.2:
define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
@@ -1660,7 +1660,7 @@ fsevents@^1.2.7:
nan "^2.12.1"
node-pre-gyp "^0.12.0"
function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
function-bind@^1.1.0, function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
@@ -3305,21 +3305,21 @@ string-width@^1.0.1:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string.prototype.trimleft@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz#68b6aa8e162c6a80e76e3a8a0c2e747186e271ff"
integrity sha1-aLaqjhYsaoDnbjqKDC50cYbicf8=
string.prototype.trimleft@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string.prototype.trimright@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz#ab4a56d802a01fbe7293e11e84f24dc8164661dd"
integrity sha1-q0pW2AKgH75yk+EehPJNyBZGYd0=
string.prototype.trimright@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string_decoder@~0.10.31, string_decoder@~0.10.x:
version "0.10.31"

View File

@@ -5746,7 +5746,7 @@ fsevents@^2.0.6:
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a"
integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==
function-bind@^1.0.2, function-bind@^1.1.1:
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
@@ -11917,21 +11917,21 @@ string.prototype.trim@^1.1.2:
es-abstract "^1.13.0"
function-bind "^1.1.1"
string.prototype.trimleft@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz#68b6aa8e162c6a80e76e3a8a0c2e747186e271ff"
integrity sha1-aLaqjhYsaoDnbjqKDC50cYbicf8=
string.prototype.trimleft@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string.prototype.trimright@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz#ab4a56d802a01fbe7293e11e84f24dc8164661dd"
integrity sha1-q0pW2AKgH75yk+EehPJNyBZGYd0=
string.prototype.trimright@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
dependencies:
define-properties "^1.1.2"
function-bind "^1.0.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
string_decoder@^1.0.0, string_decoder@^1.1.1:
version "1.3.0"

View File

@@ -0,0 +1,2 @@
package.json
yarn.lock

File diff suppressed because it is too large Load Diff

View File

@@ -915,7 +915,7 @@ test('ensure we render a warning for deployments with no files', async t => {
// Ensure the warning is printed
t.true(
stderr.includes(
'> WARN! There are no files (or only files starting with a dot) inside your deployment.'
'WARN! There are no files (or only files starting with a dot) inside your deployment.'
)
);

View File

@@ -17,7 +17,7 @@ import getURL from './helpers/get-url';
import {
npm as getNpmFiles_,
docker as getDockerFiles_,
staticFiles as getStaticFiles_
staticFiles as getStaticFiles_,
} from '../src/util/get-files';
import didYouMean from '../src/util/init/did-you-mean';
import { isValidName } from '../src/util/is-valid-name';
@@ -32,7 +32,7 @@ const fixture = name => join(prefix, name);
const getNpmFiles = async dir => {
const { pkg, nowConfig, hasNowJson } = await readMetadata(dir, {
quiet: true,
strict: false
strict: false,
});
return getNpmFiles_(dir, pkg, nowConfig, { hasNowJson, output });
@@ -41,7 +41,7 @@ const getNpmFiles = async dir => {
const getDockerFiles = async dir => {
const { nowConfig, hasNowJson } = await readMetadata(dir, {
quiet: true,
strict: false
strict: false,
});
return getDockerFiles_(dir, nowConfig, { hasNowJson, output });
@@ -51,7 +51,7 @@ const getStaticFiles = async (dir, isBuilds = false) => {
const { nowConfig, hasNowJson } = await readMetadata(dir, {
deploymentType: 'static',
quiet: true,
strict: false
strict: false,
});
return getStaticFiles_(dir, nowConfig, { hasNowJson, output, isBuilds });
@@ -169,11 +169,9 @@ test('`now.files` overrides `.gitignore` in Static with custom config path', asy
const path = 'now-json-static-gitignore-override';
// Simulate custom args passed by the user
process.argv = [...process.argv, '--local-config', './now.json']
process.argv = [...process.argv, '--local-config', './now.json'];
let files = await getStaticFiles(
fixture(path)
);
let files = await getStaticFiles(fixture(path));
files = files.sort(alpha);
@@ -185,9 +183,7 @@ test('`now.files` overrides `.gitignore` in Static with custom config path', asy
test('`now.files` overrides `.gitignore` in Static', async t => {
const path = 'now-json-static-gitignore-override';
let files = await getStaticFiles(
fixture(path)
);
let files = await getStaticFiles(fixture(path));
files = files.sort(alpha);
t.is(files.length, 3);
@@ -337,7 +333,7 @@ test('support docker', async t => {
test('gets correct name of docker deployment', async t => {
const { name, deploymentType } = await readMetadata(fixture('dockerfile'), {
quiet: true,
strict: false
strict: false,
});
t.is(deploymentType, 'docker');
@@ -375,7 +371,7 @@ test('throw for unsupported `now.json` type property', async t => {
try {
await readMetadata(f, {
quiet: true,
strict: false
strict: false,
});
} catch (err) {
t.is(err.code, 'unsupported_deployment_type');
@@ -387,7 +383,7 @@ test('support `now.json` files with package.json non quiet', async t => {
const f = fixture('now-json-no-name');
const { deploymentType } = await readMetadata(f, {
quiet: false,
strict: false
strict: false,
});
t.is(deploymentType, 'npm');
@@ -404,7 +400,7 @@ test('support `now.json` files with package.json non quiet', async t => {
test('support `now.json` files with package.json non quiet not specified', async t => {
const f = fixture('now-json-no-name');
const { deploymentType } = await readMetadata(f, {
strict: false
strict: false,
});
t.is(deploymentType, 'npm');
@@ -423,7 +419,7 @@ test('No commands in Dockerfile with automatic strictness', async t => {
try {
await readMetadata(f, {
quiet: true
quiet: true,
});
} catch (err) {
t.is(err.code, 'no_dockerfile_commands');
@@ -437,7 +433,7 @@ test('No commands in Dockerfile', async t => {
try {
await readMetadata(f, {
quiet: true,
strict: true
strict: true,
});
} catch (err) {
t.is(err.code, 'no_dockerfile_commands');
@@ -451,7 +447,7 @@ test('Missing Dockerfile for `docker` type', async t => {
try {
await readMetadata(f, {
quiet: true,
strict: true
strict: true,
});
} catch (err) {
t.is(err.code, 'dockerfile_missing');
@@ -463,7 +459,7 @@ test('support `now.json` files with Dockerfile', async t => {
const f = fixture('now-json-docker');
const { deploymentType, nowConfig, hasNowJson } = await readMetadata(f, {
quiet: true,
strict: false
strict: false,
});
t.is(deploymentType, 'docker');
@@ -479,7 +475,7 @@ test('load name from Dockerfile', async t => {
const f = fixture('now-json-docker-name');
const { deploymentType, name } = await readMetadata(f, {
quiet: true,
strict: false
strict: false,
});
t.is(deploymentType, 'docker');
@@ -490,7 +486,7 @@ test('support `now.json` files with Dockerfile non quiet', async t => {
const f = fixture('now-json-docker');
const { deploymentType, nowConfig, hasNowJson } = await readMetadata(f, {
quiet: false,
strict: false
strict: false,
});
t.is(deploymentType, 'docker');
@@ -507,7 +503,7 @@ test('throws when both `now.json` and `package.json:now` exist', async t => {
try {
await readMetadata(fixture('now-json-throws'), {
quiet: true,
strict: false
strict: false,
});
} catch (err) {
e = err;
@@ -523,7 +519,7 @@ test('throws when `package.json` and `Dockerfile` exist', async t => {
try {
await readMetadata(fixture('multiple-manifests-throws'), {
quiet: true,
strict: false
strict: false,
});
} catch (err) {
e = err;
@@ -536,7 +532,7 @@ test('support `package.json:now.type` to bypass multiple manifests error', async
const f = fixture('type-in-package-now-with-dockerfile');
const { type, nowConfig, hasNowJson } = await readMetadata(f, {
quiet: true,
strict: false
strict: false,
});
t.is(type, 'npm');
t.is(nowConfig.type, 'npm');
@@ -544,16 +540,16 @@ test('support `package.json:now.type` to bypass multiple manifests error', async
});
test('friendly error for malformed JSON', async t => {
const err = await t.throwsAsync(
() => readMetadata(fixture('json-syntax-error'), {
const err = await t.throwsAsync(() =>
readMetadata(fixture('json-syntax-error'), {
quiet: true,
strict: false
strict: false,
})
);
t.is(err.name, 'JSONError');
t.is(
err.message,
'Unexpected token \'o\' at 2:5 in test/fixtures/unit/json-syntax-error/package.json\n oops\n ^'
"Unexpected token 'o' at 2:5 in test/fixtures/unit/json-syntax-error/package.json\n oops\n ^"
);
});
@@ -597,7 +593,7 @@ test('`wait` utility does not invoke spinner before n miliseconds', async t => {
const oraStub = sinon.stub().returns({
color: '',
start: () => {},
stop: () => {}
stop: () => {},
});
const timeOut = 200;
@@ -612,7 +608,7 @@ test('`wait` utility invokes spinner after n miliseconds', async t => {
const oraStub = sinon.stub().returns({
color: '',
start: () => {},
stop: () => {}
stop: () => {},
});
const timeOut = 200;
@@ -635,7 +631,7 @@ test('`wait` utility does not invoke spinner when stopped before delay', async t
const oraStub = sinon.stub().returns({
color: '',
start: () => {},
stop: () => {}
stop: () => {},
});
const timeOut = 200;
@@ -694,8 +690,8 @@ test('4xx response error as correct JSON', async t => {
const fn = async (req, res) => {
send(res, 400, {
error: {
message: 'The request is not correct'
}
message: 'The request is not correct',
},
});
};
@@ -721,7 +717,7 @@ test('5xx response error as HTML', async t => {
test('5xx response error with random JSON', async t => {
const fn = async (req, res) => {
send(res, 500, {
wrong: 'property'
wrong: 'property',
});
};
@@ -733,23 +729,27 @@ test('5xx response error with random JSON', async t => {
});
test('getProjectName with argv - option 1', t => {
const project = getProjectName({argv: {
name: 'abc'
}});
const project = getProjectName({
argv: {
name: 'abc',
},
});
t.is(project, 'abc');
});
test('getProjectName with argv - option 2', t => {
const project = getProjectName({argv: {
'--name': 'abc'
}});
const project = getProjectName({
argv: {
'--name': 'abc',
},
});
t.is(project, 'abc');
});
test('getProjectName with now.json', t => {
const project = getProjectName({
argv: {},
nowConfig: {name: 'abc'}
nowConfig: { name: 'abc' },
});
t.is(project, 'abc');
});
@@ -758,7 +758,7 @@ test('getProjectName with a file', t => {
const project = getProjectName({
argv: {},
nowConfig: {},
isFile: true
isFile: true,
});
t.is(project, 'files');
});
@@ -767,7 +767,7 @@ test('getProjectName with a multiple files', t => {
const project = getProjectName({
argv: {},
nowConfig: {},
paths: ['/tmp/aa/abc.png', '/tmp/aa/bbc.png']
paths: ['/tmp/aa/abc.png', '/tmp/aa/bbc.png'],
});
t.is(project, 'files');
});
@@ -776,7 +776,7 @@ test('getProjectName with a directory', t => {
const project = getProjectName({
argv: {},
nowConfig: {},
paths: ['/tmp/aa']
paths: ['/tmp/aa'],
});
t.is(project, 'aa');
});
@@ -797,8 +797,8 @@ test('4xx error message with proper message', async t => {
const fn = async (req, res) => {
send(res, 403, {
error: {
message: 'This is a test'
}
message: 'This is a test',
},
});
};
@@ -813,8 +813,8 @@ test('5xx error message with proper message', async t => {
const fn = async (req, res) => {
send(res, 500, {
error: {
message: 'This is a test'
}
message: 'This is a test',
},
});
};
@@ -842,8 +842,8 @@ test('4xx response error as correct JSON with more properties', async t => {
send(res, 403, {
error: {
message: 'The request is not correct',
additionalProperty: 'test'
}
additionalProperty: 'test',
},
});
};
@@ -861,8 +861,8 @@ test('429 response error with retry header', async t => {
send(res, 429, {
error: {
message: 'You were rate limited'
}
message: 'You were rate limited',
},
});
};
@@ -878,8 +878,8 @@ test('429 response error without retry header', async t => {
const fn = async (req, res) => {
send(res, 429, {
error: {
message: 'You were rate limited'
}
message: 'You were rate limited',
},
});
};
@@ -891,8 +891,41 @@ test('429 response error without retry header', async t => {
t.is(formatted.retryAfter, undefined);
});
test('guess user\'s intention with custom didYouMean', async t => {
const examples = ['apollo','create-react-app','docz','gatsby','go','gridsome','html-minifier','mdx-deck','monorepo','nextjs','nextjs-news','nextjs-static','node-server','nodejs','nodejs-canvas-partyparrot','nodejs-coffee','nodejs-express','nodejs-hapi','nodejs-koa','nodejs-koa-ts','nodejs-pdfkit','nuxt-static','optipng','php-7','puppeteer-screenshot','python','redirect','serverless-ssr-reddit','static','vue','vue-ssr','vuepress'];
test("guess user's intention with custom didYouMean", async t => {
const examples = [
'apollo',
'create-react-app',
'docz',
'gatsby',
'go',
'gridsome',
'html-minifier',
'mdx-deck',
'monorepo',
'nextjs',
'nextjs-news',
'nextjs-static',
'node-server',
'nodejs',
'nodejs-canvas-partyparrot',
'nodejs-coffee',
'nodejs-express',
'nodejs-hapi',
'nodejs-koa',
'nodejs-koa-ts',
'nodejs-pdfkit',
'nuxt-static',
'optipng',
'php-7',
'puppeteer-screenshot',
'python',
'redirect',
'serverless-ssr-reddit',
'static',
'vue',
'vue-ssr',
'vuepress',
];
t.is(didYouMean('md', examples, 0.7), 'mdx-deck');
t.is(didYouMean('koa', examples, 0.7), 'nodejs-koa');
@@ -906,16 +939,26 @@ test('check platform version chanage with `preferV2Deployment`', async t => {
const pkg = null;
const hasDockerfile = false;
const hasServerfile = false;
const reason = await preferV2Deployment({ localConfig, pkg, hasDockerfile, hasServerfile });
t.regex(reason, /Dockerfile/gm);
const reason = await preferV2Deployment({
localConfig,
pkg,
hasDockerfile,
hasServerfile,
});
t.regex(reason, /Deploying to Now 2\.0 automatically/gm);
}
{
const localConfig = undefined;
const pkg = { scripts: { 'start': 'echo hi' } };
const pkg = { scripts: { start: 'echo hi' } };
const hasDockerfile = false;
const hasServerfile = false;
const reason = await preferV2Deployment({ localConfig, pkg, hasDockerfile, hasServerfile });
const reason = await preferV2Deployment({
localConfig,
pkg,
hasDockerfile,
hasServerfile,
});
t.is(reason, null);
}
@@ -924,16 +967,26 @@ test('check platform version chanage with `preferV2Deployment`', async t => {
const pkg = { scripts: { 'now-start': 'echo hi' } };
const hasDockerfile = false;
const hasServerfile = false;
const reason = await preferV2Deployment({ localConfig, pkg, hasDockerfile, hasServerfile });
const reason = await preferV2Deployment({
localConfig,
pkg,
hasDockerfile,
hasServerfile,
});
t.is(reason, null);
}
{
const localConfig = { 'version': 1 };
const localConfig = { version: 1 };
const pkg = null;
const hasDockerfile = false;
const hasServerfile = false;
const reason = await preferV2Deployment({ localConfig, pkg, hasDockerfile, hasServerfile });
const reason = await preferV2Deployment({
localConfig,
pkg,
hasDockerfile,
hasServerfile,
});
t.is(reason, null);
}
@@ -942,16 +995,26 @@ test('check platform version chanage with `preferV2Deployment`', async t => {
const pkg = null;
const hasDockerfile = true;
const hasServerfile = false;
const reason = await preferV2Deployment({ localConfig, pkg, hasDockerfile, hasServerfile });
const reason = await preferV2Deployment({
localConfig,
pkg,
hasDockerfile,
hasServerfile,
});
t.is(reason, null);
}
{
const localConfig = undefined;
const pkg = { scripts: { 'build': 'echo hi' } };
const pkg = { scripts: { build: 'echo hi' } };
const hasDockerfile = false;
const hasServerfile = false;
const reason = await preferV2Deployment({ localConfig, pkg, hasDockerfile, hasServerfile });
const reason = await preferV2Deployment({
localConfig,
pkg,
hasDockerfile,
hasServerfile,
});
t.regex(reason, /package\.json/gm);
}
@@ -960,7 +1023,12 @@ test('check platform version chanage with `preferV2Deployment`', async t => {
const pkg = null;
const hasDockerfile = false;
const hasServerfile = true;
const reason = await preferV2Deployment({ localConfig, pkg, hasDockerfile, hasServerfile });
const reason = await preferV2Deployment({
localConfig,
pkg,
hasDockerfile,
hasServerfile,
});
t.is(reason, null);
}
});

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "2.2.0",
"version": "2.3.1",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/next-js",

View File

@@ -1,16 +1,3 @@
import { ChildProcess, fork } from 'child_process';
import {
pathExists,
readFile,
unlink as unlinkFile,
writeFile,
lstatSync,
} from 'fs-extra';
import os from 'os';
import path from 'path';
import resolveFrom from 'resolve-from';
import semver from 'semver';
import {
BuildOptions,
Config,
@@ -20,6 +7,7 @@ import {
FileBlob,
FileFsRef,
Files,
getLambdaOptionsFromFunction,
getNodeVersion,
getSpawnOptions,
glob,
@@ -27,13 +15,27 @@ import {
PackageJson,
PrepareCacheOptions,
Prerender,
Route,
runNpmInstall,
runPackageJsonScript,
getLambdaOptionsFromFunction,
} from '@now/build-utils';
import { Route } from '@now/routing-utils';
import {
convertRedirects,
convertRewrites,
} from '@now/routing-utils/dist/superstatic';
import nodeFileTrace, { NodeFileTraceReasons } from '@zeit/node-file-trace';
import { ChildProcess, fork } from 'child_process';
import {
lstatSync,
pathExists,
readFile,
unlink as unlinkFile,
writeFile,
} from 'fs-extra';
import os from 'os';
import path from 'path';
import resolveFrom from 'resolve-from';
import semver from 'semver';
import createServerlessConfig from './create-serverless-config';
import nextLegacyVersions from './legacy-versions';
import {
@@ -43,10 +45,14 @@ import {
excludeFiles,
ExperimentalTraceVersion,
getDynamicRoutes,
getExportIntent,
getExportStatus,
getNextConfig,
getPathsInside,
getPrerenderManifest,
getRoutes,
getRoutesManifest,
getSourceFilePathFromPage,
isDynamicRoute,
normalizePackageJson,
normalizePage,
@@ -54,15 +60,8 @@ import {
stringMap,
syncEnvVars,
validateEntrypoint,
getSourceFilePathFromPage,
getRoutesManifest,
} from './utils';
import {
convertRedirects,
convertRewrites,
} from '@now/routing-utils/dist/superstatic';
interface BuildParamsMeta {
isDev: boolean | undefined;
env?: EnvConfig;
@@ -355,6 +354,83 @@ export const build = async ({
}
}
const userExport = await getExportStatus(entryPath);
if (userExport) {
const exportIntent = await getExportIntent(entryPath);
const { trailingSlash = false } = exportIntent || {};
const resultingExport = await getExportStatus(entryPath);
if (!resultingExport) {
throw new Error(
'Exporting Next.js app failed. Please check your build logs and contact us if this continues.'
);
}
if (resultingExport.success !== true) {
throw new Error(
'Export of Next.js app failed. Please check your build logs.'
);
}
const outDirectory = resultingExport.outDirectory;
debug(`next export should use trailing slash: ${trailingSlash}`);
// This handles pages, `public/`, and `static/`.
const filesAfterBuild = await glob('**', outDirectory);
const output: Files = { ...filesAfterBuild };
// Strip `.html` extensions from build output
Object.entries(output)
.filter(([name]) => name.endsWith('.html'))
.forEach(([name, value]) => {
const cleanName = name.slice(0, -5);
delete output[name];
output[cleanName] = value;
if (value.type === 'FileBlob' || value.type === 'FileFsRef') {
value.contentType = value.contentType || 'text/html; charset=utf-8';
}
});
return {
output,
routes: [
// TODO: low priority: handle trailingSlash
// redirects take the highest priority
...redirects,
// Before we handle static files we need to set proper caching headers
{
// This ensures we only match known emitted-by-Next.js files and not
// user-emitted files which may be missing a hash in their filename.
src: path.join(
'/',
entryDirectory,
'_next/static/(?:[^/]+/pages|chunks|runtime|css|media)/.+'
),
// Next.js assets contain a hash or entropy in their filenames, so they
// are guaranteed to be unique and cacheable indefinitely.
headers: { 'cache-control': 'public,max-age=31536000,immutable' },
continue: true,
},
{
src: path.join('/', entryDirectory, '_next(?!/data(?:/|$))(?:/.*)?'),
},
// Next.js pages, `static/` folder, reserved assets, and `public/`
// folder
{ handle: 'filesystem' },
...rewrites,
// Dynamic routes
// TODO: do we want to do this?: ...dynamicRoutes,
],
watch: [],
childProcesses: [],
};
}
if (isLegacy) {
debug('Running npm install --production...');
await runNpmInstall(

View File

@@ -11,9 +11,9 @@ import {
FileFsRef,
streamToBuffer,
Lambda,
Route,
isSymbolicLink,
} from '@now/build-utils';
import { Route, Source } from '@now/routing-utils';
type stringMap = { [key: string]: string };
@@ -279,13 +279,13 @@ async function getRoutes(
if (!isPublic) continue;
const fileName = path.relative('public', relativePath);
const route = {
const route: Source = {
src: `${prefix}${fileName}`,
dest: `${url}/${fileName}`,
};
// Only add the route if a page is not already using it
if (!routes.some(r => r.src === route.src)) {
if (!routes.some(r => (r as Source).src === route.src)) {
routes.push(route);
}
}
@@ -624,6 +624,73 @@ export type NextPrerenderedRoutes = {
};
};
export async function getExportIntent(
entryPath: string
): Promise<false | { trailingSlash: boolean }> {
const pathExportMarker = path.join(entryPath, '.next', 'export-marker.json');
const hasExportMarker: boolean = await fs
.access(pathExportMarker, fs.constants.F_OK)
.then(() => true)
.catch(() => false);
if (!hasExportMarker) {
return false;
}
const manifest: {
version: 1;
exportTrailingSlash: boolean;
hasExportPathMap: boolean;
} = JSON.parse(await fs.readFile(pathExportMarker, 'utf8'));
switch (manifest.version) {
case 1: {
if (manifest.hasExportPathMap !== true) {
return false;
}
return { trailingSlash: manifest.exportTrailingSlash };
}
default: {
return false;
}
}
}
export async function getExportStatus(
entryPath: string
): Promise<false | { success: boolean; outDirectory: string }> {
const pathExportDetail = path.join(entryPath, '.next', 'export-detail.json');
const hasExportDetail: boolean = await fs
.access(pathExportDetail, fs.constants.F_OK)
.then(() => true)
.catch(() => false);
if (!hasExportDetail) {
return false;
}
const manifest: {
version: 1;
success: boolean;
outDirectory: string;
} = JSON.parse(await fs.readFile(pathExportDetail, 'utf8'));
switch (manifest.version) {
case 1: {
return {
success: !!manifest.success,
outDirectory: manifest.outDirectory,
};
}
default: {
return false;
}
}
}
export async function getPrerenderManifest(
entryPath: string
): Promise<NextPrerenderedRoutes> {

View File

@@ -1,8 +0,0 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{ "path": "/nested/fb", "mustContain": "Hello Firebase: <!-- -->0" },
{ "path": "/nested/moar/fb", "mustContain": "Hello Firebase: <!-- -->0" }
]
}

View File

@@ -1,11 +0,0 @@
{
"engines": {
"node": "8.10.x"
},
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"firebase": "6.3.4"
}
}

View File

@@ -1,19 +0,0 @@
import firebase from 'firebase/app';
import 'firebase/firestore';
if (!firebase.apps.length) {
firebase.initializeApp({ projectId: 'noop' });
}
const store = firebase.firestore();
const Comp = ({ results }) => {
return <div>Hello Firebase: {results}</div>;
};
Comp.getInitialProps = async () => {
const query = await store.collection('users').get();
return { results: query.size };
};
export default Comp;

View File

@@ -1,19 +0,0 @@
import firebase from 'firebase/app';
import 'firebase/firestore';
if (!firebase.apps.length) {
firebase.initializeApp({ projectId: 'noop' });
}
const store = firebase.firestore();
const Comp = ({ results }) => {
return <div>Hello Firebase: {results}</div>;
};
Comp.getInitialProps = async () => {
const query = await store.collection('users').get();
return { results: query.size };
};
export default Comp;

View File

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

View File

@@ -0,0 +1,10 @@
{
"scripts": {
"build": "next build && next export"
},
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

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

View File

@@ -0,0 +1,5 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
};

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/",
"mustContain": "Hi There"
},
{
"path": "/about",
"mustContain": "Hi on About"
},
{
"path": "/",
"mustContain": "nextExport\":true"
}
]
}

View File

@@ -0,0 +1,10 @@
{
"scripts": {
"build": "next build && next export"
},
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,7 @@
function About() {
return <div>Hi on About</div>;
}
About.getInitialProps = () => ({});
export default About;

View File

@@ -0,0 +1,7 @@
function Home() {
return <div>Hi There</div>;
}
Home.getInitialProps = () => ({});
export default Home;

View File

@@ -0,0 +1,6 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
exportPathMap: d => d,
};

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/",
"mustContain": "Hi There"
},
{
"path": "/about",
"mustContain": "Hi on About"
},
{
"path": "/",
"mustNotContain": "nextExport\":true"
}
]
}

View File

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

View File

@@ -0,0 +1,7 @@
function About() {
return <div>Hi on About</div>;
}
About.getInitialProps = () => ({});
export default About;

View File

@@ -0,0 +1,7 @@
function Home() {
return <div>Hi There</div>;
}
Home.getInitialProps = () => ({});
export default Home;

View File

@@ -0,0 +1,24 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
exportPathMap: d => d,
experimental: {
async rewrites() {
return [
{
source: '/first',
destination: '/',
},
];
},
async redirects() {
return [
{
source: '/second',
destination: '/about',
},
];
},
},
};

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/first",
"mustContain": "Hi There"
},
{
"path": "/second",
"mustContain": "Hi on About"
},
{
"path": "/",
"mustContain": "nextExport\":true"
}
]
}

View File

@@ -0,0 +1,10 @@
{
"scripts": {
"build": "next build && next export"
},
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,7 @@
function About() {
return <div>Hi on About</div>;
}
About.getInitialProps = () => ({});
export default About;

View File

@@ -0,0 +1,7 @@
function Home() {
return <div>Hi There</div>;
}
Home.getInitialProps = () => ({});
export default Home;

View File

@@ -0,0 +1,8 @@
const withOffline = require('next-offline');
module.exports = withOffline({
generateBuildId() {
return 'testing-build-id';
},
exportPathMap: d => d,
});

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/",
"mustContain": "Hi There"
},
{
"path": "/about",
"mustContain": "Hi on About"
},
{
"path": "/",
"mustNotContain": "nextExport\":true"
}
]
}

View File

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

View File

@@ -0,0 +1,7 @@
function About() {
return <div>Hi on About</div>;
}
About.getInitialProps = () => ({});
export default About;

View File

@@ -0,0 +1,7 @@
function Home() {
return <div>Hi There</div>;
}
Home.getInitialProps = () => ({});
export default Home;

View File

@@ -3,7 +3,7 @@ const path = require('path');
const {
packAndDeploy,
testDeployment
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(4 * 60 * 1000);

View File

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

View File

@@ -13,47 +13,7 @@ import {
BuildOptions,
debug,
} from '@now/build-utils';
async function pipInstall(pipPath: string, workDir: string, ...args: string[]) {
const target = '.';
// See: https://github.com/pypa/pip/issues/4222#issuecomment-417646535
//
// Disable installing to the Python user install directory, which is
// the default behavior on Debian systems and causes error:
//
// distutils.errors.DistutilsOptionError: can't combine user with
// prefix, exec_prefix/home, or install_(plat)base
process.env.PIP_USER = '0';
debug(
`Running "pip install --disable-pip-version-check --target ${target} --upgrade ${args.join(
' '
)}"...`
);
try {
await execa(
pipPath,
[
'install',
'--disable-pip-version-check',
'--target',
target,
'--upgrade',
...args,
],
{
cwd: workDir,
stdio: 'pipe',
}
);
} catch (err) {
console.log(
`Failed to run "pip install --disable-pip-version-check --target ${target} --upgrade ${args.join(
' '
)}"...`
);
throw err;
}
}
import { installRequirement, installRequirementsFile } from './install';
async function pipenvConvert(cmd: string, srcDir: string) {
debug('Running pipfile2req...');
@@ -70,16 +30,17 @@ async function pipenvConvert(cmd: string, srcDir: string) {
export const version = 3;
export const build = async ({
workPath,
files: originalFiles,
export async function downloadFilesInWorkPath({
entrypoint,
meta = {},
workPath,
files,
meta,
config,
}: BuildOptions) => {
let downloadedFiles = await download(originalFiles, workPath, meta);
if (meta.isDev) {
}: BuildOptions) {
debug('Downloading user files...');
let downloadedFiles = await download(files, workPath, meta);
if (meta && meta.isDev) {
let base = null;
if (config && config.zeroConfig) {
@@ -93,8 +54,23 @@ export const build = async ({
downloadedFiles = await glob('**', destNow);
workPath = destNow;
}
return workPath;
}
const pipPath = 'pip3';
export const build = async ({
workPath,
files: originalFiles,
entrypoint,
meta = {},
config,
}: BuildOptions) => {
workPath = await downloadFilesInWorkPath({
workPath,
files: originalFiles,
entrypoint,
meta,
config,
});
try {
// See: https://stackoverflow.com/a/44728772/376773
@@ -113,8 +89,13 @@ export const build = async ({
throw err;
}
console.log('Installing dependencies...');
await pipInstall(pipPath, workPath, 'werkzeug');
console.log('Installing required dependencies...');
await installRequirement({
dependency: 'werkzeug',
workPath,
meta,
});
let fsFiles = await glob('**', workPath);
const entryDirectory = dirname(entrypoint);
@@ -133,12 +114,12 @@ export const build = async ({
// to not be part of the lambda environment. By using pip's `--target` directive we can isolate
// it into a separate folder.
const tempDir = await getWriteableDirectory();
await pipInstall(
pipPath,
tempDir,
'pipfile-requirements',
'--no-warn-script-location'
);
await installRequirement({
dependency: 'pipfile-requirements',
workPath: tempDir,
meta,
args: ['--no-warn-script-location'],
});
// Python needs to know where to look up all the packages we just installed.
// We tell it to use the same location as used with `--target`
@@ -153,11 +134,19 @@ export const build = async ({
if (fsFiles[requirementsTxt]) {
debug('Found local "requirements.txt"');
const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
await pipInstall(pipPath, workPath, '-r', requirementsTxtPath);
await installRequirementsFile({
filePath: requirementsTxtPath,
workPath,
meta,
});
} else if (fsFiles['requirements.txt']) {
debug('Found global "requirements.txt"');
const requirementsTxtPath = fsFiles['requirements.txt'].fsPath;
await pipInstall(pipPath, workPath, '-r', requirementsTxtPath);
await installRequirementsFile({
filePath: requirementsTxtPath,
workPath,
meta,
});
}
const originalPyPath = join(__dirname, '..', 'now_init.py');
@@ -197,3 +186,6 @@ export const build = async ({
};
export { shouldServe };
// internal only - expect breaking changes if other packages depend on these exports
export { installRequirement, installRequirementsFile };

View File

@@ -0,0 +1,120 @@
import execa from 'execa';
import { debug, Meta } from '@now/build-utils';
const pipPath = 'pip3';
async function isInstalled(dependency: string, cwd: string) {
try {
await execa('python3', ['-c', `"import ${dependency}"`], {
stdio: 'pipe',
cwd,
});
return true;
} catch (err) {
return false;
}
}
const makeRequirementsCheckCode = (requirementsPath: string) => `
import distutils.text_file
import pkg_resources
from pkg_resources import DistributionNotFound, VersionConflict
dependencies = distutils.text_file.TextFile(filename='${requirementsPath}').readlines()
pkg_resources.require(dependencies)
`;
async function areRequirementsInstalled(requirementsPath: string, cwd: string) {
try {
await execa(
'python3',
['-c', makeRequirementsCheckCode(requirementsPath)],
{
stdio: 'pipe',
cwd,
}
);
return true;
} catch (err) {
return false;
}
}
async function pipInstall(workPath: string, args: string[]) {
const target = '.';
// See: https://github.com/pypa/pip/issues/4222#issuecomment-417646535
//
// Disable installing to the Python user install directory, which is
// the default behavior on Debian systems and causes error:
//
// distutils.errors.DistutilsOptionError: can't combine user with
// prefix, exec_prefix/home, or install_(plat)base
process.env.PIP_USER = '0';
debug(
`Running "pip install --disable-pip-version-check --target ${target} --upgrade ${args.join(
' '
)}"...`
);
try {
await execa(
pipPath,
[
'install',
'--disable-pip-version-check',
'--target',
target,
'--upgrade',
...args,
],
{
cwd: workPath,
stdio: 'pipe',
}
);
} catch (err) {
console.log(
`Failed to run "pip install --disable-pip-version-check --target ${target} --upgrade ${args.join(
' '
)}"...`
);
throw err;
}
}
interface InstallRequirementArg {
dependency: string;
workPath: string;
meta: Meta;
args?: string[];
}
export async function installRequirement({
dependency,
workPath,
meta,
args = [],
}: InstallRequirementArg) {
if (meta.isDev || !(await isInstalled(dependency, workPath))) {
debug(`Skipping ${dependency} dependency installation, already installed`);
return;
}
await pipInstall(workPath, [dependency, ...args]);
}
interface InstallRequirementsFileArg {
filePath: string;
workPath: string;
meta: Meta;
args?: string[];
}
export async function installRequirementsFile({
filePath,
workPath,
meta,
args = [],
}: InstallRequirementsFileArg) {
if (meta.isDev && (await areRequirementsInstalled(filePath, workPath))) {
debug(`Skipping requirements file installation, already installed`);
return;
}
await pipInstall(workPath, ['-r', filePath, ...args]);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/routing-utils",
"version": "1.4.0",
"version": "1.4.1-canary.2",
"description": "ZEIT Now routing utilities",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -23,6 +23,7 @@
"path-to-regexp": "6.1.0"
},
"devDependencies": {
"@types/node": "12.12.20",
"ajv": "^6.0.0",
"typescript": "3.5.2"
},

View File

@@ -19,44 +19,53 @@ import {
export { getCleanUrls } from './superstatic';
export { mergeRoutes } from './merge';
const VALID_HANDLE_VALUES = ['filesystem', 'hit', 'miss'] as const;
const validHandleValues = new Set<string>(VALID_HANDLE_VALUES);
export type HandleValue = typeof VALID_HANDLE_VALUES[number];
export function isHandler(route: Route): route is Handler {
return typeof (route as Handler).handle !== 'undefined';
}
export function isValidHandleValue(handle: string): handle is HandleValue {
return validHandleValues.has(handle);
}
export function normalizeRoutes(inputRoutes: Route[] | null): NormalizedRoutes {
if (!inputRoutes || inputRoutes.length === 0) {
return { routes: inputRoutes, error: null };
}
const routes: Route[] = [];
const handling: string[] = [];
const errors = [];
const handling: HandleValue[] = [];
const errors: NowErrorNested[] = [];
// We don't want to treat the input routes as references
inputRoutes.forEach(r => routes.push(Object.assign({}, r)));
for (const route of routes) {
if (isHandler(route)) {
// typeof { handle: string }
if (Object.keys(route).length !== 1) {
errors.push({
message: `Cannot have any other keys when handle is used (handle: ${route.handle})`,
handle: route.handle,
});
}
if (!['filesystem'].includes(route.handle)) {
const { handle } = route;
if (!isValidHandleValue(handle)) {
errors.push({
message: `This is not a valid handler (handle: ${route.handle})`,
handle: route.handle,
message: `This is not a valid handler (handle: ${handle})`,
handle: handle,
});
continue;
}
if (handling.includes(route.handle)) {
if (handling.includes(handle)) {
errors.push({
message: `You can only handle something once (handle: ${route.handle})`,
handle: route.handle,
message: `You can only handle something once (handle: ${handle})`,
handle: handle,
});
} else {
handling.push(route.handle);
handling.push(handle);
}
} else if (route.src) {
// Route src should always start with a '^'
@@ -76,6 +85,35 @@ export function normalizeRoutes(inputRoutes: Route[] | null): NormalizedRoutes {
if (regError) {
errors.push(regError);
}
// The last seen handling is the current handler
const handleValue = handling[handling.length - 1];
if (handleValue === 'hit') {
if (route.dest) {
errors.push({
message: `You cannot assign "dest" after "handle: hit"`,
src: route.src,
});
}
if (!route.continue) {
errors.push({
message: `You must assign "continue: true" after "handle: hit"`,
src: route.src,
});
}
} else if (handleValue === 'miss') {
if (route.dest && !route.check) {
errors.push({
message: `You must assign "check: true" after "handle: miss"`,
src: route.src,
});
} else if (!route.dest && !route.continue) {
errors.push({
message: `You must assign "continue: true" after "handle: miss"`,
src: route.src,
});
}
}
} else {
errors.push({
message: 'A route must set either handle or src',

View File

@@ -1,5 +1,5 @@
import { Route, MergeRoutesProps } from './types';
import { isHandler } from './index';
import { Route, MergeRoutesProps, Build } from './types';
import { isHandler, HandleValue } from './index';
interface BuilderToRoute {
[use: string]: Route[];
@@ -9,6 +9,19 @@ interface BuilderRoutes {
[entrypoint: string]: BuilderToRoute;
}
function getBuilderRoutesMapping(builds: Build[]) {
const builderRoutes: BuilderRoutes = {};
for (const { entrypoint, routes, use } of builds) {
if (routes) {
if (!builderRoutes[entrypoint]) {
builderRoutes[entrypoint] = {};
}
builderRoutes[entrypoint][use] = routes;
}
}
return builderRoutes;
}
function getCheckAndContinue(
routes: Route[]
): { checks: Route[]; continues: Route[]; others: Route[] } {
@@ -19,7 +32,11 @@ function getCheckAndContinue(
for (const route of routes) {
if (isHandler(route)) {
// Should never happen, only here to make TS happy
others.push(route);
throw new Error(
`Unexpected route found in getCheckAndContinue(): ${JSON.stringify(
route
)}`
);
} else if (route.check) {
checks.push(route);
} else if (route.continue) {
@@ -32,73 +49,63 @@ function getCheckAndContinue(
}
export function mergeRoutes({ userRoutes, builds }: MergeRoutesProps): Route[] {
const usersRoutesBefore: Route[] = [];
const usersRoutesAfter: Route[] = [];
const builderRoutes: BuilderRoutes = {};
const builderRoutesBefore: Route[] = [];
const builderRoutesAfter: Route[] = [];
let foundUserDefinedFilesystem = false;
const userHandleMap = new Map<HandleValue | null, Route[]>();
let userPrevHandle: HandleValue | null = null;
(userRoutes || []).forEach(route => {
if (!foundUserDefinedFilesystem) {
if (isHandler(route) && route.handle === 'filesystem') {
foundUserDefinedFilesystem = true;
} else {
usersRoutesBefore.push(route);
}
if (isHandler(route)) {
userPrevHandle = route.handle;
} else {
usersRoutesAfter.push(route);
const routes = userHandleMap.get(userPrevHandle);
if (!routes) {
userHandleMap.set(userPrevHandle, [route]);
} else {
routes.push(route);
}
}
});
// Convert build results to object mapping
for (const build of builds) {
if (build.routes) {
if (!builderRoutes[build.entrypoint]) {
builderRoutes[build.entrypoint] = {};
}
builderRoutes[build.entrypoint][build.use] = build.routes.map(route => {
//route.built = true; // TODO: is this necessary?
return route;
});
}
}
const builderHandleMap = new Map<HandleValue | null, Route[]>();
const builderRoutes = getBuilderRoutesMapping(builds);
const sortedPaths = Object.keys(builderRoutes).sort();
sortedPaths.forEach(path => {
const br = builderRoutes[path];
const sortedBuilders = Object.keys(br).sort();
sortedBuilders.forEach(use => {
let isBefore = true;
let builderPrevHandle: HandleValue | null = null;
br[use].forEach(route => {
if (isBefore) {
if (isHandler(route) && route.handle === 'filesystem') {
isBefore = false;
} else {
builderRoutesBefore.push(route);
}
if (isHandler(route)) {
builderPrevHandle = route.handle;
} else {
builderRoutesAfter.push(route);
const routes = builderHandleMap.get(builderPrevHandle);
if (!routes) {
builderHandleMap.set(builderPrevHandle, [route]);
} else {
routes.push(route);
}
}
});
});
});
const builderBefore = getCheckAndContinue(builderRoutesBefore);
const builderAfter = getCheckAndContinue(builderRoutesAfter);
const outputRoutes: Route[] = [];
outputRoutes.push(...builderBefore.continues);
outputRoutes.push(...usersRoutesBefore);
outputRoutes.push(...builderBefore.checks);
outputRoutes.push(...builderBefore.others);
if (usersRoutesAfter.length > 0 || builderRoutesAfter.length > 0) {
outputRoutes.push({ handle: 'filesystem' });
const uniqueHandleValues = new Set([
...userHandleMap.keys(),
...builderHandleMap.keys(),
]);
for (const handle of uniqueHandleValues) {
const userRoutes = userHandleMap.get(handle) || [];
const builderRoutes = builderHandleMap.get(handle) || [];
const builderSorted = getCheckAndContinue(builderRoutes);
if (
handle !== null &&
(userRoutes.length > 0 || builderRoutes.length > 0)
) {
outputRoutes.push({ handle });
}
outputRoutes.push(...builderSorted.continues);
outputRoutes.push(...userRoutes);
outputRoutes.push(...builderSorted.checks);
outputRoutes.push(...builderSorted.others);
}
outputRoutes.push(...builderAfter.continues);
outputRoutes.push(...usersRoutesAfter);
outputRoutes.push(...builderAfter.checks);
outputRoutes.push(...builderAfter.others);
return outputRoutes;
}

View File

@@ -2,8 +2,8 @@
* This converts Superstatic configuration to Now.json Routes
* See https://github.com/firebase/superstatic#configuration
*/
import { pathToRegexp, Key } from 'path-to-regexp';
import { parse as parseUrl, format as formatUrl } from 'url';
import { pathToRegexp, compile, Key } from 'path-to-regexp';
import { Route, NowRedirect, NowRewrite, NowHeader } from './types';
export function getCleanUrls(
@@ -108,10 +108,21 @@ function isString(key: any): key is string {
}
function replaceSegments(segments: string[], destination: string): string {
if (destination.includes(':')) {
const parsedDestination = parseUrl(destination, true);
let { pathname } = parsedDestination;
pathname = pathname || '';
if (pathname.includes(':') && segments.length > 0) {
const compiler = compile(pathname);
const indexes: { [k: string]: string } = {};
segments.forEach((name, index) => {
const r = new RegExp(':' + name, 'g');
destination = destination.replace(r, toSegmentDest(index));
indexes[name] = toSegmentDest(index);
});
pathname = compiler(indexes);
destination = formatUrl({
...parsedDestination,
pathname,
});
} else if (segments.length > 0) {
let prefix = '?';

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