Compare commits

..

10 Commits

Author SHA1 Message Date
Ethan Arrowood
92f5b6e0c9 Publish Stable
- vercel@28.13.0
 - @vercel/gatsby-plugin-vercel-builder@0.1.2
 - @vercel/next@3.3.18
 - @vercel/node@2.8.15
 - @vercel/static-build@1.2.0
2023-01-20 10:27:31 -07:00
JJ Kasper
ed6ce1149a [next] Ensure outputDirectory is normalized (#9269)
This ensures we normalize an absolute `outputDirectory` passed into the Next.js builder as this can cause incorrect resolving. We should probably normalize this higher up as well but this is a starting point. 

Fixes: [slack thread](https://vercel.slack.com/archives/C0289CGVAR2/p1674176040821579?thread_ts=1674168748.257019&cid=C0289CGVAR2)
2023-01-20 17:08:32 +00:00
Ethan Arrowood
fc3611fb80 [static-build] add gatsby v5 fixture (#9267)
Adds a gatsby v5 fixture using ssr and dsg to demonstrate plugin works

Co-authored-by: Nathan Rajlich <n@n8.io>
2023-01-20 03:02:26 -08:00
Nathan Rajlich
ed33c2b27c [gatsby-plugin-vercel-builder] Remove some leftover console.log calls (#9265) 2023-01-19 16:05:48 -08:00
Nathan Rajlich
a7a5bf1a12 [tests] Pass in builder to runBuildLambda() (#9262)
Removes the need for `next`/`static-build` to be present in the root `package.json` file.
2023-01-19 15:10:06 -08:00
JJ Kasper
cc687b3880 [next] Correct RSC route for app dir with trailingSlash (#9263) 2023-01-19 15:03:58 -08:00
Nathan Rajlich
053ec92d5f [node] Add build logs probe for TypeScript usage messages (#9261)
Follow-up to https://github.com/vercel/vercel/pull/9258, even though that issue seemed to only happen when linked to the monorepo locally. In any case, this test will ensure those log messages are intact for any other change around that part of the codebase in the future.
2023-01-19 21:39:27 +00:00
Ethan Arrowood
4838dc336a [static-build] run pnpm install --lockfile-only after injecting gatsby plugins (#9259)
Fixes a bug where after injecting plugins and modifying the users package.json, pnpm would fail normal install
2023-01-19 20:07:53 +00:00
Nathan Rajlich
eae45f4019 [node] Fix TypeScript built-in compiler log message check (#9258)
This check started breaking probably after the pnpm switch, so switch to a simpler solution that doesn't require comparing the paths.
2023-01-19 13:12:31 +00:00
Ethan Arrowood
02feb564a7 [static-build][fs-detectors] add gatsby builder plugin injection (#9252)
Modifies the `@vercel/static-build` injection logic in order to now inject both the analytics and builder gatsby plugins.
2023-01-19 03:34:47 +00:00
165 changed files with 41011 additions and 240 deletions

View File

@@ -13,7 +13,6 @@
"@typescript-eslint/parser": "5.21.0",
"@vercel/build-utils": "*",
"@vercel/ncc": "0.24.0",
"@vercel/next": "*",
"async-retry": "1.2.3",
"buffer-replace": "1.0.0",
"create-svelte": "2.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.12.8",
"version": "28.13.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -44,13 +44,13 @@
"@vercel/build-utils": "5.9.0",
"@vercel/go": "2.2.30",
"@vercel/hydrogen": "0.0.44",
"@vercel/next": "3.3.17",
"@vercel/node": "2.8.14",
"@vercel/next": "3.3.18",
"@vercel/node": "2.8.15",
"@vercel/python": "3.1.40",
"@vercel/redwood": "1.0.51",
"@vercel/remix": "1.2.7",
"@vercel/ruby": "1.3.56",
"@vercel/static-build": "1.1.7"
"@vercel/static-build": "1.2.0"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "0.1.1",
"version": "0.1.2",
"main": "dist/index.js",
"files": [
"dist",
@@ -15,7 +15,7 @@
},
"dependencies": {
"@vercel/build-utils": "5.9.0",
"@vercel/node": "2.8.14",
"@vercel/node": "2.8.15",
"@vercel/routing-utils": "2.1.8",
"ajv": "8.12.0",
"esbuild": "0.16.17",

View File

@@ -31,8 +31,6 @@ export async function generateVercelBuildOutputAPI3Output({
functions: gatsbyStoreState.functions,
};
console.log(state);
if (validateGatsbyState(state)) {
console.log('▲ Creating Vercel build output');
await remove(join('.vercel', 'output'));
@@ -96,8 +94,6 @@ export async function generateVercelBuildOutputAPI3Output({
routes: routes || undefined,
};
console.log(config);
await writeJson(exportPath, config);
console.log('Vercel output has been generated');
} else {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.3.17",
"version": "3.3.18",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",

View File

@@ -214,7 +214,7 @@ export const build: BuildV2 = async ({
repoRootPath = entryPath;
entryPath = path.join(entryPath, config.rootDirectory as string);
}
const outputDirectory = config.outputDirectory || '.next';
const outputDirectory = path.join('./', config.outputDirectory || '.next');
const dotNextStatic = path.join(entryPath, outputDirectory, 'static');
// TODO: remove after testing used for simulating root directory monorepo
// setting that can't be triggered with vercel.json
@@ -2595,7 +2595,7 @@ export const prepareCache: PrepareCache = async ({
debug('Preparing cache...');
const entryDirectory = path.dirname(entrypoint);
const entryPath = path.join(workPath, entryDirectory);
const outputDirectory = config.outputDirectory || '.next';
const outputDirectory = path.join('./', config.outputDirectory || '.next');
const nextVersionRange = await getNextVersionRange(entryPath);
const isLegacy = nextVersionRange && isLegacyNext(nextVersionRange);

View File

@@ -1417,7 +1417,7 @@ export async function serverBuild({
src: `^${path.posix.join(
'/',
entryDirectory,
'/((?!.+\\.rsc).+)$'
'/((?!.+\\.rsc).+?)(?:/)?$'
)}`,
has: [
{

View File

@@ -0,0 +1,12 @@
/* eslint-env jest */
const path = require('path');
const { deployAndTest } = require('../../utils');
const ctx = {};
describe(`${__dirname.split(path.sep).pop()}`, () => {
it('should deploy and pass probe checks', async () => {
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
});

View File

@@ -0,0 +1,12 @@
{
"name": "test-root-dir",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@next/font": "13.1.2",
"next": "13.1.2",
"react": "18.2.0",
"react-dom": "18.2.0"
}
}

View File

@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
module.exports = nextConfig;

View File

@@ -0,0 +1,12 @@
{
"name": "client",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {}
}

View File

@@ -0,0 +1,9 @@
import { NextResponse } from 'next/server';
export default function handler(req) {
return NextResponse.json({ hello: 'world', now: Date.now(), url: req.url });
}
export const config = {
runtime: 'edge',
};

View File

@@ -0,0 +1,5 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' });
}

View File

@@ -0,0 +1,3 @@
export default function Index() {
return <p>Index page</p>;
}

View File

@@ -0,0 +1,20 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next",
"config": {
"buildCommand": "cd packages/client && yarn build",
"outputDirectory": "/packages/client/.next"
}
}
],
"probes": [
{
"path": "/api/data/first",
"status": 200,
"mustContain": "\"hello\":\"world\""
}
]
}

View File

@@ -0,0 +1,7 @@
export default function AnotherPage(props) {
return (
<>
<p>hello from newroot/dashboard/another</p>
</>
);
}

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html className="this-is-another-document-html">
<head>
<title>{`hello world`}</title>
</head>
<body className="this-is-another-document-body">{children}</body>
</html>
);
}

View File

@@ -0,0 +1,7 @@
export default function ChangelogPage(props) {
return (
<>
<p>hello from app/dashboard/changelog</p>
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function HelloPage(props) {
return (
<>
<p>hello from app/dashboard/rootonly/hello</p>
</>
);
}

View File

@@ -0,0 +1,20 @@
'use client';
import { useState, useEffect } from 'react';
import style from './style.module.css';
import './style.css';
export default function ClientComponentRoute() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(1);
}, [count]);
return (
<>
<p className={style.red}>
hello from app/client-component-route. <b>count: {count}</b>
</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
b {
color: blue;
}

View File

@@ -0,0 +1,3 @@
.red {
color: red;
}

View File

@@ -0,0 +1,20 @@
'use client';
import { useState, useEffect } from 'react';
import styles from './style.module.css';
import './style.css';
export default function ClientNestedLayout({ children }) {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(1);
}, []);
return (
<>
<h1 className={styles.red}>Client Nested. Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function ClientPage() {
return (
<>
<p>hello from app/client-nested</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
button {
color: red;
}

View File

@@ -0,0 +1,3 @@
.red {
color: red;
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsBreakdownPage(props) {
return (
<>
<p>hello from app/dashboard/(custom)/deployments/breakdown</p>
</>
);
}

View File

@@ -0,0 +1,8 @@
export default function CustomDashboardRootLayout({ children }) {
return (
<>
<h2>Custom dashboard</h2>
{children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsPage(props) {
return (
<>
<p>hello from app/dashboard/deployments/[id]. ID is: {props.params.id}</p>
</>
);
}

View File

@@ -0,0 +1,10 @@
export default function DeploymentsPage(props) {
return (
<>
<p>
hello from app/dashboard/deployments/[id]/settings. ID is:{' '}
{props.params.id}
</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>catchall</p>;
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsInfoPage(props) {
return (
<>
<p>hello from app/dashboard/deployments/info</p>
</>
);
}

View File

@@ -0,0 +1,8 @@
export default function DeploymentsLayout({ message, children }) {
return (
<>
<h2>Deployments hello</h2>
{children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function IntegrationsPage(props) {
return (
<>
<p>hello from app/dashboard/integrations</p>
</>
);
}

View File

@@ -0,0 +1,8 @@
export default function DashboardLayout(props) {
return (
<>
<h1>Dashboard</h1>
{props.children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function DashboardPage(props) {
return (
<>
<p>hello from app/dashboard</p>
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function IdLayout({ children, params }) {
return (
<>
<h3>
Id Layout. Params:{' '}
<span id="id-layout-params">{JSON.stringify(params)}</span>
</h3>
{children}
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function IdPage({ children, params }) {
return (
<>
<p>
Id Page. Params:{' '}
<span id="id-page-params">{JSON.stringify(params)}</span>
</p>
{children}
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function CategoryLayout({ children, params }) {
return (
<>
<h2>
Category Layout. Params:{' '}
<span id="category-layout-params">{JSON.stringify(params)}</span>{' '}
</h2>
{children}
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function DynamicLayout({ children, params }) {
return (
<>
<h1>
Dynamic Layout. Params:{' '}
<span id="dynamic-layout-params">{JSON.stringify(params)}</span>
</h1>
{children}
</>
);
}

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html className="this-is-the-document-html">
<head>
<title>{`hello world`}</title>
</head>
<body className="this-is-the-document-body">{children}</body>
</html>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>index app page</p>;
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsPage(props) {
return (
<>
<p>hello from app/partial-match-[id]. ID is: {props.params.id}</p>
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function SharedComponentRoute() {
return (
<>
<p>hello from app/shared-component-route</p>
</>
);
}

View File

@@ -0,0 +1,9 @@
'use client';
export default function ShouldNotServeClientDotJs(props) {
return (
<>
<p>hello from app/should-not-serve-client</p>
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function ShouldNotServeServerDotJs(props) {
return (
<>
<p>hello from app/should-not-serve-server</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading-layout">Loading layout...</p>;
}

View File

@@ -0,0 +1,18 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 1000));
return {
message: 'hello from slow layout',
};
}
export default function SlowLayout(props) {
const data = use(getData());
return (
<>
<p id="slow-layout-message">{data.message}</p>
{props.children}
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading-page">Loading page...</p>;
}

View File

@@ -0,0 +1,13 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 5000));
return {
message: 'hello from slow page',
};
}
export default function SlowPage(props) {
const data = use(getData());
return <h1 id="slow-page-message">{data.message}</h1>;
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading">Loading...</p>;
}

View File

@@ -0,0 +1,18 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 5000));
return {
message: 'hello from slow layout',
};
}
export default function SlowLayout(props) {
const data = use(getData());
return (
<>
<p id="slow-layout-message">{data.message}</p>
{props.children}
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <h1 id="page-message">Hello World</h1>;
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading">Loading...</p>;
}

View File

@@ -0,0 +1,13 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 5000));
return {
message: 'hello from slow page',
};
}
export default function SlowPage(props) {
const data = use(getData());
return <h1 id="slow-page-message">{data.message}</h1>;
}

View File

@@ -0,0 +1,10 @@
export const revalidate = 3;
export default function Page() {
return (
<>
<p>hello from /ssg</p>
<p>{Date.now()}</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p id="page">Page</p>;
}

View File

@@ -0,0 +1,12 @@
/* eslint-env jest */
const path = require('path');
const { deployAndTest } = require('../../utils');
const ctx = {};
describe(`${__dirname.split(path.sep).pop()}`, () => {
it('should deploy and pass probe checks', async () => {
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
});

View File

@@ -0,0 +1,16 @@
import { NextResponse } from 'next/server';
export function middleware(request) {
if (request.nextUrl.pathname === '/middleware-to-dashboard/') {
// TODO: this does not copy __flight__ and __flight_router_state_tree__
return NextResponse.rewrite(new URL('/dashboard', request.url));
}
if (
request.nextUrl.pathname === '/ssg/' &&
request.nextUrl.searchParams.get('override')
) {
request.nextUrl.pathname = '/overridden/';
return NextResponse.redirect(request.nextUrl);
}
}

View File

@@ -0,0 +1,14 @@
module.exports = {
trailingSlash: true,
experimental: {
appDir: true,
},
rewrites: async () => {
return [
{
source: '/rewritten-to-dashboard',
destination: '/dashboard',
},
];
},
};

View File

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

View File

@@ -0,0 +1,3 @@
export default function handler(req, res) {
return res.json({ hello: 'world' });
}

View File

@@ -0,0 +1,7 @@
export default function Page(props) {
return (
<>
<p>hello from pages/blog/[slug]</p>
</>
);
}

View File

@@ -0,0 +1 @@
hello world

View File

@@ -0,0 +1,169 @@
{
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"probes": [
{
"path": "/dynamic/category-1/id-1/",
"status": 200,
"headers": {
"RSC": "1"
},
"fetchOptions": {
"redirect": "manual"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/ssg/",
"status": 200,
"mustContain": "hello from /ssg",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/ssg/",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/ssg/?override=1",
"status": 307,
"responseHeaders": {
"location": "/overridden/"
},
"fetchOptions": {
"redirect": "manual"
}
},
{
"path": "/ssg/?override=1",
"status": 307,
"responseHeaders": {
"location": "/overridden/"
},
"fetchOptions": {
"redirect": "manual"
}
},
{
"path": "/dashboard/deployments/123/settings/",
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]/settings",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/123/settings/",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/dashboard/deployments/catchall/something/",
"status": 200,
"mustContain": "catchall",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/catchall/something/",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/dashboard/",
"status": 200,
"mustContain": "hello from app/dashboard",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/",
"status": 200,
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/dashboard/",
"status": 200,
"headers": {
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream",
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/another/",
"status": 200,
"mustContain": "hello from newroot/dashboard/another"
},
{
"path": "/dashboard/deployments/123/",
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]. ID is: <!-- -->123"
},
{
"path": "/",
"status": 200,
"mustContain": "index app page"
},
{
"path": "/blog/123/",
"status": 200,
"mustContain": "hello from pages/blog/[slug]"
},
{
"path": "/dynamic/category-1/id-1/",
"status": 200,
"mustContain": "{&quot;category&quot;:&quot;category-1&quot;,&quot;id&quot;:&quot;id-1&quot;}"
},
{
"path": "/dashboard/changelog/",
"status": 200,
"mustContain": "hello from app/dashboard/changelog"
},
{
"path": "/",
"status": 200,
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
}
]
}

View File

@@ -2,8 +2,12 @@ process.env.NEXT_TELEMETRY_DISABLED = '1';
const path = require('path');
const fs = require('fs-extra');
const builder = require('../../');
const {
createRunBuildLambda,
} = require('../../../../test/lib/run-build-lambda');
const runBuildLambda = require('../../../../test/lib/run-build-lambda');
const runBuildLambda = createRunBuildLambda(builder);
jest.setTimeout(360000);

View File

@@ -6,10 +6,13 @@ import type { Context } from '../types';
import { duplicateWithConfig } from '../utils';
import fs from 'fs-extra';
import path from 'path';
import runBuildLambda from '../../../../test/lib/run-build-lambda';
import * as builder from '../../';
import { createRunBuildLambda } from '../../../../test/lib/run-build-lambda';
import { EdgeFunction, Files, streamToBuffer } from '@vercel/build-utils';
import { createHash } from 'crypto';
const runBuildLambda = createRunBuildLambda(builder);
const SIMPLE_PROJECT = path.resolve(
__dirname,
'..',

View File

@@ -1,6 +0,0 @@
describe('export', () => {
it('should require by path main', async () => {
const main = require('@vercel/next');
expect(main).toBeDefined();
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.8.14",
"version": "2.8.15",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",

View File

@@ -1,6 +1,6 @@
import _ts from 'typescript';
import { NowBuildError } from '@vercel/build-utils';
import { relative, basename, resolve, dirname } from 'path';
import { relative, basename, dirname } from 'path';
import type _ts from 'typescript';
/*
* Fork of TS-Node - https://github.com/TypeStrong/ts-node
@@ -133,21 +133,20 @@ export function register(opts: Options = {}): Register {
// Require the TypeScript compiler and configuration.
const cwd = options.basePath || process.cwd();
const nowNodeBase = resolve(__dirname, '..', '..', '..');
let compiler: string;
const require_ = eval('require');
try {
compiler = require_.resolve(options.compiler || 'typescript', {
paths: [options.project || cwd, nowNodeBase],
paths: [options.project || cwd],
});
} catch (e) {
compiler = 'typescript';
}
//eslint-disable-next-line @typescript-eslint/no-var-requires
const ts: typeof _ts = require_(compiler);
if (compiler.startsWith(nowNodeBase)) {
if (compiler === 'typescript') {
console.log(
`Using built-in TypeScript ${ts.version} since "typescript" missing from "devDependencies"`
`Using built-in TypeScript ${ts.version} since "typescript" is missing from "devDependencies"`
);
} else {
console.log(`Using TypeScript ${ts.version} (local user-provided)`);

View File

@@ -4,7 +4,8 @@
"probes": [
{
"path": "/",
"mustContain": "declare namespace Test"
"mustContain": "declare namespace Test",
"logMustContain": "Using built-in TypeScript"
}
]
}

View File

@@ -24,7 +24,8 @@
"probes": [
{
"path": "/",
"mustContain": "`"
"mustContain": "`",
"logMustContain": "Using TypeScript 3.5.3 (local user-provided)"
},
{
"path": "/functions/es5.ts",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "1.1.7",
"version": "1.2.0",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -37,8 +37,10 @@
"@types/node": "14.18.33",
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@types/semver": "7.3.13",
"@vercel/build-utils": "5.9.0",
"@vercel/frameworks": "1.2.4",
"@vercel/fs-detectors": "3.7.5",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.8",
"@vercel/static-config": "2.0.11",
@@ -49,6 +51,7 @@
"ms": "2.1.2",
"node-fetch": "2.6.7",
"rc9": "1.2.0",
"semver": "7.3.8",
"tree-kill": "1.2.2",
"ts-morph": "12.0.0",
"typescript": "4.3.4"

View File

@@ -41,6 +41,12 @@ import * as GatsbyUtils from './utils/gatsby';
import * as NuxtUtils from './utils/nuxt';
import type { ImagesConfig, BuildConfig } from './utils/_shared';
import treeKill from 'tree-kill';
import {
detectFrameworkRecord,
detectFramework,
LocalFileSystemDetector,
packageManagers,
} from '@vercel/fs-detectors';
const sleep = (n: number) => new Promise(resolve => setTimeout(resolve, n));
@@ -120,7 +126,7 @@ function getCommand(
name: 'install' | 'build' | 'dev',
pkg: PackageJson | null,
config: Config,
framework: Framework | undefined
framework?: Framework
): string | null {
if (!config.zeroConfig) {
return null;
@@ -318,6 +324,12 @@ export const build: BuildV2 = async ({
const pkg = getPkg(entrypoint, workPath);
const devScript = pkg ? getScriptName(pkg, 'dev', config) : null;
const framework = getFramework(config, pkg);
const localFileSystemDetector = new LocalFileSystemDetector(workPath);
const { detectedVersion = null } =
(await detectFrameworkRecord({
fs: localFileSystemDetector,
frameworkList: frameworks,
})) ?? {};
const devCommand = getCommand('dev', pkg, config, framework);
const buildCommand = getCommand('build', pkg, config, framework);
const installCommand = getCommand('install', pkg, config, framework);
@@ -378,15 +390,29 @@ export const build: BuildV2 = async ({
process.env[key] = value;
}
if (framework.slug === 'gatsby') {
const injectedPlugins = await GatsbyUtils.injectPlugins(
detectedVersion,
entrypointDir
);
if (injectedPlugins) {
const packageManager = await detectFramework({
fs: localFileSystemDetector,
frameworkList: packageManagers,
});
if (packageManager === 'pnpm') {
await execCommand('pnpm install --lockfile-only');
}
}
}
if (process.env.VERCEL_ANALYTICS_ID) {
const frameworkDirectory = path.join(
workPath,
path.dirname(entrypoint)
);
switch (framework.slug) {
case 'gatsby':
await GatsbyUtils.injectVercelAnalyticsPlugin(frameworkDirectory);
break;
case 'nuxtjs':
await NuxtUtils.injectVercelAnalyticsPlugin(frameworkDirectory);
break;

View File

@@ -44,7 +44,7 @@ export async function writePackageJson(
) {
await fs.writeFile(
path.join(workPath, 'package.json'),
JSON.stringify(packageJson, null, 2)
`${JSON.stringify(packageJson, null, 2)}\n`
);
}

View File

@@ -41,7 +41,7 @@ export function createBuildOutput(
meta: Meta,
buildCommand: string | null,
buildOutputPath: string,
framework?: Framework
framework: Framework | undefined
): BuildResultV2 {
if (meta.isDev) {
let buildCommandName: string;

View File

@@ -1,6 +1,7 @@
import { PackageJson } from '@vercel/build-utils';
import { promises as fs } from 'fs';
import * as path from 'path';
import semver from 'semver';
import {
fileExists,
readPackageJson,
@@ -8,72 +9,97 @@ import {
writePackageJson,
} from './_shared';
const GATSBY_PLUGIN_PACKAGE_NAME = '@vercel/gatsby-plugin-vercel-analytics';
const DEFAULT_CONFIG = {
plugins: [
{
resolve: GATSBY_PLUGIN_PACKAGE_NAME,
options: {},
},
],
const PLUGINS = {
GATSBY_PLUGIN_VERCEL_ANALYTICS: '@vercel/gatsby-plugin-vercel-analytics',
GATSBY_PLUGIN_VERCEL_BUILDER: '@vercel/gatsby-plugin-vercel-builder',
};
const GATSBY_CONFIG_FILE = 'gatsby-config';
export async function injectVercelAnalyticsPlugin(dir: string): Promise<void> {
// Gatsby requires a special variable name for environment variables to be
// exposed to the client-side JavaScript bundles:
process.env.GATSBY_VERCEL_ANALYTICS_ID = process.env.VERCEL_ANALYTICS_ID;
export async function injectPlugins(
detectedVersion: string | null,
dir: string
) {
const pluginsToInject = [];
if (process.env.VERCEL_GATSBY_BUILDER_PLUGIN && detectedVersion) {
const version = semver.coerce(detectedVersion);
if (version && semver.satisfies(version, '>=4.0.0')) {
pluginsToInject.push(PLUGINS.GATSBY_PLUGIN_VERCEL_BUILDER);
}
}
if (process.env.VERCEL_ANALYTICS_ID) {
process.env.GATSBY_VERCEL_ANALYTICS_ID = process.env.VERCEL_ANALYTICS_ID;
pluginsToInject.push(PLUGINS.GATSBY_PLUGIN_VERCEL_ANALYTICS);
}
if (pluginsToInject.length === 0) {
return false;
}
await addGatsbyPackage(dir, pluginsToInject);
const gatsbyConfigPathJs = path.join(dir, `${GATSBY_CONFIG_FILE}.js`);
const gatsbyConfigPathMjs = path.join(dir, `${GATSBY_CONFIG_FILE}.mjs`);
const gatsbyConfigPathTs = path.join(dir, `${GATSBY_CONFIG_FILE}.ts`);
const gatsbyConfigPathMjs = path.join(dir, `${GATSBY_CONFIG_FILE}.mjs`);
const gatsbyConfigPathJs = path.join(dir, `${GATSBY_CONFIG_FILE}.js`);
if (await fileExists(gatsbyConfigPathTs)) {
console.log(
`Injecting Gatsby.js analytics plugin "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`${gatsbyConfigPathTs}\``
);
await addGatsbyPackage(dir);
return updateGatsbyTsConfig(gatsbyConfigPathTs);
}
if (await fileExists(gatsbyConfigPathMjs)) {
console.log(
`Injecting Gatsby.js analytics plugin "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`${gatsbyConfigPathMjs}\``
);
await addGatsbyPackage(dir);
return updateGatsbyMjsConfig(gatsbyConfigPathMjs);
}
console.log(
`Injecting Gatsby.js analytics plugin "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`${gatsbyConfigPathJs}\``
);
await addGatsbyPackage(dir);
if (await fileExists(gatsbyConfigPathJs)) {
await updateGatsbyJsConfig(gatsbyConfigPathJs);
printInjectingPlugins(pluginsToInject, gatsbyConfigPathTs);
await updateGatsbyTsConfig(gatsbyConfigPathTs, pluginsToInject);
} else if (await fileExists(gatsbyConfigPathMjs)) {
printInjectingPlugins(pluginsToInject, gatsbyConfigPathMjs);
await updateGatsbyMjsConfig(gatsbyConfigPathMjs, pluginsToInject);
} else {
await fs.writeFile(
gatsbyConfigPathJs,
`module.exports = ${JSON.stringify(DEFAULT_CONFIG)}`
);
printInjectingPlugins(pluginsToInject, gatsbyConfigPathJs);
if (await fileExists(gatsbyConfigPathJs)) {
await updateGatsbyJsConfig(gatsbyConfigPathJs, pluginsToInject);
} else {
await fs.writeFile(
gatsbyConfigPathJs,
`module.exports = ${JSON.stringify({
plugins: pluginsToInject,
})}`
);
}
}
return true;
}
async function addGatsbyPackage(dir: string): Promise<void> {
function printInjectingPlugins(plugins: string[], configPath: string) {
let pluginsStr = 'plugin';
if (plugins.length > 1) {
pluginsStr += 's';
}
console.log(
`Injecting Gatsby.js ${pluginsStr} ${plugins
.map(p => `"${p}"`)
.join(', ')} to \`${configPath}\``
);
}
async function addGatsbyPackage(
dir: string,
plugins: Array<string>
): Promise<void> {
const pkgJson = (await readPackageJson(dir)) as DeepWriteable<PackageJson>;
if (!pkgJson.dependencies) {
pkgJson.dependencies = {};
}
if (!pkgJson.dependencies[GATSBY_PLUGIN_PACKAGE_NAME]) {
console.log(
`Adding "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`package.json\` "dependencies"`
);
pkgJson.dependencies[GATSBY_PLUGIN_PACKAGE_NAME] = 'latest';
await writePackageJson(dir, pkgJson);
for (const plugin of plugins) {
if (!pkgJson.dependencies[plugin]) {
console.log(`Adding "${plugin}" to \`package.json\` "dependencies"`);
pkgJson.dependencies[plugin] = 'latest';
}
}
await writePackageJson(dir, pkgJson);
}
async function updateGatsbyTsConfig(configPath: string): Promise<void> {
async function updateGatsbyTsConfig(
configPath: string,
plugins: Array<string>
): Promise<void> {
await fs.rename(configPath, configPath + '.__vercel_builder_backup__.ts');
await fs.writeFile(
@@ -86,25 +112,24 @@ const preferDefault = (m: any) => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p: PluginRef) =>
p && (p === "${GATSBY_PLUGIN_PACKAGE_NAME}" || p.resolve === "${GATSBY_PLUGIN_PACKAGE_NAME}")
);
for (const plugin of ${JSON.stringify(plugins)}) {
const hasPlugin = vercelConfig.plugins.find(
(p: PluginRef) =>
p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "${GATSBY_PLUGIN_PACKAGE_NAME}",
options: {},
});
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
export default vercelConfig;
@@ -112,7 +137,10 @@ export default vercelConfig;
);
}
async function updateGatsbyMjsConfig(configPath: string): Promise<void> {
async function updateGatsbyMjsConfig(
configPath: string,
plugins: Array<string>
): Promise<void> {
await fs.rename(configPath, configPath + '.__vercel_builder_backup__.mjs');
await fs.writeFile(
@@ -124,7 +152,6 @@ const preferDefault = (m) => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
@@ -132,17 +159,15 @@ if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p) =>
p && (p === "${GATSBY_PLUGIN_PACKAGE_NAME}" || p.resolve === "${GATSBY_PLUGIN_PACKAGE_NAME}")
);
for (const plugin of ${JSON.stringify(plugins)}) {
const hasPlugin = vercelConfig.plugins.find(
(p) => p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "${GATSBY_PLUGIN_PACKAGE_NAME}",
options: {},
});
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
export default vercelConfig;
@@ -150,7 +175,10 @@ export default vercelConfig;
);
}
async function updateGatsbyJsConfig(configPath: string): Promise<void> {
async function updateGatsbyJsConfig(
configPath: string,
plugins: Array<string>
): Promise<void> {
await fs.rename(configPath, configPath + '.__vercel_builder_backup__.js');
await fs.writeFile(
@@ -162,7 +190,6 @@ const preferDefault = m => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
@@ -170,18 +197,16 @@ if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p) =>
p && (p === "${GATSBY_PLUGIN_PACKAGE_NAME}" || p.resolve === "${GATSBY_PLUGIN_PACKAGE_NAME}")
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "${GATSBY_PLUGIN_PACKAGE_NAME}",
options: {},
});
}
for (const plugin of ${JSON.stringify(plugins)}) {
const hasPlugin = vercelConfig.plugins.find(
(p) => p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
module.exports = vercelConfig;
`
);

View File

@@ -1,6 +1,9 @@
const path = require('path');
const fs = require('fs-extra');
const runBuildLambda = require('../../../test/lib/run-build-lambda');
const builder = require('../');
const { createRunBuildLambda } = require('../../../test/lib/run-build-lambda');
const runBuildLambda = createRunBuildLambda(builder);
const FOUR_MINUTES = 240000;
@@ -28,10 +31,7 @@ it(
.toMatchInlineSnapshot(`
Object {
"plugins": Array [
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
}
`);
@@ -59,10 +59,7 @@ it(
.toMatchInlineSnapshot(`
Object {
"plugins": Array [
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -96,10 +93,7 @@ it(
Object {
"plugins": Array [
"gatsby-plugin-react-helmet",
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -202,10 +196,7 @@ it(
Object {
"plugins": Array [
"gatsby-plugin-react-helmet",
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -241,10 +232,7 @@ it(
},
"resolve": "gatsby-plugin-zeit-now",
},
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -274,10 +262,8 @@ it(
Object {
"default": Object {
"plugins": Array [
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-builder",
"@vercel/gatsby-plugin-vercel-analytics",
],
"siteMetadata": Object {
"siteUrl": "https://gatsby-typescript-config.vercel.app",
@@ -311,7 +297,6 @@ const preferDefault = (m) => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
@@ -319,17 +304,15 @@ if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p) =>
p && (p === "@vercel/gatsby-plugin-vercel-analytics" || p.resolve === "@vercel/gatsby-plugin-vercel-analytics")
);
for (const plugin of ["@vercel/gatsby-plugin-vercel-builder","@vercel/gatsby-plugin-vercel-analytics"]) {
const hasPlugin = vercelConfig.plugins.find(
(p) => p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "@vercel/gatsby-plugin-vercel-analytics",
options: {},
});
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
export default vercelConfig;

View File

@@ -0,0 +1,69 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# dotenv environment variable files
.env*
# gatsby files
.cache/
public
# Mac files
.DS_Store
# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity

View File

@@ -0,0 +1,8 @@
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `@gatsbyjs`,
siteUrl: `https://gatsbystarterdefaultsource.gatsbyjs.io/`,
},
};

View File

@@ -0,0 +1,20 @@
{
"name": "gatsby-v2",
"private": true,
"dependencies": {
"gatsby": "2.32.13",
"prop-types": "15.8.1",
"react": "16.14.0",
"react-dom": "16.14.0"
},
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"start": "gatsby develop",
"serve": "gatsby serve",
"clean": "gatsby clean"
},
"engines": {
"node": "14.x"
}
}

View File

@@ -0,0 +1,10 @@
{
"probes": [
{ "path": "/", "mustContain": "Welcome to <b>Gatsby!</b>" },
{ "path": "/page-2", "mustContain": "Hi from the second page" },
{
"path": "/using-typescript",
"mustContain": "Gatsby supports <b>TypeScript by default</b>"
}
]
}

View File

@@ -0,0 +1,41 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'gatsby';
const Header = ({ siteTitle }) => (
<header
style={{
margin: `0 auto`,
padding: `var(--space-4) var(--size-gutter)`,
display: `flex`,
alignItems: `center`,
justifyContent: `space-between`,
}}
>
<Link
to="/"
style={{
fontSize: `var(--font-sm)`,
textDecoration: `none`,
}}
>
{siteTitle}
</Link>
<img
alt="Gatsby logo"
height={20}
style={{ margin: 0 }}
src="data:image/svg+xml,%3Csvg fill='none' viewBox='0 0 107 28' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3CclipPath id='a'%3E%3Cpath d='m0 0h106.1v28h-106.1z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23a)'%3E%3Cg fill='%23000'%3E%3Cpath clip-rule='evenodd' d='m89 11.7c-.8 0-2.2.2-3.2 1.6v-8.10005h-2.8v16.80005h2.7v-1.3c1.1 1.5 2.6 1.5999 3.2 1.5999 3 0 5-2.2999 5-5.2999s-2-5.3-4.9-5.3zm-.7 2.5c1.7 0 2.8 1.2 2.8 2.8s-1.2 2.8-2.8 2.8c-1.7 0-2.8-1.2-2.8-2.8s1.1-2.8 2.8-2.8z' fill-rule='evenodd'/%3E%3Cpath d='m71.2 21.9999v-7.6h1.9v-2.4h-1.9v-3.40005h-2.8v3.40005h-1.1v2.4h1.1v7.6z'/%3E%3Cpath clip-rule='evenodd' d='m65.6999 12h-2.9v1.3c-.8999-1.5-2.4-1.6-3.2-1.6-2.9 0-4.8999 2.4-4.8999 5.3s1.9999 5.2999 5.0999 5.2999c.8 0 2.1001-.0999 3.1001-1.5999v1.3h2.7999zm-5.1999 7.8c-1.7001 0-2.8-1.2-2.8-2.8s1.2-2.8 2.8-2.8c1.7 0 2.7999 1.2 2.7999 2.8s-1.1999 2.8-2.7999 2.8z' fill-rule='evenodd'/%3E%3Cpath d='m79.7001 14.4c-.7-.6-1.3-.7-1.6-.7-.7 0-1.1.3-1.1.8 0 .3.1.6.9.9l.7.2c.1261.0472.2621.0945.4037.1437.7571.2632 1.6751.5823 2.0963 1.2563.3.4.5 1 .5 1.7 0 .9-.3 1.8-1.1 2.5s-1.8 1.0999-3 1.0999c-2.1 0-3.2-.9999-3.9-1.6999l1.5-1.7c.6.6 1.4 1.2 2.2 1.2s1.4-.4 1.4-1.1c0-.6-.5-.9-.9-1l-.6-.2c-.0687-.0295-.1384-.0589-.2087-.0887l-.0011-.0004c-.6458-.2729-1.3496-.5704-1.8902-1.1109-.5-.5-.8-1.1-.8-1.9 0-1 .5-1.8 1-2.3.8-.6 1.8-.7 2.6-.7.7 0 1.9.1 3.2 1.1z'/%3E%3Cpath d='m98.5 20.5-4.8-8.5h3.3l3.1 5.7 2.8-5.7h3.2l-8 15.3h-3.2z'/%3E%3Cpath d='m47 13.7h7c0 .0634.01.1267.0206.1932.0227.1435.0477.3018-.0206.5068 0 4.5-3.4 8.1-8 8.1s-8-3.6-8-8.1c0-4.49995 3.6-8.09995 8-8.09995 2.6 0 5 1.2 6.5 3.3l-2.3 1.49995c-1-1.29995-2.6-2.09995-4.2-2.09995-2.9 0-4.9 2.49995-4.9 5.39995s2.1 5.3 5 5.3c2.6 0 4-1.3 4.6-3.2h-3.7z'/%3E%3C/g%3E%3Cpath d='m18 14h7c0 5.2-3.7 9.6-8.5 10.8l-13.19995-13.2c1.1-4.9 5.5-8.6 10.69995-8.6 3.7 0 6.9 1.8 8.9 4.5l-1.5 1.3c-1.7-2.3-4.4-3.8-7.4-3.8-3.9 0-7.29995 2.5-8.49995 6l11.49995 11.5c2.9-1 5.1-3.5 5.8-6.5h-4.8z' fill='%23fff'/%3E%3Cpath d='m6.2 21.7001c-2.1-2.1-3.2-4.8-3.2-7.6l10.8 10.8c-2.7 0-5.5-1.1-7.6-3.2z' fill='%23fff'/%3E%3Cpath d='m14 0c-7.7 0-14 6.3-14 14s6.3 14 14 14 14-6.3 14-14-6.3-14-14-14zm-7.8 21.8c-2.1-2.1-3.2-4.9-3.2-7.6l10.9 10.8c-2.8-.1-5.6-1.1-7.7-3.2zm10.2 2.9-13.1-13.1c1.1-4.9 5.5-8.6 10.7-8.6 3.7 0 6.9 1.8 8.9 4.5l-1.5 1.3c-1.7-2.3-4.4-3.8-7.4-3.8-3.9 0-7.2 2.5-8.5 6l11.5 11.5c2.9-1 5.1-3.5 5.8-6.5h-4.8v-2h7c0 5.2-3.7 9.6-8.6 10.7z' fill='%237026b9'/%3E%3C/g%3E%3C/svg%3E"
/>
</header>
);
Header.propTypes = {
siteTitle: PropTypes.string,
};
Header.defaultProps = {
siteTitle: ``,
};
export default Header;

View File

@@ -0,0 +1,54 @@
.list {
display: grid;
margin: 0;
/* https://css-tricks.com/responsive-layouts-fewer-media-queries/ */
--w: 280px;
--n: 2;
gap: var(--size-gap);
grid-template-columns: repeat(
auto-fit,
minmax(max(var(--w), 100%/ (var(--n) + 1) + 0.1%), 1fr)
);
margin-bottom: var(--size-gap);
margin-top: var(--size-gap);
}
.listItem {
margin: 0;
}
.listItem::marker {
color: #e95800;
}
.listItem:nth-child(2)::marker {
color: #159bf3;
}
.listItem:nth-child(3)::marker {
color: #8eb814;
}
.listItem:nth-child(4)::marker {
color: #663399;
}
.listItemLink {
color: var(--color-primary);
font-weight: bold;
}
.listItemDescription {
color: var(--color-text);
margin-bottom: 0;
margin-top: var(--space-1);
}
.textCenter {
text-align: center;
}
.intro {
max-width: none;
line-height: var(--line-height-loose);
}

View File

@@ -0,0 +1,224 @@
:root {
--border-radius: 4px;
--color-text: #333;
--color-primary: #7026b9;
--color-code-bg: #fff4db;
--color-code: #8a6534;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
--font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--font-lg: 18px;
--font-md: 16px;
--font-sm: 14px;
--font-sx: 12px;
--line-height-loose: 1.75;
--line-height-normal: 1.5;
--line-height-dense: 1.1;
--space-1: 4px;
--space-2: 8px;
--space-3: 16px;
--space-4: 24px;
--space-5: 32px;
--space-6: 64px;
--size-content: 54rem;
--size-gutter: var(--space-5);
--size-gap: var(--space-6);
}
html {
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
font: sans-serif;
font-size: var(--font-md);
line-height: var(--line-height-normal);
overflow-y: scroll;
}
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
color: var(--color-text);
font-family: sans-serif;
font-family: var(--font-sans);
font-weight: normal;
margin: 0;
word-wrap: break-word;
}
a {
background-color: transparent;
color: var(--color-primary);
text-decoration: none;
font-weight: 500;
text-decoration: underline;
text-decoration-thickness: 1.5px;
text-underline-offset: 2px;
}
a:active,
a:hover {
outline-width: 0;
text-decoration: none;
}
abbr[title] {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
text-decoration: none;
}
b,
strong {
font-weight: inherit;
font-weight: bolder;
}
dfn {
font-style: italic;
}
h1 {
margin: 0;
margin-bottom: 3rem;
padding: 0;
line-height: var(--line-height-dense);
letter-spacing: -0.01em;
}
h1 > b {
color: var(--color-primary);
}
img {
border-style: none;
max-width: 100%;
}
code,
kbd,
pre,
samp {
font-family: var(--font-mono);
font-size: 1em;
line-height: inherit;
}
hr {
box-sizing: content-box;
overflow: visible;
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding: 0;
margin-bottom: calc(var(--space-4) - 1px);
background: hsla(0, 0%, 0%, 0.2);
border: none;
height: 1px;
}
* {
box-sizing: inherit;
}
*:before {
box-sizing: inherit;
}
*:after {
box-sizing: inherit;
}
hgroup,
img,
figure,
fieldset,
ul,
ol,
dl,
dd,
p {
margin: 0;
padding: 0;
margin-bottom: var(--space-4);
}
ul,
ol {
margin-left: var(--space-4);
list-style-position: outside;
list-style-image: none;
}
pre {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: var(--space-4);
font-size: 0.875rem;
line-height: var(--line-height-normal);
background: hsla(0, 0%, 0%, 0.04);
border-radius: var(--border-radius);
overflow: auto;
word-wrap: normal;
padding: var(--space-4);
}
b,
strong,
dt,
th {
font-weight: bold;
}
li {
margin-bottom: calc(var(--space-4) / 2);
}
ol li,
ul li {
padding-left: 0;
}
li > ol,
li > ul {
margin-bottom: calc(var(--space-4) / 2);
margin-left: var(--space-4);
margin-top: calc(var(--space-4) / 2);
}
blockquote *:last-child {
margin-bottom: 0;
}
li *:last-child {
margin-bottom: 0;
}
p *:last-child {
margin-bottom: 0;
}
li > p {
margin-bottom: calc(var(--space-4) / 2);
}
p {
max-width: 680px;
}
code {
font-size: 0.875rem;
}
kbd {
font-size: 0.875rem;
}
samp {
font-size: 0.875rem;
}
abbr {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
acronym {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
tt,
code {
background-color: var(--color-code-bg);
border-radius: var(--border-radius);
color: var(--color-code);
font-family: var(--font-mono);
padding-bottom: 0.25em;
padding-top: 0.25em;
word-break: normal;
}
pre code {
background: none;
}
code:before,
code:after,
tt:before,
tt:after {
content: "\00a0";
letter-spacing: -0.2em;
}
pre code:before,
pre code:after,
pre tt:before,
pre tt:after {
content: none;
}

View File

@@ -0,0 +1,56 @@
/**
* Layout component that queries for data
* with Gatsby's useStaticQuery component
*
* See: https://www.gatsbyjs.com/docs/use-static-query/
*/
import * as React from 'react';
import PropTypes from 'prop-types';
import { useStaticQuery, graphql } from 'gatsby';
import Header from './header';
import './layout.css';
const Layout = ({ children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`);
return (
<>
<Header siteTitle={data.site.siteMetadata?.title || `Title`} />
<div
style={{
margin: `0 auto`,
maxWidth: `var(--size-content)`,
padding: `var(--size-gutter)`,
}}
>
<main>{children}</main>
<footer
style={{
marginTop: `var(--space-5)`,
fontSize: `var(--font-sm)`,
}}
>
© {new Date().getFullYear()} &middot; Built with
{` `}
<a href="https://www.gatsbyjs.com">Gatsby</a>
</footer>
</div>
</>
);
};
Layout.propTypes = {
children: PropTypes.node.isRequired,
};
export default Layout;

View File

@@ -0,0 +1,12 @@
import * as React from 'react';
import Layout from '../components/layout';
const NotFoundPage = () => (
<Layout>
<h1>404: Not Found</h1>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
);
export default NotFoundPage;

View File

@@ -0,0 +1,108 @@
import * as React from 'react';
import { Link } from 'gatsby';
import Layout from '../components/layout';
import * as styles from '../components/index.module.css';
const links = [
{
text: 'Tutorial',
url: 'https://www.gatsbyjs.com/docs/tutorial',
description:
"A great place to get started if you're new to web development. Designed to guide you through setting up your first Gatsby site.",
},
{
text: 'Examples',
url: 'https://github.com/gatsbyjs/gatsby/tree/master/examples',
description:
'A collection of websites ranging from very basic to complex/complete that illustrate how to accomplish specific tasks within your Gatsby sites.',
},
{
text: 'Plugin Library',
url: 'https://www.gatsbyjs.com/plugins',
description:
'Learn how to add functionality and customize your Gatsby site or app with thousands of plugins built by our amazing developer community.',
},
{
text: 'Build and Host',
url: 'https://www.gatsbyjs.com/cloud',
description:
'Now youre ready to show the world! Give your Gatsby site superpowers: Build and host on Gatsby Cloud. Get started for free!',
},
];
const samplePageLinks = [
{
text: 'Page 2',
url: 'page-2',
badge: false,
description:
'A simple example of linking to another page within a Gatsby site',
},
{ text: 'TypeScript', url: 'using-typescript' },
];
const moreLinks = [
{ text: 'Join us on Discord', url: 'https://gatsby.dev/discord' },
{
text: 'Documentation',
url: 'https://gatsbyjs.com/docs/',
},
{
text: 'Starters',
url: 'https://gatsbyjs.com/starters/',
},
{
text: 'Showcase',
url: 'https://gatsbyjs.com/showcase/',
},
{
text: 'Contributing',
url: 'https://www.gatsbyjs.com/contributing/',
},
{ text: 'Issues', url: 'https://github.com/gatsbyjs/gatsby/issues' },
];
const utmParameters = `?utm_source=starter&utm_medium=start-page&utm_campaign=default-starter`;
const IndexPage = () => (
<Layout>
<div className={styles.textCenter}>
<h1>
Welcome to <b>Gatsby!</b>
</h1>
<p className={styles.intro}>
<b>Example pages:</b>{' '}
{samplePageLinks.map((link, i) => (
<React.Fragment key={link.url}>
<Link to={link.url}>{link.text}</Link>
{i !== samplePageLinks.length - 1 && <> · </>}
</React.Fragment>
))}
<br />
Edit <code>src/pages/index.js</code> to update this page.
</p>
</div>
<ul className={styles.list}>
{links.map(link => (
<li key={link.url} className={styles.listItem}>
<a
className={styles.listItemLink}
href={`${link.url}${utmParameters}`}
>
{link.text}
</a>
<p className={styles.listItemDescription}>{link.description}</p>
</li>
))}
</ul>
{moreLinks.map((link, i) => (
<React.Fragment key={link.url}>
<a href={`${link.url}${utmParameters}`}>{link.text}</a>
{i !== moreLinks.length - 1 && <> · </>}
</React.Fragment>
))}
</Layout>
);
export default IndexPage;

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import { Link } from 'gatsby';
import Layout from '../components/layout';
const SecondPage = () => (
<Layout>
<h1>Hi from the second page</h1>
<p>Welcome to page 2</p>
<Link to="/">Go back to the homepage</Link>
</Layout>
);
export default SecondPage;

View File

@@ -0,0 +1,53 @@
// If you don't want to use TypeScript you can delete this file!
import * as React from "react"
import { PageProps, Link, graphql } from "gatsby"
import Layout from "../components/layout"
type DataProps = {
site: {
buildTime: string
}
}
const UsingTypescript: React.FC<PageProps<DataProps>> = ({
data,
location,
}) => (
<Layout>
<h1>
Gatsby supports <b>TypeScript by default</b>
</h1>
<p>
This means that you can create and write <code>.ts/.tsx</code> files for
your pages, components, and <code>gatsby-*</code> configuration files (for
example <code>gatsby-config.ts</code>).
</p>
<p>
For type checking you'll want to install <code>typescript</code> via npm
and run <code>tsc --init</code> to create a <code>tsconfig</code> file.
</p>
<p>
You're currently on the page <code>{location.pathname}</code> which was
built on {data.site.buildTime}.
</p>
<p>
To learn more, head over to our{" "}
<a href="https://www.gatsbyjs.com/docs/how-to/custom-configuration/typescript/">
documentation about TypeScript
</a>
.
</p>
<Link to="/">Go back to the homepage</Link>
</Layout>
)
export default UsingTypescript
export const query = graphql`
{
site {
buildTime(formatString: "YYYY-MM-DD hh:mm a z")
}
}
`

View File

@@ -0,0 +1,69 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# dotenv environment variable files
.env*
# gatsby files
.cache/
public
# Mac files
.DS_Store
# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity

View File

@@ -0,0 +1,7 @@
/**
* Implement Gatsby's Browser APIs in this file.
*
* See: https://www.gatsbyjs.com/docs/browser-apis/
*/
// You can delete this file if you're not using it

View File

@@ -0,0 +1,34 @@
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `@gatsbyjs`,
siteUrl: `https://gatsbystarterdefaultsource.gatsbyjs.io/`,
},
plugins: [
`gatsby-plugin-image`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
// This will impact how browsers show your PWA/website
// https://css-tricks.com/meta-theme-color-and-trickery/
// theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
},
},
],
};

View File

@@ -0,0 +1,22 @@
{
"name": "gatsby-v3",
"private": true,
"dependencies": {
"gatsby": "3.15.0",
"gatsby-plugin-image": "1.15.0",
"gatsby-plugin-manifest": "3.15.0",
"gatsby-plugin-sharp": "3.15.0",
"gatsby-source-filesystem": "3.15.0",
"gatsby-transformer-sharp": "3.15.0",
"prop-types": "15.8.1",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"start": "gatsby develop",
"serve": "gatsby serve",
"clean": "gatsby clean"
}
}

View File

@@ -0,0 +1,10 @@
{
"probes": [
{ "path": "/", "mustContain": "Welcome to <b>Gatsby!</b>" },
{ "path": "/page-2", "mustContain": "Hi from the second page" },
{
"path": "/using-typescript",
"mustContain": "Gatsby supports <b>TypeScript by default</b>"
}
]
}

View File

@@ -0,0 +1,41 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'gatsby';
const Header = ({ siteTitle }) => (
<header
style={{
margin: `0 auto`,
padding: `var(--space-4) var(--size-gutter)`,
display: `flex`,
alignItems: `center`,
justifyContent: `space-between`,
}}
>
<Link
to="/"
style={{
fontSize: `var(--font-sm)`,
textDecoration: `none`,
}}
>
{siteTitle}
</Link>
<img
alt="Gatsby logo"
height={20}
style={{ margin: 0 }}
src="data:image/svg+xml,%3Csvg fill='none' viewBox='0 0 107 28' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3CclipPath id='a'%3E%3Cpath d='m0 0h106.1v28h-106.1z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23a)'%3E%3Cg fill='%23000'%3E%3Cpath clip-rule='evenodd' d='m89 11.7c-.8 0-2.2.2-3.2 1.6v-8.10005h-2.8v16.80005h2.7v-1.3c1.1 1.5 2.6 1.5999 3.2 1.5999 3 0 5-2.2999 5-5.2999s-2-5.3-4.9-5.3zm-.7 2.5c1.7 0 2.8 1.2 2.8 2.8s-1.2 2.8-2.8 2.8c-1.7 0-2.8-1.2-2.8-2.8s1.1-2.8 2.8-2.8z' fill-rule='evenodd'/%3E%3Cpath d='m71.2 21.9999v-7.6h1.9v-2.4h-1.9v-3.40005h-2.8v3.40005h-1.1v2.4h1.1v7.6z'/%3E%3Cpath clip-rule='evenodd' d='m65.6999 12h-2.9v1.3c-.8999-1.5-2.4-1.6-3.2-1.6-2.9 0-4.8999 2.4-4.8999 5.3s1.9999 5.2999 5.0999 5.2999c.8 0 2.1001-.0999 3.1001-1.5999v1.3h2.7999zm-5.1999 7.8c-1.7001 0-2.8-1.2-2.8-2.8s1.2-2.8 2.8-2.8c1.7 0 2.7999 1.2 2.7999 2.8s-1.1999 2.8-2.7999 2.8z' fill-rule='evenodd'/%3E%3Cpath d='m79.7001 14.4c-.7-.6-1.3-.7-1.6-.7-.7 0-1.1.3-1.1.8 0 .3.1.6.9.9l.7.2c.1261.0472.2621.0945.4037.1437.7571.2632 1.6751.5823 2.0963 1.2563.3.4.5 1 .5 1.7 0 .9-.3 1.8-1.1 2.5s-1.8 1.0999-3 1.0999c-2.1 0-3.2-.9999-3.9-1.6999l1.5-1.7c.6.6 1.4 1.2 2.2 1.2s1.4-.4 1.4-1.1c0-.6-.5-.9-.9-1l-.6-.2c-.0687-.0295-.1384-.0589-.2087-.0887l-.0011-.0004c-.6458-.2729-1.3496-.5704-1.8902-1.1109-.5-.5-.8-1.1-.8-1.9 0-1 .5-1.8 1-2.3.8-.6 1.8-.7 2.6-.7.7 0 1.9.1 3.2 1.1z'/%3E%3Cpath d='m98.5 20.5-4.8-8.5h3.3l3.1 5.7 2.8-5.7h3.2l-8 15.3h-3.2z'/%3E%3Cpath d='m47 13.7h7c0 .0634.01.1267.0206.1932.0227.1435.0477.3018-.0206.5068 0 4.5-3.4 8.1-8 8.1s-8-3.6-8-8.1c0-4.49995 3.6-8.09995 8-8.09995 2.6 0 5 1.2 6.5 3.3l-2.3 1.49995c-1-1.29995-2.6-2.09995-4.2-2.09995-2.9 0-4.9 2.49995-4.9 5.39995s2.1 5.3 5 5.3c2.6 0 4-1.3 4.6-3.2h-3.7z'/%3E%3C/g%3E%3Cpath d='m18 14h7c0 5.2-3.7 9.6-8.5 10.8l-13.19995-13.2c1.1-4.9 5.5-8.6 10.69995-8.6 3.7 0 6.9 1.8 8.9 4.5l-1.5 1.3c-1.7-2.3-4.4-3.8-7.4-3.8-3.9 0-7.29995 2.5-8.49995 6l11.49995 11.5c2.9-1 5.1-3.5 5.8-6.5h-4.8z' fill='%23fff'/%3E%3Cpath d='m6.2 21.7001c-2.1-2.1-3.2-4.8-3.2-7.6l10.8 10.8c-2.7 0-5.5-1.1-7.6-3.2z' fill='%23fff'/%3E%3Cpath d='m14 0c-7.7 0-14 6.3-14 14s6.3 14 14 14 14-6.3 14-14-6.3-14-14-14zm-7.8 21.8c-2.1-2.1-3.2-4.9-3.2-7.6l10.9 10.8c-2.8-.1-5.6-1.1-7.7-3.2zm10.2 2.9-13.1-13.1c1.1-4.9 5.5-8.6 10.7-8.6 3.7 0 6.9 1.8 8.9 4.5l-1.5 1.3c-1.7-2.3-4.4-3.8-7.4-3.8-3.9 0-7.2 2.5-8.5 6l11.5 11.5c2.9-1 5.1-3.5 5.8-6.5h-4.8v-2h7c0 5.2-3.7 9.6-8.6 10.7z' fill='%237026b9'/%3E%3C/g%3E%3C/svg%3E"
/>
</header>
);
Header.propTypes = {
siteTitle: PropTypes.string,
};
Header.defaultProps = {
siteTitle: ``,
};
export default Header;

View File

@@ -0,0 +1,54 @@
.list {
display: grid;
margin: 0;
/* https://css-tricks.com/responsive-layouts-fewer-media-queries/ */
--w: 280px;
--n: 2;
gap: var(--size-gap);
grid-template-columns: repeat(
auto-fit,
minmax(max(var(--w), 100%/ (var(--n) + 1) + 0.1%), 1fr)
);
margin-bottom: var(--size-gap);
margin-top: var(--size-gap);
}
.listItem {
margin: 0;
}
.listItem::marker {
color: #e95800;
}
.listItem:nth-child(2)::marker {
color: #159bf3;
}
.listItem:nth-child(3)::marker {
color: #8eb814;
}
.listItem:nth-child(4)::marker {
color: #663399;
}
.listItemLink {
color: var(--color-primary);
font-weight: bold;
}
.listItemDescription {
color: var(--color-text);
margin-bottom: 0;
margin-top: var(--space-1);
}
.textCenter {
text-align: center;
}
.intro {
max-width: none;
line-height: var(--line-height-loose);
}

View File

@@ -0,0 +1,224 @@
:root {
--border-radius: 4px;
--color-text: #333;
--color-primary: #7026b9;
--color-code-bg: #fff4db;
--color-code: #8a6534;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
--font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--font-lg: 18px;
--font-md: 16px;
--font-sm: 14px;
--font-sx: 12px;
--line-height-loose: 1.75;
--line-height-normal: 1.5;
--line-height-dense: 1.1;
--space-1: 4px;
--space-2: 8px;
--space-3: 16px;
--space-4: 24px;
--space-5: 32px;
--space-6: 64px;
--size-content: 54rem;
--size-gutter: var(--space-5);
--size-gap: var(--space-6);
}
html {
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
font: sans-serif;
font-size: var(--font-md);
line-height: var(--line-height-normal);
overflow-y: scroll;
}
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
color: var(--color-text);
font-family: sans-serif;
font-family: var(--font-sans);
font-weight: normal;
margin: 0;
word-wrap: break-word;
}
a {
background-color: transparent;
color: var(--color-primary);
text-decoration: none;
font-weight: 500;
text-decoration: underline;
text-decoration-thickness: 1.5px;
text-underline-offset: 2px;
}
a:active,
a:hover {
outline-width: 0;
text-decoration: none;
}
abbr[title] {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
text-decoration: none;
}
b,
strong {
font-weight: inherit;
font-weight: bolder;
}
dfn {
font-style: italic;
}
h1 {
margin: 0;
margin-bottom: 3rem;
padding: 0;
line-height: var(--line-height-dense);
letter-spacing: -0.01em;
}
h1 > b {
color: var(--color-primary);
}
img {
border-style: none;
max-width: 100%;
}
code,
kbd,
pre,
samp {
font-family: var(--font-mono);
font-size: 1em;
line-height: inherit;
}
hr {
box-sizing: content-box;
overflow: visible;
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding: 0;
margin-bottom: calc(var(--space-4) - 1px);
background: hsla(0, 0%, 0%, 0.2);
border: none;
height: 1px;
}
* {
box-sizing: inherit;
}
*:before {
box-sizing: inherit;
}
*:after {
box-sizing: inherit;
}
hgroup,
img,
figure,
fieldset,
ul,
ol,
dl,
dd,
p {
margin: 0;
padding: 0;
margin-bottom: var(--space-4);
}
ul,
ol {
margin-left: var(--space-4);
list-style-position: outside;
list-style-image: none;
}
pre {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: var(--space-4);
font-size: 0.875rem;
line-height: var(--line-height-normal);
background: hsla(0, 0%, 0%, 0.04);
border-radius: var(--border-radius);
overflow: auto;
word-wrap: normal;
padding: var(--space-4);
}
b,
strong,
dt,
th {
font-weight: bold;
}
li {
margin-bottom: calc(var(--space-4) / 2);
}
ol li,
ul li {
padding-left: 0;
}
li > ol,
li > ul {
margin-bottom: calc(var(--space-4) / 2);
margin-left: var(--space-4);
margin-top: calc(var(--space-4) / 2);
}
blockquote *:last-child {
margin-bottom: 0;
}
li *:last-child {
margin-bottom: 0;
}
p *:last-child {
margin-bottom: 0;
}
li > p {
margin-bottom: calc(var(--space-4) / 2);
}
p {
max-width: 680px;
}
code {
font-size: 0.875rem;
}
kbd {
font-size: 0.875rem;
}
samp {
font-size: 0.875rem;
}
abbr {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
acronym {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
tt,
code {
background-color: var(--color-code-bg);
border-radius: var(--border-radius);
color: var(--color-code);
font-family: var(--font-mono);
padding-bottom: 0.25em;
padding-top: 0.25em;
word-break: normal;
}
pre code {
background: none;
}
code:before,
code:after,
tt:before,
tt:after {
content: "\00a0";
letter-spacing: -0.2em;
}
pre code:before,
pre code:after,
pre tt:before,
pre tt:after {
content: none;
}

View File

@@ -0,0 +1,56 @@
/**
* Layout component that queries for data
* with Gatsby's useStaticQuery component
*
* See: https://www.gatsbyjs.com/docs/use-static-query/
*/
import * as React from 'react';
import PropTypes from 'prop-types';
import { useStaticQuery, graphql } from 'gatsby';
import Header from './header';
import './layout.css';
const Layout = ({ children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`);
return (
<>
<Header siteTitle={data.site.siteMetadata?.title || `Title`} />
<div
style={{
margin: `0 auto`,
maxWidth: `var(--size-content)`,
padding: `var(--size-gutter)`,
}}
>
<main>{children}</main>
<footer
style={{
marginTop: `var(--space-5)`,
fontSize: `var(--font-sm)`,
}}
>
© {new Date().getFullYear()} &middot; Built with
{` `}
<a href="https://www.gatsbyjs.com">Gatsby</a>
</footer>
</div>
</>
);
};
Layout.propTypes = {
children: PropTypes.node.isRequired,
};
export default Layout;

View File

@@ -0,0 +1,55 @@
/**
* SEO component that queries for data with
* Gatsby's useStaticQuery React hook
*
* See: https://www.gatsbyjs.com/docs/use-static-query/
*/
import * as React from 'react';
import PropTypes from 'prop-types';
import { useStaticQuery, graphql } from 'gatsby';
function Seo({ description, title, children }) {
const { site } = useStaticQuery(
graphql`
query {
site {
siteMetadata {
title
description
author
}
}
}
`
);
const metaDescription = description || site.siteMetadata.description;
const defaultTitle = site.siteMetadata?.title;
return (
<>
<title>{defaultTitle ? `${title} | ${defaultTitle}` : title}</title>
<meta name="description" content={metaDescription} />
<meta property="og:title" content={title} />
<meta property="og:description" content={metaDescription} />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content={site.siteMetadata?.author || ``} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={metaDescription} />
{children}
</>
);
}
Seo.defaultProps = {
description: ``,
};
Seo.propTypes = {
description: PropTypes.string,
title: PropTypes.string.isRequired,
};
export default Seo;

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