├── .babelrc ├── .gitignore ├── README.md ├── assets ├── Jimmypfp.jpeg ├── Kerripfp.png ├── Loganpfp.jpeg ├── Natepfp.jpg ├── Reactron_Logo.png ├── Romelopfp.jpeg ├── demo.png ├── favicon.png └── logo.png ├── controller ├── authController.js └── fsController.js ├── filesysHelpers.js ├── iframeScrape.js ├── jest.config.js ├── license ├── package.json ├── public ├── build │ ├── 08e3a5900820422b7d6a69dc1467a05f.png │ ├── 30d48bf05b4b1e448a58fc40b47ba454.jpeg │ ├── 579c456ee8ff5eabf074357513d1d580.jpeg │ ├── 8b49e0edc114dc2c2e97704d96fd9e7a.jpeg │ ├── 951105ca993206880c75c8bc07608254.png │ ├── 9738e727ed888c109ce9d88493177f5e.png │ ├── bundle.js │ ├── bundle.js.LICENSE.txt │ └── ccfd670983687647cc325767b568dc8f.jpg ├── index.html └── index.scss ├── puppeteer.js ├── routers ├── authRouter.js └── fileSysRouters.js ├── secret.html ├── server.js ├── src ├── components │ ├── App.js │ ├── AppContainer.jsx │ ├── Blurb.js │ ├── ComponentTree.jsx │ ├── DashBoard.js │ ├── Egg.js │ ├── File.jsx │ ├── Header.jsx │ ├── IndividualComponent.jsx │ ├── LandingPage.jsx │ ├── NavBar │ │ ├── FileExplorer.js │ │ ├── NavBarContainer.js │ │ ├── NavBarContainerRendered.js │ │ ├── PreviousFiles.js │ │ ├── Profile.js │ │ └── Signout.js │ ├── ReactArch.md │ ├── ReactComponent.jsx │ ├── RenderedContainer.jsx │ ├── RenderedPage.jsx │ ├── StateContainer.jsx │ ├── StateItem.jsx │ ├── Visualizer.jsx │ ├── __tests__ │ │ ├── ComponentTree.test.js │ │ ├── LandingPage.test.js │ │ ├── RenderedPage.test.js │ │ ├── Supertest.js │ │ ├── Visualizer.test.js │ │ └── __snapshots__ │ │ │ ├── ComponentTree.test.js.snap │ │ │ ├── LandingPage.test.js.snap │ │ │ ├── RenderedPage.test.js.snap │ │ │ └── Visualizer.test.js.snap │ ├── login │ │ ├── BackToSplash.js │ │ ├── Demo.js │ │ ├── Login.js │ │ ├── LoginContainer.js │ │ ├── LoginPage.js │ │ └── Logo.js │ └── splash │ │ ├── components │ │ └── TeamMemberComponent.js │ │ └── team_members.js ├── data.ts ├── index.js ├── setupTests.js ├── tree.css ├── tsx_components │ ├── App.tsx │ ├── ComponentTree.tsx │ ├── File.tsx │ ├── HeadNode.tsx │ ├── Header.tsx │ ├── LandingPage.tsx │ ├── MainContainer.tsx │ └── Node.tsx └── types │ └── AllTypes.d.ts ├── tsconfig.json ├── userInfo ├── build │ └── bundle.js ├── demo │ ├── demo │ │ ├── App.js │ │ ├── Body.js │ │ ├── Completed.js │ │ ├── Header.js │ │ ├── Task.js │ │ └── index.js │ └── index.css ├── individualComponent │ ├── build │ │ └── bundle.js │ ├── index.html │ ├── index.js │ └── style.css ├── style.css ├── test.html └── webpack.user.config.js ├── webpack.config.js ├── wiki.png └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-transform-runtime"], 3 | "presets": [ 4 | "@babel/preset-env", 5 | "@babel/react" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | package-lock.json 4 | .env 5 | datagit 6 | bundle 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reactron 2 |

3 |
4 | Reactron Logo 5 |
6 | Empowering the Development Process from Top to Bottom 7 |
8 |
9 |

10 | 11 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/oslabs-beta/reactron/pulls) 12 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/oslabs-beta/reactron/LICENSE) 13 | 14 |

A tool for the complete and total visualization of your React application

15 | 16 | [Reactron](https://reactron.io/) bolsters development by rendering three essentials parts of any project: the latest version of your application including functionality, a React Component Tree for a macroscopic view of your project, and single component rendering for a microscopic understanding. 17 | 18 | As of March 2022, Reactron is no longer an active project and cannot be accessed from reactron.io. Please feel free to continue to fork and use on your own. 19 | 20 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | ## Current Features 38 | - [x] Rendering and functionality of your total React application 39 | - [x] Comprehensive visualization of your React Component Tree 40 | - [x] Selection and rendering of specific React components 41 | - [x] Storage for users with multiple projects 42 | 43 | ## In Progress 44 | - [ ] Updating state view web-application interface 45 | - [ ] Selecting specific nodes to render specific components 46 | - [ ] Exporting updates to your VS Code 47 | - [ ] A VS Code extension for even greater developer experience 48 | 49 | ## How to Use 50 | #### [Visit Us at [Reactron.io](https://reactron.io/)] 51 | In order to utilize Reactron, you will first need to sign in with your [GitHub account](https://github.com/) 52 | ###### Otherwise view a demo of our application by pressing 'Demo!' 53 | 54 | ##### After logging in: 55 | 1. Upload your application when prompted: first your Styling File, then your Component Folder 56 | 2. Afterwards, you should see a 'Next' button pop-up which will lead to your application 57 | 4. Your screen will now feature all three visualizations of the project you uploaded 58 | 3. Select individual components you wish to see rendered and broken down into it's different parts 59 | 5. Repeat this process for any number of your components you wish to view 60 | 6. Once you're finished, logout 61 | 7. The next time you log in you will be able to pick back up where you left off 62 | 63 | ## Built With 64 | - [React](https://reactjs.org/) - Framework 65 | - [React-Tree-Graph](https://www.npmjs.com/package/react-tree-graph) - Rendering of component-tree 66 | - [TypeScript](https://www.typescriptlang.org/) - For the codebase 67 | - [Node.js](https://nodejs.org/en/) - File system, testing, core extension functionality 68 | - [Puppeteer](https://pptr.dev/) - Headless browser for react-component collection 69 | - [Jest](https://jestjs.io/) - Testing 70 | - Love ❤️ 71 | 72 | ## Contributing 73 | Reactron is currently in active development and welcomes any contributions to this product or constructive feedback towards improvements. If while using Reactron to improve your designs inspired your towards updates, please feel free to fork this repo and submit pull requests! 74 | 75 | ## Team 76 | [Jimmy Lin](https://github.com/odylic) | 77 | [Kerri Crawford](https://github.com/kerriannercrawford) | 78 | [Logan Coale](https://github.com/SteeleCoale) | 79 | [Nathaniel Armstrong](https://github.com/n8innate) | 80 | [Romelo Gilbert](https://github.com/Seymour-creates) 81 | 82 | ## License 83 | MIT - check out [license](https://github.com/oslabs-beta/reactron/blob/main/license) page for more details 84 | -------------------------------------------------------------------------------- /assets/Jimmypfp.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/Jimmypfp.jpeg -------------------------------------------------------------------------------- /assets/Kerripfp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/Kerripfp.png -------------------------------------------------------------------------------- /assets/Loganpfp.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/Loganpfp.jpeg -------------------------------------------------------------------------------- /assets/Natepfp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/Natepfp.jpg -------------------------------------------------------------------------------- /assets/Reactron_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/Reactron_Logo.png -------------------------------------------------------------------------------- /assets/Romelopfp.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/Romelopfp.jpeg -------------------------------------------------------------------------------- /assets/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/demo.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/favicon.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/assets/logo.png -------------------------------------------------------------------------------- /controller/authController.js: -------------------------------------------------------------------------------- 1 | const GitHubStrategy = require('passport-github').Strategy; 2 | const passport = require('passport'); 3 | const session = require('express-session'); 4 | const fs = require('fs'); 5 | require('dotenv').config(); 6 | 7 | const authController = {}; 8 | 9 | // session({ 10 | // secret: 'keyboard cat', 11 | // resave: false, 12 | // saveUninitialized: false, 13 | // cookie: { 14 | // httpOnly: true, 15 | // secure: false, //this would be set to true if using https 16 | // maxAge: 24 * 60 * 60 * 1000 17 | // }, 18 | // }) 19 | 20 | passport.initialize(); 21 | passport.session(); 22 | passport.serializeUser((user, cb) => { 23 | cb(null, user.id); 24 | }); 25 | passport.deserializeUser((id, cb) => { 26 | cb(null, id); 27 | }); 28 | 29 | passport.use( 30 | new GitHubStrategy( 31 | { 32 | clientID: process.env.GITHUB_CLIENT_ID, 33 | clientSecret: process.env.GITHUB_SECRET, 34 | callbackURL: 'http://localhost:3000/auth/github/callback', //will need to change this later to to reactron.io/auth/github/callback 35 | }, 36 | (accessToken, refreshToken, profile, cb) => { 37 | //this is where we'd send a datapase request to store user id? 38 | console.log(profile.username); 39 | return cb(null, profile); 40 | } 41 | ) 42 | ); 43 | 44 | authController.authenticate = (req, res, next) => { 45 | passport.authenticate('github')(req, res, next); 46 | return next(); 47 | }; 48 | 49 | authController.callback = (req, res, next) => { 50 | passport.authenticate('github', { failureRedirect: '/failure' })( 51 | req, 52 | res, 53 | next 54 | ); 55 | res.cookie('username', 'This is not connected to github yet!'); 56 | return next(); 57 | }; 58 | 59 | // authController.isAuth = (req, res, next) = { 60 | // if (req.user){ 61 | // next() 62 | // } else { 63 | 64 | // } 65 | // } 66 | 67 | authController.logout = (req, res, next) => { 68 | req.logout(); 69 | delete req.session; 70 | return next(); 71 | }; 72 | 73 | module.exports = authController; 74 | -------------------------------------------------------------------------------- /controller/fsController.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const webpack = require('webpack'); 4 | const webpackConfig = require('../userInfo/webpack.user.config'); 5 | 6 | const fsController = {}; 7 | 8 | // user will upload files: 9 | fsController.saveFiles = (req, res, next) => { 10 | // take username, project name and files from request body 11 | const { username, project, files, style } = req.body; 12 | 13 | // check if directory for username exists at userInfo/username 14 | const userDirExists = fs.existsSync( 15 | path.resolve(__dirname, `../userInfo/${username}`) 16 | ); 17 | 18 | // if not exist, create 19 | if (!userDirExists) { 20 | fs.mkdirSync(path.join(__dirname, `../userInfo/${username}`)); 21 | } 22 | 23 | // check if directory for proj exists at userInfo/projname 24 | const projDirExists = fs.existsSync( 25 | path.resolve(__dirname, `../userInfo/${username}/${project}`) 26 | ); 27 | 28 | // if not, create 29 | if (!projDirExists) { 30 | fs.mkdirSync(path.join(__dirname, `../userInfo/${username}/${project}`)); 31 | } 32 | 33 | // check if style directory for proj exists at userInfo/projname/style 34 | const styleDirExists = fs.existsSync( 35 | path.resolve(__dirname, `../userInfo/${username}/${project}/style`) 36 | ); 37 | 38 | if (!styleDirExists) { 39 | fs.mkdirSync( 40 | path.join(__dirname, `../userInfo/${username}/${project}/style`) 41 | ); 42 | } 43 | 44 | try { 45 | fs.writeFileSync( 46 | path.join( 47 | __dirname, 48 | `../userInfo/${username}/${project}/style/style.css` 49 | ), 50 | style.toString() 51 | ); 52 | } catch (err) { 53 | console.log(err); 54 | } 55 | 56 | try { 57 | fs.writeFileSync( 58 | path.join( 59 | __dirname, 60 | `../userInfo/${username}/${project}/style/style.css` 61 | ), 62 | style.toString() 63 | ); 64 | } catch (err) { 65 | console.log(err); 66 | } 67 | 68 | // for each file in array, creates file in username/project directory 69 | files.forEach((file) => { 70 | fs.writeFileSync( 71 | path.join(__dirname, `../userInfo/${username}/${project}/${file.name}`), 72 | file.contents 73 | ); 74 | }); 75 | 76 | // config object for webpack 77 | const configOptions = { 78 | ...webpackConfig, 79 | entry: { 80 | main: path.join(__dirname, `../userInfo/${username}/${project}/index.js`), 81 | }, 82 | output: { 83 | path: path.join(__dirname, `../userInfo/build`), 84 | filename: 'bundle.js', 85 | }, 86 | }; 87 | 88 | // creates webpack compiler 89 | const compiler = webpack(configOptions); 90 | 91 | // runs compiler and bundles 92 | compiler.run((err, stats) => { 93 | if (err) console.log(`There was an error: ${err}`); 94 | else { 95 | return next(); 96 | } 97 | }); 98 | res.locals.username = username; 99 | res.locals.project = project; 100 | }; 101 | 102 | 103 | fsController.individualBundle = (req, res, next) => { 104 | // config object for webpack 105 | const configOptions = { 106 | ...webpackConfig, 107 | entry: { 108 | main: path.join( 109 | __dirname, 110 | `../userInfo/${res.locals.username}/${res.locals.project}/index.js` 111 | ), 112 | }, 113 | output: { 114 | path: path.join(__dirname, `../userInfo/individualComponent/build`), 115 | filename: 'bundle.js', 116 | }, 117 | }; 118 | 119 | // creates webpack compiler 120 | const compiler = webpack(configOptions); 121 | 122 | // runs compiler and bundles 123 | compiler.run((err, stats) => { 124 | if (err) console.log(`There was an error: ${err}`); 125 | else { 126 | return next(); 127 | } 128 | }); 129 | }; 130 | 131 | fsController.demoBundle = (req, res, next) => { 132 | // config object for webpack 133 | const configOptions = { 134 | ...webpackConfig, 135 | entry: { 136 | main: path.join( 137 | __dirname, 138 | `../userInfo/demo/demo/index.js` 139 | ), 140 | }, 141 | output: { 142 | path: path.join(__dirname, `../userInfo/individualComponent/build`), 143 | filename: 'bundle.js', 144 | }, 145 | }; 146 | 147 | // creates webpack compiler 148 | const compiler = webpack(configOptions); 149 | 150 | // runs compiler and bundles 151 | compiler.run((err, stats) => { 152 | if (err) console.log(`There was an error: ${err}`); 153 | else { 154 | return next(); 155 | } 156 | }); 157 | } 158 | 159 | fsController.stylesheet = (req, res, next) => { 160 | fs.writeFileSync('./userInfo/style.css', req.body.item); 161 | fs.writeFileSync('./userInfo/individualComponent/style.css', req.body.item); 162 | return next(); 163 | }; 164 | 165 | fsController.individualComponent = (req, res, next) => { 166 | // takes file name, username and project name from request body 167 | const { name, username, project } = req.body; 168 | 169 | const createComponent = () => { 170 | // removes .js or .jsx 171 | let nameWithoutExtension = name.replace(/.jsx?/g, ''); 172 | 173 | // creates string for react component, using file name, username, and project name 174 | const reactComponent = `import React from 'react'; import ReactDOM from 'react-dom'; import ${nameWithoutExtension} from '../${username}/${project}/${name}'; ReactDOM.render(<${nameWithoutExtension} />, document.getElementById('root'))`; 175 | return reactComponent; 176 | }; 177 | 178 | // saves react string in variable file 179 | const file = createComponent(); 180 | 181 | // writes react string to index.js 182 | fs.writeFileSync( 183 | path.join(__dirname, '../userInfo/individualComponent/index.js'), 184 | file 185 | ); 186 | 187 | // config object for webpack 188 | const configOptions = { 189 | ...webpackConfig, 190 | entry: { 191 | main: path.join(__dirname, `../userInfo/individualComponent/index.js`), 192 | }, 193 | output: { 194 | path: path.join(__dirname, `../userInfo/individualComponent/build`), 195 | filename: 'bundle.js', 196 | }, 197 | }; 198 | 199 | // creates webpack compiler 200 | const compiler = webpack(configOptions); 201 | 202 | // runs compiler and bundles 203 | compiler.run((err, stats) => { 204 | if (err) console.log(`There was an error: ${err}`); 205 | else { 206 | return next(); 207 | } 208 | }); 209 | }; 210 | 211 | fsController.runDemo = (req, res, next) => { 212 | const stylesheet = fs.readFileSync( 213 | path.join(__dirname, `../userInfo/demo/index.css`), 214 | 'utf8' 215 | ); 216 | 217 | fs.writeFileSync('./userInfo/style.css', stylesheet); 218 | fs.writeFileSync('./userInfo/individualComponent/style.css', stylesheet); 219 | 220 | res.locals.username = 'demo'; 221 | res.locals.project = 'demo'; 222 | 223 | // config object for webpack 224 | const configOptions = { 225 | ...webpackConfig, 226 | entry: { 227 | main: path.join(__dirname, `../userInfo/demo/demo/index.js`), 228 | }, 229 | output: { 230 | path: path.join(__dirname, `../userInfo/build`), 231 | filename: 'bundle.js', 232 | }, 233 | }; 234 | 235 | // creates webpack compiler 236 | const compiler = webpack(configOptions); 237 | 238 | // runs compiler and bundles 239 | compiler.run((err, stats) => { 240 | if (err) console.log(`There was an error: ${err}`); 241 | else { 242 | return next(); 243 | } 244 | }); 245 | }; 246 | 247 | fsController.prevProjects = (req, res, next) => { 248 | const { username } = req.body; 249 | 250 | const userDirExists = fs.existsSync( 251 | path.resolve(__dirname, `../userInfo/${username}`) 252 | ); 253 | 254 | if (userDirExists) { 255 | const result = fs.readdirSync( 256 | path.resolve(__dirname, `../userInfo/${username}`) 257 | ); 258 | res.locals.projects = result; 259 | return next(); 260 | } 261 | 262 | res.locals.projects = []; 263 | return next(); 264 | }; 265 | 266 | fsController.prevProjectUpload = (req, res, next) => { 267 | const { projName, username } = req.body; 268 | 269 | console.log('in prev proj upload controller'); 270 | 271 | const confirmDirExists = fs.existsSync( 272 | path.resolve(__dirname, `../userInfo/${username}/${projName}`) 273 | ); 274 | 275 | if (!confirmDirExists) return res.status(400).send('Error'); 276 | 277 | const stylesheet = fs.readFileSync( 278 | path.join(__dirname, `../userInfo/${username}/${projName}/style/style.css`), 279 | 'utf8' 280 | ); 281 | 282 | fs.writeFileSync('./userInfo/style.css', stylesheet); 283 | fs.writeFileSync('./userInfo/individualComponent/style.css', stylesheet); 284 | 285 | const fileObjs = fs.readdirSync( 286 | path.join(__dirname, `../userInfo/${username}/${projName}`) 287 | ); 288 | 289 | res.locals.files = []; 290 | 291 | fileObjs.splice(fileObjs.indexOf('style'), 1); 292 | 293 | fileObjs.forEach((file) => { 294 | res.locals.files.push({ name: file }); 295 | }); 296 | 297 | res.locals.username = username; 298 | res.locals.project = projName; 299 | 300 | // config object for webpack 301 | const configOptions = { 302 | ...webpackConfig, 303 | entry: { 304 | main: path.join( 305 | __dirname, 306 | `../userInfo/${username}/${projName}/index.js` 307 | ), 308 | }, 309 | output: { 310 | path: path.join(__dirname, `../userInfo/build`), 311 | filename: 'bundle.js', 312 | }, 313 | }; 314 | 315 | // creates webpack compiler 316 | const compiler = webpack(configOptions); 317 | 318 | // runs compiler and bundles 319 | compiler.run((err, stats) => { 320 | if (err) console.log(`There was an error: ${err}`); 321 | else { 322 | return next(); 323 | } 324 | }); 325 | }; 326 | 327 | module.exports = fsController; 328 | -------------------------------------------------------------------------------- /filesysHelpers.js: -------------------------------------------------------------------------------- 1 | const fsHelpers = {}; 2 | 3 | fsHelpers.result = {}; 4 | 5 | // accepts a directory handle and returns an object with all files and directories contained within 6 | fsHelpers.directoryLogger = async (fileHandle) => { 7 | const fileObj = {}; 8 | // sets a key on fileObj equal to name of initial directory 9 | fileObj[fileHandle.name] = { 10 | handle: fileHandle, 11 | files: [], 12 | }; 13 | 14 | // intended structure of file obj 15 | // fileObj = {container: {handle: FILEHANDLE, files: [{name: something, file: something}]}} 16 | 17 | // iterates over fileHandle 18 | for await (let [name, handle] of fileHandle) { 19 | // if the current handle is a directory, is not a hidden file, and is not node modules 20 | if ( 21 | handle.kind === 'directory' && 22 | handle.name[0] !== '.' && 23 | handle.name !== 'node_modules' 24 | ) { 25 | // recursive call to directory logger 26 | const result = await fsHelpers.directoryLogger(handle); 27 | // results of recursive call pushed to files array on object 28 | fileObj[fileHandle.name].files.push(result); 29 | } // if the file is a file and the name is not DS_Store, push to files array 30 | else if (handle.kind === 'file' && handle.name !== '.DS_Store') { 31 | fileObj[fileHandle.name].files.push(handle); 32 | } 33 | } 34 | return fileObj; 35 | }; 36 | 37 | //files array is actually an object 38 | fsHelpers.fileDisplay = async (filesObject, type) => { 39 | // fileAdded is set to the value of the first key on fileObject 40 | const fileAdded = filesObject[Object.keys(filesObject)[0]].files; //gives array of keys 41 | const fileArr = []; 42 | 43 | fileAdded.forEach((elem) => { 44 | //gets first key in object 45 | if (elem.kind) { 46 | //if element has a kind it means its a regular file 47 | // these are regular files 48 | fileArr.push(elem); 49 | } else if (elem[Object.keys(elem)[0]].handle.kind) { 50 | //this checks if the object is a directory 51 | // these are directories 52 | const res = fsHelpers.fileDisplay(elem); //this returns a promise and recursively calls filedisplay on the nested directory 53 | res.then((data) => fileArr.push(...data)); 54 | } 55 | }); 56 | return fileArr; 57 | }; 58 | 59 | export default fsHelpers; 60 | -------------------------------------------------------------------------------- /iframeScrape.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | 3 | module.exports = async function getIFrameRoot(url) { 4 | console.log('inGetIFrameRoot') 5 | const browser = await puppeteer.launch({ 6 | headless: false, 7 | devTools: true, 8 | args: ['--no-sandbox'], 9 | }); 10 | const page = await browser.newPage(); 11 | 12 | // Allows puppeteer to "act" as a different user rather than the default headless browser user 13 | page.setUserAgent( 14 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' 15 | ); 16 | 17 | await page.goto(url); 18 | await page.waitForSelector('body'); 19 | 20 | const nodeData = page 21 | .evaluate(async () => { 22 | // Locates the root React node and returns 23 | const _rootNode = (() => { 24 | // Finds all children of body tag 25 | const iframeDocument = document.getElementsByTagName("iframe")[1].contentDocument; 26 | const elems = iframeDocument.querySelector('body').children; 27 | for (let el of elems) { 28 | if (el._reactRootContainer) { 29 | // Returns root React node 30 | return el._reactRootContainer._internalRoot.current; 31 | } 32 | } 33 | })(); 34 | 35 | console.log('this is the root node', _rootNode); 36 | 37 | function parentFinder(node) { 38 | if (!node.return) return; 39 | if (node.return.tag === 0 || node.return.tag === 1) { 40 | console.log(`This is the parent's name: ${node.return.type.name}`); 41 | return node.return.type.name; 42 | } else { 43 | return parentFinder(node.return); 44 | } 45 | } 46 | 47 | const objColors = [ 48 | 'cyan', 49 | 'red', 50 | 'orange', 51 | 'yellow', 52 | 'green', 53 | 'blue', 54 | 'indigo', 55 | 'violet', 56 | 'orange', 57 | ]; 58 | 59 | // Math.floor(Math.random() * objColors.length) 60 | function findColor() { 61 | // return objColors[Math.floor(Math.random() * objColors.length)]; 62 | return objColors[5]; 63 | } 64 | 65 | let rootObj; 66 | 67 | // class Node - corresponds to shape required for react tree graph 68 | class Node { 69 | constructor(name, parent) { 70 | (this.name = name), 71 | (this.parent = parent), 72 | (this.children = []), 73 | (this.color = findColor()), 74 | (this.pathProps = { className: findColor() }), 75 | (this.textProps = { x: -25, y: 25 }); 76 | } 77 | } 78 | 79 | // Requisite for obtaining root node's name 80 | // Ends up being an array with react component objects that point to their parent 81 | const treeNodes = [new Node('App', null)]; 82 | 83 | const state = []; 84 | 85 | // Traverses react fiber nodes similar to a linked list 86 | function fiberFinder(node) { 87 | if (node.sibling !== null) { 88 | // If it has a sibling, and the sibling has a type property with a name, it is a React component 89 | if (node.sibling.type.name) { 90 | const parent = parentFinder(node.sibling); 91 | treeNodes.push(new Node(node.sibling.type.name, parent)); 92 | console.log('here'); 93 | } 94 | fiberFinder(node.sibling); 95 | } 96 | if (node.child !== null) { 97 | // If it has a child, and the child has a type property with a name, it is a React component 98 | if (node.child.type.name) { 99 | const parent = parentFinder(node.child); 100 | treeNodes.push(new Node(node.child.type.name, parent)); 101 | } 102 | fiberFinder(node.child); 103 | } 104 | } 105 | fiberFinder(_rootNode); 106 | 107 | console.log(state); 108 | 109 | // App 110 | // - Header 111 | // - Nav 112 | for (let i = 0; i < treeNodes.length; i += 1) { 113 | // if node parent prop exist, assign node name to child of parent property 114 | if (treeNodes[i].parent) { 115 | // Header -> App 116 | const parent = treeNodes[i].parent; 117 | // search through array to find elem in array where that parent val exists 118 | // if treeNodes[j] === App, push Header to App's children array 119 | for (let j = 0; j < treeNodes.length; j += 1) { 120 | if (treeNodes[j].name === parent) { 121 | treeNodes[j].children.push(treeNodes[i]); 122 | break; 123 | } 124 | } 125 | } 126 | } 127 | 128 | rootObj = treeNodes[0]; 129 | console.log(rootObj); 130 | 131 | return rootObj; 132 | }) 133 | .catch((err) => { 134 | console.log(err); 135 | }); 136 | return nodeData; 137 | }; 138 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | moduleNameMapper: { 4 | '\\.(css|less|scss|sss|styl)$': '/node_modules/jest-css-modules', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 OSLabs Beta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactron", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "nodemon --ignore userInfo server.js", 7 | "build": "webpack", 8 | "test": "jest --verbose" 9 | }, 10 | "dependencies": { 11 | "@babel/core": "^7.14.2", 12 | "@babel/plugin-transform-runtime": "^7.14.2", 13 | "@babel/preset-react": "^7.13.13", 14 | "@babel/runtime": "^7.14.0", 15 | "@testing-library/jest-dom": "^5.11.4", 16 | "@testing-library/react": "^11.1.0", 17 | "@testing-library/user-event": "^12.1.10", 18 | "axios": "^0.21.1", 19 | "babel-loader": "^8.2.2", 20 | "body-parser": "^1.19.0", 21 | "cookie-parser": "^1.4.5", 22 | "css-loader": "^5.2.4", 23 | "dotenv": "^9.0.2", 24 | "express": "^4.17.1", 25 | "fs": "0.0.1-security", 26 | "jest": "^26.6.3", 27 | "jest-css-modules": "^2.1.0", 28 | "js-cookie": "^2.2.1", 29 | "node-sass": "^6.0.0", 30 | "nodemon": "^2.0.7", 31 | "react": "^17.0.2", 32 | "react-dom": "^17.0.2", 33 | "react-icons": "^4.2.0", 34 | "react-router-dom": "^5.2.0", 35 | "react-scripts": "4.0.3", 36 | "react-test-renderer": "^17.0.2", 37 | "react-tree-graph": "^6.0.0", 38 | "sass-loader": "^11.1.1", 39 | "style-loader": "^2.0.0", 40 | "supertest": "^6.1.3", 41 | "url-loader": "^4.1.1", 42 | "web-vitals": "^1.0.1", 43 | "webpack": "^5.37.1", 44 | "webpack-cli": "^4.7.0" 45 | }, 46 | "devDependencies": { 47 | "@babel/preset-env": "^7.14.2", 48 | "@types/react": "^17.0.8", 49 | "@typescript-eslint/eslint-plugin": "^4.23.0", 50 | "@typescript-eslint/parser": "^4.23.0", 51 | "eslint": "^7.26.0", 52 | "eslint-config-airbnb": "^18.2.1", 53 | "eslint-plugin-import": "^2.23.1", 54 | "eslint-plugin-jsx-a11y": "^6.4.1", 55 | "eslint-plugin-react": "^7.23.2", 56 | "eslint-plugin-react-hooks": "^4.2.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /public/build/08e3a5900820422b7d6a69dc1467a05f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/public/build/08e3a5900820422b7d6a69dc1467a05f.png -------------------------------------------------------------------------------- /public/build/30d48bf05b4b1e448a58fc40b47ba454.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/public/build/30d48bf05b4b1e448a58fc40b47ba454.jpeg -------------------------------------------------------------------------------- /public/build/579c456ee8ff5eabf074357513d1d580.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/public/build/579c456ee8ff5eabf074357513d1d580.jpeg -------------------------------------------------------------------------------- /public/build/8b49e0edc114dc2c2e97704d96fd9e7a.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/public/build/8b49e0edc114dc2c2e97704d96fd9e7a.jpeg -------------------------------------------------------------------------------- /public/build/951105ca993206880c75c8bc07608254.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/public/build/951105ca993206880c75c8bc07608254.png -------------------------------------------------------------------------------- /public/build/9738e727ed888c109ce9d88493177f5e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/public/build/9738e727ed888c109ce9d88493177f5e.png -------------------------------------------------------------------------------- /public/build/bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /*! 8 | * JavaScript Cookie v2.2.1 9 | * https://github.com/js-cookie/js-cookie 10 | * 11 | * Copyright 2006, 2015 Klaus Hartl & Fagner Brack 12 | * Released under the MIT license 13 | */ 14 | 15 | /** @license React v0.20.2 16 | * scheduler.production.min.js 17 | * 18 | * Copyright (c) Facebook, Inc. and its affiliates. 19 | * 20 | * This source code is licensed under the MIT license found in the 21 | * LICENSE file in the root directory of this source tree. 22 | */ 23 | 24 | /** @license React v17.0.2 25 | * react-dom.production.min.js 26 | * 27 | * Copyright (c) Facebook, Inc. and its affiliates. 28 | * 29 | * This source code is licensed under the MIT license found in the 30 | * LICENSE file in the root directory of this source tree. 31 | */ 32 | 33 | /** @license React v17.0.2 34 | * react.production.min.js 35 | * 36 | * Copyright (c) Facebook, Inc. and its affiliates. 37 | * 38 | * This source code is licensed under the MIT license found in the 39 | * LICENSE file in the root directory of this source tree. 40 | */ 41 | -------------------------------------------------------------------------------- /public/build/ccfd670983687647cc325767b568dc8f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/reactron/ce10991e644e05d3e5600e424d9df437b2aa648b/public/build/ccfd670983687647cc325767b568dc8f.jpg -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Reactron 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/index.scss: -------------------------------------------------------------------------------- 1 | $background: rgba(31, 31, 31, 0.986); 2 | $header: rgba(119, 172, 183, 0.76); 3 | $pfont: white; 4 | $button-background: #1d3e53; 5 | $button-text: white; 6 | $button-hover: #1d3e53ec; 7 | $button-text-hover: grey; 8 | 9 | * { 10 | margin: 0; 11 | padding: 0; 12 | box-sizing: border-box; 13 | // overflow: auto; 14 | } 15 | 16 | body { 17 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 18 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 19 | sans-serif; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | background-color: $background; 23 | height: 100vh; 24 | width: 100vw; 25 | } 26 | 27 | // code { 28 | // font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 29 | // monospace; 30 | // } 31 | 32 | /* MAIN CONTAINER */ 33 | .mainContainer { 34 | display: grid; 35 | color: $pfont; 36 | background-color: $background; 37 | } 38 | 39 | /* LANDING PAGE */ 40 | .landingPage { 41 | height: 100vh; 42 | width: 100vw; 43 | display: grid; 44 | grid: repeat(12, 1fr) / repeat(12, 1fr); 45 | grid-gap: 15px; 46 | 47 | .LandingPageDashboard { 48 | grid-area: 1/1/13/13; 49 | display: grid; 50 | grid: repeat(12, 1fr) / repeat(12, 1fr); 51 | } 52 | 53 | .header { 54 | display: grid; 55 | grid-area: 1/3/3/13; 56 | 57 | h1 { 58 | font-size: 100px; 59 | text-align: center; 60 | color: $header !important; 61 | } 62 | } 63 | 64 | .PreviousFilesButton { 65 | width: 14em; 66 | color: black; 67 | background-color: white; 68 | border-radius: 5px; 69 | border: none; 70 | height: 30px; 71 | margin-bottom: 5px; 72 | margin-right: 40px; 73 | 74 | &:hover { 75 | background-color: rgb(214, 214, 214); 76 | } 77 | } 78 | 79 | .PreviousFiles { 80 | grid-area: 3/1/12/3; 81 | display: flex; 82 | flex-flow: column; 83 | z-index: 2; 84 | padding-left: 10px; 85 | 86 | h2 { 87 | margin-right: 10px; 88 | } 89 | } 90 | 91 | .instructions { 92 | display: grid; 93 | grid-area: 3/4/5/12; 94 | text-align: center; 95 | place-items: center; 96 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 97 | border-radius: 5px; 98 | } 99 | 100 | // .staticInstr { 101 | // display: grid; 102 | // grid-area: 5/4/7/8; 103 | // place-items: center; 104 | // box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 105 | // border-radius: 5px; 106 | // } 107 | 108 | .staticFiles { 109 | display: grid; 110 | grid-area: 5/4/9/8; 111 | place-items: center; 112 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 113 | border-radius: 5px; 114 | } 115 | 116 | // .componentInstr { 117 | // display: grid; 118 | // grid-area: 5/8/7/12; 119 | // place-items: center; 120 | // box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 121 | // } 122 | .componentFiles { 123 | display: grid; 124 | grid-area: 5/8/9/12; 125 | place-items: center; 126 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 127 | border-radius: 5px; 128 | } 129 | 130 | .next { 131 | display: grid; 132 | grid-area: 9/5/12/11; 133 | text-align: center; 134 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 135 | padding: 20px; 136 | border-radius: 5px; 137 | 138 | input { 139 | color: black; 140 | width: 175px; 141 | height: 40px; 142 | padding: 5px; 143 | border: none; 144 | border-radius: 5px; 145 | } 146 | } 147 | 148 | .button { 149 | padding: 10px; 150 | width: 120px; 151 | border-radius: 8%; 152 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); 153 | border: none; 154 | background-color: $button-background; 155 | color: $button-text; 156 | margin-top: 10px; 157 | } 158 | 159 | .button:hover { 160 | background-color: $button-hover; 161 | color: $button-text-hover; 162 | } 163 | 164 | .button:active { 165 | background-color: $button-hover; 166 | transform: translateY(4px); 167 | } 168 | } 169 | 170 | /* RENDERED PAGE */ 171 | .renderedPage { 172 | min-width: 1000px; 173 | display: grid; 174 | grid: repeat(12, 1fr) / repeat(12, 1fr); 175 | 176 | .FileExplorerButton { 177 | width: 15em; 178 | color: black; 179 | background-color: white; 180 | border-radius: 5px; 181 | border: none; 182 | height: 30px; 183 | margin-bottom: 5px; 184 | margin-right: 40px; 185 | font-weight: bold; 186 | 187 | &:hover { 188 | background-color: rgb(214, 214, 214); 189 | } 190 | } 191 | 192 | .ScrollableRenderedContainer { 193 | grid-area: 1/3/13/13; 194 | max-height: 800px; 195 | overflow: scroll; 196 | } 197 | 198 | .header { 199 | display: grid; 200 | grid-area: 1/3/3/13; 201 | 202 | h1 { 203 | font-size: 100px; 204 | text-align: center; 205 | color: $header !important; 206 | } 207 | } 208 | 209 | .FileExplorer { 210 | grid-area: 3/1/12/3; 211 | display: flex; 212 | flex-flow: column; 213 | padding: 10px; 214 | } 215 | 216 | .DashBoard { 217 | grid-area: 2/4/13/13; 218 | display: grid; 219 | grid: repeat(12, 1fr) / repeat(12, 1fr); 220 | grid-gap: 20px; 221 | padding: 20px; 222 | overflow: scroll; 223 | max-height: 1200px; 224 | z-index: 0; 225 | 226 | h2 { 227 | text-align: center; 228 | font-size: 30px; 229 | } 230 | 231 | .IndividualComponent { 232 | grid-area: 1/1/7/7; 233 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 234 | } 235 | .visualizer { 236 | grid-area: 7/1/13/7; 237 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 238 | } 239 | .componentTree { 240 | grid-area: 7/7/13/13; 241 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 242 | } 243 | .Blurb { 244 | grid-area: 1/7/7/13; 245 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 246 | padding: 15px; 247 | position: relative; 248 | display: grid; 249 | place-items: center; 250 | } 251 | } 252 | } 253 | 254 | .Blurb-Container { 255 | font-size: 20px; 256 | } 257 | 258 | .BlurbComponentlabel { 259 | background-color: rgba(0, 0, 0, 0.2); 260 | width: 100%; 261 | height: auto; 262 | position: absolute; 263 | top: 0px; 264 | } 265 | 266 | // .container { 267 | // display: grid; 268 | // grid-template: 1fr/ 1fr 1fr; 269 | // background-color: $container-background; 270 | // border: 1px solid black; 271 | // } 272 | 273 | /* COMPONENT TREE */ 274 | // .componentTree { 275 | // display: grid; 276 | // grid-column: 1 / span 1; 277 | // min-height: 700px; 278 | // // border: 2px solid blue; 279 | // margin-top: 20px; 280 | // } 281 | 282 | .treeGraph { 283 | //margin-top: 15vh; 284 | // text { 285 | // writing-mode: vertical-lr; 286 | // text-orientation: mixed; 287 | // } 288 | } 289 | 290 | .node { 291 | color: white; 292 | } 293 | 294 | /* VISUALIZER */ 295 | // .visualizer { 296 | // grid-column: 2 / span 1; 297 | // // border: 2px solid red; 298 | // } 299 | 300 | /* STATE CONTAINER */ 301 | .stateContainer { 302 | // color: red; 303 | } 304 | 305 | /* STATE ITEM */ 306 | .stateItem { 307 | // color: black; 308 | } 309 | 310 | /* RENDERED CONTAINER */ 311 | .renderedContainer { 312 | // color: red; 313 | } 314 | 315 | /* REACT COMPONENT RENDERED */ 316 | .reactComponent { 317 | // color: black; 318 | } 319 | 320 | iframe { 321 | border: none; 322 | width: 100%; 323 | height: 500px; 324 | } 325 | 326 | // jimmy css 327 | 328 | * { 329 | margin: 0; 330 | padding: 0; 331 | box-sizing: border-box; 332 | color: white; 333 | } 334 | 335 | body { 336 | // overflow: auto; 337 | // display: flex; 338 | // flex-direction: column; 339 | // align-items: center; 340 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 341 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 342 | sans-serif; 343 | -webkit-font-smoothing: antialiased; 344 | -moz-osx-font-smoothing: grayscale; 345 | /* lighter option */ 346 | /* background-color: rgba(226, 226, 226, 0.103); */ 347 | /* dark option */ 348 | background-color: rgba(31, 31, 31, 0.986); 349 | } 350 | 351 | #root { 352 | /* border: #68828a solid 15px; */ 353 | // min-height: 100vh; 354 | // height: auto; 355 | /* width: 80%; */ 356 | // width: clamp(200px, 80%, 80%); 357 | 358 | /* display: grid; 359 | grid-template-areas: 360 | "head head"; */ 361 | /* "nav main" 362 | "nav foot"; */ 363 | // grid-template-columns: 1fr; 364 | } 365 | 366 | #main-container-div { 367 | /* grid-area: head; */ 368 | } 369 | 370 | .header { 371 | /* box-shadow: 0px 8px 24px 0px rgb(149, 157, 165, 0.411); */ 372 | grid-area: header; 373 | /* background-color: #282c34; */ 374 | /* min-height: 100vh; */ 375 | /* display: flex; 376 | flex-direction: column; 377 | align-items: center; 378 | justify-content: center; 379 | font-size: calc(10px + 2vmin); 380 | color: white; */ 381 | margin-bottom: 100px; 382 | } 383 | 384 | .main { 385 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 386 | grid-area: main; 387 | 388 | padding-top: 50px; 389 | display: flex; 390 | justify-content: center; 391 | flex-direction: column; 392 | } 393 | 394 | .footer { 395 | border: #a1e0f1 solid 5px; 396 | grid-area: footer; 397 | } 398 | 399 | .App-link { 400 | color: #61dafb; 401 | } 402 | 403 | .NavBar { 404 | display: grid; 405 | grid-template-columns: 1fr 1fr; 406 | grid-template-areas: 'team_link website_link'; 407 | place-items: center; 408 | } 409 | 410 | .NavBar > a { 411 | text-decoration: none; 412 | font-size: 30px; 413 | font-weight: bold; 414 | background-color: rgba(102, 0, 0, 0.678); 415 | width: 200px; 416 | border-radius: 5px; 417 | padding: 8px 0; 418 | } 419 | 420 | .NavBar > a:hover { 421 | color: grey; 422 | background-color: rgba(100, 0, 0, 0.897); 423 | cursor: pointer; 424 | } 425 | 426 | .website_link { 427 | grid-area: 'website_link'; 428 | } 429 | 430 | .team_link { 431 | grid-area: 'team_link'; 432 | } 433 | 434 | .AboutReactron { 435 | /* border: 1px solid blue; */ 436 | display: grid; 437 | place-items: center; 438 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 439 | border-radius: 5px; 440 | margin: 20px 0; 441 | padding: 70px; 442 | } 443 | 444 | .GifDisplayDiv { 445 | /* border: 1px solid red; */ 446 | /* height: 200px; */ 447 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 448 | border-radius: 5px; 449 | margin: 20px 0; 450 | } 451 | 452 | img { 453 | width: 100%; 454 | } 455 | 456 | .TeamMembersWrapper > h2 { 457 | padding: 15px; 458 | } 459 | 460 | .TeamMemberComponentDiv { 461 | padding: 3px; 462 | border-radius: 7px; 463 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 464 | background-color: rgba(31, 31, 31, 0.603); 465 | margin: 3px; 466 | } 467 | 468 | .TeamMemberComponentDiv > a { 469 | text-decoration: none; 470 | color: white; 471 | font-size: 1.4em; 472 | font-weight: bold; 473 | padding: 10px; 474 | } 475 | 476 | .TeamMemberComponentDiv > a:hover { 477 | color: grey; 478 | } 479 | 480 | .TeamMembersDiv { 481 | /* border: 1px solid black; */ 482 | display: grid; 483 | grid-template: 1fr/ 1fr 1fr 1fr 1fr 1fr; 484 | place-items: center; 485 | border-radius: 5px; 486 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 487 | background-color: rgba(20, 20, 20, 0.699); 488 | } 489 | 490 | .Wrapper { 491 | display: flex; 492 | justify-content: center; 493 | } 494 | 495 | .MainContainerDiv { 496 | max-width: 850px; 497 | text-align: center; 498 | } 499 | 500 | .profileImage { 501 | border-radius: 100%; 502 | height: 150px; 503 | width: 150px; 504 | padding: 5px; 505 | } 506 | 507 | /* mobile */ 508 | @media (max-width: 650px) { 509 | .TeamMembersDiv { 510 | grid-template: 511 | 1fr 512 | 1fr/ 1fr 1fr; 513 | height: 800px; 514 | } 515 | } 516 | 517 | @media (max-width: 450px) { 518 | .TeamMembersDiv { 519 | display: flex; 520 | flex-flow: column; 521 | height: 1200px; 522 | } 523 | } 524 | 525 | // @media (min-width: 850px) { 526 | 527 | // .MainContainerDiv{ 528 | // max-width: 1500px; 529 | // } 530 | 531 | // .profileImage { 532 | // height: 270px; 533 | // width: 270px; 534 | // } 535 | 536 | // h2 { 537 | // font-size: 70px; 538 | // margin-bottom: 40px; 539 | // } 540 | 541 | // p { 542 | // font-size: 30px; 543 | // } 544 | 545 | // .GifDisplayDiv { 546 | // margin-top: 50px; 547 | // margin-bottom: 400px; 548 | // padding: 20px 0; 549 | // } 550 | 551 | // .TeamMembersWrapper { 552 | // margin-bottom: 300px; 553 | // } 554 | 555 | // .AboutReactron { 556 | // margin-bottom: 200px; 557 | // } 558 | 559 | // .header { 560 | // margin-bottom: 300px; 561 | // } 562 | 563 | // .NavBar > a{ 564 | // font-size: 50px; 565 | // width: 300px; 566 | // } 567 | 568 | // } 569 | 570 | .LoginPageWrapper { 571 | display: grid; 572 | place-items: center; 573 | 574 | .LoginPage { 575 | display: grid; 576 | grid: repeat(12, 1fr) / repeat(12, 1fr); 577 | max-width: 1200px; 578 | height: 100vh; 579 | width: 100vw; 580 | 581 | .LoginContainer { 582 | grid-area: 3/4 / span 7 / span 6; 583 | box-shadow: 0px 8px 24px 0px rgba(149, 157, 165, 0.411); 584 | } 585 | 586 | .Login { 587 | grid-area: 8/5 / span 1 / span 2; 588 | border: 1px solid white; 589 | display: grid; 590 | place-items: center; 591 | margin: 10px; 592 | border-radius: 5px; 593 | 594 | a { 595 | text-decoration: none; 596 | } 597 | 598 | a:hover { 599 | color: grey; 600 | } 601 | } 602 | 603 | .Demo { 604 | grid-area: 8/7 / span 1 / span 2; 605 | border: 1px solid white; 606 | display: grid; 607 | place-items: center; 608 | margin: 10px; 609 | border-radius: 5px; 610 | 611 | a:hover { 612 | cursor: pointer; 613 | color: grey; 614 | } 615 | } 616 | 617 | .Login .Demo { 618 | background-color: grey; 619 | } 620 | 621 | .BackToSplash { 622 | grid-area: 3/4 / span 1 / span 2; 623 | margin: 10px; 624 | } 625 | 626 | .Logo { 627 | grid-area: 4/5/7/9; 628 | img { 629 | width: 100%; 630 | } 631 | } 632 | } 633 | } 634 | 635 | .NavBarContainer { 636 | grid-area: 1/1 / span 12 / span 2; 637 | border-right: 1px solid white; 638 | display: grid; 639 | grid: repeat(12, 1fr) / 1fr; 640 | max-height: 100vh; 641 | position: fixed; 642 | z-index: 1; 643 | overflow: hidden; 644 | background-color: $background; 645 | 646 | .Profile { 647 | grid-area: 1/1 / span 4 / span 1; 648 | height: 100px; 649 | } 650 | 651 | .Profile h2 { 652 | padding: 10px; 653 | font-size: 16px; 654 | } 655 | 656 | .Signout { 657 | grid-area: 12/1 / span 1 / span 1; 658 | text-align: center; 659 | padding: 30px 0; 660 | border-top: 1px solid white; 661 | 662 | a:hover { 663 | color: grey; 664 | } 665 | } 666 | } 667 | 668 | @media (min-width: 1500px) { 669 | .renderedPage .DashBoard { 670 | grid-area: 2/3/13/13; 671 | } 672 | 673 | .LandingPageDashboard { 674 | grid-column: 1/13 !important; 675 | } 676 | } 677 | 678 | @media (max-width: 1000px) { 679 | .DashBoard { 680 | grid-column: 4/13 !important; 681 | } 682 | } 683 | 684 | @media (max-width: 850px) { 685 | .LandingPageDashboard { 686 | grid-column: 5/13 !important; 687 | } 688 | 689 | .DashBoard { 690 | grid-column: 5/13 !important; 691 | } 692 | 693 | } 694 | 695 | @media (max-width: 450px) { 696 | .LandingPageDashboard { 697 | grid-column: 6/13 !important; 698 | } 699 | } 700 | 701 | .refreshbutton { 702 | width: 15em; 703 | color: black; 704 | background-color: white; 705 | border-radius: 5px; 706 | border: none; 707 | height: 30px; 708 | margin-bottom: 5px; 709 | margin-right: 40px; 710 | font-weight: bold; 711 | 712 | &:hover { 713 | background-color: rgb(214, 214, 214); 714 | } 715 | } 716 | 717 | .SignOutButton { 718 | width: 14em; 719 | color: black; 720 | background-color: white; 721 | border-radius: 5px; 722 | border: none; 723 | height: 30px; 724 | margin-bottom: 5px; 725 | margin-right: 40px; 726 | 727 | a { 728 | text-decoration: none; 729 | color: black; 730 | } 731 | 732 | &:hover { 733 | background-color: rgb(214, 214, 214); 734 | } 735 | } -------------------------------------------------------------------------------- /puppeteer.js: -------------------------------------------------------------------------------- 1 | // const puppeteer = require('puppeteer'); 2 | 3 | // module.exports = async function getRoot(url) { 4 | // const browser = await puppeteer.launch({ 5 | // headless: true, 6 | // devTools: true, 7 | // args: ['--no-sandbox'], 8 | // }); 9 | // const page = await browser.newPage(); 10 | 11 | // // Allows puppeteer to "act" as a different user rather than the default headless browser user 12 | // page.setUserAgent( 13 | // 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' 14 | // ); 15 | 16 | // await page.goto(url); 17 | // await page.waitForSelector('body'); 18 | 19 | // const nodeData = page 20 | // .evaluate(async () => { 21 | // // Locates the root React node and returns 22 | // const _rootNode = (() => { 23 | // // Finds all children of body tag 24 | // const elems = document.querySelector('body').children; 25 | // for (let el of elems) { 26 | // if (el._reactRootContainer) { 27 | // // Returns root React node 28 | // return el._reactRootContainer._internalRoot.current; 29 | // } 30 | // } 31 | // })(); 32 | 33 | // console.log('this is the root node', _rootNode); 34 | 35 | // function parentFinder(node) { 36 | // if (!node.return) return; 37 | // if (node.return.tag === 0 || node.return.tag === 1) { 38 | // console.log(`This is the parent's name: ${node.return.type.name}`); 39 | // return node.return.type.name; 40 | // } else { 41 | // return parentFinder(node.return); 42 | // } 43 | // } 44 | 45 | // const objColors = [ 46 | // 'cyan', 47 | // 'red', 48 | // 'orange', 49 | // 'yellow', 50 | // 'green', 51 | // 'blue', 52 | // 'indigo', 53 | // 'violet', 54 | // 'orange', 55 | // ]; 56 | 57 | // // Math.floor(Math.random() * objColors.length) 58 | // function findColor() { 59 | // // return objColors[Math.floor(Math.random() * objColors.length)]; 60 | // return objColors[5]; 61 | // } 62 | 63 | // let rootObj; 64 | 65 | // // class Node - corresponds to shape required for react tree graph 66 | // class Node { 67 | // constructor(name, parent) { 68 | // (this.name = name), 69 | // (this.parent = parent), 70 | // (this.children = []), 71 | // (this.color = findColor()), 72 | // (this.pathProps = { className: findColor() }), 73 | // (this.textProps = { x: -25, y: 25 }); 74 | // } 75 | // } 76 | 77 | // // Requisite for obtaining root node's name 78 | // // Ends up being an array with react component objects that point to their parent 79 | // const treeNodes = [new Node('App', null)]; 80 | 81 | // const state = []; 82 | 83 | // // Traverses react fiber nodes similar to a linked list 84 | // function fiberFinder(node) { 85 | // if (node.sibling !== null) { 86 | // // If it has a sibling, and the sibling has a type property with a name, it is a React component 87 | // if (node.sibling.type.name) { 88 | // const parent = parentFinder(node.sibling); 89 | // treeNodes.push(new Node(node.sibling.type.name, parent)); 90 | // console.log('here'); 91 | // } 92 | // fiberFinder(node.sibling); 93 | // } 94 | // if (node.child !== null) { 95 | // // If it has a child, and the child has a type property with a name, it is a React component 96 | // if (node.child.type.name) { 97 | // const parent = parentFinder(node.child); 98 | // treeNodes.push(new Node(node.child.type.name, parent)); 99 | // } 100 | // fiberFinder(node.child); 101 | // } 102 | // } 103 | // fiberFinder(_rootNode); 104 | 105 | // console.log(state); 106 | 107 | // // App 108 | // // - Header 109 | // // - Nav 110 | // for (let i = 0; i < treeNodes.length; i += 1) { 111 | // // if node parent prop exist, assign node name to child of parent property 112 | // if (treeNodes[i].parent) { 113 | // // Header -> App 114 | // const parent = treeNodes[i].parent; 115 | // // search through array to find elem in array where that parent val exists 116 | // // if treeNodes[j] === App, push Header to App's children array 117 | // for (let j = 0; j < treeNodes.length; j += 1) { 118 | // if (treeNodes[j].name === parent) { 119 | // treeNodes[j].children.push(treeNodes[i]); 120 | // break; 121 | // } 122 | // } 123 | // } 124 | // } 125 | 126 | // rootObj = treeNodes[0]; 127 | // console.log(rootObj); 128 | 129 | // return rootObj; 130 | // }) 131 | // .catch((err) => { 132 | // console.log(err); 133 | // }); 134 | // return nodeData; 135 | // }; 136 | -------------------------------------------------------------------------------- /routers/authRouter.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const authController = require('../controller/authController.js'); 4 | require('dotenv').config(); 5 | 6 | router.get('/github', authController.authenticate, (req, res) => { 7 | // return res.status(200) 8 | }); 9 | 10 | router.get('/github/callback', authController.callback, (req, res) => { 11 | res.redirect('/'); 12 | }); 13 | 14 | router.get('/logout', authController.logout, (req, res) => { 15 | return res.redirect('/'); 16 | }); 17 | 18 | router.get('/newauth', (req, res) => { 19 | res.redirect( 20 | `https://github.com/login/oauth/authorize?client_id=${process.env.GITHUB_CLIENT_ID}` 21 | ); 22 | }); 23 | 24 | module.exports = router; 25 | -------------------------------------------------------------------------------- /routers/fileSysRouters.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const fsController = require('../controller/fsController.js'); 4 | 5 | router.post( 6 | '/upload', 7 | fsController.saveFiles, 8 | fsController.individualBundle, 9 | (req, res) => { 10 | return res.status(200).send('OK'); 11 | } 12 | ); 13 | 14 | router.post('/stylesheet', fsController.stylesheet, (req, res) => { 15 | return res.status(200).send('OK'); 16 | }); 17 | 18 | 19 | router.post('/individual', fsController.individualComponent, (req, res) => { 20 | return res.status(200).send('OK'); 21 | }); 22 | 23 | router.get( 24 | '/demo', 25 | fsController.runDemo, 26 | fsController.demoBundle, 27 | (req, res) => { 28 | res.status(200); 29 | } 30 | ); 31 | 32 | router.post('/prevprojs', fsController.prevProjects, (req, res) => { 33 | res.status(200).json(res.locals.projects); 34 | }); 35 | 36 | router.post( 37 | '/prevupload', 38 | fsController.prevProjectUpload, 39 | fsController.individualBundle, 40 | (req, res) => { 41 | res.status(200).json(res.locals.files); 42 | } 43 | ); 44 | 45 | module.exports = router; 46 | -------------------------------------------------------------------------------- /secret.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | secretReactron 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({path: __dirname + '/.env'}); 2 | const axios = require('axios'); 3 | const express = require('express'); 4 | const path = require('path'); 5 | const cookieParser = require('cookie-parser'); 6 | 7 | const fileSysRouters = require('./routers/fileSysRouters.js'); 8 | // const authRouter = require('./routers/authRouter.js'); 9 | 10 | // For Main Server 11 | const MAIN_PORT = 3000; 12 | const mainApp = express(); 13 | 14 | // const getRoot = require('./puppeteer.js'); 15 | 16 | // getRoot('http://localhost:5000'); 17 | 18 | // MAIN APP 19 | mainApp.use(express.json({ limit: '50mb', extended: true })); 20 | mainApp.use(express.static(__dirname + '/public')); 21 | mainApp.use(cookieParser()); 22 | 23 | mainApp.get('/', (req, res) => { 24 | res.send(200); 25 | }); 26 | 27 | mainApp.get('/secret', (req, res) => { 28 | res.sendFile(path.join(__dirname, './secret.html')); 29 | }); 30 | 31 | mainApp.get('/secret/build', (req, res) => { 32 | res.sendFile(path.join(__dirname, './userInfo/build/bundle.js')); 33 | }); 34 | mainApp.get('/secret/style', (req, res) => { 35 | res.sendFile(path.join(__dirname, './userInfo/style.css')); 36 | }); 37 | 38 | mainApp.get('/individual', (req, res) => { 39 | res.sendFile(path.join(__dirname, './userInfo/individualComponent/index.html')) 40 | }) 41 | 42 | mainApp.get('/individual/build', (req, res) => { 43 | res.sendFile(path.join(__dirname, './userInfo/individualComponent/build/bundle.js')) 44 | }) 45 | 46 | mainApp.get('/individual/style', (req, res) => { 47 | res.sendFile(path.join(__dirname, './userInfo/individualComponent/style.css')) 48 | }) 49 | 50 | mainApp.use('/fs', fileSysRouters); 51 | // mainApp.use('/auth', authRouter); 52 | 53 | let token; 54 | 55 | mainApp.get('/auth', (req, res) => { 56 | res.redirect( 57 | `https://github.com/login/oauth/authorize?client_id=${process.env.GITHUB_CLIENT_ID}` 58 | ); 59 | }); 60 | 61 | mainApp.get('/oauth-callback', (req, res) => { 62 | const body = { 63 | client_id: process.env.GITHUB_CLIENT_ID, 64 | client_secret: process.env.GITHUB_SECRET, 65 | code: req.query.code, 66 | }; 67 | const opts = { headers: { accept: 'application/json' } }; 68 | axios 69 | .post('https://github.com/login/oauth/access_token', body, opts) 70 | .then((res) => res.data['access_token']) 71 | .then((_token) => { 72 | token = _token; 73 | axios 74 | .get(`https://api.github.com/user`, { 75 | headers: { Authorization: `token ${token}` }, 76 | }) 77 | .then((data) => { 78 | res.cookie('username', data.data.login); 79 | res.redirect('/'); 80 | }) 81 | .catch((err) => console.log(err)); 82 | }) 83 | .catch((err) => res.status(500).json({ message: err.message })); 84 | }); 85 | 86 | mainApp.get('/logout', (req, res) => { 87 | res.clearCookie('username'); 88 | res.redirect('/'); 89 | }); 90 | 91 | mainApp.use((err, req, res, next) => { 92 | const defaultErr = { 93 | log: 'Express error handler caught unknown middleware error', 94 | status: 500, 95 | message: { err: 'An error occurred' }, 96 | }; 97 | 98 | const errObj = Object.assign({}, defaultErr, err); 99 | 100 | console.log(errObj.log); 101 | 102 | return res.status(errObj.status).json(errObj.message); 103 | }); 104 | 105 | mainApp.listen(MAIN_PORT, () => { 106 | console.log(`Main server listening on port ${MAIN_PORT}`); 107 | }); 108 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'; 3 | import AppContainer from './AppContainer.jsx'; 4 | import LoginPage from './login/LoginPage.js'; 5 | import { useState } from 'react'; 6 | import RenderedPage from './RenderedPage.jsx'; 7 | import LandingPage from './LandingPage.jsx'; 8 | import Cookies from 'js-cookie'; 9 | 10 | export default function App() { 11 | const [username, useUsername] = useState(Cookies.get('username')); 12 | const [projName, useProjName] = useState(undefined); 13 | const [filesArr, useFilesArr] = useState([]); 14 | const [loadStatus, useLoadStatus] = useState(false); 15 | 16 | if (username === undefined) 17 | return ; 18 | if (username === 'demo') 19 | return ; 20 | else 21 | return ( 22 |
23 | {loadStatus ? ( 24 | 30 | ) : ( 31 | 37 | )} 38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/components/AppContainer.jsx: -------------------------------------------------------------------------------- 1 | import LandingPage from './LandingPage.jsx'; 2 | import RenderedPage from './RenderedPage.jsx'; 3 | import React from 'react'; 4 | import { useState } from 'react'; 5 | 6 | // Houses the future conditional rendering 7 | // If file has not been imported, landing page should display 8 | // If file has been imported, rendered page should display 9 | 10 | export default function AppContainer() { 11 | const [loadStatus, useLoadStatus] = useState(true); 12 | const [filesArr, useFilesArr] = useState([]); 13 | 14 | return ( 15 |
16 | {/* */} 17 | {loadStatus ? ( 18 | 19 | ) : ( 20 | 21 | )} 22 | {/* */} 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Blurb.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Blurb() { 4 | return ( 5 |
6 |

About

7 |
8 | Far-Left: 9 |

10 | {' '} 11 | Select one of your individual components listed to the side. This 12 | will allow you to view it individually outside of your total app. 13 |

14 |
15 | Top-Left: 16 |

17 | {' '} 18 | The rendered component of your choosing, regardless of state or 19 | props. Some rendered components will also carry with them 20 | functionality. 21 |

22 |
23 | Bottom-Left: 24 |

25 | {' '} 26 | A full rendition of your total application so that you can inspect 27 | your progress macroscopically. 28 |

29 |
30 | Bottom-Right: 31 |

32 | {' '} 33 | Component Fiber-Tree of you application, displaying each of your 34 | components and where they appear in reference to the total project. 35 | Click the Render button to see your tree update. 36 |

37 |
38 |
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/components/ComponentTree.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Tree from 'react-tree-graph'; 3 | // import data from '../data.ts'; 4 | import '../tree.css'; 5 | 6 | export default function ComponentTree(props) { 7 | const [data, setData] = useState({}); 8 | 9 | const findFrameNodes = () => { 10 | let _rootNode; 11 | 12 | const iframeDocument = 13 | document.getElementsByTagName('iframe')[1].contentDocument; 14 | const elems = iframeDocument.querySelector('body').children; 15 | 16 | for (let el of elems) { 17 | if (el._reactRootContainer) { 18 | // Returns root React node 19 | _rootNode = el._reactRootContainer._internalRoot.current; 20 | } 21 | } 22 | 23 | function parentFinder(node) { 24 | if (!node.return) return; 25 | if (node.return.tag === 0 || node.return.tag === 1) { 26 | return node.return.type.name; 27 | } else { 28 | return parentFinder(node.return); 29 | } 30 | } 31 | 32 | const objColors = [ 33 | 'cyan', 34 | 'red', 35 | 'orange', 36 | 'yellow', 37 | 'green', 38 | 'blue', 39 | 'indigo', 40 | 'violet', 41 | 'orange', 42 | ]; 43 | 44 | // Math.floor(Math.random() * objColors.length) 45 | function findColor() { 46 | // return objColors[Math.floor(Math.random() * objColors.length)]; 47 | return objColors[5]; 48 | } 49 | 50 | let rootObj; 51 | 52 | // class Node - corresponds to shape required for react tree graph 53 | class Node { 54 | constructor(name, parent) { 55 | (this.name = name), 56 | (this.parent = parent || 'root'), 57 | (this.children = []), 58 | (this.color = findColor()), 59 | (this.pathProps = { className: findColor() }), 60 | (this.textProps = { x: -25, y: 25 }); 61 | } 62 | } 63 | 64 | // Requisite for obtaining root node's name 65 | // Ends up being an array with react component objects that point to their parent 66 | const treeNodes = [new Node('App', 'top')]; 67 | 68 | const state = []; 69 | 70 | // Traverses react fiber nodes similar to a linked list 71 | function fiberFinder(node) { 72 | if (node.sibling !== null) { 73 | // If it has a sibling, and the sibling has a type property with a name, it is a React component 74 | if (node.sibling.type) { 75 | const parent = parentFinder(node); 76 | treeNodes.push(new Node(node.sibling.type.name, parent)); 77 | } 78 | fiberFinder(node.sibling); 79 | } 80 | if (node.child !== null) { 81 | // If it has a child, and the child has a type property with a name, it is a React component 82 | if (node.child.type) { 83 | const parent = parentFinder(node); 84 | treeNodes.push(new Node(node.child.type.name, parent)); 85 | } 86 | fiberFinder(node.child); 87 | } 88 | } 89 | fiberFinder(_rootNode); 90 | // console.log('this is tree nodes', treeNodes) 91 | 92 | // console.log(state); 93 | 94 | // App 95 | // - Header 96 | // - Nav 97 | for (let i = 0; i < treeNodes.length; i += 1) { 98 | if (treeNodes[i].name) { 99 | // if node parent prop exist, assign node name to child of parent property 100 | // console.log('which loop trough treeNodes', i) 101 | if (treeNodes[i].parent) { 102 | // Header -> App 103 | const parent = treeNodes[i].parent; 104 | // search through array to find elem in array where that parent val exists 105 | // if treeNodes[j] === App, push Header to App's children array 106 | for (let j = 0; j < treeNodes.length; j += 1) { 107 | if (treeNodes[j].name === parent) { 108 | treeNodes[j].children.push(treeNodes[i]); 109 | break; 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | rootObj = treeNodes[0]; 117 | setData(rootObj); 118 | }; 119 | 120 | return ( 121 |
122 |

Component Tree

123 |
124 | 139 |
140 | 149 |
150 |
151 | ); 152 | } 153 | -------------------------------------------------------------------------------- /src/components/DashBoard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ComponentTree from './ComponentTree.jsx'; 3 | import Visualizer from './Visualizer.jsx'; 4 | import IndividualComponent from './IndividualComponent.jsx'; 5 | import Blurb from './Blurb'; 6 | 7 | export default function DashBoard(props) { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Egg.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Egg() { 4 | return
Hi Nate
; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/File.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function File(props) { 4 | return ( 5 | 8 | ); 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // Will eventually house header info 4 | // Need to add img, how to get img working in webpack? 5 | 6 | export default function Header() { 7 | return ( 8 |
9 |

Reactron

10 |
11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/components/IndividualComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function IndividualComponent() { 4 | return ( 5 |
6 |

Individual Component

7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/components/LandingPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import logo from '../../assets/logo.png'; 3 | import { useState } from 'react'; 4 | import filesysHelpers from '../../filesysHelpers.js'; 5 | import axios from 'axios'; 6 | import NavBarContainer from './NavBar/NavBarContainer'; 7 | import PreviousFiles from './NavBar/PreviousFiles'; 8 | import Header from './Header.jsx'; 9 | 10 | // Will house the landing page / initial render page 11 | // Will import files from here 12 | export default function LandingPage(props) { 13 | 14 | const [staticFile, useStaticFile] = useState(); 15 | 16 | const [components, useComponents] = useState(); 17 | 18 | // opens file picker for style sheet 19 | const staticOnClick = async () => { 20 | const fileHandle = window.showOpenFilePicker(); 21 | // native operation to file handle object 22 | fileHandle 23 | // gets single file, getFile is native operation 24 | .then((data) => data[0].getFile()) 25 | // grabs the contents 26 | .then((res) => res.text()) 27 | .then((data) => { 28 | // sends to the backend for upload 29 | axios.post('/fs/stylesheet', { item: data }); 30 | // the hook to set static file 31 | useStaticFile(data); 32 | }); 33 | }; 34 | 35 | // opens window for component file picker 36 | const componentOnClick = async () => { 37 | // declare file handle constant for directory picker to grab the folder of components 38 | const fileHandle = await window.showDirectoryPicker(); 39 | // declare results constant that receives data from the filesystem helper directory logger that turns the directory into a usable object 40 | const result = await filesysHelpers.directoryLogger(fileHandle); 41 | // the useComponents hook to set components 42 | useComponents(result); 43 | }; 44 | 45 | 46 | // sends the file names and contents to the backend to be uploaded 47 | const submitDirs = async (e) => { 48 | // prevents default activity of event 49 | e.preventDefault(); 50 | // gets the project name from the input box 51 | const projName = e.target[0].value; 52 | 53 | // organizing the files for the backend, found in filesysHelpers.js file 54 | filesysHelpers 55 | // recursively generates an array of all the component files 56 | .fileDisplay(components, 'componentFiles') 57 | .then(async (data) => { 58 | // declare temporary Array and name Arrays 59 | const tempArr = []; 60 | const nameArr = []; 61 | // iterates through the data 62 | data.forEach(async (elem) => { 63 | // this is the array of file names 64 | nameArr.push(elem.getFile().then((res) => res.name)); 65 | // this is the array of file contents 66 | tempArr.push(elem.getFile().then((res) => res.text())); 67 | }); 68 | // returns an array of fufilled promises 69 | const fileContents = await Promise.all(tempArr); 70 | const nameContents = await Promise.all(nameArr); 71 | 72 | // declares an empty result array 73 | const resultArr = []; 74 | 75 | 76 | for (let i = 0; i < fileContents.length; i += 1) { 77 | // when user authentication is implemeneted, file name needs to be updated: 78 | // 'username'/'projectname'/'filename' 79 | resultArr.push({ 80 | name: nameContents[i], 81 | contents: fileContents[i], 82 | }); 83 | } 84 | 85 | props.useFilesArr(resultArr); 86 | props.useProjName(projName); 87 | 88 | axios.post('/fs/upload', { 89 | files: resultArr, 90 | username: props.username, 91 | project: projName, 92 | style: staticFile, 93 | }); 94 | 95 | props.useLoadStatus(true); //calls useloadStatus to change state to true 96 | }); 97 | }; 98 | 99 | return ( 100 |
101 | 107 |
108 |
109 |
110 |

111 | Select a Previous Project from the Side 112 |
113 | - OR -
114 | Upload a New Project Below 115 |
116 | {/* In order for Reactron to process your application files correctly, 117 | please follow these instructions.
118 | If you have a CSS or SCSS file you would like processed, please upload 119 | it under the Static Directory.
120 | Reactron will look for an index.js file that connects to an{' '} 121 | App.jsx component. Please upload your index.js, App.jsx, and 122 | any other component files in one directory under the Component 123 | Directory below. */} 124 |

125 |
126 |
127 |

128 | StaticDirectory 129 |
Example 130 |
- style.css 131 |
or 132 |
- style.scss 133 |
134 |
135 |

136 | 137 | Upload Styling 138 | 141 |

{staticFile ? `The file has been uploaded` : ''}

142 |
143 |
144 |

145 | Component Directory 146 |
Example 147 |
- index.js 148 |
- App.jsx 149 |
- Component1.jsx 150 |
- Component2.jsx 151 |

152 | Upload Components 153 | 156 |

157 | {components 158 | ? `The ${ 159 | components[Object.keys(components)[0]].handle.name 160 | } directory has been uploaded` 161 | : ''} 162 |

163 |
164 | {staticFile && components ? ( 165 |
166 |

167 | Your files have been successfully uploaded. Give your project a name 168 | and hit the next button for Reactron to begin the rendering process. 169 |

170 |
171 | 177 |
178 | 181 |
182 |
183 | ) : ( 184 | '' 185 | )} 186 |
187 |
188 | ); 189 | } 190 | -------------------------------------------------------------------------------- /src/components/NavBar/FileExplorer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import File from '../File.jsx'; 3 | import axios from 'axios'; 4 | 5 | export default function FileExplorer(props) { 6 | const renderFile = (name) => { 7 | axios 8 | .post('/fs/individual', { 9 | name: name, 10 | username: props.username, 11 | project: props.project, 12 | }) 13 | .then((res) => { 14 | const iframe = document.getElementById('indcomp'); 15 | iframe.src = iframe.src; 16 | }) 17 | .catch((err) => console.log(err)); 18 | }; 19 | 20 | const returnArr = () => { 21 | const arr = []; 22 | props.files.forEach((file) => { 23 | if (file.name !== 'index.js') { 24 | arr.push(); 25 | } 26 | }); 27 | return arr; 28 | }; 29 | return ( 30 |
31 | {props.username !== 'demo' ? ( 32 | 38 | ) : ( 39 | '' 40 | )} 41 | 42 |

43 |

Files Uploaded

44 | {props.files ? returnArr() : ''} 45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBarContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Profile from './Profile'; 3 | import FileExplorer from './FileExplorer'; 4 | import Signout from './Signout'; 5 | import PreviousFiles from './PreviousFiles'; 6 | 7 | export default function NavBarContainer(props) { 8 | return ( 9 |
10 | 11 | 17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBarContainerRendered.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Profile from './Profile'; 3 | import FileExplorer from './FileExplorer'; 4 | import Signout from './Signout'; 5 | 6 | export default function NavBarContainer(props) { 7 | return ( 8 |
9 | 10 | 16 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/NavBar/PreviousFiles.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import axios from 'axios'; 3 | 4 | export default class PreviousFiles extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | prevFiles: [], 9 | }; 10 | this.handlePrevious = this.handlePrevious.bind(this); 11 | } 12 | 13 | handlePrevious(name) { 14 | axios 15 | .post('/fs/prevupload', { 16 | projName: name, 17 | username: this.props.username, 18 | }) 19 | .then((res) => { 20 | this.props.useLoadStatus(true); 21 | this.props.useFilesArr(res.data); 22 | this.props.useProjName(name); 23 | }); 24 | } 25 | 26 | componentDidMount() { 27 | axios 28 | .post('/fs/prevprojs', { username: this.props.username }) 29 | .then((res) => this.setState({ prevFiles: res.data })); 30 | } 31 | 32 | render() { 33 | const returnArr = []; 34 | for (let i = 0; i < this.state.prevFiles.length; i += 1) { 35 | returnArr.push( 36 | 42 | ); 43 | } 44 | return ( 45 |
46 |

Previous Projects

47 |
48 | {this.state.prevFiles.length > 0 49 | ? returnArr 50 | : 'No projects yet! Create one to get started.'} 51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/NavBar/Profile.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Profile(props) { 4 | return ( 5 |
6 |

{props.username}

7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /src/components/NavBar/Signout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Signout() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/components/ReactArch.md: -------------------------------------------------------------------------------- 1 | Current React component architecture 2 | 3 | - App 4 | - Splash Page 5 | - Log In 6 | - Oauth 7 | - LandingPage 8 | - LandingPage (if file not imported) 9 | - RenderedPage (if file imported) 10 | - Header 11 | - If Full App View: 12 | - ComponentTree 13 | - Visualizer 14 | - StateContainer 15 | - StateItem 16 | - RenderedContainer 17 | - ReactComponent 18 | - If Indv Component View: 19 | - FileList 20 | - File 21 | - IndividualComponent 22 | -------------------------------------------------------------------------------- /src/components/ReactComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import axios from 'axios'; 3 | 4 | // export default function ReactComponent() { 5 | // useEffect(() => { 6 | 7 | // }, []); 8 | 9 | // } 10 | 11 | export default class ReactComponent extends React.Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | component: [], 16 | }; 17 | } 18 | 19 | componentDidMount() { 20 | const arr = [ 21 | , 22 | ]; 23 | this.setState({ component: arr }); 24 | } 25 | 26 | render() { 27 | const arr = []; 28 | for (let i = 0; i < this.state.component.length; i += 1) { 29 | arr.push(this.state.component[i]); 30 | } 31 | return
{arr}
; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/RenderedContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactComponent from './ReactComponent.jsx'; 3 | 4 | export default function RenderedContainer() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/components/RenderedPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Header from './Header.jsx'; 3 | import NavBarContainerRendered from './NavBar/NavBarContainerRendered'; 4 | import DashBoard from './DashBoard'; 5 | 6 | // Page that will show once directory has been imported 7 | export default function RenderedPage(props) { 8 | const [refresh, setRefresh] = useState(0); 9 | 10 | const refreshTree = () => { 11 | setRefresh(refresh + 1) 12 | }; 13 | 14 | return ( 15 |
16 |
17 | 23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/components/StateContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import StateItem from './StateItem.jsx'; 3 | 4 | // conditional rendering based on how many state items 5 | 6 | export default function StateContainer() { 7 | return ( 8 |
9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/components/StateItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function StateItem() { 4 | return
State / Prop
; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Visualizer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import StateContainer from './StateContainer.jsx'; 3 | import RenderedContainer from './RenderedContainer.jsx'; 4 | 5 | // shows the selected React Component and it's props / state / methods 6 | 7 | export default function Visualizer() { 8 | return ( 9 |
10 |

Application

11 | {/* */} 12 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/components/__tests__/ComponentTree.test.js: -------------------------------------------------------------------------------- 1 | import {render, cleanup, screen, fireEvent} from '@testing-library/react'; 2 | import renderer from 'react-test-renderer'; 3 | import '@testing-library/jest-dom'; 4 | import React from 'react'; 5 | import ComponentTree from '../ComponentTree'; 6 | 7 | afterEach(() => { 8 | cleanup(); 9 | }); 10 | 11 | test('test', () => { 12 | expect(true).toBe(true); 13 | }); 14 | 15 | test('should render ComponentTree', () => { 16 | render(); 17 | const componentTree = screen.getByTestId('ComponentTree'); 18 | expect(componentTree).toBeInTheDocument(); 19 | expect(componentTree).toContainHTML('div'); 20 | expect(componentTree).toHaveClass('componentTree'); 21 | }); 22 | 23 | test('should render Tree', () => { 24 | render(); 25 | const tree = screen.getByTestId('Tree'); 26 | expect(tree).toBeInTheDocument(); 27 | }); 28 | 29 | // if snapshot test fails, press u in the test terminal to update snapshot 30 | xtest('should match ComponentTree snapshot', () => { 31 | const snapCT = renderer.create().toJSON(); 32 | expect(snapCT).toMatchSnapshot(); 33 | }); 34 | -------------------------------------------------------------------------------- /src/components/__tests__/LandingPage.test.js: -------------------------------------------------------------------------------- 1 | import {render, cleanup, screen, fireEvent} from '@testing-library/react'; 2 | import renderer from 'react-test-renderer'; 3 | import '@testing-library/jest-dom'; 4 | import React from 'react'; 5 | import LandingPage from '../LandingPage'; 6 | 7 | afterEach(() => { 8 | cleanup(); 9 | }); 10 | 11 | test('test', () => { 12 | expect(true).toBe(true); 13 | }); 14 | 15 | test('should render LandingPage', () => { 16 | render(); 17 | const landing = screen.getByTestId('LandingPage'); 18 | expect(landing).toBeInTheDocument(); 19 | expect(landing).toContainHTML('div'); 20 | expect(landing).toContainHTML('p'); 21 | expect(landing).toContainHTML('h1'); 22 | expect(landing).toContainHTML('button'); 23 | expect(landing).toHaveTextContent('Reactron'); 24 | }); 25 | 26 | 27 | // if snapshot test fails, press u in the test terminal to update snapshot 28 | xtest('should match LandingPage snapshot', () => { 29 | const snap = renderer.create().toJSON(); 30 | expect(snap).toMatchSnapshot(); 31 | }); 32 | -------------------------------------------------------------------------------- /src/components/__tests__/RenderedPage.test.js: -------------------------------------------------------------------------------- 1 | import { render, cleanup, screen, fireEvent } from '@testing-library/react'; 2 | import renderer from 'react-test-renderer'; 3 | import '@testing-library/jest-dom'; 4 | import React from 'react'; 5 | import RenderedPage from '../RenderedPage'; 6 | 7 | afterEach(() => { 8 | cleanup(); 9 | }); 10 | 11 | test('test', () => { 12 | expect(true).toBe(true); 13 | }); 14 | 15 | test('should render Rendered Page', () => { 16 | render(); 17 | const rendered = screen.getByTestId('RenderedPage'); 18 | expect(rendered).toBeInTheDocument(); 19 | expect(rendered).toContainHTML('div'); 20 | expect(rendered).toContainHTML('iframe'); 21 | expect(rendered).toContainHTML('h1'); 22 | expect(rendered).toHaveTextContent('Reactron'); 23 | }); 24 | 25 | 26 | // if snapshot test fails, press u in the test terminal to update snapshot 27 | xtest('should match RenderedPage snapshot', () => { 28 | const snapRend = renderer.create().toJSON(); 29 | expect(snapRend).toMatchSnapshot(); 30 | }); 31 | -------------------------------------------------------------------------------- /src/components/__tests__/Supertest.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | const server = 'http://localhost:3000'; 6 | 7 | 8 | describe('Route integration', () => { 9 | it('responds with 200 status', () => { 10 | return request (server) 11 | .get('/') 12 | .expect(200) 13 | }) 14 | }) 15 | 16 | describe('Wrong Url', () => { 17 | it('responds with 404 not found', () => { 18 | return request(server) 19 | .get('/wrongurl/blahblah') 20 | .expect(404) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /src/components/__tests__/Visualizer.test.js: -------------------------------------------------------------------------------- 1 | import {render, cleanup, screen, fireEvent} from '@testing-library/react'; 2 | import renderer from 'react-test-renderer'; 3 | import '@testing-library/jest-dom'; 4 | import React from 'react'; 5 | import Visualizer from '../Visualizer'; 6 | 7 | afterEach(() => { 8 | cleanup(); 9 | }); 10 | 11 | test('test', () => { 12 | expect(true).toBe(true); 13 | }); 14 | 15 | test('should render Rendered Page', () => { 16 | render(); 17 | const visualizer = screen.getByTestId('Visualizer'); 18 | expect(visualizer).toBeInTheDocument(); 19 | expect(visualizer).toContainHTML('div'); 20 | expect(visualizer).toContainHTML('iframe'); 21 | expect(visualizer).toHaveClass('visualizer'); 22 | }); 23 | 24 | // if snapshot test fails, press u in the test terminal to update snapshot 25 | xtest('should match Visualizer snapshot', () => { 26 | const snapVis = renderer.create().toJSON(); 27 | expect(snapVis).toMatchSnapshot(); 28 | }); 29 | -------------------------------------------------------------------------------- /src/components/__tests__/__snapshots__/ComponentTree.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`should match ComponentTree snapshot 1`] = ` 4 |
8 |
12 | 17 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 46 | 50 | 54 | 58 | 62 | 66 | 69 | 75 | App 76 | 77 | 78 | 82 | 85 | 91 | Game 92 | 93 | 94 | 98 | 101 | 107 | Board 108 | 109 | 110 | 114 | 117 | 123 | Square 124 | 125 | 126 | 130 | 133 | 139 | Square 140 | 141 | 142 | 146 | 149 | 155 | Square 156 | 157 | 158 | 162 | 165 | 171 | Square 172 | 173 | 174 | 178 | 181 | 187 | Square 188 | 189 | 190 | 194 | 197 | 203 | Square 204 | 205 | 206 | 210 | 213 | 219 | Square 220 | 221 | 222 | 226 | 229 | 235 | Square 236 | 237 | 238 | 242 | 245 | 251 | Square 252 | 253 | 254 | 255 | 256 |
257 |
258 | `; 259 | -------------------------------------------------------------------------------- /src/components/__tests__/__snapshots__/LandingPage.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`should match LandingPage snapshot 1`] = ` 4 |
8 |
11 |

12 | Reactron 13 |

14 |
15 |
18 |

19 | In order for Reactron to process your application files correctly, please follow these instructions. 20 |
21 | If you have a CSS or SCSS file you would like processed, please upload it under the Static Directory. 22 |
23 | Reactron will look for an 24 | 25 | index.js 26 | 27 | file that connects to an 28 | 29 | 30 | App.jsx 31 | 32 | component. Please upload your index.js, App.jsx, and any other component files in one directory under the Component Directory below. 33 |

34 |
35 |
38 |

39 | 40 | StaticDirectory 41 | 42 |
43 | 44 | 45 | Example 46 | 47 |
48 | - style.css 49 |
50 | 51 | 52 | or 53 | 54 |
55 | - style.scss 56 |
57 |
58 |

59 |
60 |
63 |

64 | 65 | Component Directory 66 | 67 |
68 | 69 | 70 | Example 71 | 72 |
73 | - index.js 74 |
75 | - App.jsx 76 |
77 | - Component1.jsx 78 |
79 | - Component2.jsx 80 |

81 |
82 |
85 | 86 | Static Files 87 | 88 |

89 | Please upload your static directory here. 90 |

91 |

92 | 93 |

94 | 101 |
102 |
105 | 106 | Component Files 107 | 108 |

109 | Please upload your component files here. 110 |

111 |

112 | 113 |

114 | 121 |
122 | 123 |
124 | `; 125 | -------------------------------------------------------------------------------- /src/components/__tests__/__snapshots__/RenderedPage.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`should match RenderedPage snapshot 1`] = ` 4 |
8 |
11 |

12 | Reactron 13 |

14 |
15 |
18 |
21 |
24 | 29 | 30 | 34 | 38 | 42 | 46 | 50 | 54 | 58 | 62 | 66 | 70 | 74 | 78 | 81 | 87 | App 88 | 89 | 90 | 94 | 97 | 103 | Game 104 | 105 | 106 | 110 | 113 | 119 | Board 120 | 121 | 122 | 126 | 129 | 135 | Square 136 | 137 | 138 | 142 | 145 | 151 | Square 152 | 153 | 154 | 158 | 161 | 167 | Square 168 | 169 | 170 | 174 | 177 | 183 | Square 184 | 185 | 186 | 190 | 193 | 199 | Square 200 | 201 | 202 | 206 | 209 | 215 | Square 216 | 217 | 218 | 222 | 225 | 231 | Square 232 | 233 | 234 | 238 | 241 | 247 | Square 248 | 249 | 250 | 254 | 257 | 263 | Square 264 | 265 | 266 | 267 | 268 |
269 |
270 |
273 |
276 |
279 |