|
touch tsconfig.json |
|
|
|
cat << 'EOF' > styles/globals.css |
|
@tailwind base; |
|
@tailwind components; |
|
@tailwind utilities; |
|
|
|
/* purgecss start ignore */ |
|
body { |
|
@apply bg-gray-100; |
|
min-height: 100vh; |
|
} |
|
/* purgecss end ignore */ |
|
EOF |
|
|
|
mkdir -p lib |
|
cat << 'EOF' > lib/config.ts |
|
export const config = { |
|
apiBase: process.env.NEXT_PUBLIC_API_BASE, |
|
}; |
|
EOF |
|
|
|
cat << 'EOF' > lib/fetch.ts |
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment */ |
|
import fetch from "isomorphic-unfetch"; |
|
|
|
export default async function libFetch( |
|
input: RequestInfo, |
|
init?: RequestInit | undefined |
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
|
): Promise<any> { |
|
const res = await fetch(input, init); |
|
return res.json(); |
|
} |
|
EOF |
|
|
|
rm -rf pages/api/hello.js |
|
cat << 'EOF' > pages/api/hello.ts |
|
import type { NowRequest, NowResponse } from "@vercel/node"; |
|
|
|
export default function hello(_req: NowRequest, res: NowResponse): void { |
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access |
|
res.status(200).json({ name: "John Doe" }); |
|
}; |
|
EOF |
|
|
|
rm -f pages/_app.js |
|
cat << 'EOF' > pages/_app.tsx |
|
import { NextComponentType } from "next"; |
|
import { AppContext, AppInitialProps, AppProps } from "next/app"; |
|
|
|
import "../styles/globals.css"; |
|
|
|
const App: NextComponentType<AppContext, AppInitialProps, AppProps> = ({ |
|
Component, |
|
pageProps, |
|
}: AppProps) => { |
|
return <Component {...pageProps} />; |
|
}; |
|
|
|
export default App; |
|
EOF |
|
|
|
rm -rf pages/index.js |
|
cat << 'EOF' > pages/index.tsx |
|
import React from "react"; |
|
import { NextPage } from "next"; |
|
import Head from "next/head"; |
|
import useSWR from "swr"; |
|
|
|
import libFetch from "../lib/fetch"; |
|
import { config } from "../lib/config"; |
|
|
|
const Index: NextPage = () => { |
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
|
const { data, error } = useSWR<{ name: string }>(`${config.apiBase}/api/hello`, libFetch); |
|
return ( |
|
<div className="container"> |
|
<Head> |
|
<title>Create Next App</title> |
|
<link rel="icon" href="/favicon.ico" /> |
|
</Head> |
|
<header data-testid="hello-world">Hello There!</header> |
|
{data ? ( |
|
<div data-testid="result">{data.name}</div> |
|
) : error ? ( |
|
<pre>{JSON.stringify(error)}</pre> |
|
) : null} |
|
</div> |
|
); |
|
}; |
|
|
|
export default Index; |
|
EOF |
|
|
|
mkdir -p __tests__ |
|
cat << 'EOF' > __tests__/index.test.tsx |
|
/* eslint-disable @typescript-eslint/no-unsafe-return */ |
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ |
|
/* eslint-disable @typescript-eslint/no-unsafe-call */ |
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ |
|
import React from "react"; |
|
import { render, screen } from "@testing-library/react"; |
|
import { rest } from "msw"; |
|
import { setupServer } from "msw/node"; |
|
|
|
import { config } from "../lib/config"; |
|
import Home from "../pages/index"; |
|
|
|
const server = setupServer( |
|
rest.get(`${config.apiBase}/api/hello`, (_req, res, ctx) => { |
|
return res(ctx.json({ name: "Johnny" })); |
|
}) |
|
); |
|
|
|
beforeAll(() => server.listen()); |
|
afterEach(() => server.resetHandlers()); |
|
afterAll(() => server.close()); |
|
|
|
test("displays greeting", () => { |
|
render(<Home />); |
|
expect(screen.getByTestId("hello-world")).toHaveTextContent("Hello There!"); |
|
}); |
|
|
|
test("displays response", async () => { |
|
render(<Home />); |
|
expect(await screen.findByTestId("result")).toHaveTextContent("Johnny"); |
|
}); |
|
EOF |
|
|
|
cat << 'EOF' > .env.local |
|
NEXT_PUBLIC_API_BASE= |
|
EOF |
|
|
|
cat << 'EOF' > .env |
|
NEXT_PUBLIC_API_BASE= |
|
EOF |
|
|
|
cat << 'EOF' > .prettierrc |
|
{ |
|
"singleQuote": false, |
|
"printWidth": 100, |
|
"trailingComma": "es5", |
|
"arrowParens": "always" |
|
} |
|
EOF |
|
|
|
cat << 'EOF' > .prettierignore |
|
.next |
|
__generated__ |
|
.now |
|
.vscode/settings.json |
|
.devcontainer |
|
EOF |
|
|
|
cat << 'EOF' > .editorconfig |
|
# EditorConfig is awesome: http://EditorConfig.org |
|
root = true |
|
|
|
[*] |
|
end_of_line = lf |
|
insert_final_newline = true |
|
charset = utf-8 |
|
indent_style = tab |
|
EOF |
|
|
|
cat << 'EOF' > .eslintignore |
|
.next |
|
.now |
|
__generated__ |
|
EOF |
|
|
|
cat << 'EOF' > .eslintrc |
|
{ |
|
"parser": "@typescript-eslint/parser", |
|
"parserOptions": { |
|
"project": "./tsconfig.eslint.json" |
|
}, |
|
"plugins": ["@typescript-eslint", "sonarjs"], |
|
"extends": [ |
|
"eslint:recommended", |
|
"plugin:react/recommended", |
|
"plugin:@typescript-eslint/eslint-recommended", |
|
"plugin:@typescript-eslint/recommended", |
|
"plugin:@typescript-eslint/recommended-requiring-type-checking", |
|
"plugin:sonarjs/recommended", |
|
"prettier", |
|
"plugin:prettier/recommended" |
|
], |
|
"rules": { |
|
"@typescript-eslint/no-empty-interface": "off", |
|
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], |
|
"@typescript-eslint/no-use-before-define": "off", |
|
"@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }], |
|
"react/react-in-jsx-scope": "off", |
|
"react/prop-types": "off" |
|
}, |
|
"settings": { |
|
"react": { |
|
"version": "detect" |
|
} |
|
} |
|
} |
|
EOF |
|
|
|
TEST_URL=http://api.backend.test |
|
cat << EOF > jest.setup.ts |
|
import "@testing-library/jest-dom"; |
|
|
|
process.env.NEXT_PUBLIC_API_BASE = "$TEST_URL"; |
|
EOF |
|
|
|
cat << 'EOF' > jest.config.js |
|
/* eslint-env node */ |
|
module.exports = { |
|
preset: "ts-jest/presets/js-with-ts", |
|
moduleFileExtensions: ["ts", "tsx", "js"], |
|
moduleNameMapper: {}, |
|
transform: { |
|
"^.+.(ts|tsx)$": "ts-jest", |
|
}, |
|
testMatch: ["**/__tests__/*.(ts|tsx)"], |
|
setupFilesAfterEnv: ["./jest.setup.ts"], |
|
testPathIgnorePatterns: ["./.next/", "./node_modules/"], |
|
globals: { |
|
"ts-jest": { |
|
tsConfig: "tsconfig.jest.json", |
|
}, |
|
}, |
|
}; |
|
EOF |
|
|
|
cat << 'EOF' > postcss.config.js |
|
/* eslint-env node */ |
|
module.exports = { |
|
plugins: { |
|
tailwindcss: {}, |
|
autoprefixer: {}, |
|
}, |
|
}; |
|
EOF |
|
|
|
cat << 'EOF' > tailwind.config.js |
|
/* eslint-env node */ |
|
module.exports = { |
|
purge: ['./pages/**/*.tsx', './components/**/*.tsx'], |
|
darkMode: false, |
|
theme: { |
|
extend: {}, |
|
}, |
|
variants: { |
|
backgroundColor: ["responsive", "hover", "focus", "active"], |
|
}, |
|
plugins: [], |
|
}; |
|
EOF |
|
|
|
cat << 'EOF' > tsconfig.eslint.json |
|
{ |
|
"extends": "./tsconfig.json", |
|
"include": ["**/*.tsx", "**/*.ts", "*.ts", "**/*.js", "*.js", "next-env.d.ts"], |
|
"exclude": ["node_modules", "dist", ".next", "out"] |
|
} |
|
EOF |
|
|
|
cat << 'EOF' > tsconfig.jest.json |
|
{ |
|
"compilerOptions": { |
|
"jsx": "react", |
|
"allowJs": true, |
|
"allowSyntheticDefaultImports": true, |
|
"esModuleInterop": true, |
|
"noImplicitAny": true, |
|
"sourceMap": true, |
|
"target": "es5" |
|
}, |
|
"include": ["**/*.ts", "**/*.tsx"] |
|
} |
|
EOF |
|
|
|
yarn add isomorphic-unfetch \ |
|
swr \ |
|
autoprefixer \ |
|
immer \ |
|
tailwindcss \ |
|
postcss |
|
|
|
|
|
yarn add --dev typescript \ |
|
msw \ |
|
@types/react \ |
|
@types/node \ |
|
@vercel/node \ |
|
prettier \ |
|
eslint \ |
|
eslint-config-prettier \ |
|
eslint-plugin-import \ |
|
eslint-plugin-prettier \ |
|
eslint-plugin-sonarjs \ |
|
eslint-plugin-react \ |
|
@typescript-eslint/eslint-plugin \ |
|
@typescript-eslint/parser \ |
|
husky \ |
|
lint-staged \ |
|
jest \ |
|
@testing-library/jest-dom \ |
|
@testing-library/react \ |
|
ts-jest \ |
|
npm-run-all |
|
|
|
npx dot-json package.json "scripts.lint" "npm-run-all -p lint:*" |
|
npx dot-json package.json "scripts.lint:tsc" "tsc --noEmit" |
|
npx dot-json package.json "scripts.lint:eslint" "eslint --ext .js,.ts,.tsx ." |
|
npx dot-json package.json "scripts.lint:prettier" "prettier --check ." |
|
npx dot-json package.json "scripts.fix" "prettier --write . && eslint --fix --ext .js,.ts,.tsx ." |
|
npx dot-json package.json "scripts.test" "jest" |
|
|
|
npx dot-json package.json "husky.hooks.pre-commit" "tsc --noEmit && lint-staged && yarn test" |