diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 1aa7cc3..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,129 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - 'arrow-spacing': [ - 'warn', - { - 'before': true, - 'after': true - } - ], - 'brace-style': [ - 'error', - 'stroustrup', - { - 'allowSingleLine': true - } - ], - 'comma-dangle': [ - 'error', - 'always-multiline' - ], - 'comma-spacing': 'error', - 'comma-style': 'error', - 'curly': [ - 'error', - 'multi-line', - 'consistent' - ], - 'dot-location': [ - 'error', - 'property' - ], - 'handle-callback-err': 'off', - 'indent': [ - 'error', - 'tab' - ], - 'keyword-spacing': 'error', - 'max-nested-callbacks': [ - 'error', - { - 'max': 4 - } - ], - 'max-statements-per-line': [ - 'error', - { - 'max': 2 - } - ], - 'no-console': 'off', - 'no-empty-function': 'error', - 'no-floating-decimal': 'error', - 'no-inline-comments': 'error', - 'no-lonely-if': 'error', - 'no-multi-spaces': 'error', - 'no-multiple-empty-lines': [ - 'error', - { - 'max': 2, - 'maxEOF': 1, - 'maxBOF': 0 - } - ], - 'no-shadow': [ - 'error', - { - 'allow': [ - 'err', - 'resolve', - 'reject' - ] - } - ], - 'no-trailing-spaces': [ - 'error' - ], - 'no-var': 'error', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - 'argsIgnorePattern': '^_', - 'destructuredArrayIgnorePattern': '^_', - 'varsIgnorePattern': '^_' - } - ], - 'no-unused-vars': 'off', - 'object-curly-spacing': [ - 'error', - 'always' - ], - 'prefer-const': 'error', - 'quotes': [ - 'error', - 'single' - ], - 'semi': [ - 'error', - 'always' - ], - 'space-before-blocks': 'error', - 'space-before-function-paren': [ - 'error', - { - 'anonymous': 'never', - 'named': 'never', - 'asyncArrow': 'always' - } - ], - 'space-in-parens': 'error', - 'space-infix-ops': 'error', - 'space-unary-ops': 'error', - 'spaced-comment': 'error', - 'yoda': 'error' - }, -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100755 index 0000000..c9ae5e8 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "env": { + "browser": true, + "node": true, + "es2021": true + }, + "extends": ["plugin:react/recommended", "prettier", "eslint:recommended"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": ["react", "@typescript-eslint", "react-hooks"], + "rules": { + "no-use-before-define": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-use-before-define": ["error"], + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn" + } +} diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index a547bf3..63a5015 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,46 @@ -# Logs -logs +lib-cov +*.seed *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.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 -dist -dist-ssr -*.local +bower_components -# Editor directories and files -.vscode/* -!.vscode/extensions.json +# Editors .idea +*.iml + +# OS metadata .DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? +Thumbs.db + +# Ignore built ts files +dist/**/* + +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions diff --git a/.nvmrc b/.nvmrc new file mode 100755 index 0000000..62ccda5 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v19 diff --git a/.prettierignore b/.prettierignore new file mode 100755 index 0000000..5bff081 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +dist +build +node_modules +.github +.yarn diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 0d6babe..b3c6743 --- a/README.md +++ b/README.md @@ -1,30 +1,63 @@ -# React + TypeScript + Vite +# Vite Typescript React 18 SSR -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +[](https://github.com/jonluca/vite-typescript-ssr-react/actions/workflows/nodejs.yml) -Currently, two official plugins are available: +A _blazingly_ modern web development stack. This template repo tries to achieve the minimum viable example for each of the following: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + -## Expanding the ESLint configuration +- [React 18](https://reactjs.org/blog/2022/03/29/react-v18.html) +- [Typescript 4.9](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/) +- [Vite with Vite SSR](https://vitejs.dev/guide/ssr.html) +- [GitHub Actions](https://github.com/features/actions) +- [Tailwind CSS](https://tailwindui.com/) +- [Prettier](https://prettier.io/) & [ESLint](https://eslint.org/) -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +## Development -- Configure the top-level `parserOptions` property like this: +``` +yarn +yarn dev:server +``` + +That should start the server. It will open to http://localhost:7456. + +If you'd like to just develop the UI, you can use + +```bash +yarn +yarn dev:client +``` + +To start the native vite client. + +## Building -```js -export default { - // other rules... - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - project: ['./tsconfig.json', './tsconfig.node.json'], - tsconfigRootDir: __dirname, - }, -} ``` +yarn build +yarn serve +``` + +yarn build will create the assets in `dist` - a `client` and `server` folder. Serve will run `dist/server.js` with Node, but feel free to change this to use Docker or some other process manager to suit your deployment needs. + +## Files + +`eslintrc.js` - a barebones eslint configuration for 2021, that extends off of the recommended ESLint config and prettier + +`.prettierrc.js` - the prettier config + +`index.html` - the vite entrypoint, that includes the entry point for the client + +`postcss.config.cjs` - CommonJS module that defines the PostCSS config + +`server.ts` - The barebones Express server with logic for SSRing Vite pages + +`tailwind.config.cjs` - CommonJS module that defines the Tailwind config + +`tsconfig.json` - TypeScript configuration + +`vite.config.ts` - Vite configuration + +## CI -- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` -- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list +We use GitHub actions to build the app. The badge is at the top of the repo. Currently it just confirms that everything builds properly. diff --git a/backend/server.ts b/backend/server.ts deleted file mode 100644 index 9d4aaec..0000000 --- a/backend/server.ts +++ /dev/null @@ -1,52 +0,0 @@ -import express from 'express'; -import { createServer as createViteServer } from 'vite'; -import { fileURLToPath } from 'url'; -import path from 'path'; -import fs from 'fs'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) - -const app = express(); - -const vite = await createViteServer({ - server: { middlewareMode: true }, - appType: 'custom', -}); - -app.use(vite.middlewares); - -app.use('*', async (req, res, next) => { - - const url = req.originalUrl; - - try { - - let template = fs.readFileSync( - path.resolve(__dirname, 'index.html'), - 'utf-8', - ); - - template = await vite.transformIndexHtml(url, template); - - const { render } = await vite.ssrLoadModule('/src/entry-server.tsx') - - const appHtml = await render(url); - - const html = template - .replace(``, appHtml.head ?? '') - .replace(``, appHtml.html ?? '') - - res.status(200).set({ 'Content-Type': 'text/html' }).end(html); - - } catch (e) { - - vite.ssrFixStacktrace(e as Error); - next(e); - - } - -}) - -app.listen(3000, () => { - console.log('Listening on :3000') -}); \ No newline at end of file diff --git a/backend/tsconfig.json b/backend/tsconfig.json deleted file mode 100644 index 006fe6f..0000000 --- a/backend/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020"], - "module": "NodeNext", - "skipLibCheck": true, - "outDir": "../dist", - - /* Bundler mode */ - "moduleResolution": "NodeNext", - "resolveJsonModule": true, - "isolatedModules": true, - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, -} - \ No newline at end of file diff --git a/index.html b/index.html old mode 100644 new mode 100755 index 530535d..a77e995 --- a/index.html +++ b/index.html @@ -1,14 +1,28 @@ - +
- -