Skip to content

Commit

Permalink
initial repo construction
Browse files Browse the repository at this point in the history
  • Loading branch information
jonluca committed May 16, 2021
0 parents commit be93db6
Show file tree
Hide file tree
Showing 22 changed files with 5,237 additions and 0 deletions.
21 changes: 21 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ["plugin:react/recommended", "standard", "prettier", "eslint:recommended"],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: "module",
},
plugins: ["react", "@typescript-eslint"],
rules: {
// note you must disable the base rule as it can report incorrect errors
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": ["error"],
},
};
39 changes: 39 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
*.swp

pids
logs
results
tmp

# Build
public/css/main.css

# Coverage reports
coverage

# API keys and secrets
.env

# Dependency directory
node_modules
bower_components

# Editors
.idea
*.iml

# OS metadata
.DS_Store
Thumbs.db

# Ignore built ts files
dist/**/*

9 changes: 9 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
printWidth: 120,
semi: true,
singleQuote: false,
trailingComma: "all",
bracketSpacing: true,
jsxBracketSameLine: false,
arrowParens: "avoid",
};
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Vite Typescript React 17

ImageConverter uses a mix of Vite, TypeScript, and React 17 to create a modern web application. It also server renders its pages.
It also imports Tailwind CSS for the CSS framework

## Development

```
yarn
yarn dev
```

That should be enough to get started. It will open to http://localhost:7456

## Building

```
yarn build
yarn serve
```

yarn build will create the assets in `dist` - a `client` and `server` folder.
13 changes: 13 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta content="width=device-width,initial-scale=1,maximum-scale=10,minimum-scale=.25,user-scalable=yes" name="viewport">
<meta content="text/html; charset=utf-8" http-equiv="content-type">
</head>
<body>
<div id="app" style="height: 100%;width: 100%;"><!--app-html--></div>
<script type="module" src="/src/client/entry-client.tsx"></script>
</body>
</html>
66 changes: 66 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "vite-typescript-ssr-react",
"version": "1.0.0",
"description": "Boilerplate for a modern web stack",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/TypeScript-Node-Starter"
},
"author": "JonLuca DeCaro",
"license": "MIT",
"scripts": {
"dev": "tsc && concurrently 'yarn watch:tsc' 'yarn watch:server' 'yarn watch:static'",
"watch:server": "nodemon --watch dist/server.js --watch dist/src/server dist/server.js",
"watch:static": "nodemon --watch static --watch index.html --exec yarn copy-files",
"watch:tsc": "tsc -w",
"build": "yarn build:client && yarn build:server && yarn copy-files",
"build:client": "vite build --outDir dist/client",
"build:server": "vite build --ssr src/client/entry-server.tsx --outDir dist/server",
"serve": "yarn build && cross-env NODE_ENV=production node --experimental-modules dist/server.js",
"clean": "rimraf dist/",
"copy-files": "copyfiles static/* dist/assets && copyfiles index.html dist"
},
"dependencies": {
"async": "3.2.0",
"autoprefixer": "^10.2.5",
"compression": "1.7.4",
"cross-env": "^7.0.3",
"eslint-config-airbnb": "^18.2.1",
"express": "4.17.1",
"nodemon": "^2.0.7",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"serve-static": "^1.14.1"
},
"devDependencies": {
"@types/compression": "1.7.0",
"@types/concurrently": "6.0.1",
"@types/eslint": "7.2.10",
"@types/express": "4.17.11",
"@types/node": "15.3.0",
"@types/react": "^17.0.5",
"@types/react-dom": "^17.0.5",
"@types/react-router-dom": "^5.1.7",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"@vitejs/plugin-react-refresh": "^1.3.3",
"concurrently": "6.1.0",
"copyfiles": "^2.4.1",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.23.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-react": "^7.23.2",
"postcss": "8.2.15",
"prettier": "^2.3.0",
"rimraf": "^3.0.2",
"tailwindcss": "2.1.2",
"typescript": "4.3.1-rc",
"vite": "2.3.2",
"vite-plugin-legacy": "^2.1.0"
}
}
7 changes: 7 additions & 0 deletions postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// postcss.config.cjs
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
96 changes: 96 additions & 0 deletions server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import fs from "fs";
import path from "path";
import express from "express";
import { createServer as createViteServer } from "vite";
import serveStatic from "serve-static";
import compression from "compression";
import { getApi } from "./src/server/routes/api";

const isTest = process.env.NODE_ENV === "test" || !!process.env.VITE_TEST_BUILD;

const createServer = async (root = process.cwd(), isProd = process.env.NODE_ENV === "production") => {
const resolve = (p: string) => path.resolve(__dirname, p);

const indexProd = isProd ? fs.readFileSync(resolve("./client/index.html"), "utf-8") : "";

const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

let vite: any;

if (!isProd) {
vite = await createViteServer({
root,
logLevel: isTest ? "error" : "info",
server: {
middlewareMode: true,
watch: {
// During tests we edit the files too ft and sometimes chokidar
// misses change events, so enforce polling for consistency
usePolling: true,
interval: 100,
},
},
});
// use vite's connect instance as middleware
app.use(vite.middlewares);
} else {
app.use(compression());
app.use(
serveStatic(resolve("./client"), {
index: false,
}),
);
}

app.use("/api", getApi);

app.use("*", async ({ originalUrl }, res) => {
try {
const url = originalUrl;

let template;
let render;
if (!isProd) {
// always read fresh template in dev
template = fs.readFileSync(resolve("index.html"), "utf-8");
template = await vite.transformIndexHtml(url, template);
render = (await vite.ssrLoadModule("/src/client/entry-server.tsx")).render;
} else {
template = indexProd;
const entryServer = require("./server/entry-server.js");
render = entryServer.render;
}

const context: any = {};
const appHtml = render(url, context);

if (context.url) {
// Somewhere a `<Redirect>` was rendered
return res.redirect(301, context.url);
}

const html = template.replace(`<!--app-html-->`, appHtml);

res.status(200).set({ "Content-Type": "text/html" }).end(html);
} catch (e) {
!isProd && vite.ssrFixStacktrace(e);
console.error(e.stack);
res.status(500).end(e.stack);
}
});

return { app, vite };
};

createServer().then(({ app }) => {
const port = process.env.PORT || 7456
app.listen(Number(port), "0.0.0.0", () => {
console.log(`App is listening on port ${port}`);
});
});

// for test use
export { createServer };
13 changes: 13 additions & 0 deletions src/client/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import Main from "./pages/Main";
import { ContextWrapper } from "./Context";

export const App = () => {
return (
<ContextWrapper>
<Main />
</ContextWrapper>
);
};

export default App;
21 changes: 21 additions & 0 deletions src/client/Context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { useContext, useState } from "react";

export interface Context {
name: string;
setName: (val: string) => void;
}
const defaultVal = {
name: '',
setName: () => {}
} as Context;

const context = React.createContext(defaultVal);

const { Provider } = context;

export const ContextWrapper = ({ children }: { children: any }) => {
const [name, setName] = useState(defaultVal.name);
return <Provider value={{ name,setName }}>{children}</Provider>;
};

export const useAppContext = () => useContext(context);
12 changes: 12 additions & 0 deletions src/client/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";

export const Footer = () => {
return (
<footer className={"justify-center items-center"}>
&copy; {new Date().getFullYear()} - <a href={"https://jonlu.ca"}>JonLuca DeCaro</a> -{" "}
<a className={"p-1"} href={"mailto:[email protected]"}>
Support
</a>
</footer>
);
};
12 changes: 12 additions & 0 deletions src/client/entry-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { App } from "./App";
import "./index.css";

ReactDOM.hydrate(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("app"),
);
14 changes: 14 additions & 0 deletions src/client/entry-server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom";
import { App } from "./App";
import { StaticRouterContext } from "react-router";
import "./index.css";

export function render(url: string, context: StaticRouterContext) {
return ReactDOMServer.renderToString(
<StaticRouter location={url} context={context}>
<App />
</StaticRouter>,
);
}
10 changes: 10 additions & 0 deletions src/client/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import "tailwindcss/base";

@import "tailwindcss/components";

@import "tailwindcss/utilities";

body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
25 changes: 25 additions & 0 deletions src/client/pages/Main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import { Footer } from "../components/Footer";
import { useAppContext } from "../Context";

const Main = () => {
const { name, setName } = useAppContext();
return (
<div className="flex bg-white-100 font-sans items-center flex-col justify-between">
<div className="flex items-center flex-col h-screen">
<h1 className="font-bold text-gray-900 text-5xl lg:text-7xl text-center ">Hi{name ? `, ${name}!` : ""}</h1>
<h2 className={"w-2/5 p-5 items-center flex align-middle text-center min-w-[320px]"}>
This is a Vite React SSR Tailwind boilerplate!
</h2>
<input
placeholder={"Enter your name"}
onChange={e => setName(e.currentTarget.value)}
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full text-2xl border-gray-300 rounded-md p-2"
/>
</div>
<Footer />
</div>
);
};

export default Main;
Loading

0 comments on commit be93db6

Please sign in to comment.