diff --git a/.gitignore b/.gitignore index 0068e2c..b4c3368 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ vendor composer.lock *.phar +/dist + +views-mappings.php # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. @@ -29,4 +32,3 @@ package-lock.json npm-debug.log* yarn-debug.log* yarn-error.log* ->>>>>>> 85d7cd8 (setup ts-react) diff --git a/Documentation/react-php-integration.md b/Documentation/react-php-integration.md new file mode 100644 index 0000000..0c06574 --- /dev/null +++ b/Documentation/react-php-integration.md @@ -0,0 +1,28 @@ +# How is react used with php +This document explains how we use react and php together, +and how to create a react component that can be sent to the front end from php. + +## Folder tree of the project +```tree +. +├── ci // CI/CD related directory. +├── Documentation // Documentation directory. +├── front // React code goes here. +│ ├── views // React views goes here. +│ └── assets -> // assets goes here (images, svg etc) +├── profiles // PHP server environment profiles. +├── public // index.php goes here +└── src // php code goes here + ├── Controller + ├── Data + └── ... +``` + +we'll take a view later on each folder. + +## Compilation Constraint +We use typescript and react for the front, which requires to be transpiled to Javascript in order to be executed by browsers. +The fact that our `.tsx` components (our views) needs to be compiled to a js file to be executed does not allows us to dumbly refer to their file path in the php source code. + +## Use of ViteJS to build our react components +We use [ViteJS](https://vitejs.dev/guide/) to build the react components. diff --git a/ci/.drone.yml b/ci/.drone.yml new file mode 100644 index 0000000..7d8f9a4 --- /dev/null +++ b/ci/.drone.yml @@ -0,0 +1,47 @@ +kind: pipeline +type: docker +name: "Deploy on maxou.dev" + +volumes: + - name: server + temp: {} + +steps: + - image: node:latest + name: "build node" + volumes: &outputs + - name: server + path: /outputs + commands: + - curl -L moshell.dev/setup.sh > /tmp/moshell_setup.sh + - chmod +x /tmp/moshell_setup.sh + - echo n | /tmp/moshell_setup.sh + + - /root/.local/bin/moshell ci/build_react.msh + + - image: composer:latest + name: "prepare php" + volumes: *outputs + commands: + - mkdir -p /outputs/public + - sed -iE 's/\\/\\*PROFILE_FILE\\*\\/\\s*".*"/"profiles\\/prod-config-profile.php"/' config.php + - composer install && composer update + - rm profiles/dev-config-profile.php + - mv src config.php profiles vendor /outputs/ + + - image: eeacms/rsync:latest + name: Deliver on server + depends_on: + - "prepare php" + - "build node" + volumes: *outputs + environment: + SERVER_PRIVATE_KEY: + from_secret: SERVER_PRIVATE_KEY + commands: + - mkdir ~/.ssh + - echo "$SERVER_PRIVATE_KEY" > ~/.ssh/id_rsa + - chmod 0600 ~/.ssh + - chmod 0500 ~/.ssh/id_rsa* + - rsync -avz -e "ssh -p 80 -o 'StrictHostKeyChecking=no'" --delete /outputs/* iqball@maxou.dev:/server/nginx/IQBall/ + diff --git a/ci/build_react.msh b/ci/build_react.msh new file mode 100755 index 0000000..9bd9d52 --- /dev/null +++ b/ci/build_react.msh @@ -0,0 +1,31 @@ +#!/usr/bin/env moshell + +npm build react +mkdir -p /outputs/public + +apt update && apt install jq -y +npm install +npm run build -- --base=/IQBall/public + +// Read generated mappings from build +val result = $(jq -r 'to_entries|map(.key + " " +.value.file)|.[]' dist/manifest.json) +val mappings = $result.split('\n') + + +echo ' views-mappings.php + +while $mappings.len() > 0 { + val mapping = $mappings.pop().unwrap(); + val mapping = $mapping.split(' '); + val source_file = $mapping[0] + val build_file = $mapping[1] + echo "\t'$source_file' => '$build_file'," >> views-mappings.php +} + +echo "];" >> views-mappings.php + +chmod +r views-mappings.php + +// moshell does not supports file patterns +bash <<< "mv dist/* public/* front/assets/ /outputs/public/" +mv views-mappings.php /outputs/ diff --git a/composer.json b/composer.json index d022007..664e469 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,9 @@ "App\\": "src/" } }, + "include-path": ["src"], "require": { - "altorouter/altorouter": "1.2.0" + "altorouter/altorouter": "1.2.0", + "ext-json": "*" } } \ No newline at end of file diff --git a/config.php b/config.php index e69de29..111b11b 100644 --- a/config.php +++ b/config.php @@ -0,0 +1,22 @@ + { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/front/App.tsx b/front/App.tsx deleted file mode 100644 index 30e8f48..0000000 --- a/front/App.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import logo from './logo.svg'; -import './App.css'; - -function App() { - return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React here! ptn de merde hahahaha ! trest qzd - -
-
- ); -} - -export default App; diff --git a/public/favicon.ico b/front/assets/favicon.ico similarity index 100% rename from public/favicon.ico rename to front/assets/favicon.ico diff --git a/front/logo.svg b/front/assets/logo.svg similarity index 100% rename from front/logo.svg rename to front/assets/logo.svg diff --git a/public/logo192.png b/front/assets/logo192.png similarity index 100% rename from public/logo192.png rename to front/assets/logo192.png diff --git a/public/logo512.png b/front/assets/logo512.png similarity index 100% rename from public/logo512.png rename to front/assets/logo512.png diff --git a/front/index.css b/front/index.css deleted file mode 100644 index ec2585e..0000000 --- a/front/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/front/index.html b/front/index.html deleted file mode 100644 index d988be7..0000000 --- a/front/index.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - React App - - - - -
- - - - - - diff --git a/front/index.tsx b/front/index.tsx deleted file mode 100644 index 032464f..0000000 --- a/front/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/front/react-app-env.d.ts b/front/react-app-env.d.ts deleted file mode 100644 index 6431bc5..0000000 --- a/front/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/front/reportWebVitals.ts b/front/reportWebVitals.ts deleted file mode 100644 index 49a2a16..0000000 --- a/front/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/front/setupTests.ts b/front/setupTests.ts deleted file mode 100644 index 8f2609b..0000000 --- a/front/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// 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'; diff --git a/front/views/DisplayResults.tsx b/front/views/DisplayResults.tsx new file mode 100644 index 0000000..c9c7ed1 --- /dev/null +++ b/front/views/DisplayResults.tsx @@ -0,0 +1,24 @@ +import ReactDOM from "react-dom/client"; +import React from "react"; + + +function DisplayResults({username, password}: any) { + return ( +
+

username: {username}

+

password: {password}

+
+ ) +} + + +export function render(args: any) { + const root = ReactDOM.createRoot( + document.getElementById('root') as HTMLElement + ); + root.render( + + + + ); +} \ No newline at end of file diff --git a/front/views/SampleForm.tsx b/front/views/SampleForm.tsx new file mode 100644 index 0000000..b64650a --- /dev/null +++ b/front/views/SampleForm.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; + +function SampleForm() { + return ( +
+

Hello, this is a sample form made in react !

+
+ + + + + +
+
+ ) +} + +export function render(args: any) { + const root = ReactDOM.createRoot( + document.getElementById('root') as HTMLElement + ); + root.render( + + + + ); +} + + diff --git a/package.json b/package.json index fd6d84a..7f10a8c 100644 --- a/package.json +++ b/package.json @@ -12,15 +12,14 @@ "@types/react-dom": "^18.2.14", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-scripts": "5.0.1", "typescript": "^4.9.5", + "vite": "^4.5.0", "web-vitals": "^2.1.4" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" + "start": "vite --host", + "build": "vite build", + "test": "vite test" }, "eslintConfig": { "extends": [ @@ -39,5 +38,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.1.0" } } diff --git a/profiles/dev-config-profile.php b/profiles/dev-config-profile.php new file mode 100644 index 0000000..9d9772e --- /dev/null +++ b/profiles/dev-config-profile.php @@ -0,0 +1,8 @@ +map("GET", "/", fn() => $helloController->display()); +$router->setBasePath($basePath); + +$sampleFormController = new SampleFormController(); +$router->map("GET", "/", fn() => $sampleFormController->displayForm()); +$router->map("POST", "/result", fn() => $sampleFormController->displayResults($_POST)); $match = $router->match(); if ($match == null) { // TODO redirect to a 404 not found page instead (issue #1) + echo "page non trouvée"; header('HTTP/1.1 404 Not Found'); exit(1); } diff --git a/public/manifest.json b/public/manifest.json deleted file mode 100644 index 080d6c7..0000000 --- a/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App 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" -} diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index e9e57dc..0000000 --- a/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/src/Controller/HelloPageController.php b/src/Controller/HelloPageController.php deleted file mode 100755 index 1498afe..0000000 --- a/src/Controller/HelloPageController.php +++ /dev/null @@ -1,10 +0,0 @@ - + "> @@ -16,6 +13,13 @@ Document - + + + + \ No newline at end of file diff --git a/src/react-display.php b/src/react-display.php new file mode 100644 index 0000000..b4c6f1e --- /dev/null +++ b/src/react-display.php @@ -0,0 +1,12 @@ + { + if (fs.lstatSync(`${dirname}/${file_name}`).isFile()) { + return [[`${dirname}/${file_name}`, `${dirname}/${file_name}`]] + } else { + return resolve_entries(`${dirname}/${file_name}`) + } + }) +} + +export default defineConfig({ + root: 'front', + base: '/front', + build: { + target: 'es2021', + assetsDir: '', + outDir: "../dist", + manifest: true, + rollupOptions: { + input: Object.fromEntries(resolve_entries("front")), + preserveEntrySignatures: "allow-extension" + } + }, + plugins: [ + react() + ] +})