From 9a80fb6355f652fa1d9e627aed540f329bd65833 Mon Sep 17 00:00:00 2001 From: gbrochar Date: Sun, 3 Jan 2021 16:08:06 +0100 Subject: [PATCH] boilerplate --- .babelrc | 16 ++++++++ .eslintrc.js | 34 +++++++++++++++++ config/tsconfig.client.json | 10 +++++ config/tsconfig.server.json | 10 +++++ config/webpack.client.common.js | 28 ++++++++++++++ config/webpack.client.dev.js | 9 +++++ config/webpack.client.prod.js | 14 +++++++ config/webpack.server.common.js | 29 ++++++++++++++ config/webpack.server.dev.js | 9 +++++ config/webpack.server.prod.js | 14 +++++++ package.json | 68 +++++++++++++++++++++++++++++++++ src/client/components/Root.tsx | 12 ++++++ src/client/main.tsx | 13 +++++++ src/server/main.ts | 15 ++++++++ tsconfig.json | 21 ++++++++++ views/index.ejs | 11 ++++++ 16 files changed, 313 insertions(+) create mode 100644 .babelrc create mode 100644 .eslintrc.js create mode 100644 config/tsconfig.client.json create mode 100644 config/tsconfig.server.json create mode 100644 config/webpack.client.common.js create mode 100644 config/webpack.client.dev.js create mode 100644 config/webpack.client.prod.js create mode 100644 config/webpack.server.common.js create mode 100644 config/webpack.server.dev.js create mode 100644 config/webpack.server.prod.js create mode 100644 package.json create mode 100644 src/client/components/Root.tsx create mode 100644 src/client/main.tsx create mode 100644 src/server/main.ts create mode 100644 tsconfig.json create mode 100644 views/index.ejs diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..67e1a4a --- /dev/null +++ b/.babelrc @@ -0,0 +1,16 @@ +{ + "presets": [ + [ + "@babel/env", { + useBuiltIns: "usage", + corejs: 3, + } + ], + "@babel/typescript", + "@babel/react", + ], + "plugins": [ + "@babel/proposal-class-properties", + "@babel/proposal-object-rest-spread", + ] +} \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..d0e08a6 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,34 @@ +module.exports = { + 'env': { + 'browser': true, + 'es2021': true, + 'node': true, + }, + 'extends': [ + 'plugin:react/recommended', + 'google', + ], + 'parser': '@typescript-eslint/parser', + 'parserOptions': { + 'ecmaFeatures': { + 'jsx': true, + }, + 'ecmaVersion': 12, + 'sourceType': 'module', + }, + 'plugins': [ + 'react', + '@typescript-eslint', + ], + 'settings': { + 'react': { + 'version': 'detect', + }, + }, + 'rules': { + 'indent': ['error', "tab"], + 'no-tabs': ['error', {'allowIndentationTabs': true}], + 'max-len': ['error', {'tabWidth': 4}], + }, + }; + \ No newline at end of file diff --git a/config/tsconfig.client.json b/config/tsconfig.client.json new file mode 100644 index 0000000..abc051d --- /dev/null +++ b/config/tsconfig.client.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "target": "es5", + "declarationDir": "../dist/types" + }, + "include": [ + "../src/client/**/*" + ] +} diff --git a/config/tsconfig.server.json b/config/tsconfig.server.json new file mode 100644 index 0000000..cc57326 --- /dev/null +++ b/config/tsconfig.server.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "target": "es6", + "declarationDir": "../dist/types" + }, + "include": [ + "../src/server/**/*" + ] +} diff --git a/config/webpack.client.common.js b/config/webpack.client.common.js new file mode 100644 index 0000000..6b33b86 --- /dev/null +++ b/config/webpack.client.common.js @@ -0,0 +1,28 @@ +const path = require('path'); +const nodeExternals = require('webpack-node-externals'); +const EsLint = require('eslint-webpack-plugin'); + +module.exports = { + entry: path.resolve(__dirname, '../src/client/main.tsx'), + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, '../dist/client'), + }, + target: 'web', + resolve: { + extensions: ['.ts', '.tsx', '.js', '.json'] + }, + module: { + rules: [{ + test: /\.tsx?$/, + loader: 'babel-loader', + include: path.join(__dirname, '../src/client'), + }], + }, + plugins: [ + new EsLint({ + extensions: ['.ts', '.tsx'], + context: path.join(__dirname, '../src/client'), + }), + ], +}; diff --git a/config/webpack.client.dev.js b/config/webpack.client.dev.js new file mode 100644 index 0000000..aa3fc2f --- /dev/null +++ b/config/webpack.client.dev.js @@ -0,0 +1,9 @@ +const path = require('path'); +const { merge } = require('webpack-merge'); +const common = require('./webpack.client.common.js'); + +module.exports = merge(common, { + devtool: 'eval-cheap-module-source-map', + mode: 'development', + watch: true +}); diff --git a/config/webpack.client.prod.js b/config/webpack.client.prod.js new file mode 100644 index 0000000..47b3edf --- /dev/null +++ b/config/webpack.client.prod.js @@ -0,0 +1,14 @@ +const path = require('path'); +const { merge } = require('webpack-merge'); +const common = require('./webpack.client.common.js'); +const TerserPlugin = require('terser-webpack-plugin'); + +module.exports = merge(common, { + mode: 'production', + optimization: { + minimize: true, + minimizer: [new TerserPlugin({ + parallel: true + })] + } +}); diff --git a/config/webpack.server.common.js b/config/webpack.server.common.js new file mode 100644 index 0000000..9e740ea --- /dev/null +++ b/config/webpack.server.common.js @@ -0,0 +1,29 @@ +const path = require('path'); +const nodeExternals = require('webpack-node-externals'); +const EsLint = require('eslint-webpack-plugin'); + +module.exports = { + entry: path.resolve(__dirname, '../src/server/main.ts'), + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, '../dist/server'), + }, + target: 'node', + externals: [nodeExternals()], + resolve: { + extensions: ['.ts', '.tsx', '.js', '.json'] + }, + module: { + rules: [{ + test: /\.tsx?$/, + loader: 'babel-loader', + include: path.join(__dirname, '../src/server'), + }], + }, + plugins: [ + new EsLint({ + extensions: ['.ts', '.tsx'], + context: path.join(__dirname, '../src/server'), + }), + ], +}; diff --git a/config/webpack.server.dev.js b/config/webpack.server.dev.js new file mode 100644 index 0000000..73bbd8f --- /dev/null +++ b/config/webpack.server.dev.js @@ -0,0 +1,9 @@ +const path = require('path'); +const { merge } = require('webpack-merge'); +const common = require('./webpack.server.common.js'); + +module.exports = merge(common, { + devtool: 'eval-cheap-module-source-map', + mode: 'development', + watch: true +}); diff --git a/config/webpack.server.prod.js b/config/webpack.server.prod.js new file mode 100644 index 0000000..91dfae9 --- /dev/null +++ b/config/webpack.server.prod.js @@ -0,0 +1,14 @@ +const path = require('path'); +const { merge } = require('webpack-merge'); +const common = require('./webpack.server.common.js'); +const TerserPlugin = require('terser-webpack-plugin'); + +module.exports = merge(common, { + mode: 'production', + optimization: { + minimize: true, + minimizer: [new TerserPlugin({ + parallel: true + })] + } +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..afb863d --- /dev/null +++ b/package.json @@ -0,0 +1,68 @@ +{ + "name": "expo-web", + "version": "1.0.0", + "description": "The web version of my digital art exhibition", + "scripts": { + "type-check:server": "tsc --pretty --project config/tsconfig.server.json --noEmit", + "type-check:client": "tsc --pretty --project config/tsconfig.client.json --noEmit", + "type-check": "concurrently \"npm:type-check:server\" \"npm:type-check:client\"", + "type-check:server:watch": "tsc --watch --preserveWatchOutput --pretty --project config/tsconfig.server.dev.json --noEmit", + "type-check:client:watch": "tsc --watch --preserveWatchOutput --pretty --project config/tsconfig.client.dev.json --noEmit", + "type-check:watch": "concurrently \"npm:type-check:server:watch\" \"npm:type-check:client:watch\"", + "build:server:types": "tsc --emitDeclarationOnly --project config/tsconfig.server.json", + "build:server:js": "webpack --config config/webpack.server.prod.js", + "build:server": "npm run build:server:types && npm run build:server:js", + "build:client:types": "tsc --emitDeclarationOnly --project config/tsconfig.client.json", + "build:client:js": "webpack --config config/webpack.client.prod.js", + "build:client": "npm run build:client:types && npm run build:client:js", + "build": "rm -rf dist && npm run build:server && npm run build:client", + "dev:server:types": "tsc --emitDeclarationOnly --watch --preserveWatchOutput --pretty --project config/tsconfig.server.json", + "dev:server:js": "webpack --config config/webpack.server.dev.js", + "dev:server": "concurrently \"npm:dev:server:types\" \"npm:dev:server:js\"", + "dev:client:types": "tsc --emitDeclarationOnly --watch --preserveWatchOutput --pretty --project config/tsconfig.client.json", + "dev:client:js": "webpack --config config/webpack.client.dev.js", + "dev:client": "concurrently \"npm:dev:client:types\" \"npm:dev:client:js\"", + "dev:start": "mkdir -p dist/server && touch dist/server/bundle.js && nodemon dist/server/bundle.js", + "dev": "rm -rf dist && concurrently \"npm:dev:server\" \"npm:dev:client\" \"npm:dev:start\"", + "start": "node dist/server/bundle.js" + }, + "repository": { + "type": "git", + "url": "https://git.gaetanbrochard.dev/gbrochar/expo-web.git" + }, + "author": "gbrochar", + "license": "AGPL-3.0-only", + "devDependencies": { + "@babel/core": "^7.12.3", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@babel/preset-typescript": "^7.12.1", + "@babel/runtime-corejs3": "^7.12.5", + "@types/express": "^4.17.9", + "@types/react": "^16.14.0", + "@types/react-dom": "^16.9.9", + "@typescript-eslint/eslint-plugin": "^4.8.1", + "@typescript-eslint/parser": "^4.8.1", + "babel-loader": "^8.2.1", + "concurrently": "^5.3.0", + "core-js": "^3.7.0", + "eslint": "^7.13.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-react": "^7.21.5", + "eslint-webpack-plugin": "^2.4.0", + "nodemon": "^2.0.6", + "typescript": "^4.1.2", + "webpack": "^5.6.0", + "webpack-cli": "^4.2.0", + "webpack-merge": "^5.4.0", + "webpack-node-externals": "^2.5.2" + }, + "dependencies": { + "ejs": "^3.1.5", + "express": "^4.17.1", + "react": "^17.0.1", + "react-dom": "^17.0.1" + } +} diff --git a/src/client/components/Root.tsx b/src/client/components/Root.tsx new file mode 100644 index 0000000..ec75a0f --- /dev/null +++ b/src/client/components/Root.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +/** + * @return {jsx} The root component + */ +export default function Root() { + return ( +
+

Hello World !

+
+ ); +} diff --git a/src/client/main.tsx b/src/client/main.tsx new file mode 100644 index 0000000..e30f6b8 --- /dev/null +++ b/src/client/main.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import {hydrate, render} from 'react-dom'; +import Root from './components/Root'; + +const root = document.getElementById('root'); +let renderMethod; +if (root && root.innerHTML !== '') { + renderMethod = hydrate; +} else { + renderMethod = render; +} + +renderMethod(, document.getElementById('root')); diff --git a/src/server/main.ts b/src/server/main.ts new file mode 100644 index 0000000..4c7ff25 --- /dev/null +++ b/src/server/main.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import path from 'path'; + +const app = express(); + +app.set('view engine', 'ejs'); + +app.use('/static', express.static(path.join(__dirname, '../../public'))); +app.use('/js', express.static(path.join(__dirname, '../../dist/client'))); + +app.get('/', (_req, res) => { + res.render('index'); +}); + +app.listen(3000); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0a1329b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "noFallthroughCasesInSwitch": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "esModuleInterop": true, + "noUnusedLocals": true, + "noImplicitAny": true, + "declaration": true, + "jsx" : "preserve", + "module": "commonjs", + "strict": true + }, + "exclude": [ + "node_modules", + "config", + "views", + "dist" + ] +} diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 0000000..5d9431c --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,11 @@ + + + + + + + +
+ + + \ No newline at end of file