Compare commits
29 Commits
@now/node@
...
@now/next@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92a40db048 | ||
|
|
502aad7c2b | ||
|
|
b49afb61a6 | ||
|
|
d380902ad3 | ||
|
|
ffaed62094 | ||
|
|
b0adeb68fe | ||
|
|
2372832654 | ||
|
|
e6a9586b7e | ||
|
|
9687415eed | ||
|
|
49b375ed6a | ||
|
|
906dea096e | ||
|
|
3225a83084 | ||
|
|
87794cfcc5 | ||
|
|
79ad0ce0c4 | ||
|
|
fda5987465 | ||
|
|
5cfdd5a6b2 | ||
|
|
bd6e0f9f93 | ||
|
|
aa8d002309 | ||
|
|
42ce9aca86 | ||
|
|
156f596189 | ||
|
|
8acfd5bf71 | ||
|
|
76c99ebb28 | ||
|
|
5fb119b99c | ||
|
|
99b766d9cb | ||
|
|
c207cf9b40 | ||
|
|
dd00ac4621 | ||
|
|
3d18a067a0 | ||
|
|
c48571a799 | ||
|
|
6eeb6983d9 |
1
.gitignore
vendored
@@ -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
|
||||
|
||||
249
packages/frameworks/frameworks.json
Normal 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 world’s 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."
|
||||
}
|
||||
]
|
||||
1
packages/frameworks/logos/angular.svg
Normal 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 |
4
packages/frameworks/logos/assemble.svg
Normal 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 |
61
packages/frameworks/logos/aurelia.svg
Normal 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 |
3
packages/frameworks/logos/brunch.svg
Normal 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 |
15
packages/frameworks/logos/chargejs.svg
Normal 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 |
1
packages/frameworks/logos/docusaurus.svg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
11
packages/frameworks/logos/docz.svg
Normal 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 |
1
packages/frameworks/logos/eleventy.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
1
packages/frameworks/logos/ember.svg
Normal file
|
After Width: | Height: | Size: 39 KiB |
1
packages/frameworks/logos/gatsby.svg
Normal 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 |
1
packages/frameworks/logos/gridsome.svg
Normal 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 |
1
packages/frameworks/logos/hexo.svg
Normal 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 |
5
packages/frameworks/logos/hugo.svg
Normal file
|
After Width: | Height: | Size: 30 KiB |
4
packages/frameworks/logos/hyperapp.svg
Normal 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 |
4
packages/frameworks/logos/ionic-react.svg
Normal 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 |
19
packages/frameworks/logos/jekyll.svg
Normal 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 |
9
packages/frameworks/logos/middleman.svg
Normal 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 |
4
packages/frameworks/logos/mithriljs.svg
Normal 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 |
4
packages/frameworks/logos/mkdocs.svg
Normal 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 |
1
packages/frameworks/logos/next.svg
Normal 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 |
5
packages/frameworks/logos/nuxt.svg
Normal 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 |
1
packages/frameworks/logos/polymer.svg
Normal 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 |
1
packages/frameworks/logos/preact.svg
Normal 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 |
1
packages/frameworks/logos/react.svg
Normal 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 |
1
packages/frameworks/logos/saber.svg
Normal 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 |
1
packages/frameworks/logos/sendgrid.svg
Normal 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 |
4
packages/frameworks/logos/stencil.svg
Normal 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 |
9
packages/frameworks/logos/storybook.svg
Normal 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 |
1
packages/frameworks/logos/svelte.svg
Normal 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 |
1
packages/frameworks/logos/umi.svg
Normal 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 |
4
packages/frameworks/logos/vanilla.svg
Normal 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 |
1
packages/frameworks/logos/vue.svg
Normal 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 |
4
packages/frameworks/logos/zola.svg
Normal 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 |
6
packages/frameworks/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@now/frameworks",
|
||||
"version": "0.0.2-canary.0",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
432
packages/now-build-utils/src/detect-builders-legacy.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
299
packages/now-build-utils/src/detect-routes-legacy.ts
Normal 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;
|
||||
}
|
||||
@@ -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`,
|
||||
|
||||
@@ -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',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
201
packages/now-build-utils/test/unit.test.js
vendored
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "./hugo"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
2
packages/now-cli/test/dev/fixtures/trigger-static-build/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
package.json
|
||||
yarn.lock
|
||||
2
packages/now-cli/test/integration.js
vendored
@@ -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.'
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
202
packages/now-cli/test/unit.js
vendored
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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" }
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
16
packages/now-next/test/fixtures/10-export-cache-headers/now.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
packages/now-next/test/fixtures/10-export-cache-headers/package.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build && next export"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
5
packages/now-next/test/fixtures/10-export-cache-headers/pages/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
const Page = () => 'Hi';
|
||||
|
||||
Page.getInitialProps = () => ({ hello: 'world' });
|
||||
|
||||
export default Page;
|
||||
5
packages/now-next/test/fixtures/11-export-clean-urls/next.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
18
packages/now-next/test/fixtures/11-export-clean-urls/now.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
packages/now-next/test/fixtures/11-export-clean-urls/package.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build && next export"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
7
packages/now-next/test/fixtures/11-export-clean-urls/pages/about.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function About() {
|
||||
return <div>Hi on About</div>;
|
||||
}
|
||||
|
||||
About.getInitialProps = () => ({});
|
||||
|
||||
export default About;
|
||||
7
packages/now-next/test/fixtures/11-export-clean-urls/pages/index.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function Home() {
|
||||
return <div>Hi There</div>;
|
||||
}
|
||||
|
||||
Home.getInitialProps = () => ({});
|
||||
|
||||
export default Home;
|
||||
6
packages/now-next/test/fixtures/12-no-export-auto/next.config.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
exportPathMap: d => d,
|
||||
};
|
||||
18
packages/now-next/test/fixtures/12-no-export-auto/now.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/12-no-export-auto/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
7
packages/now-next/test/fixtures/12-no-export-auto/pages/about.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function About() {
|
||||
return <div>Hi on About</div>;
|
||||
}
|
||||
|
||||
About.getInitialProps = () => ({});
|
||||
|
||||
export default About;
|
||||
7
packages/now-next/test/fixtures/12-no-export-auto/pages/index.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function Home() {
|
||||
return <div>Hi There</div>;
|
||||
}
|
||||
|
||||
Home.getInitialProps = () => ({});
|
||||
|
||||
export default Home;
|
||||
24
packages/now-next/test/fixtures/13-export-custom-routes/next.config.js
vendored
Normal 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',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
};
|
||||
18
packages/now-next/test/fixtures/13-export-custom-routes/now.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
packages/now-next/test/fixtures/13-export-custom-routes/package.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build && next export"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
7
packages/now-next/test/fixtures/13-export-custom-routes/pages/about.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function About() {
|
||||
return <div>Hi on About</div>;
|
||||
}
|
||||
|
||||
About.getInitialProps = () => ({});
|
||||
|
||||
export default About;
|
||||
7
packages/now-next/test/fixtures/13-export-custom-routes/pages/index.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function Home() {
|
||||
return <div>Hi There</div>;
|
||||
}
|
||||
|
||||
Home.getInitialProps = () => ({});
|
||||
|
||||
export default Home;
|
||||
8
packages/now-next/test/fixtures/14-next-offline/next.config.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
const withOffline = require('next-offline');
|
||||
|
||||
module.exports = withOffline({
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
exportPathMap: d => d,
|
||||
});
|
||||
18
packages/now-next/test/fixtures/14-next-offline/now.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
8
packages/now-next/test/fixtures/14-next-offline/package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"next-offline": "4.0.6",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
7
packages/now-next/test/fixtures/14-next-offline/pages/about.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function About() {
|
||||
return <div>Hi on About</div>;
|
||||
}
|
||||
|
||||
About.getInitialProps = () => ({});
|
||||
|
||||
export default About;
|
||||
7
packages/now-next/test/fixtures/14-next-offline/pages/index.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function Home() {
|
||||
return <div>Hi There</div>;
|
||||
}
|
||||
|
||||
Home.getInitialProps = () => ({});
|
||||
|
||||
export default Home;
|
||||
2
packages/now-next/test/test.js
vendored
@@ -3,7 +3,7 @@ const path = require('path');
|
||||
|
||||
const {
|
||||
packAndDeploy,
|
||||
testDeployment
|
||||
testDeployment,
|
||||
} = require('../../../test/lib/deployment/test-deployment.js');
|
||||
|
||||
jest.setTimeout(4 * 60 * 1000);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 };
|
||||
|
||||
120
packages/now-python/src/install.ts
Normal 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]);
|
||||
}
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = '?';
|
||||
|
||||