From d4f2474436f809d5295f40fb46dc967590000078 Mon Sep 17 00:00:00 2001 From: Alejandro Lembke Barrientos Date: Fri, 22 Sep 2023 16:56:31 +0000 Subject: [PATCH] First commit creating version 0.0.1 --- .babelrc | 7 + .env.example | 10 + .eslintignore | 10 + .eslintrc.js | 52 +++++ .gitignore | 12 + LICENSE | 21 ++ PRNameGenerator.ts | 11 + README.md | 78 +++++++ config/index.ts | 17 ++ cypress.config.ts | 24 ++ jest.config.js | 19 ++ package.json | 122 ++++++++++ public/favicon.ico | Bin 0 -> 3870 bytes public/img/logo.svg | 1 + public/index.html | 22 ++ public/logo192.png | Bin 0 -> 5347 bytes public/logo512.png | Bin 0 -> 9664 bytes public/manifest.json | 25 ++ service-worker.ts | 61 +++++ serviceWorkerRegistration.ts | 21 ++ setupTest.ts | 20 ++ src/@types/express/index.d.ts | 9 + src/@types/index.d.ts | 4 + src/frontend/actions/index.ts | 9 + src/frontend/actions/testAction.ts | 25 ++ src/frontend/components/App.tsx | 17 ++ src/frontend/components/InitialComponent.scss | 38 +++ src/frontend/components/InitialComponent.tsx | 25 ++ src/frontend/components/OtherComponent.tsx | 18 ++ src/frontend/components/PrincipalRoutes.tsx | 11 + .../components/__tests__/App.test.cy.tsx | 16 ++ .../components/__tests__/App.test.tsx | 23 ++ src/frontend/converter/tsxToString.tsx | 16 ++ src/frontend/index.tsx | 81 +++++++ src/frontend/reducers/index.ts | 14 ++ src/frontend/reducers/initialState.ts | 2 + src/frontend/reducers/testReducer.ts | 20 ++ src/frontend/setStore.ts | 27 +++ src/frontend/styles/global.scss | 5 + src/routes/index.tsx | 15 ++ src/server/config/config.go | 13 ++ src/server/go.mod | 49 ++++ src/server/go.sum | 212 +++++++++++++++++ src/server/main.go | 106 +++++++++ src/server/utils/jsxToStringExecuter.go | 37 +++ src/server/utils/readRoutes.go | 31 +++ src/server/web/web.go | 61 +++++ tsconfig.json | 57 +++++ webpack.config.ts | 216 ++++++++++++++++++ webpack.cy.config.ts | 97 ++++++++ 50 files changed, 1787 insertions(+) create mode 100644 .babelrc create mode 100644 .env.example create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 PRNameGenerator.ts create mode 100644 README.md create mode 100644 config/index.ts create mode 100644 cypress.config.ts create mode 100644 jest.config.js create mode 100644 package.json create mode 100644 public/favicon.ico create mode 100644 public/img/logo.svg create mode 100644 public/index.html create mode 100644 public/logo192.png create mode 100644 public/logo512.png create mode 100644 public/manifest.json create mode 100644 service-worker.ts create mode 100644 serviceWorkerRegistration.ts create mode 100644 setupTest.ts create mode 100644 src/@types/express/index.d.ts create mode 100644 src/@types/index.d.ts create mode 100644 src/frontend/actions/index.ts create mode 100644 src/frontend/actions/testAction.ts create mode 100644 src/frontend/components/App.tsx create mode 100644 src/frontend/components/InitialComponent.scss create mode 100644 src/frontend/components/InitialComponent.tsx create mode 100644 src/frontend/components/OtherComponent.tsx create mode 100644 src/frontend/components/PrincipalRoutes.tsx create mode 100644 src/frontend/components/__tests__/App.test.cy.tsx create mode 100644 src/frontend/components/__tests__/App.test.tsx create mode 100644 src/frontend/converter/tsxToString.tsx create mode 100644 src/frontend/index.tsx create mode 100644 src/frontend/reducers/index.ts create mode 100644 src/frontend/reducers/initialState.ts create mode 100644 src/frontend/reducers/testReducer.ts create mode 100644 src/frontend/setStore.ts create mode 100644 src/frontend/styles/global.scss create mode 100644 src/routes/index.tsx create mode 100644 src/server/config/config.go create mode 100644 src/server/go.mod create mode 100644 src/server/go.sum create mode 100644 src/server/main.go create mode 100644 src/server/utils/jsxToStringExecuter.go create mode 100644 src/server/utils/readRoutes.go create mode 100644 src/server/web/web.go create mode 100644 tsconfig.json create mode 100644 webpack.config.ts create mode 100644 webpack.cy.config.ts diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..8e494e5 --- /dev/null +++ b/.babelrc @@ -0,0 +1,7 @@ +{ + "presets": [ + ["@babel/preset-env", {"targets": {"node": "current"}}], + "@babel/preset-react", + "@babel/preset-typescript" + ] +} \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f90dd93 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +#Environment +ENV= #Default production +#App Port +PORT= #Default 80 +#PUBLIC URL +PUBLIC_URL= #Default 'auto' +#Prefix URL +PREFIX_URL= #Default '' +#ONLY EXACT PATH +ONLY_EXACT_PATH= #Default false \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..8a28323 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,10 @@ +#Build +build +#Webpack +webpack.config.ts +webpack.config.dev.ts +webpack.config.dev.server.ts +webpack.cy.config.ts +#Service Worker +service-worker.ts +serviceWorkerRegistration.ts \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..c17b153 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,52 @@ +module.exports = { + 'env': { + 'browser': true, + 'node': true, + 'es2021': true + }, + 'extends': [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:@typescript-eslint/recommended' + ], + 'parser': '@typescript-eslint/parser', + 'parserOptions': { + 'ecmaFeatures': { + 'jsx': true + }, + 'ecmaVersion': 'latest', + 'sourceType': 'module' + }, + 'plugins': [ + 'react', + '@typescript-eslint' + ], + 'rules': { + 'indent': [ + 'error', + 'tab' + ], + 'linebreak-style': [ + 'error', + 'unix' + ], + 'quotes': [ + 'error', + 'single' + ], + 'semi': [ + 'error', + 'always' + ], + 'eol-last': [ + 'error', + 'always' + ], + '@typescript-eslint/no-var-requires': 0, + }, + 'settings': { + 'react': { + 'version': 'detect', + } + } +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d29ae59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +#node_modules ignore +node_modules +#.envs +.env +#builds +build +src/server/main +src/server/react-server +src/server/web/dist +src/server/web/utils +#cypress +cypress/videos \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..62ec352 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Alejandro Lembke Barrientos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PRNameGenerator.ts b/PRNameGenerator.ts new file mode 100644 index 0000000..d6a30a5 --- /dev/null +++ b/PRNameGenerator.ts @@ -0,0 +1,11 @@ +const PRName = function () { + let ID = ''; + // let characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + const characters = '0123456789'; + for ( let i = 0; i < 6; i++ ) { + ID += characters.charAt(Math.floor(Math.random() * 10)); + } + return 'PR-'+ID; +}; + +console.log(PRName()); diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7680f6 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# Create React SSR + +This project aims to have a starter kit for creating a new React app with Server Side Rendering and tools that generally go along with it. + +It is not a project like create-react-app, create-react-app is used as a starter kit that handles all your scripts underneath, this is a project for developers who want more control over their application. + +Tech(Library or Framework) | Version | +--- | --- | +React (Render Library) | 18.2.0 +Redux (Global State Management) | 4.2.1 +React Router DOM (Routing) | 6.15.0 +Jest (Testing) | 29.6.4 +Cypress (E2E Testing) | 13.1.0 +Typescript | 5.2.2 + +## Setup +To create a new project run in the terminal: +``` +npx @aleleba/create-react-ssr app-name +``` +Then run: +``` +cd app-name +``` +You will need to create a new .env file at the root of the project for global config. +This is an exaple of config. +``` +#Environment +ENV= #Default production +#App Port +PORT= #Default 80 +#PUBLIC URL +PUBLIC_URL= #Default 'auto' +#Prefix URL +PREFIX_URL= #Default '' +#ONLY EXACT PATH +ONLY_EXACT_PATH= #Default false +``` +The default environment is production, the app port defauld is 80 and the default public url is "auto", use prefix url if you want a prefix on base url, use exact path to validate if you want to have strict exact paths. + +### For Development +In the terminal run: +``` +npm run start:dev +``` +The ENV enviroment variable should be "development" and choose the port of your preference with the enviroment variable PORT. + +You will find the root component on: +``` +scr/frontend/components/App.tsx +``` +You will find the Initial Component on: +``` +scr/frontend/components/InitialComponent.tsx +``` + +The manage of the routes you should find on: +``` +scr/routes +``` +It is using "useRoutes" hook for working, more information for this here: (https://reactrouter.com/docs/en/v6/api#useroutes) + +This will start the app in development mode, also it have Hot Reloading! +Enjoy coding! + +### For Production +In the terminal run: +``` +npm run build +``` +It will create a build folder and run: +``` +npm start +``` +This will start the app. + +## Cheers +Hope you enjoy this proyect! Sincerely Alejandro Lembke Barrientos. diff --git a/config/index.ts b/config/index.ts new file mode 100644 index 0000000..b36bb24 --- /dev/null +++ b/config/index.ts @@ -0,0 +1,17 @@ +export const deFaultValues = { + ENV: 'production', + PORT: 80, + PUBLIC_URL: 'auto', + PREFIX_URL: '', + ONLY_EXACT_PATH: false, +}; + +export const config = { + ENV: process.env.ENV ? process.env.ENV : deFaultValues.ENV, + PORT: process.env.PORT ? process.env.PORT : deFaultValues.PORT, + PUBLIC_URL: process.env.PUBLIC_URL ? process.env.PUBLIC_URL : deFaultValues.PUBLIC_URL, + PREFIX_URL: process.env.PREFIX_URL ? process.env.PREFIX_URL : deFaultValues.PREFIX_URL, + ONLY_EXACT_PATH: process.env.ONLY_EXACT_PATH ? process.env.ONLY_EXACT_PATH === 'true' : deFaultValues.ONLY_EXACT_PATH, +}; + +export default config; diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 0000000..6a51df3 --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from 'cypress'; +import webpackConfig from './webpack.cy.config'; + +export default defineConfig({ + env: {}, + e2e: { + /*setupNodeEvents(on, config) { + // implement node event listeners here + },*/ + baseUrl: 'http://localhost:3000', + specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}', + experimentalRunAllSpecs: true, + }, + component: { + specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}', + devServer: { + framework: 'react', + bundler: 'webpack', + webpackConfig: webpackConfig, + }, + viewportWidth: 1280, + viewportHeight: 720, + } +}); diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..f94867b --- /dev/null +++ b/jest.config.js @@ -0,0 +1,19 @@ +const { pathsToModuleNameMapper } = require('ts-jest'); +const { compilerOptions } = require('./tsconfig'); + +const aliases = pathsToModuleNameMapper(compilerOptions.paths, { + prefix: '' +}); + +module.exports = { + setupFilesAfterEnv: ['/setupTest.ts'], + testPathIgnorePatterns: ['/node_modules/', '\\.cy.(js|jsx|ts|tsx)$'], + testEnvironment: 'jsdom', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], + modulePathIgnorePatterns: ['/cypress/'], + moduleNameMapper: { + ...aliases, + '\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/src/__mocks__/fileMock.ts', + '\\.(css|sass|scss|less)$': 'identity-obj-proxy', + } +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..72930fd --- /dev/null +++ b/package.json @@ -0,0 +1,122 @@ +{ + "name": "@aleleba/create-react-go-ssr", + "version": "0.0.1", + "description": "Starter Kit of server side render of react", + "bin": "./bin/cli.js", + "main": "src/server/index", + "scripts": { + "start": "webpack watch --config webpack.config.ts", + "buildTsxToString": "webpack --config webpack.config.convert.ts", + "start:dev": "rm -rf build && webpack --mode=development --config webpack.config.dev.server.ts", + "start:dev-win": "(if exist build rmdir /s /Q build) && webpack --mode=development --config webpack.config.dev.server.ts", + "build": "webpack-cli --config webpack.config.ts", + "lint": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx", + "lint:fix": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx --fix", + "test": "jest", + "test:watch": "jest --watch", + "check-updates": "npx npm-check-updates -u && npm i", + "cy:open": "npx cypress open", + "cy:run": "cypress run", + "cy:run-component": "cypress run --headless --component" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/aleleba/create-react-ssr.git" + }, + "keywords": [ + "create react app", + "react", + "ssr", + "typescript", + "redux" + ], + "author": "Alejandro Lembke Barrientos", + "license": "MIT", + "bugs": { + "url": "https://github.com/aleleba/create-react-ssr/issues" + }, + "homepage": "https://github.com/aleleba/create-react-ssr#readme", + "dependencies": { + "@babel/register": "^7.22.15", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "helmet": "^7.0.0", + "history": "^5.3.0", + "ignore-styles": "^5.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-redux": "^8.1.2", + "react-router-dom": "^6.15.0", + "react-router-hash-link": "^2.4.3", + "redux": "^4.2.1", + "webpack": "^5.88.2", + "webpack-manifest-plugin": "^5.0.0", + "workbox-background-sync": "^7.0.0", + "workbox-broadcast-update": "^7.0.0", + "workbox-cacheable-response": "^7.0.0", + "workbox-core": "^7.0.0", + "workbox-expiration": "^7.0.0", + "workbox-google-analytics": "^7.0.0", + "workbox-navigation-preload": "^7.0.0", + "workbox-precaching": "^7.0.0", + "workbox-range-requests": "^7.0.0", + "workbox-routing": "^7.0.0", + "workbox-strategies": "^7.0.0", + "workbox-streams": "^7.0.0" + }, + "devDependencies": { + "@babel/core": "^7.22.17", + "@babel/preset-env": "^7.22.15", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.22.15", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", + "@redux-devtools/extension": "^3.2.5", + "@testing-library/jest-dom": "^6.1.3", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3", + "@types/jest": "^29.5.4", + "@types/node": "^20.6.0", + "@types/react": "^18.2.21", + "@types/react-dom": "^18.2.7", + "@types/webpack": "^5.28.2", + "@types/webpack-hot-middleware": "^2.25.6", + "@types/webpack-node-externals": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^6.6.0", + "@typescript-eslint/parser": "^6.6.0", + "babel-jest": "^29.6.4", + "babel-loader": "^9.1.3", + "clean-webpack-plugin": "^4.0.0", + "compression-webpack-plugin": "^10.0.0", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.8.1", + "css-minimizer-webpack-plugin": "^5.0.1", + "cypress": "^13.1.0", + "eslint": "^8.49.0", + "eslint-plugin-react": "^7.33.2", + "eslint-webpack-plugin": "^4.0.1", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.3", + "identity-obj-proxy": "^3.0.0", + "jest": "^29.6.4", + "jest-environment-jsdom": "^29.6.4", + "jest-fetch-mock": "^3.0.3", + "mini-css-extract-plugin": "^2.7.6", + "react-refresh": "^0.14.0", + "resolve-ts-aliases": "^1.0.1", + "sass": "^1.66.1", + "sass-loader": "^13.3.2", + "style-loader": "^3.3.3", + "terser-webpack-plugin": "^5.3.9", + "ts-jest": "^29.1.1", + "typescript": "^5.2.2", + "url-loader": "^4.1.1", + "webpack-cli": "^5.1.4", + "webpack-dev-middleware": "^6.1.1", + "webpack-dev-server": "^4.15.1", + "webpack-hot-middleware": "^2.25.4", + "webpack-node-externals": "^3.0.0", + "webpack-shell-plugin-next": "^2.3.1", + "workbox-webpack-plugin": "^7.0.0", + "workbox-window": "^7.0.0" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/public/img/logo.svg b/public/img/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/public/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..62140b9 --- /dev/null +++ b/public/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + App + + +
+ + + + + \ No newline at end of file diff --git a/public/logo192.png b/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..8508431 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "React App SSR Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": "/", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" + } \ No newline at end of file diff --git a/service-worker.ts b/service-worker.ts new file mode 100644 index 0000000..c91ff17 --- /dev/null +++ b/service-worker.ts @@ -0,0 +1,61 @@ +// This service worker can be customized! +// See https://developers.google.com/web/tools/workbox/modules +// for the list of available Workbox modules, or add any other +// code you'd like. +// You can also remove this file if you'd prefer not to use a +// service worker, and the Workbox build step will be skipped. + +import { clientsClaim } from 'workbox-core'; +import { ExpirationPlugin } from 'workbox-expiration'; +import { precacheAndRoute } from 'workbox-precaching'; //createHandlerBoundToURL +import { registerRoute } from 'workbox-routing'; +import { StaleWhileRevalidate, NetworkFirst } from 'workbox-strategies'; + +clientsClaim(); + +// This allows the web app to trigger skipWaiting via +// @ts-ignore:next-line +self.skipWaiting(); + +// Precache all of the assets generated by your build process. +// Their URLs are injected into the manifest variable below. +// This variable must be present somewhere in your service worker file, +// even if you decide not to use precaching. See https://cra.link/PWA +// @ts-ignore:next-line +precacheAndRoute(self.__WB_MANIFEST); + +// An example runtime caching route for requests that aren't handled by the +// precache, in this case same-origin .png requests like those from in public/ +registerRoute( + // Add in any other file extensions or routing criteria as needed. + ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // Customize this strategy as needed, e.g., by changing to CacheFirst. + new StaleWhileRevalidate({ + cacheName: 'images', + plugins: [ + // Ensure that once this runtime cache reaches a maximum size the + // least-recently used images are removed. + new ExpirationPlugin({ maxEntries: 50 }), + ], + }) +); + +// Any other custom service worker logic can go here. + +//Estrategy for Default. It should be at the end. +registerRoute(/^https?.*/, new NetworkFirst(), 'GET'); + +//Wait for Notification. +self.addEventListener('push', function (e) { + + // @ts-ignore:next-line + const data = e.data.json(); + + // @ts-ignore:next-line + registration.showNotification(data.title, { + body: data.message, + icon: 'favicon.ico' + }); + +}); + +console.log('hello from service-worker.js the new one.'); diff --git a/serviceWorkerRegistration.ts b/serviceWorkerRegistration.ts new file mode 100644 index 0000000..abd873a --- /dev/null +++ b/serviceWorkerRegistration.ts @@ -0,0 +1,21 @@ +import { Workbox } from 'workbox-window'; +import packageJson from './package.json'; + +const serviceWorkerRegistration = () => { + if ('serviceWorker' in navigator) { + const wb = new Workbox('service-worker.js'); + + wb.addEventListener('installed', event => { + if(event.isUpdate){ + if(confirm('New app update is avaible, Click Ok to refresh')){ + window.location.reload(); + }; + console.log(`Se actualiza la app a version ${packageJson.version}`); + }; + }); + + wb.register(); + }; +}; + +export default serviceWorkerRegistration; \ No newline at end of file diff --git a/setupTest.ts b/setupTest.ts new file mode 100644 index 0000000..21f0c55 --- /dev/null +++ b/setupTest.ts @@ -0,0 +1,20 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; + +//import fetch Mock +import fetchMock from 'jest-fetch-mock'; +fetchMock.enableMocks(); + +//Fixing Pollyfill for react-slick +window.matchMedia = +window.matchMedia || +function() { + return { + matches: false, + addListener: () => {/**/}, + removeListener: () => {/**/} + }; +}; diff --git a/src/@types/express/index.d.ts b/src/@types/express/index.d.ts new file mode 100644 index 0000000..301ddee --- /dev/null +++ b/src/@types/express/index.d.ts @@ -0,0 +1,9 @@ +import * as express from "express" + +declare global { + namespace Express { + interface Request { + hashManifest?: Record + } + } +} \ No newline at end of file diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts new file mode 100644 index 0000000..156473a --- /dev/null +++ b/src/@types/index.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg" { + const content: any; + export default content; +} diff --git a/src/frontend/actions/index.ts b/src/frontend/actions/index.ts new file mode 100644 index 0000000..b95c148 --- /dev/null +++ b/src/frontend/actions/index.ts @@ -0,0 +1,9 @@ +import test, { TTest } from './testAction'; + +export type TAction = TTest + +const actions = { + test +} + +export default actions \ No newline at end of file diff --git a/src/frontend/actions/testAction.ts b/src/frontend/actions/testAction.ts new file mode 100644 index 0000000..30593bf --- /dev/null +++ b/src/frontend/actions/testAction.ts @@ -0,0 +1,25 @@ +export enum ActionTypesTest { + ChangeHello = 'CHANGE_HELLO' +} + +export interface IChangeHello { + type: ActionTypesTest.ChangeHello + payload: IChangeHelloPayload +} + +export interface IChangeHelloPayload { + hello: any | undefined +} + +export type TTest = IChangeHello + +const changeHello = (payload: string) => ({ + type: ActionTypesTest.ChangeHello, + payload +}) + +const actions = { + changeHello +} + +export default actions \ No newline at end of file diff --git a/src/frontend/components/App.tsx b/src/frontend/components/App.tsx new file mode 100644 index 0000000..cd9f890 --- /dev/null +++ b/src/frontend/components/App.tsx @@ -0,0 +1,17 @@ +import React, { useEffect } from 'react'; +import PrincipalRoutes from './PrincipalRoutes'; + +const App = () => { + useEffect(() => { + const ws = new WebSocket('wss://xs70kvlc-3000.use.devtunnels.ms/ws'); + ws.onmessage = (event) => { + if (event.data === 'reload') { + window.location.reload(); + } + }; + }, []); + + return ; +}; + +export default App; diff --git a/src/frontend/components/InitialComponent.scss b/src/frontend/components/InitialComponent.scss new file mode 100644 index 0000000..a988b49 --- /dev/null +++ b/src/frontend/components/InitialComponent.scss @@ -0,0 +1,38 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/src/frontend/components/InitialComponent.tsx b/src/frontend/components/InitialComponent.tsx new file mode 100644 index 0000000..fb05136 --- /dev/null +++ b/src/frontend/components/InitialComponent.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import './InitialComponent.scss'; +import { Link } from 'react-router-dom'; + +const InitialComponent = () => ( + +); + +export default InitialComponent; diff --git a/src/frontend/components/OtherComponent.tsx b/src/frontend/components/OtherComponent.tsx new file mode 100644 index 0000000..b249292 --- /dev/null +++ b/src/frontend/components/OtherComponent.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +// import logo from '../logo.svg'; +import './InitialComponent.scss'; +import { Link } from 'react-router-dom'; + +const OtherComponent = () => ( +
+
+ logo +

+ Edit src/frontend/OtherComponent.jsx and save to reload. +

+ Initial Component +
+
+); + +export default OtherComponent; diff --git a/src/frontend/components/PrincipalRoutes.tsx b/src/frontend/components/PrincipalRoutes.tsx new file mode 100644 index 0000000..73bf9ef --- /dev/null +++ b/src/frontend/components/PrincipalRoutes.tsx @@ -0,0 +1,11 @@ +//Router +import { useRoutes } from 'react-router-dom'; +//Routes +import routes from '../../routes'; + +const PrincipalRoutes = () => { + let element = useRoutes(routes); + return element; +}; + +export default PrincipalRoutes; diff --git a/src/frontend/components/__tests__/App.test.cy.tsx b/src/frontend/components/__tests__/App.test.cy.tsx new file mode 100644 index 0000000..face8a4 --- /dev/null +++ b/src/frontend/components/__tests__/App.test.cy.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { ProviderMock } from '@mocks'; +import App from '@components/App'; + +describe('Testing Card Component', () => { + beforeEach(() => { + cy.mount( + + + + ); + }); + it('Show Text', () => { + cy.get('p').contains('Edit src/frontend/InitialComponent.jsx and save to reload.'); + }); +}); diff --git a/src/frontend/components/__tests__/App.test.tsx b/src/frontend/components/__tests__/App.test.tsx new file mode 100644 index 0000000..6e03a0d --- /dev/null +++ b/src/frontend/components/__tests__/App.test.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { ProviderMock } from '@mocks'; +import App from '@components/App'; + +describe(' Component', () => { + beforeEach(() => { + fetchMock.resetMocks(); + }); + + it('Should render root Component', async () => { + fetchMock.mockResponseOnce(JSON.stringify({ + //First Data Fetch + data: 'data' + })); + + render( + + + + ) + }) +}) \ No newline at end of file diff --git a/src/frontend/converter/tsxToString.tsx b/src/frontend/converter/tsxToString.tsx new file mode 100644 index 0000000..9f77ebc --- /dev/null +++ b/src/frontend/converter/tsxToString.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { renderToString } from 'react-dom/server'; +import { StaticRouter } from 'react-router-dom/server'; +import App from '../components/App'; + +const url = process.argv[2]; + +const render = () => { + return renderToString( + + + + ); +}; + +console.log(render()); diff --git a/src/frontend/index.tsx b/src/frontend/index.tsx new file mode 100644 index 0000000..885e5a8 --- /dev/null +++ b/src/frontend/index.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { hydrateRoot, createRoot } from 'react-dom/client'; +// Router +import { BrowserRouter as Router } from 'react-router-dom'; +// Redux +import { Provider } from 'react-redux'; +import { IInitialState } from './reducers/index'; +import setStore from './setStore'; +import { config } from '../../config'; + +import './styles/global.scss'; +import App from './components/App'; +// import serviceWorkerRegistration from '../../serviceWorkerRegistration'; + +declare global { + interface Window { + __PRELOADED_STATE__?: IInitialState; + } +} + +declare global { + interface NodeModule { + hot?: IHot; + } +} + +interface IHot { + accept: unknown +} + +const { ENV, PREFIX_URL } = config; + +const preloadedState = window.__PRELOADED_STATE__; +const store = setStore({ initialState: preloadedState }); + +delete window.__PRELOADED_STATE__; + +const container = document.getElementById('app')!; + +if(ENV === 'development') { + const root = createRoot(container); + root.render( + + + + + + ); +} + +// add "const root" to be able to rerender. +ENV === 'production' && hydrateRoot(container, + + + + + , + // Add this comment to update later app and remove warning + /* { + onRecoverableError: (error) => { + console.error("recoverable", error); + } + }, */ +); + +// Use root.render to update later the app +/* root.render( + + + + + +); */ + +/* if((ENV) && (ENV === 'production')){ + serviceWorkerRegistration(); +} */ + +/* if(module.hot){ + module.hot.accept(); +} */ diff --git a/src/frontend/reducers/index.ts b/src/frontend/reducers/index.ts new file mode 100644 index 0000000..29d9721 --- /dev/null +++ b/src/frontend/reducers/index.ts @@ -0,0 +1,14 @@ +import { combineReducers } from 'redux'; +import testReducer from './testReducer'; +import { IChangeHelloPayload } from '../actions/testAction'; + +export interface IInitialState { + testReducer?: IChangeHelloPayload | undefined +} + +const rootReducer = combineReducers({ + // Here comes the reducers + testReducer +}); + +export default rootReducer; diff --git a/src/frontend/reducers/initialState.ts b/src/frontend/reducers/initialState.ts new file mode 100644 index 0000000..411deaa --- /dev/null +++ b/src/frontend/reducers/initialState.ts @@ -0,0 +1,2 @@ +const initialState = {}; +export default initialState; diff --git a/src/frontend/reducers/testReducer.ts b/src/frontend/reducers/testReducer.ts new file mode 100644 index 0000000..6c27a59 --- /dev/null +++ b/src/frontend/reducers/testReducer.ts @@ -0,0 +1,20 @@ +import { TAction } from '../actions'; + +const initialState = { + hello: 'world' +}; + +const testReducer = (state = initialState, action: TAction) => { + switch (action.type){ + case 'CHANGE_HELLO': { + const newHello = action.payload.hello; + return { + hello: newHello + }; + } + default: + return state; + } +}; + +export default testReducer; diff --git a/src/frontend/setStore.ts b/src/frontend/setStore.ts new file mode 100644 index 0000000..ace5fe7 --- /dev/null +++ b/src/frontend/setStore.ts @@ -0,0 +1,27 @@ +// Redux +import { legacy_createStore as createStore} from 'redux'; //, applyMiddleware +// import { Provider } from 'react-redux'; +import { composeWithDevTools as composeWithDevToolsWeb } from '@redux-devtools/extension'; +import { config } from '../../config'; +import reducer, { IInitialState } from './reducers'; + + +const { ENV } = config; + +const composeEnhancers = composeWithDevToolsWeb({ + // Specify here name, actionsBlacklist, actionsCreators and other options +}); + +const setStore = ({ initialState }: { initialState: IInitialState | undefined }) => { + const store = ENV === 'development' ? createStore( + reducer, + initialState, + composeEnhancers(), + ) : createStore( + reducer, + initialState, + ); + return store; +}; + +export default setStore; diff --git a/src/frontend/styles/global.scss b/src/frontend/styles/global.scss new file mode 100644 index 0000000..fda9030 --- /dev/null +++ b/src/frontend/styles/global.scss @@ -0,0 +1,5 @@ +$base-color: #282c34; + +body { + background-color: $base-color; +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx new file mode 100644 index 0000000..0bac04a --- /dev/null +++ b/src/routes/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import InitialComponent from '../frontend/components/InitialComponent'; +import OtherComponent from '../frontend/components/OtherComponent'; + +const OTHER_COMPONENT = { + path: '/other-component', + element: +}; + +const INITIAL_COMPONENT = { + path: '/', + element: , +}; + +export default [ INITIAL_COMPONENT, OTHER_COMPONENT ]; diff --git a/src/server/config/config.go b/src/server/config/config.go new file mode 100644 index 0000000..881dd9b --- /dev/null +++ b/src/server/config/config.go @@ -0,0 +1,13 @@ +package config + +import ( + "log" + "github.com/joho/godotenv" +) + +func LoadEnv() { + err := godotenv.Load(".env") + if err != nil { + log.Fatal("Error loading .env file") + } +} \ No newline at end of file diff --git a/src/server/go.mod b/src/server/go.mod new file mode 100644 index 0000000..d7f93e0 --- /dev/null +++ b/src/server/go.mod @@ -0,0 +1,49 @@ +module aleleba/react-server + +go 1.21.1 + +require github.com/joho/godotenv v1.5.1 + +require github.com/gofiber/fiber/v2 v2.49.1 + +require ( + cloudeng.io/os v0.0.0-20230309184059-9263072b1423 // indirect + cloudeng.io/webapp v0.0.0-20230913164637-56a6ca867a22 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/clarkmcc/go-typescript v0.7.0 // indirect + github.com/dlclark/regexp2 v1.7.0 // indirect + github.com/dop251/goja v0.0.0-20230919151941-fc55792775de // indirect + github.com/fasthttp/websocket v1.5.3 // indirect + github.com/fatih/color v1.9.0 // indirect + github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/githubnemo/CompileDaemon v1.4.0 // indirect + github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect + github.com/gofiber/adaptor/v2 v2.2.1 // indirect + github.com/gofiber/template v1.8.2 // indirect + github.com/gofiber/template/html/v2 v2.0.5 // indirect + github.com/gofiber/utils v1.1.0 // indirect + github.com/gofiber/websocket/v2 v2.2.1 // indirect + github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/labstack/echo/v4 v4.11.1 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/radovskyb/watcher v1.0.7 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/robertkrimen/otto v0.2.1 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.49.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.11.0 // indirect + gopkg.in/sourcemap.v1 v1.0.5 // indirect +) diff --git a/src/server/go.sum b/src/server/go.sum new file mode 100644 index 0000000..64a75a4 --- /dev/null +++ b/src/server/go.sum @@ -0,0 +1,212 @@ +cloudeng.io/algo v0.0.0-20221118174443-5eef698118a6/go.mod h1:dgJhQ6JXLysnBQG09/5Q56rwJfgyKXlnDbKzko9MhdA= +cloudeng.io/algo v0.0.0-20230304024356-5d6b315e83f0/go.mod h1:80eC8gNGsJP8ZBw+xCIh1kIpt77az1W7fJ+zqLRiE64= +cloudeng.io/algo v0.0.0-20230307023515-8a194fbc7867/go.mod h1:80eC8gNGsJP8ZBw+xCIh1kIpt77az1W7fJ+zqLRiE64= +cloudeng.io/cmdutil v0.0.0-20230130231933-fe585c604aed/go.mod h1:nX3v2San8WLgYYmtom/+1GxmfKh3wWSyvnLvoxUz784= +cloudeng.io/cmdutil v0.0.0-20230309184059-9263072b1423/go.mod h1:HOYjjVIvIbRF5li5EIzqyFYxlzPmxNskXO74HDICgk8= +cloudeng.io/debug v0.0.0-20230105021008-948d22470af5/go.mod h1:UDKUtuaCh0+iaDe5+i70G3l3liexOPeU3h0lyUeAt+o= +cloudeng.io/errors v0.0.7/go.mod h1:xWamLL6tn3roKI6MRRFkw1jUkJL9s7CJzFYfaxuhHZk= +cloudeng.io/errors v0.0.8/go.mod h1:xWamLL6tn3roKI6MRRFkw1jUkJL9s7CJzFYfaxuhHZk= +cloudeng.io/file v0.0.0-20230221162436-db7c7fd2cf04/go.mod h1:kcAJeuW9/AL/sw/sySiIoF+BHjDKcHawqTPyDAdwmrQ= +cloudeng.io/file v0.0.0-20230306215119-e71b407605cc/go.mod h1:1wtCIz069qanFkcbpb8F3msDf+W7G3Wjw5Om+KISyuQ= +cloudeng.io/file v0.0.0-20230309184059-9263072b1423/go.mod h1:Ow6f4Wu+6U8XQFP4U2Kr9izv+oU/jVMZyuCTmf6FaOc= +cloudeng.io/io v0.0.0-20230309184059-9263072b1423/go.mod h1:6Scpv2/l9yI5tTE0esMr5SN1bemcRhm4d+RR6tCeH1M= +cloudeng.io/net v0.0.0-20230214072348-e50d5014b274/go.mod h1:13BN0nf3e35fdeiFH0pgduqEhOgWLMvnhYtRYg4WKE8= +cloudeng.io/net v0.0.0-20230304024356-5d6b315e83f0/go.mod h1:D0bZpZHSBIlyvBG/i+JOubQDjxbYJX/eas8v9byykfc= +cloudeng.io/net v0.0.0-20230307023515-8a194fbc7867/go.mod h1:b7cZgzjeLZxVX1DJAEEQSdI9zLhruVy9XG20voObvKk= +cloudeng.io/os v0.0.0-20221118174443-5eef698118a6/go.mod h1:hhGubbGRTXGbe6T0ZU2RSXfCo1Ktvzfb0BcpkK4xqvY= +cloudeng.io/os v0.0.0-20230304024356-5d6b315e83f0/go.mod h1:Ak4HQwvBx5/6w5XT9VKS1BekYSi6QQAEvlbOtzN7eJA= +cloudeng.io/os v0.0.0-20230307023515-8a194fbc7867/go.mod h1:gBS7WALqIPNN8ovv1DIuf5/MlQ90mKDjMSjwN/ZvoD4= +cloudeng.io/os v0.0.0-20230309184059-9263072b1423 h1:+COcYDTwuT4AhsSq2G/BreMJgky2J9atZpVfGnh+Now= +cloudeng.io/os v0.0.0-20230309184059-9263072b1423/go.mod h1:6qBe7uAG6enJ7Nt+4Z8ddua57LYK4pnlmaDMGJxa/3s= +cloudeng.io/path v0.0.4/go.mod h1:HTU24UoiQVjzSjN+K2kD8hpkBnoLGZh0OITJr3kfxUk= +cloudeng.io/path v0.0.8/go.mod h1:S9dU9pNDkx0EUIHY+Pcyfzs3/cItCI9lyClkwqv0PtM= +cloudeng.io/sync v0.0.8/go.mod h1:76qdZzMQSN+iPeQxY9MSbnSELKQmcd9E6pnfRgWgN8s= +cloudeng.io/sys v0.0.0-20211030052257-ec4c6f8b878e/go.mod h1:fNIWqX26NzdShrH3lBfxzPhP1jju7JOt7Rg8PxBvYvA= +cloudeng.io/sys v0.0.0-20221118174443-5eef698118a6/go.mod h1:fNIWqX26NzdShrH3lBfxzPhP1jju7JOt7Rg8PxBvYvA= +cloudeng.io/sys v0.0.0-20230221162436-db7c7fd2cf04/go.mod h1:DPEAeWYUvJ684GySwz0pzPtYxaPnZ7dyz1NHUcdNCEk= +cloudeng.io/sys v0.0.0-20230304024356-5d6b315e83f0/go.mod h1:R5nydEKnw8t2BjpQH87CGG59WEBb2rcRRTsqz1Ak5/s= +cloudeng.io/sys v0.0.0-20230306215119-e71b407605cc/go.mod h1:Lt8onmpJi8I5Xt2EyHtIfPkn4YaH12n4ehTr199ZGjY= +cloudeng.io/sys v0.0.0-20230307023515-8a194fbc7867/go.mod h1:Lt8onmpJi8I5Xt2EyHtIfPkn4YaH12n4ehTr199ZGjY= +cloudeng.io/text v0.0.11/go.mod h1:99L3CQ55YhUy2+lHlFPowYyCoXO86fmkvNtcMT2X3GU= +cloudeng.io/webapp v0.0.0-20230913164637-56a6ca867a22 h1:q1HmQki39a8BC9qPu62b3OnGsk0kohKzZseATT/8PpU= +cloudeng.io/webapp v0.0.0-20230913164637-56a6ca867a22/go.mod h1:udLHBvexojKGB0JgZjJW3ZzDaSCqNU566KTbpK9EFmM= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clarkmcc/go-typescript v0.7.0 h1:3nVeaPYyTCWjX6Lf8GoEOTxME2bM5tLuWmwhSZ86uxg= +github.com/clarkmcc/go-typescript v0.7.0/go.mod h1:IZ/nzoVeydAmyfX7l6Jmp8lJDOEnae3jffoXwP4UyYg= +github.com/cosnicolaou/pudge v1.0.6/go.mod h1:PSbC4eylkssiQ+gAE+AWaMQSo41g/otqflfbcsMrupk= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20211115154819-26ebff68a7d5 h1:7eyn0Cp9ezNbo2Vb4ttgJyWsFrRWP3oyHEw4PHKYlps= +github.com/dop251/goja v0.0.0-20211115154819-26ebff68a7d5/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20230919151941-fc55792775de h1:lA38Xtzr1Wo+iQdkN2E11ziKXJYRxLlzK/e2/fdxoEI= +github.com/dop251/goja v0.0.0-20230919151941-fc55792775de/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= +github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek= +github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/githubnemo/CompileDaemon v1.4.0 h1:z96Qu4tj+RzRfF+L7f1O6E8ion5JQlisWeXWc2wzwDQ= +github.com/githubnemo/CompileDaemon v1.4.0/go.mod h1:/G125r3YBIp6rcXtCZfiEHwFzcl7GSsNSwylxSNrkMA= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/gofiber/adaptor/v2 v2.2.1 h1:givE7iViQWlsTR4Jh7tB4iXzrlKBgiraB/yTdHs9Lv4= +github.com/gofiber/adaptor/v2 v2.2.1/go.mod h1:AhR16dEqs25W2FY/l8gSj1b51Azg5dtPDmm+pruNOrc= +github.com/gofiber/fiber/v2 v2.49.1 h1:0W2DRWevSirc8pJl4o8r8QejDR8TV6ZUCawHxwbIdOk= +github.com/gofiber/fiber/v2 v2.49.1/go.mod h1:nPUeEBUeeYGgwbDm59Gp7vS8MDyScL6ezr/Np9A13WU= +github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk= +github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= +github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20= +github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM= +github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= +github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= +github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IUygH7w= +github.com/gofiber/websocket/v2 v2.2.1/go.mod h1:Ao/+nyNnX5u/hIFPuHl28a+NIkrqK7PRimyKaj4JxVU= +github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= +github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE= +github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0= +github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.49.0 h1:9FdvCpmxB74LH4dPb7IJ1cOSsluR07XG3I1txXWwJpE= +github.com/valyala/fasthttp v1.49.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/server/main.go b/src/server/main.go new file mode 100644 index 0000000..d494302 --- /dev/null +++ b/src/server/main.go @@ -0,0 +1,106 @@ +package main + +import ( + "os" + "fmt" + "net/http" + "path/filepath" + "github.com/labstack/echo/v4" + "github.com/gorilla/websocket" + "github.com/fsnotify/fsnotify" + "aleleba/react-server/config" + "aleleba/react-server/web" + "aleleba/react-server/utils" +) + +func init() { + config.LoadEnv() +} + +func main() { + //Getting the port from the environment + port := os.Getenv("PORT") + + paths := utils.GetRoutes() + + e := echo.New() + + // Watch for file changes in the ./web/dist directory + watcher, err := fsnotify.NewWatcher() + if err != nil { + panic(err) + } + defer watcher.Close() + + if err := filepath.Walk("./web", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + if err := watcher.Add(path); err != nil { + return err + } + } + return nil + }); err != nil { + panic(err) + } + + // Create a websocket upgrader + upgrader := websocket.Upgrader{} + + // Handle websocket connections + e.GET("/ws", func(c echo.Context) error { + // Upgrade the HTTP connection to a websocket connection + ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil) + if err != nil { + return err + } + + // Watch for file changes in the ./web/dist directory + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + if event.Op&fsnotify.Write == fsnotify.Write { + fmt.Println("modified file:", event.Name) + + // Send a message to the client to reload the page + if err := ws.WriteMessage(websocket.TextMessage, []byte("reload")); err != nil { + fmt.Println("error sending message:", err) + } + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + fmt.Println("error:", err) + } + } + }() + + // Read messages from the client (not used in this example) + /*for { + _, _, err := ws.ReadMessage() + if err != nil { + fmt.Println("error reading message:", err) + break + } + }*/ + + return nil + }) + + web.RegisterHandlers(e, paths) + + // Add your custom api routes here. + // Example: + e.GET("/api", func(c echo.Context) error { + return c.String(http.StatusOK, "Hello, World!") + }) + + e.Logger.Fatal(e.Start(":" + port)) +} \ No newline at end of file diff --git a/src/server/utils/jsxToStringExecuter.go b/src/server/utils/jsxToStringExecuter.go new file mode 100644 index 0000000..5015e05 --- /dev/null +++ b/src/server/utils/jsxToStringExecuter.go @@ -0,0 +1,37 @@ +package utils + +import ( + "fmt" + "os/exec" +) + +func JsxToString(url string) string { + // Set the path to the jsxToString.js file + jsFilePath := "./web/utils/tsxToString.js" + + // Create the command to run Node.js with the jsxToString.js file + cmd := exec.Command("node", jsFilePath, url) + + // Get a pipe to the standard input of the Node.js process + stdin, err := cmd.StdinPipe() + if err != nil { + fmt.Println(err) + return "" + } + + // Write the URL parameter to the standard input of the Node.js process + fmt.Fprintf(stdin, "%s\n", url) + + // Close the standard input pipe + stdin.Close() + + // Get the output and error of the Node.js process + output, err := cmd.CombinedOutput() + if err != nil { + fmt.Println(err) + return string(output) + } + + // Return the output of the Node.js process + return string(output) +} \ No newline at end of file diff --git a/src/server/utils/readRoutes.go b/src/server/utils/readRoutes.go new file mode 100644 index 0000000..361c13d --- /dev/null +++ b/src/server/utils/readRoutes.go @@ -0,0 +1,31 @@ +package utils + +import ( + "os" + "regexp" + "github.com/clarkmcc/go-typescript" +) + +func GetRoutes () []string { + // Read the contents of the TypeScript file + content, err := os.ReadFile("../routes/index.tsx") + if err != nil { + panic(err) + } + + // Transpile the TypeScript code to JavaScript + jsCode, err := typescript.TranspileString(string(content)) + if err != nil { + panic(err) + } + + // Extract the "path" values from the transpiled JavaScript code + re := regexp.MustCompile(`path:\s*['"]([^'"]+)['"]`) + matches := re.FindAllStringSubmatch(jsCode, -1) + paths := make([]string, 0) + for _, match := range matches { + paths = append(paths, match[1]) + } + + return paths +} \ No newline at end of file diff --git a/src/server/web/web.go b/src/server/web/web.go new file mode 100644 index 0000000..75440bc --- /dev/null +++ b/src/server/web/web.go @@ -0,0 +1,61 @@ +package web + +import ( + "embed" + //"path/filepath" + //"fmt" + "net/http" + "github.com/labstack/echo/v4" + "aleleba/react-server/utils" +) + +// embed the dist folder +var ( + //go:embed dist/* + dist embed.FS + + indexHTML embed.FS + distDirFS = echo.MustSubFS(dist, "dist") + //distIndexHtml = echo.MustSubFS(indexHTML, "dist") +) + +func RegisterHandlers(e *echo.Echo, paths []string) { + // Print the "path" values + for _, path := range paths { + //e.FileFS(path, "index.html", distIndexHtml) + //e.StaticFS(path, distDirFS) + e.Static(path, "web/dist") + e.GET(path, func(c echo.Context) error { + // Construct the file path + //filePath := filepath.Join("web", "dist", "index.html") + // Return the HTML file + //return c.File(filePath) + url := c.Request().URL.String() + component := utils.JsxToString(url) + html := ` + + + + + + + + + + + App + + +
`+ component +`
+ + + + + ` + return c.HTMLBlob(http.StatusOK, []byte(html)) + }) + } + +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5765297 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,57 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "noEmit": false, + "jsx": "react-jsx", + "experimentalDecorators": true, + "esModuleInterop": true, + "isolatedModules": false, + "typeRoots" : ["./src/@types", "./node_modules/@types"], + "types": [ + "react", + "react-dom", + "node" + ], + "sourceMap": false, + "baseUrl": ".", + "outDir": "build", + "skipLibCheck": true, + "noImplicitAny": false, + "paths": { + "@components/*": ["src/frontend/components/*"], + "@components": ["src/frontend/components"], + "@styles/*": ["src/frontend/styles/*"], + "@styles": ["src/frontend/styles"], + "@actions/*": ["src/frontend/actions/*"], + "@actions": ["src/frontend/actions"], + "@reducers/*": ["src/frontend/reducers"], + "@reducers": ["src/frontend/reducers"], + "@config/*": ["config/*"], + "@config": ["config"], + "@mocks/*": ["src/__mocks__/*"], + "@mocks": ["src/__mocks__"] + } + }, + "include": [ + "." + ], + "exclude": [ + "node_modules", + "build", + "PRNameGenerator.ts", + "cypress.config.ts" + ] +} \ No newline at end of file diff --git a/webpack.config.ts b/webpack.config.ts new file mode 100644 index 0000000..8dd20fc --- /dev/null +++ b/webpack.config.ts @@ -0,0 +1,216 @@ +import path from 'path'; +import fs from 'fs'; +import { config as envConfig } from './config'; +import webpack from 'webpack'; +import CompressionWebpackPlugin from 'compression-webpack-plugin'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; +import TerserPlugin from 'terser-webpack-plugin'; +import { WebpackManifestPlugin } from 'webpack-manifest-plugin'; +import { CleanWebpackPlugin } from 'clean-webpack-plugin'; +import ESLintPlugin from 'eslint-webpack-plugin'; +import CopyPlugin from 'copy-webpack-plugin'; +import { resolveTsAliases } from 'resolve-ts-aliases'; + +const ROOT_DIR = path.resolve(__dirname); +const resolvePath = (...args) => path.resolve(ROOT_DIR, ...args); +const BUILD_DIR = resolvePath(__dirname + '/src/server/web/dist'); +const BUILD_DIR_CONVERSION = resolvePath(__dirname + '/src/server/web/utils'); +//const { InjectManifest } = require('workbox-webpack-plugin'); +//const nodeExternals = require('webpack-node-externals'); +const alias = resolveTsAliases(path.resolve('tsconfig.json')); + +const copyPatterns = [ + { + from: `${ROOT_DIR}/public/manifest.json`, to: '', + }, + { + from: `${ROOT_DIR}/public/favicon.ico`, to: '', + }, + { + from: `${ROOT_DIR}/public/logo192.png`, to: '', + }, + { + from: `${ROOT_DIR}/public/logo512.png`, to: '', + }, + +]; + +if(fs.existsSync(`${ROOT_DIR}/public/img`)){ + copyPatterns.push({ + from: `${ROOT_DIR}/public/img`, to: 'assets/img', + }); +} + +const configReact = { + entry: { + frontend: `${ROOT_DIR}/src/frontend/index.tsx`, + }, + output: { + path: BUILD_DIR, + //filename: 'assets/app-[name]-[fullhash].js', + filename: 'assets/app-[name].js', + publicPath: envConfig.PUBLIC_URL, + }, + resolve: { + extensions: ['.js', '.jsx','.ts','.tsx', '.json'], + alias, + }, + mode: 'production', + module: { + rules: [ + { + test: /\.(js|jsx|ts|tsx)$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + }, + }, + { + test: /\.(css|sass|scss)$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'sass-loader', + ], + }, + { + test: /\.(png|jpg|jpeg|gif|svg|ico|mp4|avi|ttf|otf|eot|woff|woff2|pdf)$/, + loader: 'file-loader', + options: { + name: 'assets/[name].[ext]', + }, + }, + { + test: /\.(ttf|otf|eot|woff|woff2)$/, + loader: 'url-loader', + options: { + name: 'assets/fonts/[name].[ext]', + esModule: false, + }, + }, + ], + }, + plugins: [ + new CompressionWebpackPlugin({ + test: /\.(js|css)$/, + filename: '[path][base].gz', + }), + new MiniCssExtractPlugin({ + //filename: 'assets/[name]-[fullhash].css', + filename: 'assets/[name].css', + }), + new WebpackManifestPlugin({ + fileName: 'assets/manifest-hash.json', + publicPath: envConfig.PREFIX_URL, + }), + new CleanWebpackPlugin({ + cleanOnceBeforeBuildPatterns: [ + '**/*', + '!server/**', + ], + }), + new ESLintPlugin(), + new webpack.EnvironmentPlugin({ + ...envConfig, + }), + new CopyPlugin({ + patterns: copyPatterns + }), + /*new InjectManifest({ + swSrc: './service-worker.ts', + swDest: 'service-worker.js', + }),*/ + ], + optimization: { + minimize: true, + minimizer: [ + new CssMinimizerPlugin(), + new TerserPlugin(), + ], + splitChunks: { + chunks: 'async', + cacheGroups: { + vendors: { + name: 'vendors', + chunks: 'all', + reuseExistingChunk: true, + priority: 1, + //filename: 'assets/vendor-[name]-[fullhash].js', + filename: 'assets/vendor-[name].js', + enforce: true, + test (module, chunks){ + const name = module.nameForCondition && module.nameForCondition(); + return chunks.name !== 'vendors' && /[\\/]node_modules[\\/]/.test(name); + }, + }, + }, + }, + }, +}; + +const configTSXConversion = { + entry: { + tsxToString: `${ROOT_DIR}/src/frontend/converter/tsxToString.tsx`, + }, + output: { + path: BUILD_DIR_CONVERSION, + //filename: 'assets/app-[name]-[fullhash].js', + filename: '[name].js', + publicPath: envConfig.PUBLIC_URL, + globalObject: 'this', + }, + resolve: { + extensions: ['.js', '.jsx','.ts','.tsx', '.json'], + alias, + }, + mode: 'production', + module: { + rules: [ + { + test: /\.(js|jsx|ts|tsx)$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + }, + }, + { + test: /\.(css|sass|scss)$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'sass-loader', + ], + }, + ], + }, + plugins: [ + new CompressionWebpackPlugin({ + test: /\.(js|css)$/, + filename: '[path][base].gz', + }), + new MiniCssExtractPlugin({ + //filename: 'assets/[name]-[fullhash].css', + filename: '[name].css', + }), + new CleanWebpackPlugin({ + cleanOnceBeforeBuildPatterns: [ + '**/*', + '!server/**', + ], + }), + new ESLintPlugin(), + new webpack.EnvironmentPlugin({ + ...envConfig, + }), + ], + optimization: { + minimize: true, + minimizer: [ + new CssMinimizerPlugin(), + new TerserPlugin(), + ], + }, +}; + +export default [configReact, configTSXConversion]; diff --git a/webpack.cy.config.ts b/webpack.cy.config.ts new file mode 100644 index 0000000..9676086 --- /dev/null +++ b/webpack.cy.config.ts @@ -0,0 +1,97 @@ +import path from 'path'; +import fs from 'fs'; +import { config as envConfig } from './config'; +import webpack from 'webpack'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import { CleanWebpackPlugin } from 'clean-webpack-plugin'; +import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; +import TerserPlugin from 'terser-webpack-plugin'; +import ESLintPlugin from 'eslint-webpack-plugin'; +import CopyPlugin from 'copy-webpack-plugin'; +import { resolveTsAliases } from 'resolve-ts-aliases'; + +const alias = resolveTsAliases(path.resolve('tsconfig.json')); +const copyPatterns: {from: string, to: string}[] = []; + +const copyFromUrl = `${path.resolve(__dirname)}/public/img`; +const copyToUrl = 'assets/img'; + +if(fs.existsSync(copyFromUrl)){ + copyPatterns.push({ + from: copyFromUrl, to: copyToUrl, + }); +} + +export default { + entry: './src/frontend/components/index.tsx', + resolve: { + extensions: ['.js', '.jsx','.ts','.tsx', '.json'], + alias, + }, + mode: 'development', + output: { + path: path.resolve(__dirname, 'build'), + }, + target: 'web', + plugins: [ + new CleanWebpackPlugin(), + new MiniCssExtractPlugin({ + filename: 'index.css', + }), + new ESLintPlugin(), + new webpack.EnvironmentPlugin({ + ...envConfig, + }), + new CopyPlugin({ + patterns: copyPatterns + }), + new HtmlWebpackPlugin({ + template: path.join(__dirname, 'build', 'index.html'), + }), + new webpack.ProvidePlugin({ + React: 'react', + }), + ], + module: { + rules: [ + { + test: /\.(js|jsx|ts|tsx)$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + }, + }, + { + test: /\.(css|sass|scss)$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'sass-loader', + ], + }, + { + test: /\.(png|jpg|jpeg|gif|svg|ico|mp4|avi|ttf|otf|eot|woff|woff2|pdf)$/, + loader: 'file-loader', + options: { + name: 'assets/[name].[ext]', + }, + }, + { + test: /\.(ttf|otf|eot|woff|woff2)$/, + loader: 'url-loader', + options: { + name: 'assets/fonts/[name].[ext]', + esModule: false, + }, + }, + ] + }, + optimization: { + minimize: true, + minimizer: [ + new CssMinimizerPlugin(), + new TerserPlugin(), + ], + }, +};