mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-12 12:57:47 +00:00
Compare commits
66 Commits
@vercel/py
...
@vercel/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c76781fac9 | ||
|
|
451e0b0cfb | ||
|
|
cf477d45b2 | ||
|
|
cdd2d69e07 | ||
|
|
43f1f8b257 | ||
|
|
34055e3599 | ||
|
|
26abb0a85a | ||
|
|
a1e337e0dd | ||
|
|
b72ead480f | ||
|
|
77cf68105f | ||
|
|
d800f55dfa | ||
|
|
9dde99f19e | ||
|
|
fae7a083fc | ||
|
|
cbd651d6ee | ||
|
|
6077a706d1 | ||
|
|
cedc82dd9e | ||
|
|
b420006401 | ||
|
|
8ba604e8fc | ||
|
|
fadeee4568 | ||
|
|
2b15ba7f46 | ||
|
|
4cdfd0e58c | ||
|
|
b3ccb5f3ef | ||
|
|
584acc43b7 | ||
|
|
f64be93b94 | ||
|
|
9abd31769e | ||
|
|
09e3b35e74 | ||
|
|
8aa9a0ea05 | ||
|
|
b2d0ed74c6 | ||
|
|
aef936af0f | ||
|
|
501be936c0 | ||
|
|
7eba282af5 | ||
|
|
cf3e4bd726 | ||
|
|
ee5361b00e | ||
|
|
bd929dd5c5 | ||
|
|
4ee064bb61 | ||
|
|
312a2090a6 | ||
|
|
a82f117217 | ||
|
|
e908378486 | ||
|
|
8cda5263eb | ||
|
|
a24fd64bce | ||
|
|
0c515a46d5 | ||
|
|
f19690dc32 | ||
|
|
6b2a1c3866 | ||
|
|
1e54d606d7 | ||
|
|
c4ab0ebe9c | ||
|
|
321f1232a1 | ||
|
|
8a8203e149 | ||
|
|
33527165e7 | ||
|
|
db10383bc8 | ||
|
|
56960e506e | ||
|
|
a17f3a96ec | ||
|
|
9ff86a896c | ||
|
|
6ccb4354f9 | ||
|
|
82ba932447 | ||
|
|
b995618afb | ||
|
|
c897b24417 | ||
|
|
e64b2e1993 | ||
|
|
9358a5469e | ||
|
|
2a24210b7d | ||
|
|
2644a59984 | ||
|
|
4eeb8c298c | ||
|
|
0351f02dff | ||
|
|
0d7fa2f912 | ||
|
|
3b646880e7 | ||
|
|
350a0e5f36 | ||
|
|
5c21d400bd |
@@ -1,7 +1,12 @@
|
||||
node_modules
|
||||
dist
|
||||
examples
|
||||
|
||||
packages/node/src/bridge.ts
|
||||
|
||||
packages/*/test/fixtures
|
||||
|
||||
# cli
|
||||
packages/cli/@types
|
||||
packages/cli/download
|
||||
packages/cli/dist
|
||||
@@ -9,11 +14,24 @@ packages/cli/test/dev/fixtures
|
||||
packages/cli/bin
|
||||
packages/cli/link
|
||||
packages/cli/src/util/dev/templates/*.ts
|
||||
|
||||
# client
|
||||
packages/client/tests/fixtures
|
||||
packages/client/lib
|
||||
packages/node/src/bridge.ts
|
||||
|
||||
# node-bridge
|
||||
packages/node-bridge/bridge.js
|
||||
packages/node-bridge/launcher.js
|
||||
packages/node-bridge/helpers.js
|
||||
packages/node-bridge/source-map-support.js
|
||||
|
||||
# middleware
|
||||
packages/middleware/src/entries.js
|
||||
|
||||
# static-build
|
||||
packages/static-build/test/fixtures
|
||||
packages/static-build/test/build-fixtures
|
||||
packages/static-build/test/cache-fixtures
|
||||
|
||||
# redwood
|
||||
packages/redwood/test/fixtures
|
||||
|
||||
6
.github/CONTRIBUTING.md
vendored
6
.github/CONTRIBUTING.md
vendored
@@ -16,17 +16,17 @@ yarn install
|
||||
yarn bootstrap
|
||||
yarn build
|
||||
yarn lint
|
||||
yarn test
|
||||
yarn test-unit
|
||||
```
|
||||
|
||||
Make sure all the tests pass before making changes.
|
||||
|
||||
## Verifying your change
|
||||
|
||||
Once you are done with your changes (we even suggest doing it along the way), make sure all the test still run by running:
|
||||
Once you are done with your changes (we even suggest doing it along the way), make sure all the test still pass by running:
|
||||
|
||||
```
|
||||
yarn build && yarn test
|
||||
yarn test-unit
|
||||
```
|
||||
|
||||
from the root of the project.
|
||||
|
||||
@@ -9,7 +9,6 @@ A Runtime is an npm module that implements the following interface:
|
||||
interface Runtime {
|
||||
version: number;
|
||||
build: (options: BuildOptions) => Promise<BuildResult>;
|
||||
analyze?: (options: AnalyzeOptions) => Promise<string>;
|
||||
prepareCache?: (options: PrepareCacheOptions) => Promise<CacheOutputs>;
|
||||
shouldServe?: (options: ShouldServeOptions) => Promise<boolean>;
|
||||
startDevServer?: (
|
||||
@@ -72,26 +71,6 @@ export async function build(options: BuildOptions) {
|
||||
}
|
||||
```
|
||||
|
||||
### `analyze()`
|
||||
|
||||
An **optional** exported function that returns a unique fingerprint used for the
|
||||
purpose of [build
|
||||
de-duplication](https://vercel.com/docs/v2/platform/deployments#deduplication).
|
||||
If the `analyze()` function is not supplied, then a random fingerprint is
|
||||
assigned to each build.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
import { AnalyzeOptions } from '@vercel/build-utils';
|
||||
|
||||
export async function analyze(options: AnalyzeOptions) {
|
||||
// Do calculations to generate a fingerprint based off the source code here…
|
||||
|
||||
return 'fingerprint goes here';
|
||||
}
|
||||
```
|
||||
|
||||
### `prepareCache()`
|
||||
|
||||
An **optional** exported function that is executed after [`build()`](#build) is
|
||||
|
||||
46
api/_lib/script/build.ts
Normal file
46
api/_lib/script/build.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import fs from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
import { getExampleList } from '../examples/example-list';
|
||||
import { mapOldToNew } from '../examples/map-old-to-new';
|
||||
|
||||
const repoRoot = join(__dirname, '..', '..', '..');
|
||||
const pubDir = join(repoRoot, 'public');
|
||||
|
||||
async function main() {
|
||||
console.log(`Building static frontend ${repoRoot}...`);
|
||||
|
||||
await fs.rm(pubDir, { recursive: true, force: true });
|
||||
await fs.mkdir(pubDir);
|
||||
|
||||
const examples = await getExampleList();
|
||||
const pathListAll = join(pubDir, 'list-all.json');
|
||||
await fs.writeFile(pathListAll, JSON.stringify(examples));
|
||||
|
||||
const exampleDirs = await fs.readdir(join(repoRoot, 'examples'), {
|
||||
withFileTypes: true,
|
||||
});
|
||||
|
||||
const existingExamples = exampleDirs
|
||||
.filter(dir => dir.isDirectory())
|
||||
.map(dir => ({
|
||||
name: dir.name,
|
||||
visible: true,
|
||||
suggestions: [],
|
||||
}));
|
||||
|
||||
const oldExamples = Object.keys(mapOldToNew).map(key => ({
|
||||
name: key,
|
||||
visible: false,
|
||||
suggestions: mapOldToNew[key],
|
||||
}));
|
||||
|
||||
const pathList = join(pubDir, 'list.json');
|
||||
await fs.writeFile(
|
||||
pathList,
|
||||
JSON.stringify([...existingExamples, ...oldExamples])
|
||||
);
|
||||
|
||||
console.log('Completed building static frontend.');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,10 +0,0 @@
|
||||
import { VercelRequest, VercelResponse } from '@vercel/node';
|
||||
import { getExampleList } from '../_lib/examples/example-list';
|
||||
import { withApiHandler } from '../_lib/util/with-api-handler';
|
||||
|
||||
export default withApiHandler(async function (
|
||||
req: VercelRequest,
|
||||
res: VercelResponse
|
||||
) {
|
||||
res.status(200).json(await getExampleList());
|
||||
});
|
||||
@@ -1,27 +0,0 @@
|
||||
import { extract } from '../_lib/examples/extract';
|
||||
import { summary } from '../_lib/examples/summary';
|
||||
import { VercelRequest, VercelResponse } from '@vercel/node';
|
||||
import { mapOldToNew } from '../_lib/examples/map-old-to-new';
|
||||
import { withApiHandler } from '../_lib/util/with-api-handler';
|
||||
|
||||
export default withApiHandler(async function (
|
||||
req: VercelRequest,
|
||||
res: VercelResponse
|
||||
) {
|
||||
await extract('https://github.com/vercel/vercel/archive/main.zip', '/tmp');
|
||||
const exampleList = summary('/tmp/vercel-main/examples');
|
||||
|
||||
const existingExamples = Array.from(exampleList).map(key => ({
|
||||
name: key,
|
||||
visible: true,
|
||||
suggestions: [],
|
||||
}));
|
||||
|
||||
const oldExamples = Object.keys(mapOldToNew).map(key => ({
|
||||
name: key,
|
||||
visible: false,
|
||||
suggestions: mapOldToNew[key],
|
||||
}));
|
||||
|
||||
res.status(200).json([...existingExamples, ...oldExamples]);
|
||||
});
|
||||
20
examples/eleventy/LICENSE.md
Normal file
20
examples/eleventy/LICENSE.md
Normal file
@@ -0,0 +1,20 @@
|
||||
MIT License
|
||||
Copyright (c) 2022 Zach Leatherman @zachleat
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,5 +1,19 @@
|
||||
{
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/11ty/eleventy-base-blog.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Zach Leatherman",
|
||||
"email": "zachleatherman@gmail.com",
|
||||
"url": "https://zachleat.com/"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/11ty/eleventy-base-blog/issues"
|
||||
},
|
||||
"homepage": "https://github.com/11ty/eleventy-base-blog#readme",
|
||||
"scripts": {
|
||||
"build": "eleventy",
|
||||
"watch": "eleventy --watch",
|
||||
|
||||
17
examples/hugo/.gitignore
vendored
17
examples/hugo/.gitignore
vendored
@@ -1,2 +1,19 @@
|
||||
# Hugo
|
||||
public
|
||||
.hugo_build.lock
|
||||
|
||||
## OS Files
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Env Variables
|
||||
.env
|
||||
.env.build
|
||||
|
||||
.vercel
|
||||
@@ -1,4 +1,39 @@
|
||||
baseURL = "http://example.org/"
|
||||
languageCode = "en-us"
|
||||
title = "My New Hugo Site"
|
||||
theme = "ananke"
|
||||
baseURL = "https://hugo-template.vercel.app"
|
||||
title = "Hugo Template"
|
||||
theme = "etch"
|
||||
languageCode = "en-US"
|
||||
enableInlineShortcodes = true
|
||||
pygmentsCodeFences = true
|
||||
pygmentsUseClasses = true
|
||||
ignoreErrors = ["error-missing-instagram-accesstoken"]
|
||||
|
||||
[params]
|
||||
description = "A Hugo template, hosted with Vercel."
|
||||
copyright = "Copyright © 2021 Your Name"
|
||||
dark = "auto"
|
||||
highlight = true
|
||||
|
||||
[menu]
|
||||
[[menu.main]]
|
||||
identifier = "posts"
|
||||
name = "posts"
|
||||
title = "posts"
|
||||
url = "/"
|
||||
weight = 10
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "about"
|
||||
name = "about"
|
||||
title = "about"
|
||||
url = "/about/"
|
||||
weight = 20
|
||||
|
||||
[permalinks]
|
||||
posts = "/:title/"
|
||||
|
||||
[markup.goldmark.renderer]
|
||||
# Allow HTML in Markdown
|
||||
unsafe = true
|
||||
|
||||
[markup.tableOfContents]
|
||||
ordered = true
|
||||
|
||||
5
examples/hugo/content/_index.md
Normal file
5
examples/hugo/content/_index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: 'Home'
|
||||
---
|
||||
|
||||
This is some info about me.
|
||||
21
examples/hugo/content/about/index.md
Normal file
21
examples/hugo/content/about/index.md
Normal file
@@ -0,0 +1,21 @@
|
||||
+++
|
||||
title = "About"
|
||||
+++
|
||||
|
||||
Written in Go, Hugo is an open source static site generator available under the [Apache Licence 2.0.](https://github.com/gohugoio/hugo/blob/master/LICENSE) Hugo supports TOML, YAML and JSON data file types, Markdown and HTML content files and uses shortcodes to add rich content. Other notable features are taxonomies, multilingual mode, image processing, custom output formats, HTML/CSS/JS minification and support for Sass SCSS workflows.
|
||||
|
||||
Hugo makes use of a variety of open source projects including:
|
||||
|
||||
- https://github.com/yuin/goldmark
|
||||
- https://github.com/alecthomas/chroma
|
||||
- https://github.com/muesli/smartcrop
|
||||
- https://github.com/spf13/cobra
|
||||
- https://github.com/spf13/viper
|
||||
|
||||
Hugo is ideal for blogs, corporate websites, creative portfolios, online magazines, single page applications or even a website with thousands of pages.
|
||||
|
||||
Hugo is for people who want to hand code their own website without worrying about setting up complicated runtimes, dependencies and databases.
|
||||
|
||||
Websites built with Hugo are extremelly fast, secure and can be deployed anywhere including, AWS, GitHub Pages, Heroku, Netlify and any other hosting provider.
|
||||
|
||||
Learn more and contribute on [GitHub](https://github.com/gohugoio).
|
||||
50
examples/hugo/content/posts/emoji-support.md
Normal file
50
examples/hugo/content/posts/emoji-support.md
Normal file
@@ -0,0 +1,50 @@
|
||||
+++
|
||||
author = "Hugo Authors"
|
||||
title = "Emoji Support"
|
||||
date = "2019-03-05"
|
||||
description = "Guide to emoji usage in Hugo"
|
||||
tags = [
|
||||
"emoji",
|
||||
]
|
||||
+++
|
||||
|
||||
Emoji can be enabled in a Hugo project in a number of ways.
|
||||
|
||||
<!--more-->
|
||||
|
||||
The [`emojify`](https://gohugo.io/functions/emojify/) function can be called directly in templates or [Inline Shortcodes](https://gohugo.io/templates/shortcode-templates/#inline-shortcodes).
|
||||
|
||||
To enable emoji globally, set `enableEmoji` to `true` in your site’s [configuration](https://gohugo.io/getting-started/configuration/) and then you can type emoji shorthand codes directly in content files; e.g.
|
||||
|
||||
<p><span class="nowrap"><span class="emojify">🙈</span> <code>:see_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙉</span> <code>:hear_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙊</span> <code>:speak_no_evil:</code></span></p>
|
||||
<br>
|
||||
|
||||
The [Emoji cheat sheet](http://www.emoji-cheat-sheet.com/) is a useful reference for emoji shorthand codes.
|
||||
|
||||
---
|
||||
|
||||
**N.B.** The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.
|
||||
|
||||
{{< highlight html >}}
|
||||
.emoji {
|
||||
font-family: Apple Color Emoji,Segoe UI Emoji,NotoColorEmoji,Segoe UI Symbol,Android Emoji,EmojiSymbols;
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
{{< css.inline >}}
|
||||
|
||||
<style>
|
||||
.emojify {
|
||||
font-family: Apple Color Emoji,Segoe UI Emoji,NotoColorEmoji,Segoe UI Symbol,Android Emoji,EmojiSymbols;
|
||||
font-size: 2rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@media screen and (max-width:650px) {
|
||||
.nowrap {
|
||||
display: block;
|
||||
margin: 25px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{{< /css.inline >}}
|
||||
155
examples/hugo/content/posts/markdown-syntax.md
Normal file
155
examples/hugo/content/posts/markdown-syntax.md
Normal file
@@ -0,0 +1,155 @@
|
||||
+++
|
||||
author = "Hugo Authors"
|
||||
title = "Markdown Syntax Guide"
|
||||
date = "2019-03-11"
|
||||
description = "Sample article showcasing basic Markdown syntax and formatting for HTML elements."
|
||||
tags = [
|
||||
"markdown",
|
||||
"css",
|
||||
"html",
|
||||
"themes",
|
||||
]
|
||||
categories = [
|
||||
"themes",
|
||||
"syntax",
|
||||
]
|
||||
series = ["Themes Guide"]
|
||||
aliases = ["migrate-from-jekyl"]
|
||||
+++
|
||||
|
||||
This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Headings
|
||||
|
||||
The following HTML `<h1>`—`<h6>` elements represent six levels of section headings. `<h1>` is the highest section level while `<h6>` is the lowest.
|
||||
|
||||
# H1
|
||||
|
||||
## H2
|
||||
|
||||
### H3
|
||||
|
||||
#### H4
|
||||
|
||||
##### H5
|
||||
|
||||
###### H6
|
||||
|
||||
## Paragraph
|
||||
|
||||
Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.
|
||||
|
||||
Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.
|
||||
|
||||
## Blockquotes
|
||||
|
||||
The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
|
||||
|
||||
#### Blockquote without attribution
|
||||
|
||||
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
|
||||
> **Note** that you can use _Markdown syntax_ within a blockquote.
|
||||
|
||||
#### Blockquote with attribution
|
||||
|
||||
> Don't communicate by sharing memory, share memory by communicating.</p>
|
||||
> — <cite>Rob Pike[^1]</cite>
|
||||
|
||||
[^1]: The above quote is excerpted from Rob Pike's [talk](https://www.youtube.com/watch?v=PAAkCSZUG1c) during Gopherfest, November 18, 2015.
|
||||
|
||||
## Tables
|
||||
|
||||
Tables aren't part of the core Markdown spec, but Hugo supports supports them out-of-the-box.
|
||||
|
||||
| Name | Age |
|
||||
| ----- | --- |
|
||||
| Bob | 27 |
|
||||
| Alice | 23 |
|
||||
|
||||
#### Inline Markdown within tables
|
||||
|
||||
| Inline | Markdown | In | Table |
|
||||
| ------------------------ | -------------------------- | ----------------------------------- | ------ |
|
||||
| _italics_ | **bold** | ~~strikethrough~~ | `code` |
|
||||
|
||||
## Code Blocks
|
||||
|
||||
#### Code block with backticks
|
||||
|
||||
```
|
||||
html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### Code block indented with four spaces
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
#### Code block with Hugo's internal highlight shortcode
|
||||
|
||||
{{< highlight html >}}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
{{< /highlight >}}
|
||||
|
||||
## List Types
|
||||
|
||||
#### Ordered List
|
||||
|
||||
1. First item
|
||||
2. Second item
|
||||
3. Third item
|
||||
|
||||
#### Unordered List
|
||||
|
||||
- List item
|
||||
- Another item
|
||||
- And another item
|
||||
|
||||
#### Nested list
|
||||
|
||||
- Item
|
||||
|
||||
1. First Sub-item
|
||||
2. Second Sub-item
|
||||
|
||||
## Other Elements — abbr, sub, sup, kbd, mark
|
||||
|
||||
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
|
||||
|
||||
H<sub>2</sub>O
|
||||
|
||||
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
|
||||
|
||||
Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
|
||||
|
||||
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
title: 'My First Post'
|
||||
date: 2019-07-18T17:18:05+01:00
|
||||
draft: false
|
||||
---
|
||||
|
||||
# Hugo on Vercel
|
||||
60
examples/hugo/content/posts/placeholder-text.md
Normal file
60
examples/hugo/content/posts/placeholder-text.md
Normal file
@@ -0,0 +1,60 @@
|
||||
+++
|
||||
author = "Hugo Authors"
|
||||
title = "Placeholder Text"
|
||||
date = "2019-03-09"
|
||||
description = "Lorem Ipsum Dolor Si Amet"
|
||||
tags = [
|
||||
"markdown",
|
||||
"text",
|
||||
]
|
||||
+++
|
||||
|
||||
Lorem est tota propiore conpellat pectoribus de
|
||||
pectora summo. <!--more-->Redit teque digerit hominumque toris verebor lumina non cervice
|
||||
subde tollit usus habet Arctonque, furores quas nec ferunt. Quoque montibus nunc
|
||||
caluere tempus inhospita parcite confusaque translucet patri vestro qui optatis
|
||||
lumine cognoscere flos nubis! Fronde ipsamque patulos Dryopen deorum.
|
||||
|
||||
1. Exierant elisi ambit vivere dedere
|
||||
2. Duce pollice
|
||||
3. Eris modo
|
||||
4. Spargitque ferrea quos palude
|
||||
|
||||
Rursus nulli murmur; hastile inridet ut ab gravi sententia! Nomine potitus
|
||||
silentia flumen, sustinet placuit petis in dilapsa erat sunt. Atria
|
||||
tractus malis.
|
||||
|
||||
1. Comas hunc haec pietate fetum procerum dixit
|
||||
2. Post torum vates letum Tiresia
|
||||
3. Flumen querellas
|
||||
4. Arcanaque montibus omnes
|
||||
5. Quidem et
|
||||
|
||||
# Vagus elidunt
|
||||
|
||||
<svg class="canon" xmlns="http://www.w3.org/2000/svg" overflow="visible" viewBox="0 0 496 373" height="373" width="496"><g fill="none"><path stroke="#000" stroke-width=".75" d="M.599 372.348L495.263 1.206M.312.633l494.95 370.853M.312 372.633L247.643.92M248.502.92l246.76 370.566M330.828 123.869V1.134M330.396 1.134L165.104 124.515"></path><path stroke="#ED1C24" stroke-width=".75" d="M275.73 41.616h166.224v249.05H275.73zM54.478 41.616h166.225v249.052H54.478z"></path><path stroke="#000" stroke-width=".75" d="M.479.375h495v372h-495zM247.979.875v372"></path><ellipse cx="498.729" cy="177.625" rx=".75" ry="1.25"></ellipse><ellipse cx="247.229" cy="377.375" rx=".75" ry="1.25"></ellipse></g></svg>
|
||||
|
||||
[The Van de Graaf Canon](https://en.wikipedia.org/wiki/Canons_of_page_construction#Van_de_Graaf_canon)
|
||||
|
||||
## Mane refeci capiebant unda mulcebat
|
||||
|
||||
Victa caducifer, malo vulnere contra
|
||||
dicere aurato, ludit regale, voca! Retorsit colit est profanae esse virescere
|
||||
furit nec; iaculi matertera et visa est, viribus. Divesque creatis, tecta novat collumque vulnus est, parvas. **Faces illo pepulere** tempus adest. Tendit flamma, ab opes virum sustinet, sidus sequendo urbis.
|
||||
|
||||
Iubar proles corpore raptos vero auctor imperium; sed et huic: manus caeli
|
||||
Lelegas tu lux. Verbis obstitit intus oblectamina fixis linguisque ausus sperare
|
||||
Echionides cornuaque tenent clausit possit. Omnia putatur. Praeteritae refert
|
||||
ausus; ferebant e primus lora nutat, vici quae mea ipse. Et iter nil spectatae
|
||||
vulnus haerentia iuste et exercebat, sui et.
|
||||
|
||||
Eurytus Hector, materna ipsumque ut Politen, nec, nate, ignari, vernum cohaesit sequitur. Vel **mitis temploque** vocatus, inque alis, _oculos nomen_ non silvis corpore coniunx ne displicet illa. Crescunt non unus, vidit visa quantum inmiti flumina mortis facto sic: undique a alios vincula sunt iactata abdita! Suspenderat ego fuit tendit: luna, ante urbem
|
||||
Propoetides **parte**.
|
||||
|
||||
{{< css.inline >}}
|
||||
|
||||
<style>
|
||||
.canon { background: white; width: 100%; height: auto;}
|
||||
</style>
|
||||
|
||||
{{< /css.inline >}}
|
||||
42
examples/hugo/content/posts/rich-content.md
Normal file
42
examples/hugo/content/posts/rich-content.md
Normal file
@@ -0,0 +1,42 @@
|
||||
+++
|
||||
author = "Hugo Authors"
|
||||
title = "Rich Content"
|
||||
date = "2019-03-10"
|
||||
description = "A brief description of Hugo Shortcodes"
|
||||
tags = [
|
||||
"shortcodes",
|
||||
"privacy",
|
||||
]
|
||||
+++
|
||||
|
||||
Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-management/shortcodes/#use-hugo-s-built-in-shortcodes) for rich content, along with a [Privacy Config](https://gohugo.io/about/hugo-and-gdpr/) and a set of Simple Shortcodes that enable static and no-JS versions of various social media embeds.
|
||||
|
||||
## <!--more-->
|
||||
|
||||
## Instagram Simple Shortcode
|
||||
|
||||
{{< instagram_simple BGvuInzyFAe hidecaption >}}
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
## YouTube Privacy Enhanced Shortcode
|
||||
|
||||
{{< youtube ZJthWmvUzzc >}}
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
## Twitter Simple Shortcode
|
||||
|
||||
{{< twitter_simple 1085870671291310081 >}}
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
## Vimeo Simple Shortcode
|
||||
|
||||
{{< vimeo_simple 48912912 >}}
|
||||
@@ -1,17 +0,0 @@
|
||||
[more]
|
||||
other = "Ещё"
|
||||
|
||||
[allTitle]
|
||||
other = "Все {{.Title }}"
|
||||
|
||||
[recentTitle]
|
||||
other = "Недавние {{.Title }}"
|
||||
|
||||
[readMore]
|
||||
other = "читать дальше"
|
||||
|
||||
[whatsInThis]
|
||||
other = "Содержание {{ .Type }}"
|
||||
|
||||
[related]
|
||||
other = "Схожие"
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
!function(n){function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=n,t.c=r,t.i=function(n){return n},t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:e})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="",t(t.s=1)}([function(n,t){},function(n,t,r){"use strict";var e=r(0);!function(n){n&&n.__esModule}(e)}]);
|
||||
1
examples/hugo/themes/etch/.gitignore
vendored
Normal file
1
examples/hugo/themes/etch/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.DS_Store
|
||||
20
examples/hugo/themes/etch/LICENSE
Normal file
20
examples/hugo/themes/etch/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 Lukas Joswiak
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
33
examples/hugo/themes/etch/README.md
Normal file
33
examples/hugo/themes/etch/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Etch
|
||||
|
||||
Etch is a simple, responsive theme for [Hugo](https://gohugo.io) with a focus on writing. A live demo is available at https://lukasjoswiak.github.io/etch/.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/LukasJoswiak/etch/master/images/screenshot_small.png" alt="screenshot" width="545px">
|
||||
|
||||
## Features:
|
||||
|
||||
- Homepage with list of posts.
|
||||
- Support for pages.
|
||||
- Responsive design for optimized mobile experience.
|
||||
- Syntax highlighting with customizable theme.
|
||||
- Dark theme which automatically adjusts based on users' setting ([example](https://github.com/LukasJoswiak/etch/wiki/Dark-mode)).
|
||||
- No external dependencies, no JavaScript, no web fonts.
|
||||
- Internationalization friendly: use default English translations or create your own
|
||||
|
||||
## Installation
|
||||
|
||||
To install `etch`, download the repository into the `themes` folder in the root of your site.
|
||||
|
||||
```
|
||||
$ git submodule add https://github.com/LukasJoswiak/etch.git themes/etch
|
||||
```
|
||||
|
||||
Then, use the theme to generate your site.
|
||||
|
||||
```
|
||||
$ hugo server -t etch
|
||||
```
|
||||
|
||||
Use the [sample configuration](https://github.com/LukasJoswiak/etch/wiki/Configuration#sample-configuration) as a starting point. See the [configuration](https://github.com/LukasJoswiak/etch/wiki/Configuration) page for more info.
|
||||
|
||||
Read the [wiki](https://github.com/LukasJoswiak/etch/wiki) to learn about more options.
|
||||
2
examples/hugo/themes/etch/archetypes/default.md
Normal file
2
examples/hugo/themes/etch/archetypes/default.md
Normal file
@@ -0,0 +1,2 @@
|
||||
+++
|
||||
+++
|
||||
53
examples/hugo/themes/etch/assets/css/dark.css
Normal file
53
examples/hugo/themes/etch/assets/css/dark.css
Normal file
@@ -0,0 +1,53 @@
|
||||
{{ if not (eq .Site.Params.dark "on") -}}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
{{ end -}}
|
||||
html {
|
||||
scrollbar-color: #6c6c6c #2e2e2e;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #ebebeb;
|
||||
background: #121212;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
header#banner a {
|
||||
color: #e0e0e0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
header#banner nav ul li a {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
main#content a {
|
||||
color: #00b1ed;
|
||||
}
|
||||
|
||||
main#content p {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
|
||||
main#content hr {
|
||||
background: #5c5c5c;
|
||||
}
|
||||
|
||||
main#content #toc h4 {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
main#content ul#posts small {
|
||||
color: #a7a7a7;
|
||||
}
|
||||
|
||||
main#content ul#posts li a:hover {
|
||||
color: #21c7ff;
|
||||
}
|
||||
|
||||
main#content header#post-header div {
|
||||
color: #a7a7a7;
|
||||
}
|
||||
{{- if not (eq .Site.Params.dark "on") -}}
|
||||
}
|
||||
{{- end -}}
|
||||
281
examples/hugo/themes/etch/assets/css/main.css
Normal file
281
examples/hugo/themes/etch/assets/css/main.css
Normal file
@@ -0,0 +1,281 @@
|
||||
*, *:before, *:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
font-size: 1.6rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
color: #313a3d;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
header#banner {
|
||||
margin: 25px 0;
|
||||
}
|
||||
|
||||
header#banner a {
|
||||
color: #313a3d;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
header#banner a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
header#banner h2 {
|
||||
display: inline;
|
||||
font-size: 21px;
|
||||
font-size: 2.1rem;
|
||||
margin: 0 8px 0 0;
|
||||
}
|
||||
|
||||
header#banner nav {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
header#banner nav ul {
|
||||
list-style-type: none;
|
||||
font-size: 1.05em;
|
||||
text-transform: lowercase;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header#banner nav ul li {
|
||||
display: inline;
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
header#banner nav ul li a {
|
||||
color: #454545;
|
||||
}
|
||||
|
||||
main#content a {
|
||||
color: #007dfa;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
main#content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
main#content h1,
|
||||
main#content h2,
|
||||
main#content h3,
|
||||
main#content h4,
|
||||
main#content h5,
|
||||
main#content h6 {
|
||||
margin-bottom: 0;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
main#content h3 {
|
||||
font-size: 19px;
|
||||
font-size: 1.9rem;
|
||||
}
|
||||
|
||||
main#content h1 + p,
|
||||
main#content h2 + p,
|
||||
main#content h3 + p,
|
||||
main#content h4 + p,
|
||||
main#content h5 + p,
|
||||
main#content h6 + p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
main#content p {
|
||||
color: #394548;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
main#content hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #d8d8d8;
|
||||
}
|
||||
|
||||
main#content abbr {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* index.html styles */
|
||||
main#content ul#posts {
|
||||
list-style-type: none;
|
||||
font-size: 16px;
|
||||
font-size: 1.6rem;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main#content ul#posts li {
|
||||
margin: 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main#content ul#posts small {
|
||||
font-size: 0.8em;
|
||||
color: #767676;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
main#content ul#posts li a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
main#content ul#posts li a:hover {
|
||||
color: #369aff;
|
||||
}
|
||||
|
||||
main#content ul#posts li a:hover small {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* single.html styles */
|
||||
main#content header#post-header h1 {
|
||||
display: block;
|
||||
font-size: 23px;
|
||||
font-size: 2.3rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
main#content header#post-header > div {
|
||||
display: block;
|
||||
font-size: 0.85em;
|
||||
color: #767676;
|
||||
}
|
||||
|
||||
main#content #toc {
|
||||
border: 1px solid #b1b1b1;
|
||||
border-radius: 1px;
|
||||
line-height: 26px;
|
||||
margin: 16px 0;
|
||||
padding: 9px 14px;
|
||||
}
|
||||
|
||||
main#content #toc h4 {
|
||||
font-size: 1.06em;
|
||||
color: #3d3d3d;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
main#content #toc nav#TableOfContents {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
main#content #toc nav#TableOfContents > ul, main#content #toc nav#TableOfContents > ol {
|
||||
margin-left: -40px;
|
||||
}
|
||||
|
||||
main#content #toc ul, main#content #toc ol {
|
||||
font-size: 0.98em;
|
||||
margin: 0;
|
||||
padding: 0 0 0 40px;
|
||||
}
|
||||
|
||||
main#content #toc ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
main#content #toc ol {
|
||||
counter-reset: item;
|
||||
}
|
||||
|
||||
main#content #toc ol li {
|
||||
display: block;
|
||||
}
|
||||
|
||||
main#content #toc ol li:before {
|
||||
content: counters(item, ".") ". ";
|
||||
counter-increment: item;
|
||||
}
|
||||
|
||||
main#content img {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
main#content figure {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
main#content figure img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
main#content figure figcaption {
|
||||
font-size: 0.92em;
|
||||
font-style: italic;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
margin-top: 6px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
main#content figure figcaption h4 {
|
||||
font-style: normal;
|
||||
display: inline;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
main#content figure figcaption p {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
main#content blockquote {
|
||||
font-style: italic;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 50px;
|
||||
padding-left: 15px;
|
||||
border-left: 3px solid #ccc;
|
||||
}
|
||||
|
||||
main#content code,
|
||||
main#content pre {
|
||||
font-family: 'Menlo', monospace;
|
||||
}
|
||||
|
||||
main#content code {
|
||||
font-size: 0.96em;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
main#content pre {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
font-size: 14px;
|
||||
font-size: 1.4rem;
|
||||
white-space: pre;
|
||||
margin: 20px 0;
|
||||
padding: 1.5rem 1.5rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
main#content pre code {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main#content section.footnotes {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
footer#footer {
|
||||
font-size: 14px;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
color: #b3b3b3;
|
||||
margin: 40px 0;
|
||||
}
|
||||
52
examples/hugo/themes/etch/assets/css/min770px.css
Normal file
52
examples/hugo/themes/etch/assets/css/min770px.css
Normal file
@@ -0,0 +1,52 @@
|
||||
@media (min-width: 770px) {
|
||||
body {
|
||||
width: 600px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
main#content hr {
|
||||
width: 108%;
|
||||
margin-left: -3.8%;
|
||||
}
|
||||
|
||||
/* index.html styles */
|
||||
header#banner h2 {
|
||||
font-size: 25px;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
main#content h3 {
|
||||
font-size: 20px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
main#content ul#posts {
|
||||
font-size: 18px;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
/* single.html styles */
|
||||
main#content header#post-header h1 {
|
||||
font-size: 24px;
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
main#content img {
|
||||
max-width: 108%;
|
||||
margin-left: -3.8%;
|
||||
}
|
||||
|
||||
main#content figure {
|
||||
margin-left: -3.8%;
|
||||
}
|
||||
|
||||
main#content figure img {
|
||||
max-width: 108%;
|
||||
}
|
||||
|
||||
main#content pre {
|
||||
width: 108%;
|
||||
margin-left: -3.8%;
|
||||
padding: 1.5rem 2.2rem;
|
||||
}
|
||||
}
|
||||
59
examples/hugo/themes/etch/assets/css/syntax.css
Normal file
59
examples/hugo/themes/etch/assets/css/syntax.css
Normal file
@@ -0,0 +1,59 @@
|
||||
/* Background */ .chroma { color: #f8f8f2; background-color: #272822 }
|
||||
/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
|
||||
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
|
||||
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
|
||||
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
|
||||
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||
/* Keyword */ .chroma .k { color: #66d9ef }
|
||||
/* KeywordConstant */ .chroma .kc { color: #66d9ef }
|
||||
/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
|
||||
/* KeywordNamespace */ .chroma .kn { color: #f92672 }
|
||||
/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
|
||||
/* KeywordReserved */ .chroma .kr { color: #66d9ef }
|
||||
/* KeywordType */ .chroma .kt { color: #66d9ef }
|
||||
/* NameAttribute */ .chroma .na { color: #a6e22e }
|
||||
/* NameClass */ .chroma .nc { color: #a6e22e }
|
||||
/* NameConstant */ .chroma .no { color: #66d9ef }
|
||||
/* NameDecorator */ .chroma .nd { color: #a6e22e }
|
||||
/* NameException */ .chroma .ne { color: #a6e22e }
|
||||
/* NameFunction */ .chroma .nf { color: #a6e22e }
|
||||
/* NameOther */ .chroma .nx { color: #a6e22e }
|
||||
/* NameTag */ .chroma .nt { color: #f92672 }
|
||||
/* Literal */ .chroma .l { color: #ae81ff }
|
||||
/* LiteralDate */ .chroma .ld { color: #e6db74 }
|
||||
/* LiteralString */ .chroma .s { color: #e6db74 }
|
||||
/* LiteralStringAffix */ .chroma .sa { color: #e6db74 }
|
||||
/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 }
|
||||
/* LiteralStringChar */ .chroma .sc { color: #e6db74 }
|
||||
/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 }
|
||||
/* LiteralStringDoc */ .chroma .sd { color: #e6db74 }
|
||||
/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 }
|
||||
/* LiteralStringEscape */ .chroma .se { color: #ae81ff }
|
||||
/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 }
|
||||
/* LiteralStringInterpol */ .chroma .si { color: #e6db74 }
|
||||
/* LiteralStringOther */ .chroma .sx { color: #e6db74 }
|
||||
/* LiteralStringRegex */ .chroma .sr { color: #e6db74 }
|
||||
/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 }
|
||||
/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 }
|
||||
/* LiteralNumber */ .chroma .m { color: #ae81ff }
|
||||
/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
|
||||
/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
|
||||
/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
|
||||
/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
|
||||
/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
|
||||
/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
|
||||
/* Operator */ .chroma .o { color: #f92672 }
|
||||
/* OperatorWord */ .chroma .ow { color: #f92672 }
|
||||
/* Comment */ .chroma .c { color: #75715e }
|
||||
/* CommentHashbang */ .chroma .ch { color: #75715e }
|
||||
/* CommentMultiline */ .chroma .cm { color: #75715e }
|
||||
/* CommentSingle */ .chroma .c1 { color: #75715e }
|
||||
/* CommentSpecial */ .chroma .cs { color: #75715e }
|
||||
/* CommentPreproc */ .chroma .cp { color: #75715e }
|
||||
/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
|
||||
/* GenericDeleted */ .chroma .gd { color: #f92672 }
|
||||
/* GenericEmph */ .chroma .ge { font-style: italic }
|
||||
/* GenericInserted */ .chroma .gi { color: #a6e22e }
|
||||
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
||||
/* GenericSubheading */ .chroma .gu { color: #75715e }
|
||||
19
examples/hugo/themes/etch/i18n/en.toml
Normal file
19
examples/hugo/themes/etch/i18n/en.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
# Learn how to use Date format (date, created, updated)
|
||||
# -> https://gohugo.io/functions/dateformat/
|
||||
|
||||
[posts]
|
||||
|
||||
[posts.title]
|
||||
other = "Posts"
|
||||
|
||||
[posts.date]
|
||||
other = "Jan 2, 2006"
|
||||
|
||||
|
||||
[post]
|
||||
|
||||
[post.created]
|
||||
other = "January 2, 2006"
|
||||
|
||||
[post.updated]
|
||||
other = "Updated January 2, 2006"
|
||||
BIN
examples/hugo/themes/etch/images/screenshot.png
Normal file
BIN
examples/hugo/themes/etch/images/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
BIN
examples/hugo/themes/etch/images/screenshot_dark.png
Normal file
BIN
examples/hugo/themes/etch/images/screenshot_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
BIN
examples/hugo/themes/etch/images/screenshot_small.png
Normal file
BIN
examples/hugo/themes/etch/images/screenshot_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
BIN
examples/hugo/themes/etch/images/tn.png
Normal file
BIN
examples/hugo/themes/etch/images/tn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
examples/hugo/themes/etch/images/tn_dark.png
Normal file
BIN
examples/hugo/themes/etch/images/tn_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
0
examples/hugo/themes/etch/layouts/404.html
Normal file
0
examples/hugo/themes/etch/layouts/404.html
Normal file
11
examples/hugo/themes/etch/layouts/_default/baseof.html
Normal file
11
examples/hugo/themes/etch/layouts/_default/baseof.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
{{- partial "header.html" . -}}
|
||||
<main id="content">
|
||||
{{- block "main" . }}{{- end }}
|
||||
</main>
|
||||
{{- partial "footer.html" . -}}
|
||||
</body>
|
||||
</html>
|
||||
6
examples/hugo/themes/etch/layouts/_default/li.html
Normal file
6
examples/hugo/themes/etch/layouts/_default/li.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<li>
|
||||
<a href="{{ .Permalink }}">
|
||||
{{ .Title }}
|
||||
<small><time>{{ .Date | time.Format (i18n "posts.date") }}</time></small>
|
||||
</a>
|
||||
</li>
|
||||
3
examples/hugo/themes/etch/layouts/_default/list.html
Normal file
3
examples/hugo/themes/etch/layouts/_default/list.html
Normal file
@@ -0,0 +1,3 @@
|
||||
{{ define "main" }}
|
||||
{{- partial "posts.html" . -}}
|
||||
{{ end }}
|
||||
41
examples/hugo/themes/etch/layouts/_default/rss.xml
Normal file
41
examples/hugo/themes/etch/layouts/_default/rss.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
{{- $pctx := . -}}
|
||||
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
|
||||
{{- $pages := slice -}}
|
||||
{{- if or $.IsHome $.IsSection -}}
|
||||
{{- $pages = $pctx.RegularPages -}}
|
||||
{{- else -}}
|
||||
{{- $pages = $pctx.Pages -}}
|
||||
{{- end -}}
|
||||
{{- $limit := .Site.Config.Services.RSS.Limit -}}
|
||||
{{- if ge $limit 1 -}}
|
||||
{{- $pages = $pages | first $limit -}}
|
||||
{{- end -}}
|
||||
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
|
||||
<generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
|
||||
<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
|
||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||
{{ with .OutputFormats.Get "RSS" }}
|
||||
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
|
||||
{{ end }}
|
||||
{{ range where .Site.Pages "Kind" "page" }}
|
||||
{{ if or (eq .Section "posts") (eq .Section "post") }}
|
||||
<item>
|
||||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||
{{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>{{ .Content | html }}</description>
|
||||
</item>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</channel>
|
||||
</rss>
|
||||
17
examples/hugo/themes/etch/layouts/_default/single.html
Normal file
17
examples/hugo/themes/etch/layouts/_default/single.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{{ define "main" }}
|
||||
<article>
|
||||
<header id="post-header">
|
||||
<h1>{{ .Title }}</h1>
|
||||
<div>
|
||||
{{- if isset .Params "date" -}}
|
||||
{{ if eq .Lastmod .Date }}
|
||||
<time>{{ .Date | time.Format (i18n "post.created") }}</time>
|
||||
{{ else }}
|
||||
<time>{{ .Lastmod | time.Format (i18n "post.updated") }}</time>
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
</div>
|
||||
</header>
|
||||
{{- .Content -}}
|
||||
</article>
|
||||
{{ end }}
|
||||
8
examples/hugo/themes/etch/layouts/_default/taxonomy.html
Normal file
8
examples/hugo/themes/etch/layouts/_default/taxonomy.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{{ define "main" }}
|
||||
<h3>{{ .Title }}</h3>
|
||||
<ul id="posts">
|
||||
{{- range .Pages }}
|
||||
{{ .Render "li" }}
|
||||
{{- end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
4
examples/hugo/themes/etch/layouts/index.html
Normal file
4
examples/hugo/themes/etch/layouts/index.html
Normal file
@@ -0,0 +1,4 @@
|
||||
{{ define "main" }}
|
||||
{{ .Content }}
|
||||
{{- partial "posts.html" . -}}
|
||||
{{ end }}
|
||||
3
examples/hugo/themes/etch/layouts/partials/footer.html
Normal file
3
examples/hugo/themes/etch/layouts/partials/footer.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<footer id="footer">
|
||||
{{ .Site.Params.copyright }}
|
||||
</footer>
|
||||
31
examples/hugo/themes/etch/layouts/partials/head.html
Normal file
31
examples/hugo/themes/etch/layouts/partials/head.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{{ with .Site.Params.description -}}
|
||||
<meta name="description" content="{{ . }}">
|
||||
{{ end }}
|
||||
{{ printf `<link rel="shortcut icon" href="%s">` ("favicon.ico" | absURL) | safeHTML }}
|
||||
{{ with .OutputFormats.Get "rss" -}}
|
||||
{{ printf `<link rel="%s" type="%s" href="%s" title="%s">` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
|
||||
{{ end -}}
|
||||
|
||||
{{ $resources := slice -}}
|
||||
|
||||
{{ $resources = $resources | append (resources.Get "css/main.css") -}}
|
||||
|
||||
{{ $resources = $resources | append (resources.Get "css/min770px.css") -}}
|
||||
|
||||
{{ $dark := .Site.Params.dark | default "auto" -}}
|
||||
{{ if not (eq $dark "off") -}}
|
||||
{{ $resources = $resources | append (resources.Get "css/dark.css" | resources.ExecuteAsTemplate "dark.css" .) -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ if .Site.Params.highlight -}}
|
||||
{{ $resources = $resources | append (resources.Get "css/syntax.css") -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ $css := $resources | resources.Concat "css/style.css" | minify }}
|
||||
{{ printf `<link rel="stylesheet" href="%s">` $css.RelPermalink | safeHTML }}
|
||||
|
||||
<title>{{ .Title }}</title>
|
||||
</head>
|
||||
12
examples/hugo/themes/etch/layouts/partials/header.html
Normal file
12
examples/hugo/themes/etch/layouts/partials/header.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<header id="banner">
|
||||
<h2><a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a></h2>
|
||||
<nav>
|
||||
<ul>
|
||||
{{ range .Site.Menus.main.ByWeight -}}
|
||||
<li>
|
||||
{{ .Pre }}<a href="{{ .URL }}" title="{{ .Title }}">{{- .Name -}}</a>{{ .Post }}
|
||||
</li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
6
examples/hugo/themes/etch/layouts/partials/posts.html
Normal file
6
examples/hugo/themes/etch/layouts/partials/posts.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<h3>{{ i18n "posts.title" }}</h3>
|
||||
<ul id="posts">
|
||||
{{- range where site.RegularPages "Type" "in" site.Params.mainSections }}
|
||||
{{ .Render "li" }}
|
||||
{{- end }}
|
||||
</ul>
|
||||
4
examples/hugo/themes/etch/layouts/shortcodes/toc.html
Normal file
4
examples/hugo/themes/etch/layouts/shortcodes/toc.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<aside id="toc">
|
||||
<h4>Table of Contents</h4>
|
||||
{{ .Page.TableOfContents }}
|
||||
</aside>
|
||||
13
examples/hugo/themes/etch/theme.toml
Normal file
13
examples/hugo/themes/etch/theme.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
name = "Etch"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/LukasJoswiak/etch/blob/master/LICENSE"
|
||||
description = "Lightweight Hugo theme with a focus on content"
|
||||
homepage = "https://github.com/LukasJoswiak/etch"
|
||||
demosite = "https://lukasjoswiak.github.io/etch/"
|
||||
tags = ["simple", "minimal", "clean", "fast", "blog", "responsive", "dark mode", "privacy"]
|
||||
features = ["fast", "blog", "syntax highlighting", "dark mode"]
|
||||
min_version = "0.41"
|
||||
|
||||
[author]
|
||||
name = "Lukas Joswiak"
|
||||
homepage = "https://lukasjoswiak.com"
|
||||
1
examples/nextjs/.gitignore
vendored
1
examples/nextjs/.gitignore
vendored
@@ -23,6 +23,7 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
module.exports = {
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
6477
examples/nextjs/package-lock.json
generated
6477
examples/nextjs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,12 +7,12 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^12.0.8",
|
||||
"next": "12.1.0",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.7.0",
|
||||
"eslint-config-next": "^12.0.8"
|
||||
"eslint": "8.9.0",
|
||||
"eslint-config-next": "12.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
1633
examples/nextjs/yarn.lock
Normal file
1633
examples/nextjs/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@ export default function Index() {
|
||||
<p>
|
||||
Wait a sec...<em>its children</em>? To understand what we mean by
|
||||
this,{" "}
|
||||
<a href="https://remix.run/tutorial/4-nested-routes-params">
|
||||
<a href="https://remix.run/docs/en/v1/guides/routing">
|
||||
read all about nested routes in the docs
|
||||
</a>
|
||||
.
|
||||
|
||||
670
examples/sveltekit/package-lock.json
generated
670
examples/sveltekit/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "sveltekit",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "svelte-kit dev",
|
||||
"build": "svelte-kit build",
|
||||
@@ -9,7 +11,7 @@
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "next",
|
||||
"@sveltejs/kit": "next",
|
||||
"svelte": "^3.44.0"
|
||||
"svelte": "^3.46.0"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body>
|
||||
<div id="svelte">%svelte.body%</div>
|
||||
<div>%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
1
examples/sveltekit/src/global.d.ts
vendored
1
examples/sveltekit/src/global.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
@@ -1,19 +1,22 @@
|
||||
import cookie from 'cookie';
|
||||
import { v4 as uuid } from '@lukeed/uuid';
|
||||
|
||||
export const handle = async ({ request, resolve }) => {
|
||||
const cookies = cookie.parse(request.headers.cookie || '');
|
||||
request.locals.userid = cookies.userid || uuid();
|
||||
export const handle = async ({ event, resolve }) => {
|
||||
const cookies = cookie.parse(event.request.headers.get('cookie') || '');
|
||||
event.locals.userid = cookies.userid || uuid();
|
||||
|
||||
const response = await resolve(request);
|
||||
const response = await resolve(event);
|
||||
|
||||
if (!cookies.userid) {
|
||||
// if this is the first time the user has visited this app,
|
||||
// set a cookie so that we recognise them when they return
|
||||
response.headers['set-cookie'] = cookie.serialize('userid', request.locals.userid, {
|
||||
response.headers.set(
|
||||
'set-cookie',
|
||||
cookie.serialize('userid', event.locals.userid, {
|
||||
path: '/',
|
||||
httpOnly: true
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<div class="counter-viewport">
|
||||
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
|
||||
<strong style="top: -100%" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
|
||||
<strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
|
||||
<strong>{Math.floor($displayed_count)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,4 +94,9 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
top: -100%;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { invalidate } from '$app/navigation';
|
||||
|
||||
// this action (https://svelte.dev/tutorial/actions) allows us to
|
||||
// progressively enhance a <form> that already works without JS
|
||||
export function enhance(form, { pending, error, result }) {
|
||||
export function enhance(form, { pending, error, result } = {}) {
|
||||
let current_token;
|
||||
|
||||
async function handle_submit(e) {
|
||||
@@ -8,31 +10,35 @@ export function enhance(form, { pending, error, result }) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const body = new FormData(form);
|
||||
const data = new FormData(form);
|
||||
|
||||
if (pending) pending(body, form);
|
||||
if (pending) pending({ data, form });
|
||||
|
||||
try {
|
||||
const res = await fetch(form.action, {
|
||||
const response = await fetch(form.action, {
|
||||
method: form.method,
|
||||
headers: {
|
||||
accept: 'application/json'
|
||||
},
|
||||
body
|
||||
body: data
|
||||
});
|
||||
|
||||
if (token !== current_token) return;
|
||||
|
||||
if (res.ok) {
|
||||
result(res, form);
|
||||
if (response.ok) {
|
||||
if (result) result({ data, form, response });
|
||||
|
||||
const url = new URL(form.action);
|
||||
url.search = url.hash = '';
|
||||
invalidate(url.href);
|
||||
} else if (error) {
|
||||
error(res, null, form);
|
||||
error({ data, form, error: null, response });
|
||||
} else {
|
||||
console.error(await res.text());
|
||||
console.error(await response.text());
|
||||
}
|
||||
} catch (e) {
|
||||
if (error) {
|
||||
error(null, e, form);
|
||||
error({ data, form, error: e, response: null });
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { api } from './_api';
|
||||
|
||||
// PATCH /todos/:uid.json
|
||||
export const patch = async (request) => {
|
||||
return api(request, `todos/${request.locals.userid}/${request.params.uid}`, {
|
||||
text: request.body.get('text'),
|
||||
done: request.body.has('done') ? !!request.body.get('done') : undefined
|
||||
});
|
||||
};
|
||||
|
||||
// DELETE /todos/:uid.json
|
||||
export const del = async (request) => {
|
||||
return api(request, `todos/${request.locals.userid}/${request.params.uid}`);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
This module is used by the /todos.json and /todos/[uid].json
|
||||
endpoints to make calls to api.svelte.dev, which stores todos
|
||||
This module is used by the /todos endpoint to
|
||||
make calls to api.svelte.dev, which stores todos
|
||||
for each user. The leading underscore indicates that this is
|
||||
a private module, _not_ an endpoint — visiting /todos/_api
|
||||
will net you a 404 response.
|
||||
@@ -11,35 +11,12 @@
|
||||
|
||||
const base = 'https://api.svelte.dev';
|
||||
|
||||
export async function api(request, resource, data) {
|
||||
// user must have a cookie set
|
||||
if (!request.locals.userid) {
|
||||
return { status: 401 };
|
||||
}
|
||||
|
||||
const res = await fetch(`${base}/${resource}`, {
|
||||
method: request.method,
|
||||
export function api(method, resource, data) {
|
||||
return fetch(`${base}/${resource}`, {
|
||||
method,
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: data && JSON.stringify(data)
|
||||
});
|
||||
|
||||
// if the request came from a <form> submission, the browser's default
|
||||
// behaviour is to show the URL corresponding to the form's "action"
|
||||
// attribute. in those cases, we want to redirect them back to the
|
||||
// /todos page, rather than showing the response
|
||||
if (res.ok && request.method !== 'GET' && request.headers.accept !== 'application/json') {
|
||||
return {
|
||||
status: 303,
|
||||
headers: {
|
||||
location: '/todos'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: res.status,
|
||||
body: await res.json()
|
||||
};
|
||||
}
|
||||
|
||||
66
examples/sveltekit/src/routes/todos/index.js
Normal file
66
examples/sveltekit/src/routes/todos/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import { api } from './_api';
|
||||
|
||||
export const get = async ({ locals }) => {
|
||||
// locals.userid comes from src/hooks.js
|
||||
const response = await api('get', `todos/${locals.userid}`);
|
||||
|
||||
if (response.status === 404) {
|
||||
// user hasn't created a todo list.
|
||||
// start with an empty array
|
||||
return {
|
||||
body: {
|
||||
todos: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
return {
|
||||
body: {
|
||||
todos: await response.json()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: response.status
|
||||
};
|
||||
};
|
||||
|
||||
export const post = async ({ request, locals }) => {
|
||||
const form = await request.formData();
|
||||
|
||||
await api('post', `todos/${locals.userid}`, {
|
||||
text: form.get('text')
|
||||
});
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
// If the user has JavaScript disabled, the URL will change to
|
||||
// include the method override unless we redirect back to /todos
|
||||
const redirect = {
|
||||
status: 303,
|
||||
headers: {
|
||||
location: '/todos'
|
||||
}
|
||||
};
|
||||
|
||||
export const patch = async ({ request, locals }) => {
|
||||
const form = await request.formData();
|
||||
|
||||
await api('patch', `todos/${locals.userid}/${form.get('uid')}`, {
|
||||
text: form.has('text') ? form.get('text') : undefined,
|
||||
done: form.has('done') ? !!form.get('done') : undefined
|
||||
});
|
||||
|
||||
return redirect;
|
||||
};
|
||||
|
||||
export const del = async ({ request, locals }) => {
|
||||
const form = await request.formData();
|
||||
|
||||
await api('delete', `todos/${locals.userid}/${form.get('uid')}`);
|
||||
|
||||
return redirect;
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
import { api } from './_api';
|
||||
|
||||
// GET /todos.json
|
||||
export const get = async (request) => {
|
||||
// request.locals.userid comes from src/hooks.js
|
||||
const response = await api(request, `todos/${request.locals.userid}`);
|
||||
|
||||
if (response.status === 404) {
|
||||
// user hasn't created a todo list.
|
||||
// start with an empty array
|
||||
return { body: [] };
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
// POST /todos.json
|
||||
export const post = async (request) => {
|
||||
const response = await api(request, `todos/${request.locals.userid}`, {
|
||||
// because index.svelte posts a FormData object,
|
||||
// request.body is _also_ a (readonly) FormData
|
||||
// object, which allows us to get form data
|
||||
// with the `body.get(key)` method
|
||||
text: request.body.get('text')
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
@@ -1,40 +1,9 @@
|
||||
<script context="module">
|
||||
import { enhance } from '$lib/form';
|
||||
|
||||
// see https://kit.svelte.dev/docs#loading
|
||||
export const load = async ({ fetch }) => {
|
||||
const res = await fetch('/todos.json');
|
||||
|
||||
if (res.ok) {
|
||||
const todos = await res.json();
|
||||
|
||||
return {
|
||||
props: { todos }
|
||||
};
|
||||
}
|
||||
|
||||
const { message } = await res.json();
|
||||
|
||||
return {
|
||||
error: new Error(message)
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { enhance } from '$lib/form';
|
||||
import { scale } from 'svelte/transition';
|
||||
import { flip } from 'svelte/animate';
|
||||
|
||||
export let todos;
|
||||
|
||||
async function patch(res) {
|
||||
const todo = await res.json();
|
||||
|
||||
todos = todos.map((t) => {
|
||||
if (t.uid === todo.uid) return todo;
|
||||
return t;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -46,13 +15,10 @@
|
||||
|
||||
<form
|
||||
class="new"
|
||||
action="/todos.json"
|
||||
action="/todos"
|
||||
method="post"
|
||||
use:enhance={{
|
||||
result: async (res, form) => {
|
||||
const created = await res.json();
|
||||
todos = [...todos, created];
|
||||
|
||||
result: async ({ form }) => {
|
||||
form.reset();
|
||||
}
|
||||
}}
|
||||
@@ -68,41 +34,33 @@
|
||||
animate:flip={{ duration: 200 }}
|
||||
>
|
||||
<form
|
||||
action="/todos/{todo.uid}.json?_method=PATCH"
|
||||
action="/todos?_method=PATCH"
|
||||
method="post"
|
||||
use:enhance={{
|
||||
pending: (data) => {
|
||||
pending: ({ data }) => {
|
||||
todo.done = !!data.get('done');
|
||||
},
|
||||
result: patch
|
||||
}
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="uid" value={todo.uid} />
|
||||
<input type="hidden" name="done" value={todo.done ? '' : 'true'} />
|
||||
<button class="toggle" aria-label="Mark todo as {todo.done ? 'not done' : 'done'}" />
|
||||
</form>
|
||||
|
||||
<form
|
||||
class="text"
|
||||
action="/todos/{todo.uid}.json?_method=PATCH"
|
||||
method="post"
|
||||
use:enhance={{
|
||||
result: patch
|
||||
}}
|
||||
>
|
||||
<form class="text" action="/todos?_method=PATCH" method="post" use:enhance>
|
||||
<input type="hidden" name="uid" value={todo.uid} />
|
||||
<input aria-label="Edit todo" type="text" name="text" value={todo.text} />
|
||||
<button class="save" aria-label="Save todo" />
|
||||
</form>
|
||||
|
||||
<form
|
||||
action="/todos/{todo.uid}.json?_method=DELETE"
|
||||
action="/todos?_method=DELETE"
|
||||
method="post"
|
||||
use:enhance={{
|
||||
pending: () => (todo.pending_delete = true),
|
||||
result: () => {
|
||||
todos = todos.filter((t) => t.uid !== todo.uid);
|
||||
}
|
||||
pending: () => (todo.pending_delete = true)
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="uid" value={todo.uid} />
|
||||
<button class="delete" aria-label="Delete todo" disabled={todo.pending_delete} />
|
||||
</form>
|
||||
</div>
|
||||
@@ -158,7 +116,7 @@
|
||||
.done {
|
||||
transform: none;
|
||||
opacity: 0.4;
|
||||
filter: drop-shadow(0px 0px 1px rgba(0, 0, 0, 0.1));
|
||||
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
|
||||
form.text {
|
||||
|
||||
@@ -5,8 +5,10 @@ const config = {
|
||||
kit: {
|
||||
adapter: adapter(),
|
||||
|
||||
// hydrate the <div id="svelte"> element in src/app.html
|
||||
target: '#svelte'
|
||||
// Override http methods in the Todo forms
|
||||
methodOverride: {
|
||||
allowed: ['PATCH', 'DELETE']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
"@typescript-eslint/parser": "4.28.0",
|
||||
"async-retry": "1.2.3",
|
||||
"buffer-replace": "1.0.0",
|
||||
"cheerio": "1.0.0-rc.3",
|
||||
"eslint": "7.29.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-jest": "24.3.6",
|
||||
@@ -43,8 +42,9 @@
|
||||
"node-fetch": "2.6.1",
|
||||
"npm-package-arg": "6.1.0",
|
||||
"prettier": "2.3.1",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "27.0.4",
|
||||
"turbo": "1.0.18"
|
||||
"turbo": "1.1.9"
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
@@ -54,9 +54,10 @@
|
||||
"publish-from-github": "./utils/publish.sh",
|
||||
"changelog": "node utils/changelog.js",
|
||||
"build": "turbo run build",
|
||||
"vercel-build": "mkdir -p public && echo '<a href=\"https://vercel.com/import\">Import</a>' > public/output.html",
|
||||
"vercel-build": "yarn build && cd api && node -r ts-eager/register ./_lib/script/build.ts",
|
||||
"pre-commit": "lint-staged",
|
||||
"test-unit": "node utils/run.js test-unit",
|
||||
"test": "jest --rootDir=\"test\" --testPathPattern=\"\\.test.js\"",
|
||||
"test-unit": "yarn test && node utils/run.js test-unit",
|
||||
"test-integration-cli": "node utils/run.js test-integration-cli",
|
||||
"test-integration-once": "node utils/run.js test-integration-once",
|
||||
"test-integration-dev": "node utils/run.js test-integration-dev",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.14.1-canary.0",
|
||||
"version": "2.15.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
@@ -30,7 +30,7 @@
|
||||
"@types/node-fetch": "^2.1.6",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "^2.4.1",
|
||||
"@vercel/frameworks": "0.6.1-canary.0",
|
||||
"@vercel/frameworks": "0.7.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"aggregate-error": "3.0.1",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -67,8 +67,7 @@ function getPublicBuilder(
|
||||
typeof builder.src === 'string' &&
|
||||
isOfficialRuntime('static', builder.use) &&
|
||||
/^.*\/\*\*\/\*$/.test(builder.src) &&
|
||||
builder.config &&
|
||||
builder.config.zeroConfig === true
|
||||
builder.config?.zeroConfig === true
|
||||
) {
|
||||
return builder as Builder & { src: string };
|
||||
}
|
||||
|
||||
44
packages/build-utils/src/edge-function.ts
Normal file
44
packages/build-utils/src/edge-function.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { Files } from './types';
|
||||
|
||||
/**
|
||||
* An Edge Functions output
|
||||
*/
|
||||
export class EdgeFunction {
|
||||
type: 'EdgeFunction';
|
||||
|
||||
/**
|
||||
* A display name for the edge function.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The deployment target.
|
||||
* Only v8-worker is currently supported.
|
||||
*/
|
||||
deploymentTarget: 'v8-worker';
|
||||
|
||||
/**
|
||||
* The entrypoint for the edge function.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* The list of files to be included in the edge function bundle.
|
||||
*/
|
||||
files: Files;
|
||||
|
||||
/**
|
||||
* Extra environment variables in use for the user code, to be
|
||||
* assigned to the edge function.
|
||||
*/
|
||||
envVarsInUse?: string[];
|
||||
|
||||
constructor(params: Omit<EdgeFunction, 'type'>) {
|
||||
this.type = 'EdgeFunction';
|
||||
this.name = params.name;
|
||||
this.deploymentTarget = params.deploymentTarget;
|
||||
this.entrypoint = params.entrypoint;
|
||||
this.files = params.files;
|
||||
this.envVarsInUse = params.envVarsInUse;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import assert from 'assert';
|
||||
import intoStream from 'into-stream';
|
||||
import { File } from './types';
|
||||
import { FileBase } from './types';
|
||||
|
||||
interface FileBlobOptions {
|
||||
mode?: number;
|
||||
@@ -14,7 +14,7 @@ interface FromStreamOptions {
|
||||
stream: NodeJS.ReadableStream;
|
||||
}
|
||||
|
||||
export default class FileBlob implements File {
|
||||
export default class FileBlob implements FileBase {
|
||||
public type: 'FileBlob';
|
||||
public mode: number;
|
||||
public data: string | Buffer;
|
||||
@@ -48,6 +48,10 @@ export default class FileBlob implements File {
|
||||
return new FileBlob({ mode, contentType, data });
|
||||
}
|
||||
|
||||
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
|
||||
return this.toStream();
|
||||
}
|
||||
|
||||
toStream(): NodeJS.ReadableStream {
|
||||
return intoStream(this.data);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import fs from 'fs-extra';
|
||||
import multiStream from 'multistream';
|
||||
import path from 'path';
|
||||
import Sema from 'async-sema';
|
||||
import { File } from './types';
|
||||
import { FileBase } from './types';
|
||||
|
||||
const semaToPreventEMFILE = new Sema(20);
|
||||
|
||||
@@ -20,7 +20,7 @@ interface FromStreamOptions {
|
||||
fsPath: string;
|
||||
}
|
||||
|
||||
class FileFsRef implements File {
|
||||
class FileFsRef implements FileBase {
|
||||
public type: 'FileFsRef';
|
||||
public mode: number;
|
||||
public fsPath: string;
|
||||
|
||||
@@ -3,7 +3,7 @@ import fetch from 'node-fetch';
|
||||
import multiStream from 'multistream';
|
||||
import retry from 'async-retry';
|
||||
import Sema from 'async-sema';
|
||||
import { File } from './types';
|
||||
import { FileBase } from './types';
|
||||
|
||||
interface FileRefOptions {
|
||||
mode?: number;
|
||||
@@ -23,7 +23,7 @@ class BailableError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export default class FileRef implements File {
|
||||
export default class FileRef implements FileBase {
|
||||
public type: 'FileRef';
|
||||
public mode: number;
|
||||
public digest: string;
|
||||
|
||||
@@ -3,6 +3,7 @@ import debug from '../debug';
|
||||
import FileFsRef from '../file-fs-ref';
|
||||
import { File, Files, Meta } from '../types';
|
||||
import { remove, mkdirp, readlink, symlink } from 'fs-extra';
|
||||
import streamToBuffer from './stream-to-buffer';
|
||||
|
||||
export interface DownloadedFiles {
|
||||
[filePath: string]: FileFsRef;
|
||||
@@ -15,20 +16,45 @@ export function isSymbolicLink(mode: number): boolean {
|
||||
return (mode & S_IFMT) === S_IFLNK;
|
||||
}
|
||||
|
||||
async function prepareSymlinkTarget(
|
||||
file: File,
|
||||
fsPath: string
|
||||
): Promise<string> {
|
||||
const mkdirPromise = mkdirp(path.dirname(fsPath));
|
||||
if (file.type === 'FileFsRef') {
|
||||
const [target] = await Promise.all([readlink(file.fsPath), mkdirPromise]);
|
||||
return target;
|
||||
}
|
||||
|
||||
if (file.type === 'FileRef' || file.type === 'FileBlob') {
|
||||
const targetPathBufferPromise = await streamToBuffer(
|
||||
await file.toStreamAsync()
|
||||
);
|
||||
const [targetPathBuffer] = await Promise.all([
|
||||
targetPathBufferPromise,
|
||||
mkdirPromise,
|
||||
]);
|
||||
return targetPathBuffer.toString('utf8');
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`file.type "${(file as any).type}" not supported for symlink`
|
||||
);
|
||||
}
|
||||
|
||||
async function downloadFile(file: File, fsPath: string): Promise<FileFsRef> {
|
||||
const { mode } = file;
|
||||
if (mode && isSymbolicLink(mode) && file.type === 'FileFsRef') {
|
||||
const [target] = await Promise.all([
|
||||
readlink((file as FileFsRef).fsPath),
|
||||
mkdirp(path.dirname(fsPath)),
|
||||
]);
|
||||
|
||||
if (isSymbolicLink(mode)) {
|
||||
const target = await prepareSymlinkTarget(file, fsPath);
|
||||
|
||||
await symlink(target, fsPath);
|
||||
return FileFsRef.fromFsPath({ mode, fsPath });
|
||||
} else {
|
||||
}
|
||||
|
||||
const stream = file.toStream();
|
||||
return FileFsRef.fromStream({ mode, stream, fsPath });
|
||||
}
|
||||
}
|
||||
|
||||
async function removeFile(basePath: string, fileMatched: string) {
|
||||
const file = path.join(basePath, fileMatched);
|
||||
|
||||
@@ -46,8 +46,8 @@ export default async function glob(
|
||||
const files = await vanillaGlob(pattern, options);
|
||||
|
||||
for (const relativePath of files) {
|
||||
const fsPath = normalizePath(path.join(options.cwd!, relativePath));
|
||||
let stat: Stats = options.statCache![fsPath] as Stats;
|
||||
const fsPath = normalizePath(path.join(options.cwd, relativePath));
|
||||
let stat: Stats = options.statCache[fsPath] as Stats;
|
||||
assert(
|
||||
stat,
|
||||
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`
|
||||
|
||||
@@ -8,12 +8,13 @@ import { deprecate } from 'util';
|
||||
import { NowBuildError } from '../errors';
|
||||
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
||||
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
|
||||
import { readConfigFile } from './read-config-file';
|
||||
|
||||
export type CliType = 'yarn' | 'npm';
|
||||
export type CliType = 'yarn' | 'npm' | 'pnpm';
|
||||
|
||||
export interface ScanParentDirsResult {
|
||||
/**
|
||||
* "yarn" or "npm", depending on the presence of lockfiles.
|
||||
* "yarn", "npm", or "pnpm" depending on the presence of lockfiles.
|
||||
*/
|
||||
cliType: CliType;
|
||||
/**
|
||||
@@ -252,7 +253,7 @@ export async function scanParentDirs(
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const [packageLockJson, hasYarnLock] = await Promise.all([
|
||||
const [packageLockJson, hasYarnLock, pnpmLockYaml] = await Promise.all([
|
||||
fs
|
||||
.readJson(path.join(currentDestPath, 'package-lock.json'))
|
||||
.catch(error => {
|
||||
@@ -263,17 +264,26 @@ export async function scanParentDirs(
|
||||
throw error;
|
||||
}),
|
||||
fs.pathExists(path.join(currentDestPath, 'yarn.lock')),
|
||||
readConfigFile<{ lockfileVersion: number }>(
|
||||
path.join(currentDestPath, 'pnpm-lock.yaml')
|
||||
),
|
||||
]);
|
||||
|
||||
if (packageLockJson && !hasYarnLock) {
|
||||
if (packageLockJson && !hasYarnLock && !pnpmLockYaml) {
|
||||
cliType = 'npm';
|
||||
lockfileVersion = packageLockJson.lockfileVersion;
|
||||
}
|
||||
|
||||
if (!packageLockJson && !hasYarnLock && pnpmLockYaml) {
|
||||
cliType = 'pnpm';
|
||||
// just ensure that it is read as a number and not a string
|
||||
lockfileVersion = Number(pnpmLockYaml.lockfileVersion);
|
||||
}
|
||||
|
||||
// Only stop iterating if a lockfile was found, because it's possible
|
||||
// that the lockfile is in a higher path than where the `package.json`
|
||||
// file was found.
|
||||
if (packageLockJson || hasYarnLock) {
|
||||
if (packageLockJson || hasYarnLock || pnpmLockYaml) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -341,6 +351,13 @@ export async function runNpmInstall(
|
||||
commandArgs = args
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.concat(['install', '--no-audit', '--unsafe-perm']);
|
||||
} else if (cliType === 'pnpm') {
|
||||
// PNPM's install command is similar to NPM's but without the audit nonsense
|
||||
// @see options https://pnpm.io/cli/install
|
||||
opts.prettyCommand = 'pnpm install';
|
||||
commandArgs = args
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.concat(['install', '--unsafe-perm']);
|
||||
} else {
|
||||
opts.prettyCommand = 'yarn install';
|
||||
commandArgs = ['install', ...args];
|
||||
@@ -448,6 +465,8 @@ export async function runPackageJsonScript(
|
||||
|
||||
if (cliType === 'npm') {
|
||||
opts.prettyCommand = `npm run ${scriptName}`;
|
||||
} else if (cliType === 'pnpm') {
|
||||
opts.prettyCommand = `pnpm run ${scriptName}`;
|
||||
} else {
|
||||
opts.prettyCommand = `yarn run ${scriptName}`;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ export {
|
||||
getIgnoreFilter,
|
||||
};
|
||||
|
||||
export { EdgeFunction } from './edge-function';
|
||||
export {
|
||||
detectBuilders,
|
||||
detectOutputDirectory,
|
||||
|
||||
@@ -3,17 +3,17 @@ import Sema from 'async-sema';
|
||||
import { ZipFile } from 'yazl';
|
||||
import minimatch from 'minimatch';
|
||||
import { readlink } from 'fs-extra';
|
||||
import { Files, Config } from './types';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
import { isSymbolicLink } from './fs/download';
|
||||
import streamToBuffer from './fs/stream-to-buffer';
|
||||
import type { Files, Config } from './types';
|
||||
|
||||
interface Environment {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface LambdaOptions {
|
||||
files: Files;
|
||||
export type LambdaOptions = LambdaOptionsWithFiles | LambdaOptionsWithZipBuffer;
|
||||
|
||||
export interface LambdaOptionsBase {
|
||||
handler: string;
|
||||
runtime: string;
|
||||
memory?: number;
|
||||
@@ -21,10 +21,21 @@ export interface LambdaOptions {
|
||||
environment?: Environment;
|
||||
allowQuery?: string[];
|
||||
regions?: string[];
|
||||
supportsMultiPayloads?: boolean;
|
||||
}
|
||||
|
||||
export interface LambdaOptionsWithFiles extends LambdaOptionsBase {
|
||||
files: Files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `LambdaOptionsWithFiles` instead.
|
||||
*/
|
||||
export interface LambdaOptionsWithZipBuffer extends LambdaOptionsBase {
|
||||
/**
|
||||
* @deprecated Use `files` property instead.
|
||||
*/
|
||||
zipBuffer?: Buffer;
|
||||
zipBuffer: Buffer;
|
||||
}
|
||||
|
||||
interface GetLambdaOptionsFromFunctionOptions {
|
||||
@@ -34,7 +45,7 @@ interface GetLambdaOptionsFromFunctionOptions {
|
||||
|
||||
export class Lambda {
|
||||
type: 'Lambda';
|
||||
files: Files;
|
||||
files?: Files;
|
||||
handler: string;
|
||||
runtime: string;
|
||||
memory?: number;
|
||||
@@ -46,9 +57,10 @@ export class Lambda {
|
||||
* @deprecated Use `await lambda.createZip()` instead.
|
||||
*/
|
||||
zipBuffer?: Buffer;
|
||||
supportsMultiPayloads?: boolean;
|
||||
|
||||
constructor({
|
||||
files,
|
||||
constructor(opts: LambdaOptions) {
|
||||
const {
|
||||
handler,
|
||||
runtime,
|
||||
maxDuration,
|
||||
@@ -56,10 +68,13 @@ export class Lambda {
|
||||
environment = {},
|
||||
allowQuery,
|
||||
regions,
|
||||
zipBuffer,
|
||||
}: LambdaOptions) {
|
||||
if (!zipBuffer) {
|
||||
assert(typeof files === 'object', '"files" must be an object');
|
||||
supportsMultiPayloads,
|
||||
} = opts;
|
||||
if ('files' in opts) {
|
||||
assert(typeof opts.files === 'object', '"files" must be an object');
|
||||
}
|
||||
if ('zipBuffer' in opts) {
|
||||
assert(Buffer.isBuffer(opts.zipBuffer), '"zipBuffer" must be a Buffer');
|
||||
}
|
||||
assert(typeof handler === 'string', '"handler" is not a string');
|
||||
assert(typeof runtime === 'string', '"runtime" is not a string');
|
||||
@@ -81,6 +96,13 @@ export class Lambda {
|
||||
);
|
||||
}
|
||||
|
||||
if (supportsMultiPayloads !== undefined) {
|
||||
assert(
|
||||
typeof supportsMultiPayloads === 'boolean',
|
||||
'"supportsMultiPayloads" is not a boolean'
|
||||
);
|
||||
}
|
||||
|
||||
if (regions !== undefined) {
|
||||
assert(Array.isArray(regions), '"regions" is not an Array');
|
||||
assert(
|
||||
@@ -89,7 +111,7 @@ export class Lambda {
|
||||
);
|
||||
}
|
||||
this.type = 'Lambda';
|
||||
this.files = files;
|
||||
this.files = 'files' in opts ? opts.files : undefined;
|
||||
this.handler = handler;
|
||||
this.runtime = runtime;
|
||||
this.memory = memory;
|
||||
@@ -97,12 +119,16 @@ export class Lambda {
|
||||
this.environment = environment;
|
||||
this.allowQuery = allowQuery;
|
||||
this.regions = regions;
|
||||
this.zipBuffer = zipBuffer;
|
||||
this.zipBuffer = 'zipBuffer' in opts ? opts.zipBuffer : undefined;
|
||||
this.supportsMultiPayloads = supportsMultiPayloads;
|
||||
}
|
||||
|
||||
async createZip(): Promise<Buffer> {
|
||||
let { zipBuffer } = this;
|
||||
if (!zipBuffer) {
|
||||
if (!this.files) {
|
||||
throw new Error('`files` is not defined');
|
||||
}
|
||||
await sema.acquire();
|
||||
try {
|
||||
zipBuffer = await createZip(this.files);
|
||||
@@ -136,7 +162,7 @@ export async function createZip(files: Files): Promise<Buffer> {
|
||||
for (const name of names) {
|
||||
const file = files[name];
|
||||
if (file.mode && isSymbolicLink(file.mode) && file.type === 'FileFsRef') {
|
||||
const symlinkTarget = await readlink((file as FileFsRef).fsPath);
|
||||
const symlinkTarget = await readlink(file.fsPath);
|
||||
symlinkTargets.set(name, symlinkTarget);
|
||||
}
|
||||
}
|
||||
@@ -150,7 +176,7 @@ export async function createZip(files: Files): Promise<Buffer> {
|
||||
if (typeof symlinkTarget === 'string') {
|
||||
zipFile.addBuffer(Buffer.from(symlinkTarget, 'utf8'), name, opts);
|
||||
} else {
|
||||
const stream = file.toStream() as import('stream').Readable;
|
||||
const stream = file.toStream();
|
||||
stream.on('error', reject);
|
||||
zipFile.addReadStream(stream, name, opts);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Lambda, LambdaOptions } from './lambda';
|
||||
import { Lambda, LambdaOptionsWithFiles } from './lambda';
|
||||
|
||||
interface NodejsLambdaOptions extends LambdaOptions {
|
||||
interface NodejsLambdaOptions extends LambdaOptionsWithFiles {
|
||||
shouldAddHelpers: boolean;
|
||||
shouldAddSourcemapSupport: boolean;
|
||||
awsLambdaHandler?: string;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import FileBlob from './file-blob';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
import FileRef from './file-ref';
|
||||
import { File } from './types';
|
||||
import { Lambda } from './lambda';
|
||||
|
||||
interface PrerenderOptions {
|
||||
expiration: number | false;
|
||||
lambda: Lambda;
|
||||
fallback: FileBlob | FileFsRef | FileRef | null;
|
||||
fallback: File | null;
|
||||
group?: number;
|
||||
bypassToken?: string | null /* optional to be non-breaking change */;
|
||||
allowQuery?: string[];
|
||||
@@ -16,7 +14,7 @@ export class Prerender {
|
||||
public type: 'Prerender';
|
||||
public expiration: number | false;
|
||||
public lambda: Lambda;
|
||||
public fallback: FileBlob | FileFsRef | FileRef | null;
|
||||
public fallback: File | null;
|
||||
public group?: number;
|
||||
public bypassToken: string | null;
|
||||
public allowQuery?: string[];
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import FileRef from './file-ref';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
import { Lambda } from './lambda';
|
||||
import type FileRef from './file-ref';
|
||||
import type FileFsRef from './file-fs-ref';
|
||||
import type FileBlob from './file-blob';
|
||||
import type { Lambda } from './lambda';
|
||||
import type { Prerender } from './prerender';
|
||||
import type { EdgeFunction } from './edge-function';
|
||||
|
||||
export interface Env {
|
||||
[name: string]: string | undefined;
|
||||
}
|
||||
|
||||
export interface File {
|
||||
export type File = FileRef | FileFsRef | FileBlob;
|
||||
export interface FileBase {
|
||||
type: string;
|
||||
mode: number;
|
||||
contentType?: string;
|
||||
toStream: () => NodeJS.ReadableStream;
|
||||
toStreamAsync?: () => Promise<NodeJS.ReadableStream>;
|
||||
/**
|
||||
* The absolute path to the file in the filesystem
|
||||
*/
|
||||
fsPath?: string;
|
||||
}
|
||||
|
||||
export interface Files {
|
||||
@@ -23,16 +23,6 @@ export interface Files {
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
[key: string]:
|
||||
| string
|
||||
| string[]
|
||||
| boolean
|
||||
| number
|
||||
| { [key: string]: string }
|
||||
| BuilderFunctions
|
||||
| ProjectSettings
|
||||
| undefined
|
||||
| null;
|
||||
maxLambdaSize?: string;
|
||||
includeFiles?: string | string[];
|
||||
excludeFiles?: string | string[];
|
||||
@@ -51,6 +41,7 @@ export interface Config {
|
||||
devCommand?: string;
|
||||
framework?: string | null;
|
||||
nodeVersion?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
@@ -63,35 +54,7 @@ export interface Meta {
|
||||
env?: Env;
|
||||
buildEnv?: Env;
|
||||
avoidTopLevelInstall?: boolean;
|
||||
}
|
||||
|
||||
export interface AnalyzeOptions {
|
||||
/**
|
||||
* All source files of the project
|
||||
*/
|
||||
files: {
|
||||
[filePath: string]: FileRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* Name of entrypoint file for this particular build job. Value
|
||||
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
|
||||
* `entrypoint` is always a discrete file and never a glob, since globs are
|
||||
* expanded into separate builds at deployment time.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you are encouraged to perform your
|
||||
* build process. This directory will be populated with the restored cache.
|
||||
*/
|
||||
workPath: string;
|
||||
|
||||
/**
|
||||
* An arbitrary object passed by the user in the build definition defined
|
||||
* in `vercel.json`.
|
||||
*/
|
||||
config: Config;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface BuildOptions {
|
||||
@@ -394,10 +357,10 @@ export interface Images {
|
||||
|
||||
export interface BuildResultV2 {
|
||||
// TODO: use proper `Route` type from `routing-utils` (perhaps move types to a common package)
|
||||
routes: any[];
|
||||
routes?: any[];
|
||||
images?: Images;
|
||||
output: {
|
||||
[key: string]: File | Lambda;
|
||||
[key: string]: FileBase | Lambda | Prerender | EdgeFunction;
|
||||
};
|
||||
wildcard?: Array<{
|
||||
domain: string;
|
||||
|
||||
15
packages/build-utils/test/fixtures/22-pnpm/package.json
vendored
Normal file
15
packages/build-utils/test/fixtures/22-pnpm/package.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "22-pnpm",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "mkdir -p public && (printf \"pnpm version: \" && pnpm -v) > public/index.txt"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
}
|
||||
19
packages/build-utils/test/fixtures/22-pnpm/pnpm-lock.yaml
generated
vendored
Normal file
19
packages/build-utils/test/fixtures/22-pnpm/pnpm-lock.yaml
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
lockfileVersion: 5.3
|
||||
|
||||
specifiers:
|
||||
once: ^1.4.0
|
||||
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
packages:
|
||||
|
||||
/once/1.4.0:
|
||||
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
dev: false
|
||||
|
||||
/wrappy/1.0.2:
|
||||
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
|
||||
dev: false
|
||||
11
packages/build-utils/test/fixtures/22-pnpm/vercel.json
vendored
Normal file
11
packages/build-utils/test/fixtures/22-pnpm/vercel.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "pnpm version: 6",
|
||||
"logMustContain": "pnpm run build"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
packages/build-utils/test/fixtures/23-pnpm-workspaces/c/package.json
vendored
Normal file
5
packages/build-utils/test/fixtures/23-pnpm-workspaces/c/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "c",
|
||||
"license": "MIT",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
8
packages/build-utils/test/fixtures/23-pnpm-workspaces/d/package.json
vendored
Normal file
8
packages/build-utils/test/fixtures/23-pnpm-workspaces/d/package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "d",
|
||||
"license": "MIT",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"once": "1.4.0"
|
||||
}
|
||||
}
|
||||
6
packages/build-utils/test/fixtures/23-pnpm-workspaces/package.json
vendored
Normal file
6
packages/build-utils/test/fixtures/23-pnpm-workspaces/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "23-pnpm-workspaces",
|
||||
"license": "MIT",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
27
packages/build-utils/test/fixtures/23-pnpm-workspaces/pnpm-lock.yaml
generated
vendored
Normal file
27
packages/build-utils/test/fixtures/23-pnpm-workspaces/pnpm-lock.yaml
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
lockfileVersion: 5.3
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
specifiers: {}
|
||||
|
||||
c:
|
||||
specifiers: {}
|
||||
|
||||
d:
|
||||
specifiers:
|
||||
once: 1.4.0
|
||||
devDependencies:
|
||||
once: 1.4.0
|
||||
|
||||
packages:
|
||||
|
||||
/once/1.4.0:
|
||||
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/wrappy/1.0.2:
|
||||
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
|
||||
dev: true
|
||||
3
packages/build-utils/test/fixtures/23-pnpm-workspaces/pnpm-workspace.yaml
vendored
Normal file
3
packages/build-utils/test/fixtures/23-pnpm-workspaces/pnpm-workspace.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
- 'c'
|
||||
- 'd'
|
||||
1
packages/build-utils/test/fixtures/24-pnpm-hoisted/.gitignore
vendored
Normal file
1
packages/build-utils/test/fixtures/24-pnpm-hoisted/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vercel
|
||||
1
packages/build-utils/test/fixtures/24-pnpm-hoisted/.npmrc
vendored
Normal file
1
packages/build-utils/test/fixtures/24-pnpm-hoisted/.npmrc
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node-linker=hoisted
|
||||
5
packages/build-utils/test/fixtures/24-pnpm-hoisted/a/index.js
vendored
Normal file
5
packages/build-utils/test/fixtures/24-pnpm-hoisted/a/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
const once = require('once');
|
||||
|
||||
module.exports = () => {
|
||||
once(() => {});
|
||||
};
|
||||
5
packages/build-utils/test/fixtures/24-pnpm-hoisted/b/index.js
vendored
Normal file
5
packages/build-utils/test/fixtures/24-pnpm-hoisted/b/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
const once = require('once');
|
||||
|
||||
module.exports = () => {
|
||||
once(() => {});
|
||||
};
|
||||
10
packages/build-utils/test/fixtures/24-pnpm-hoisted/index.js
vendored
Normal file
10
packages/build-utils/test/fixtures/24-pnpm-hoisted/index.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
const { exec } = require('exeggcute');
|
||||
|
||||
const a = require('./a');
|
||||
const b = require('./b');
|
||||
|
||||
a();
|
||||
b();
|
||||
|
||||
exec('mkdir public', __dirname);
|
||||
exec('echo "Hello, World!" > public/index.html', __dirname);
|
||||
11
packages/build-utils/test/fixtures/24-pnpm-hoisted/package.json
vendored
Normal file
11
packages/build-utils/test/fixtures/24-pnpm-hoisted/package.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"private": "true",
|
||||
"name": "24-pnpm-hoisted",
|
||||
"scripts": {
|
||||
"build": "ls -Al node_modules && node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"exeggcute": "^1.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
}
|
||||
25
packages/build-utils/test/fixtures/24-pnpm-hoisted/pnpm-lock.yaml
generated
vendored
Normal file
25
packages/build-utils/test/fixtures/24-pnpm-hoisted/pnpm-lock.yaml
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
lockfileVersion: 5.3
|
||||
|
||||
specifiers:
|
||||
exeggcute: ^1.0.0
|
||||
once: ^1.4.0
|
||||
|
||||
dependencies:
|
||||
exeggcute: 1.0.0
|
||||
once: 1.4.0
|
||||
|
||||
packages:
|
||||
|
||||
/exeggcute/1.0.0:
|
||||
resolution: {integrity: sha1-qLXakIhjGCDm9ggx4CvEc5mnbBU=}
|
||||
dev: false
|
||||
|
||||
/once/1.4.0:
|
||||
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
dev: false
|
||||
|
||||
/wrappy/1.0.2:
|
||||
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
|
||||
dev: false
|
||||
20
packages/build-utils/test/fixtures/24-pnpm-hoisted/vercel.json
vendored
Normal file
20
packages/build-utils/test/fixtures/24-pnpm-hoisted/vercel.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "Hello, World!"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"logMustContain": "once"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"logMustContain": "exeggcute"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"logMustContain": "wrappy"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -31,6 +31,7 @@ const skipFixtures: string[] = [
|
||||
'07-zero-config-jekyll',
|
||||
'08-zero-config-middleman',
|
||||
'21-npm-workspaces',
|
||||
'23-pnpm-workspaces',
|
||||
];
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
|
||||
87
packages/build-utils/test/unit.test.ts
vendored
87
packages/build-utils/test/unit.test.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import ms from 'ms';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { strict as assert } from 'assert';
|
||||
import fs, { readlink } from 'fs-extra';
|
||||
import { strict as assert, strictEqual } from 'assert';
|
||||
import { createZip } from '../src/lambda';
|
||||
import { getSupportedNodeVersion } from '../src/fs/node-version';
|
||||
import download from '../src/fs/download';
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
runNpmInstall,
|
||||
runPackageJsonScript,
|
||||
scanParentDirs,
|
||||
FileBlob,
|
||||
} from '../src';
|
||||
|
||||
async function expectBuilderError(promise: Promise<any>, pattern: string) {
|
||||
@@ -47,7 +48,7 @@ afterEach(() => {
|
||||
console.warn = originalConsoleWarn;
|
||||
});
|
||||
|
||||
it('should re-create symlinks properly', async () => {
|
||||
it('should re-create FileFsRef symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
@@ -69,6 +70,72 @@ it('should re-create symlinks properly', async () => {
|
||||
assert(linkStat.isSymbolicLink());
|
||||
assert(linkDirStat.isSymbolicLink());
|
||||
assert(aStat.isFile());
|
||||
|
||||
const [linkDirContents, linkTextContents] = await Promise.all([
|
||||
readlink(path.join(outDir, 'link-dir')),
|
||||
readlink(path.join(outDir, 'link.txt')),
|
||||
]);
|
||||
|
||||
strictEqual(linkDirContents, 'dir');
|
||||
strictEqual(linkTextContents, './a.txt');
|
||||
});
|
||||
|
||||
it('should re-create FileBlob symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
|
||||
const files = {
|
||||
'a.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'a text',
|
||||
}),
|
||||
'dir/b.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'b text',
|
||||
}),
|
||||
'link-dir': new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'dir',
|
||||
}),
|
||||
'link.txt': new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'a.txt',
|
||||
}),
|
||||
};
|
||||
|
||||
strictEqual(Object.keys(files).length, 4);
|
||||
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
|
||||
const files2 = await download(files, outDir);
|
||||
strictEqual(Object.keys(files2).length, 4);
|
||||
|
||||
const [linkStat, linkDirStat, aStat, dirStat] = await Promise.all([
|
||||
fs.lstat(path.join(outDir, 'link.txt')),
|
||||
fs.lstat(path.join(outDir, 'link-dir')),
|
||||
fs.lstat(path.join(outDir, 'a.txt')),
|
||||
fs.lstat(path.join(outDir, 'dir')),
|
||||
]);
|
||||
|
||||
assert(linkStat.isSymbolicLink());
|
||||
assert(linkDirStat.isSymbolicLink());
|
||||
assert(aStat.isFile());
|
||||
assert(dirStat.isDirectory());
|
||||
|
||||
const [linkDirContents, linkTextContents] = await Promise.all([
|
||||
readlink(path.join(outDir, 'link-dir')),
|
||||
readlink(path.join(outDir, 'link.txt')),
|
||||
]);
|
||||
|
||||
strictEqual(linkDirContents, 'dir');
|
||||
strictEqual(linkTextContents, 'a.txt');
|
||||
});
|
||||
|
||||
it('should create zip files with symlinks properly', async () => {
|
||||
@@ -332,3 +399,17 @@ it('should detect npm Workspaces', async () => {
|
||||
expect(result.cliType).toEqual('npm');
|
||||
expect(result.lockfileVersion).toEqual(2);
|
||||
});
|
||||
|
||||
it('should detect pnpm', async () => {
|
||||
const fixture = path.join(__dirname, 'fixtures', '22-pnpm');
|
||||
const result = await scanParentDirs(fixture);
|
||||
expect(result.cliType).toEqual('pnpm');
|
||||
expect(result.lockfileVersion).toEqual(5.3);
|
||||
});
|
||||
|
||||
it('should detect pnpm Workspaces', async () => {
|
||||
const fixture = path.join(__dirname, 'fixtures', '23-pnpm-workspaces/a');
|
||||
const result = await scanParentDirs(fixture);
|
||||
expect(result.cliType).toEqual('pnpm');
|
||||
expect(result.lockfileVersion).toEqual(5.3);
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user