├── .gitignore ├── .travis.yml ├── README.md ├── api-server ├── .babelrc ├── .commitlintrc.json ├── .dockerignore ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── Dockerfile.dev ├── README.md ├── database │ ├── init-database.js │ └── seeds │ │ ├── contents.template.js │ │ ├── file.seed.js │ │ ├── index.js │ │ ├── project.seed.js │ │ └── user.seed.js ├── dev.env ├── ecosystem.config.js ├── package-lock.json ├── package.json ├── src │ ├── app.js │ ├── app.test.js │ ├── config │ │ └── index.js │ ├── middlewares │ │ └── passport.js │ ├── models │ │ ├── File.js │ │ ├── Project.js │ │ ├── User.js │ │ └── index.js │ ├── routes │ │ ├── dependency │ │ │ ├── controller.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── projects │ │ │ ├── controller.js │ │ │ ├── files │ │ │ │ ├── controller.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── users │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ └── login │ │ │ ├── controller.js │ │ │ └── index.js │ └── utils │ │ └── transaction.js └── yarn.lock ├── cocode ├── .babelrc ├── .commitlintrc.json ├── .dockerignore ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .storybook │ ├── addons.js │ ├── config.js │ └── webpack.config.js ├── Dockerfile ├── Dockerfile.dev ├── README.md ├── dev.env ├── init-script │ ├── entry-plugin.js │ ├── get-module-list-for-target-version.js │ ├── index.js │ ├── normalize-options.js │ └── usage-plugin.js ├── nginx │ └── conf.d │ │ └── default.conf ├── package-lock.json ├── package.json ├── public │ ├── favicon-16x16.png │ └── index.html ├── src │ ├── App.js │ ├── App.test.js │ ├── actions │ │ ├── API.js │ │ ├── Dashboard.js │ │ ├── Live.js │ │ ├── Project.js │ │ └── types.js │ ├── apis │ │ ├── DashBoard.js │ │ ├── File.js │ │ ├── Project.js │ │ └── User.js │ ├── components │ │ ├── Common │ │ │ ├── CloseButton │ │ │ │ ├── close.svg │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── CoconutSpinner │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── DropDownMenu │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ └── style.js │ │ │ ├── DropZone │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── ExampleButton │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ └── style.js │ │ │ ├── GlobalStyle │ │ │ │ └── index.js │ │ │ ├── LoginModalBody │ │ │ │ ├── github.svg │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── Logo │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ └── logo.svg │ │ │ ├── Modal │ │ │ │ ├── index.js │ │ │ │ ├── style.js │ │ │ │ └── test.js │ │ │ ├── ModalPortal │ │ │ │ └── index.js │ │ │ ├── SplitPane │ │ │ │ ├── LICENSE │ │ │ │ ├── Pane.js │ │ │ │ ├── Resizer.js │ │ │ │ ├── SplitPane.js │ │ │ │ └── index.js │ │ │ ├── TinyCoconutLogo │ │ │ │ └── index.js │ │ │ ├── Toast │ │ │ │ ├── ToastContainer.js │ │ │ │ ├── ToastItem.js │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ └── UserProfile │ │ │ │ ├── down.svg │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ └── style.js │ │ ├── DashBoard │ │ │ ├── CreateCoconut │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ └── style.js │ │ │ └── ProjectCard │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ └── style.js │ │ ├── Home │ │ │ ├── ReactLogo │ │ │ │ ├── index.js │ │ │ │ ├── react.svg │ │ │ │ └── style.js │ │ │ ├── ScrollDownButton │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ └── ScrollTopButton │ │ │ │ ├── Arrow.svg │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ ├── Live │ │ │ └── LiveUsers │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ └── Project │ │ │ ├── Browser │ │ │ ├── BackwardButton │ │ │ │ ├── backward.svg │ │ │ │ └── index.js │ │ │ ├── ForwardButton │ │ │ │ ├── forward.svg │ │ │ │ └── index.js │ │ │ ├── ReloadButton │ │ │ │ ├── index.js │ │ │ │ └── reload.svg │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── style.js │ │ │ ├── BrowserV2 │ │ │ ├── index.js │ │ │ ├── open.svg │ │ │ ├── search.svg │ │ │ └── style.js │ │ │ ├── Dependency │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── DependencyItem │ │ │ ├── close.svg │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── DependencyNow │ │ │ ├── dummy.js │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── DependencySearch │ │ │ ├── dummy.js │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── DependencySearchItem │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── DependencySelector │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── Directory │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── ExplorerTabIcons │ │ │ ├── DeleteIcon │ │ │ │ └── index.js │ │ │ ├── EditIcon │ │ │ │ └── index.js │ │ │ ├── NewFileIcon │ │ │ │ └── index.js │ │ │ ├── NewFolderIcon │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── File │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── FileTab │ │ │ ├── close.svg │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── FileTabBar │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── GitHubLogo │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── MonacoEditor │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── style.js │ │ │ ├── MorelessButton │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── NewFile │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── NpmLogo │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── PlusImage │ │ │ ├── index.js │ │ │ └── style.js │ │ │ ├── ProjectIcon │ │ │ ├── index.js │ │ │ └── style.js │ │ │ └── TabIcon │ │ │ ├── index.js │ │ │ └── style.js │ ├── config │ │ └── index.js │ ├── constants │ │ ├── cookie.js │ │ ├── cursorColors.js │ │ ├── fileImagesSrc.js │ │ ├── keyCode.js │ │ ├── notificationMessage.js │ │ ├── statusCode.js │ │ └── theme.js │ ├── containers │ │ ├── Common │ │ │ ├── Header │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ ├── style.js │ │ │ │ └── test.js │ │ │ └── LoadingSpinner │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ ├── DashBoard │ │ │ └── ProjectCardList │ │ │ │ ├── index.js │ │ │ │ ├── index.stories.js │ │ │ │ └── style.js │ │ ├── History │ │ │ └── CocodeHistory │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ ├── Home │ │ │ ├── AboutCocode │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── AboutUs │ │ │ │ ├── index.js │ │ │ │ ├── profiles │ │ │ │ │ ├── basiltoast.png │ │ │ │ │ ├── hzoou.png │ │ │ │ │ ├── index.js │ │ │ │ │ ├── lallaheeee.png │ │ │ │ │ └── yukjisoo.png │ │ │ │ └── style.js │ │ │ └── Main │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ ├── Live │ │ │ ├── DependencyTab │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── Editor │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── ExplorerTab │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── LiveOnTab │ │ │ │ ├── close.svg │ │ │ │ ├── copy.svg │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── LiveTab │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── TabBar │ │ │ │ ├── dependency.svg │ │ │ │ ├── explorer.svg │ │ │ │ ├── index.js │ │ │ │ ├── live.svg │ │ │ │ └── style.js │ │ │ └── TabContainer │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ ├── NotFound │ │ │ └── WeAreSorry │ │ │ │ ├── coconut.png │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ ├── Project │ │ │ ├── DependencyTab │ │ │ │ ├── dummy.js │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── Editor │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── ExplorerTab │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── InfoTab │ │ │ │ ├── index.js │ │ │ │ ├── modify.svg │ │ │ │ └── style.js │ │ │ ├── LiveOffTab │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── LiveTab │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── TabBar │ │ │ │ ├── dependency.svg │ │ │ │ ├── explorer.svg │ │ │ │ ├── index.js │ │ │ │ ├── info.svg │ │ │ │ ├── live.svg │ │ │ │ └── style.js │ │ │ └── TabContainer │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ └── SignIn │ │ │ ├── index.js │ │ │ └── style.js │ ├── contexts │ │ ├── DashBoardContext.js │ │ ├── LiveContext.js │ │ ├── ProjectContext.js │ │ ├── UserContext.js │ │ └── index.js │ ├── hooks │ │ └── useFetch.js │ ├── index.js │ ├── pages │ │ ├── DashBoard │ │ │ └── index.js │ │ ├── Empty │ │ │ └── index.js │ │ ├── Home │ │ │ └── index.js │ │ ├── Live │ │ │ ├── index.js │ │ │ └── style.js │ │ ├── NotFound │ │ │ └── index.js │ │ ├── Project │ │ │ ├── getUpdatedPackageJSON.js │ │ │ ├── index.js │ │ │ ├── parseProject.js │ │ │ └── style.js │ │ ├── SignIn │ │ │ └── index.js │ │ └── index.js │ ├── reducers │ │ ├── APIReducer.js │ │ ├── DashBoardReducer.js │ │ ├── LiveReducer.js │ │ ├── ProjectReducer.js │ │ └── index.js │ ├── stores │ │ ├── LiveStore.js │ │ └── index.js │ ├── stories │ │ └── decorators │ │ │ ├── ThemeDecorator.js │ │ │ └── index.js │ ├── template │ │ ├── copyProject.js │ │ └── react.js │ └── utils │ │ ├── addWheelEvent.js │ │ ├── controlCookie.js │ │ ├── domControl.js │ │ ├── index.js │ │ ├── keyDownEvent.js │ │ └── monacoWidget.js ├── test_setup │ └── __mocks__ │ │ └── fileMock.js ├── webpack.config.js └── yarn.lock ├── coconut ├── .commitlintrc.json ├── .dockerignore ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── Dockerfile.dev ├── README.md ├── config-overrides.js ├── dev.env ├── nginx │ └── conf.d │ │ └── default.conf ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.js │ ├── App.test.js │ ├── actions │ │ ├── API.js │ │ ├── Project.js │ │ └── types.js │ ├── apis │ │ ├── Dependency.js │ │ └── Project.js │ ├── bundler │ │ ├── codeTemplate.js │ │ ├── core.js │ │ ├── global.js │ │ ├── index.js │ │ ├── pathParser.js │ │ ├── pathParserInMain.js │ │ ├── require.js │ │ └── requireInMain.js │ ├── components │ │ ├── Coconut │ │ │ ├── index.js │ │ │ └── style.js │ │ ├── CoconutSpinner │ │ │ ├── index.js │ │ │ └── style.js │ │ ├── Logo │ │ │ ├── index.js │ │ │ └── logo.svg │ │ └── TinyCoconutLogo │ │ │ └── index.js │ ├── config │ │ └── index.js │ ├── hooks │ │ ├── index.js │ │ ├── useBuildProject.js │ │ ├── useCommunicateCocode.js │ │ ├── useConnectToIDB.js │ │ ├── useFetch.js │ │ ├── useUpdateDependency.js │ │ └── useUpdateProject.js │ ├── index.js │ ├── indexedDB │ │ └── index.js │ ├── reducers │ │ ├── APIReducer.js │ │ └── ProjectReducer.js │ ├── setupTests.js │ ├── utils │ │ └── getUpdatedPackageJSON.js │ └── worker │ │ └── build.worker.js └── yarn.lock ├── database └── docker-compose.yml ├── dependency-packager ├── .eslintignore ├── .gitignore ├── .prettierrc ├── database │ ├── index.js │ └── schema │ │ ├── index.js │ │ └── package.js ├── package.json ├── packager │ ├── index.js │ ├── package-template.js │ ├── packager.js │ ├── regex.js │ └── transpiler.js ├── server.js └── yarn.lock ├── docker-compose.yml ├── docs └── pull_request_template.md ├── env.tar.enc ├── live-server ├── .gitignore ├── .prettierrc ├── app.js ├── package.json └── yarn.lock ├── proxy ├── Dockerfile └── nginx.conf ├── script ├── deploy.sh └── update-env-files.sh └── yarn.lock /api-server/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "corejs": "3.2.1" 8 | } 9 | ] 10 | ], 11 | "plugins": [["@babel/transform-runtime"]] 12 | } 13 | -------------------------------------------------------------------------------- /api-server/.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /api-server/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | Dockerfile.dev 3 | 4 | *.md 5 | !README.md 6 | 7 | node_modules -------------------------------------------------------------------------------- /api-server/.eslintignore: -------------------------------------------------------------------------------- 1 | README.md 2 | .babelrc 3 | .prettierrc 4 | .env 5 | public/* 6 | database/seeds/contents.template.js 7 | -------------------------------------------------------------------------------- /api-server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier"], 3 | "env": { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "globals": { 8 | "Atomics": "readonly", 9 | "SharedArrayBuffer": "readonly" 10 | }, 11 | "parserOptions": { 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "ecmaVersion": 2018, 16 | "sourceType": "module" 17 | }, 18 | "plugins": ["react"], 19 | "rules": { 20 | "eqeqeq": "error", 21 | "no-empty-function": "error", 22 | "no-multiple-empty-lines": "error", 23 | "no-multi-spaces": "error", 24 | "quotes": ["error", "single"], 25 | "semi": "error", 26 | "switch-colon-spacing": "error", 27 | "array-bracket-spacing": ["error", "never"], 28 | "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], 29 | "keyword-spacing": ["error", { "before": true, "after": true }], 30 | "max-depth": ["error", 5], 31 | "max-len": ["error", { "code": 80 }], 32 | "no-mixed-spaces-and-tabs": "error", 33 | "no-trailing-spaces": "error" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /api-server/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directories 7 | node_modules/ 8 | 9 | # Optional npm cache directory 10 | .npm 11 | 12 | # Optional eslint cache 13 | .eslintcache 14 | 15 | # dotenv environment variables file 16 | .env -------------------------------------------------------------------------------- /api-server/.prettierignore: -------------------------------------------------------------------------------- 1 | .* 2 | package-lock.json 3 | package.json 4 | README.md -------------------------------------------------------------------------------- /api-server/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 4, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /api-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.13.0 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . /usr/src/app 10 | 11 | EXPOSE 3030 12 | CMD [ "npm", "start" ] 13 | -------------------------------------------------------------------------------- /api-server/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:12.13.0 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3030 12 | CMD [ "npm", "run", "dev" ] 13 | -------------------------------------------------------------------------------- /api-server/README.md: -------------------------------------------------------------------------------- 1 | # cocode 2 | 3 | ## usage 4 | 5 | ```json 6 | "scripts": { 7 | "start": "webpack-dev-server --open", 8 | "watch": "webpack --watch", 9 | "build": "webpack --production", 10 | "build:dev": "webpack --develope", 11 | "precommit": "npm test & lint-staged", 12 | "prepush": "npm test", 13 | "test": "ls" 14 | } 15 | ``` 16 | 17 | - `start` 개발용 서버를 실행합니다. 18 | - `build` 배포용 파일을 번들링합니다. 19 | 20 | ```javascript 21 | entry : __dirname + '/src/index.js', 22 | output : { 23 | path: __dirname + '/public', 24 | filename : 'bundle.js' 25 | } 26 | ``` 27 | 28 | entry 파일은 /src/index.js 파일입니다. 해당파일에서 29 | /src/app.js 파일을 body태그에 랜더링합니다. 30 | 31 | webpack-dev-server를 실행한 경우 번들링된 파일을 생성하지 않습니다. 32 | -------------------------------------------------------------------------------- /api-server/database/init-database.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | import mongoose from 'mongoose'; 3 | import { File, Project, User } from '../src/models'; 4 | import { fileSeed, projectSeed, userSeed } from './seeds'; 5 | 6 | dotenv.config(); 7 | 8 | async function drop(connection) { 9 | return connection 10 | .dropDatabase() 11 | .then(() => console.log('SUCCESS drop DATABASE! ')) 12 | .catch(err => console.error(err)); 13 | } 14 | 15 | async function insertSeed(Model, seed) { 16 | return Model.insertMany(seed) 17 | .then(() => console.log(`SUCCESS insert ${Model.modelName}`)) 18 | .catch(err => console.error(err)); 19 | } 20 | 21 | if (process.env.NODE_ENV === 'production') process.exit(0); 22 | else { 23 | const MONGODB_URI = process.env.DEV_DATABASE_URI; 24 | const connection = mongoose.connection; 25 | connection.on('error', console.error.bind(console, 'connection error:')); 26 | connection.once('open', async () => { 27 | console.log('Connection Successful!'); 28 | 29 | await drop(connection); 30 | 31 | await insertSeed(File, fileSeed); 32 | await insertSeed(Project, projectSeed); 33 | await insertSeed(User, userSeed); 34 | 35 | connection.close(); 36 | }); 37 | 38 | mongoose.connect(MONGODB_URI, { 39 | useNewUrlParser: true, 40 | useUnifiedTopology: true 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /api-server/database/seeds/file.seed.js: -------------------------------------------------------------------------------- 1 | import getTemplate from './contents.template'; 2 | 3 | const data = [ 4 | { 5 | _id: '5dd553be4561ae2bae9cb463', 6 | projectId: '5dd61901353f4858e1b5a9d0', 7 | name: 'root', 8 | type: 'directory', 9 | child: ['5dd553be4561ae2bae9cb45f', '5dd553be4561ae2bae9cb462'] 10 | }, 11 | { 12 | _id: '5dd553be4561ae2bae9cb45f', 13 | projectId: '5dd61901353f4858e1b5a9d0', 14 | name: 'src', 15 | type: 'directory', 16 | child: [ 17 | '5dd553be4561ae2bae9cb45d', 18 | '5dd553be4561ae2bae9cb45e', 19 | '5dd553be4561ae2bae9cb461' 20 | ] 21 | }, 22 | { 23 | name: 'index.js', 24 | projectId: '5dd61901353f4858e1b5a9d0', 25 | type: 'js', 26 | contents: getTemplate('version1'), 27 | _id: '5dd553be4561ae2bae9cb45d' 28 | }, 29 | { 30 | name: 'Jisoo.js', 31 | projectId: '5dd61901353f4858e1b5a9d0', 32 | type: 'js', 33 | contents: getTemplate('Apple'), 34 | _id: '5dd553be4561ae2bae9cb45e' 35 | }, 36 | { 37 | _id: '5dd553be4561ae2bae9cb461', 38 | projectId: '5dd61901353f4858e1b5a9d0', 39 | name: 'heera', 40 | type: 'directory', 41 | child: ['5dd553be4561ae2bae9cb460'] 42 | }, 43 | { 44 | name: 'Banana.js', 45 | projectId: '5dd61901353f4858e1b5a9d0', 46 | type: 'js', 47 | contents: getTemplate('Banana'), 48 | _id: '5dd553be4561ae2bae9cb460' 49 | }, 50 | { 51 | name: 'package.json', 52 | projectId: '5dd61901353f4858e1b5a9d0', 53 | type: 'npm', 54 | contents: getTemplate('package'), 55 | _id: '5dd553be4561ae2bae9cb462' 56 | } 57 | ]; 58 | 59 | export default data; 60 | -------------------------------------------------------------------------------- /api-server/database/seeds/index.js: -------------------------------------------------------------------------------- 1 | import fileSeed from './file.seed'; 2 | import projectSeed from './project.seed'; 3 | import userSeed from './user.seed'; 4 | 5 | export { fileSeed, projectSeed, userSeed }; 6 | -------------------------------------------------------------------------------- /api-server/database/seeds/project.seed.js: -------------------------------------------------------------------------------- 1 | const mock = [ 2 | { 3 | _id: '5ddbc7db8eb1b85c55a84b17', 4 | name: 'root', 5 | description: 'directory', 6 | author: 'BasilToast', 7 | root: '5dd553be4561ae2bae9cb463', 8 | entry: '5dd553be4561ae2bae9cb45d' 9 | }, 10 | { 11 | _id: '5ddbc9417010185d942569cc', 12 | name: 'root', 13 | description: 'directory', 14 | author: 'hzoou', 15 | root: '5dd553be4561ae2bae9cb463', 16 | entry: '5dd553be4561ae2bae9cb45d' 17 | }, 18 | { 19 | _id: '5dd61901353f4858e1b5a9d0', 20 | name: '안녕난희라', 21 | description: 'directory', 22 | author: 'lallaheeee', 23 | root: '5dd553be4561ae2bae9cb463', 24 | entry: '5dd553be4561ae2bae9cb45d' 25 | }, 26 | { 27 | _id: '5dd61901353f4858e1b5a9d2', 28 | name: 'root', 29 | description: 'directory', 30 | author: 'lallaheeee', 31 | root: '5dd553be4561ae2bae9cb463', 32 | entry: '5dd553be4561ae2bae9cb45d' 33 | }, 34 | { 35 | _id: '5ddbc9a59d5b885e041d5275', 36 | name: 'root', 37 | description: 'directory', 38 | author: 'lallaheeee', 39 | root: '5dd553be4561ae2bae9cb463', 40 | entry: '5dd553be4561ae2bae9cb45d' 41 | }, 42 | { 43 | _id: '5ddbc9a59d5b885e041d5276', 44 | name: '안녕 난지수', 45 | description: 'directory', 46 | author: 'lallaheeee', 47 | root: '5dd553be4561ae2bae9cb463', 48 | entry: '5dd553be4561ae2bae9cb45d' 49 | }, 50 | { 51 | _id: '5ddbc9a59d5b885e041d5277', 52 | name: 'root', 53 | description: 'directory', 54 | author: 'basiltoast', 55 | root: '5dd553be4561ae2bae9cb463', 56 | entry: '5dd553be4561ae2bae9cb45d' 57 | } 58 | ]; 59 | 60 | export default mock; 61 | -------------------------------------------------------------------------------- /api-server/database/seeds/user.seed.js: -------------------------------------------------------------------------------- 1 | const mock = [ 2 | { 3 | _id: '5ddbc8d44ee9665d2649b35c', 4 | username: 'BasilToast', 5 | projectIds: ['5ddbc7db8eb1b85c55a84b17'] 6 | }, 7 | { 8 | _id: '5ddbc974ea2c155dbbb33b49', 9 | username: 'hzoou', 10 | projectIds: ['5ddbc9417010185d942569cc'] 11 | }, 12 | { 13 | _id: '5ddbc7db8eb1b85c55a84b10', 14 | username: 'YukJiSoo', 15 | projectIds: ['5dd61901353f4858e1b5a9d0'] 16 | }, 17 | { 18 | _id: '5ddbc7db8eb1b85c55a84b11', 19 | username: 'lallaheeee', 20 | projectIds: ['5dd61901353f4858e1b5a9d2'] 21 | } 22 | ]; 23 | 24 | export default mock; 25 | -------------------------------------------------------------------------------- /api-server/dev.env: -------------------------------------------------------------------------------- 1 | DEV_DATABASE_URI= 2 | PROD_DATABASE_URI= 3 | 4 | DEV_GITHUB_CLIENT_ID= 5 | DEV_GITHUB_CLIENT_SECRET= 6 | PROD_GITHUB_CLIENT_ID= 7 | PROD_GITHUB_CLIENT_SECRET= 8 | GITHUB_REDIRECT_URI= 9 | 10 | JWT_SECRET= 11 | 12 | DEV_COCODE_CLIENT_URI= 13 | PROD_COCODE_CLIENT_URI= 14 | -------------------------------------------------------------------------------- /api-server/ecosystem.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | apps: [ 4 | { 5 | name: 'app', 6 | script: 'babel-node src/app.js', 7 | watch: false, 8 | env: { 9 | NODE_ENV: 'development' 10 | }, 11 | env_production: { 12 | NODE_ENV: 'production' 13 | } 14 | } 15 | ] 16 | }; 17 | -------------------------------------------------------------------------------- /api-server/src/app.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import mongoose from 'mongoose'; 3 | import cors from 'cors'; 4 | import cookieParser from 'cookie-parser'; 5 | import morgan from 'morgan'; 6 | import passport from './middlewares/passport'; 7 | import apiRouter from './routes'; 8 | import { PORT, DATABASE_URI, CORS_OPTION, MONGO_OPTION } from './config'; 9 | 10 | const app = express(); 11 | 12 | const db = mongoose.connection; 13 | db.on('error', console.error.bind(console, 'connection error:')); 14 | db.once('open', () => console.log('connected to Mongo')); 15 | 16 | mongoose.connect(DATABASE_URI, MONGO_OPTION); 17 | 18 | app.use(morgan('dev')); 19 | app.use(cors(CORS_OPTION)); 20 | app.use(cookieParser()); 21 | app.use(express.json()); 22 | app.use(express.urlencoded({ extended: false })); 23 | app.use(passport.initialize()); 24 | 25 | app.use('/api', apiRouter); 26 | 27 | app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`)); 28 | -------------------------------------------------------------------------------- /api-server/src/app.test.js: -------------------------------------------------------------------------------- 1 | test('server empty test', () => { 2 | expect(1).toBe(1); 3 | }); 4 | -------------------------------------------------------------------------------- /api-server/src/config/index.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | const PORT = 3030; 5 | 6 | const { 7 | DEV_GITHUB_CLIENT_ID, 8 | DEV_GITHUB_CLIENT_SECRET, 9 | DEV_GITHUB_REDIRECT_URI, 10 | PROD_GITHUB_CLIENT_ID, 11 | PROD_GITHUB_CLIENT_SECRET, 12 | PROD_GITHUB_REDIRECT_URI, 13 | JWT_SECRET 14 | } = process.env; 15 | 16 | const DATABASE_URI = 17 | process.env.NODE_ENV === 'production' 18 | ? process.env.PROD_DATABASE_URI 19 | : process.env.DEV_DATABASE_URI; 20 | console.log(process.env.NODE_ENV); 21 | 22 | const COCODE_CLIENT_URI = 23 | process.env.NODE_ENV === 'production' 24 | ? process.env.PROD_COCODE_CLIENT_URI 25 | : process.env.DEV_COCODE_CLIENT_URI; 26 | 27 | const DEV_PASSPORT_OPTION = { 28 | clientID: DEV_GITHUB_CLIENT_ID, 29 | clientSecret: DEV_GITHUB_CLIENT_SECRET, 30 | callbackURL: DEV_GITHUB_REDIRECT_URI 31 | }; 32 | 33 | const PROD_PASSPORT_OPTION = { 34 | clientID: PROD_GITHUB_CLIENT_ID, 35 | clientSecret: PROD_GITHUB_CLIENT_SECRET, 36 | callbackURL: PROD_GITHUB_REDIRECT_URI 37 | }; 38 | 39 | const PASSPORT_CLIENT_OPTION = 40 | process.env.NODE_ENV === 'production' 41 | ? PROD_PASSPORT_OPTION 42 | : DEV_PASSPORT_OPTION; 43 | 44 | const CORS_OPTION = { 45 | origin: (origin, callback) => callback(null, true), 46 | exposedHeaders: ['Set-Cookie'], 47 | credentials: true 48 | }; 49 | 50 | const MONGO_OPTION = { 51 | useNewUrlParser: true, 52 | useUnifiedTopology: true, 53 | useCreateIndex: true, 54 | useFindAndModify: false 55 | }; 56 | 57 | export { 58 | PORT, 59 | DATABASE_URI, 60 | COCODE_CLIENT_URI, 61 | PASSPORT_CLIENT_OPTION, 62 | JWT_SECRET, 63 | CORS_OPTION, 64 | MONGO_OPTION 65 | }; 66 | -------------------------------------------------------------------------------- /api-server/src/middlewares/passport.js: -------------------------------------------------------------------------------- 1 | import passport from 'passport'; 2 | import { User } from '../models'; 3 | import { Strategy as GitHubStrategy } from 'passport-github'; 4 | import { PASSPORT_CLIENT_OPTION } from '../config'; 5 | 6 | passport.serializeUser((user, done) => { 7 | done(null, user); 8 | }); 9 | 10 | passport.deserializeUser((user, done) => { 11 | done(null, user); 12 | }); 13 | 14 | passport.use( 15 | new GitHubStrategy( 16 | PASSPORT_CLIENT_OPTION, 17 | (_, __, { username, photos }, done) => { 18 | User.findOneAndUpdate( 19 | { username }, 20 | { $set: { username } }, 21 | { 22 | upsert: true, 23 | new: true 24 | } 25 | ).then(data => { 26 | const user = { 27 | username: data.username, 28 | avatar: photos[0].value 29 | }; 30 | return done(null, user); 31 | }).catch(() => { 32 | return done(null, null); 33 | }); 34 | } 35 | ) 36 | ); 37 | 38 | export default passport; 39 | -------------------------------------------------------------------------------- /api-server/src/models/File.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | const { ObjectId } = mongoose.Schema.Types; 3 | 4 | const FileSchema = new mongoose.Schema( 5 | { 6 | name: { 7 | type: String 8 | }, 9 | type: { 10 | type: String 11 | }, 12 | contents: { 13 | type: String 14 | }, 15 | projectId: { 16 | type: ObjectId 17 | }, 18 | child: [ 19 | { 20 | type: ObjectId, 21 | ref: 'File' 22 | } 23 | ] 24 | }, 25 | { timestamps: true } 26 | ); 27 | 28 | export default mongoose.model('File', FileSchema); 29 | -------------------------------------------------------------------------------- /api-server/src/models/Project.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | const { ObjectId } = mongoose.Schema.Types; 3 | 4 | const ProjectSchema = new mongoose.Schema( 5 | { 6 | name: { 7 | type: String 8 | }, 9 | description: { 10 | type: String 11 | }, 12 | author: { 13 | type: String 14 | }, 15 | root: { 16 | type: ObjectId, 17 | ref: 'File' 18 | }, 19 | entry: { 20 | type: ObjectId, 21 | ref: 'File' 22 | } 23 | }, 24 | { timestamps: true } 25 | ); 26 | 27 | export default mongoose.model('Project', ProjectSchema); 28 | -------------------------------------------------------------------------------- /api-server/src/models/User.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | const { ObjectId } = mongoose.Schema.Types; 3 | 4 | const UserSchema = new mongoose.Schema({ 5 | username: { 6 | type: String, 7 | unique: true, 8 | index: true 9 | }, 10 | projectIds: [ 11 | { 12 | type: ObjectId, 13 | ref: 'Project' 14 | } 15 | ] 16 | }); 17 | 18 | export default mongoose.model('User', UserSchema); 19 | -------------------------------------------------------------------------------- /api-server/src/models/index.js: -------------------------------------------------------------------------------- 1 | import User from './User'; 2 | import Project from './Project'; 3 | import File from './File'; 4 | 5 | export { User, Project, File }; 6 | -------------------------------------------------------------------------------- /api-server/src/routes/dependency/controller.js: -------------------------------------------------------------------------------- 1 | import search from 'libnpmsearch'; 2 | 3 | function parseNpmList(list) { 4 | return list.map(dependency => { 5 | const { 6 | name, 7 | version, 8 | links: { npm, repository } 9 | } = dependency; 10 | 11 | return { 12 | name, 13 | version, 14 | npm, 15 | github: repository 16 | }; 17 | }); 18 | } 19 | 20 | async function getDependencyList({ query }, res) { 21 | const { name } = query; 22 | if (!name) res.sendStatus(412); 23 | 24 | search(name) 25 | .then(list => { 26 | if (!list.length) res.sendStatus(404); 27 | const npmList = parseNpmList(list); 28 | res.status(200).send(npmList); 29 | }) 30 | .catch(() => res.sendStatus(500)); 31 | } 32 | 33 | export { getDependencyList }; 34 | -------------------------------------------------------------------------------- /api-server/src/routes/dependency/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { getDependencyList } from './controller'; 3 | 4 | const router = express.Router(); 5 | 6 | router.get('/search', getDependencyList); 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /api-server/src/routes/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import usersRouter from './users'; 3 | import projectRouter from './projects'; 4 | import dependencyRouter from './dependency'; 5 | 6 | const router = express.Router(); 7 | 8 | router.use('/users', usersRouter); 9 | router.use('/projects', projectRouter); 10 | router.use('/dependency', dependencyRouter); 11 | 12 | export default router; 13 | -------------------------------------------------------------------------------- /api-server/src/routes/projects/files/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { createFile, preloadFile, updateFile, deleteFile } from './controller'; 3 | const router = express.Router(); 4 | 5 | router.post('/', createFile); 6 | router.param('fileId', preloadFile); 7 | router.patch('/:fileId', updateFile); 8 | router.delete('/:fileId', deleteFile); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /api-server/src/routes/projects/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import fileRouter from './files'; 3 | import { 4 | preloadProject, 5 | getProjectByProjectId, 6 | modifyProject, 7 | deleteProject, 8 | forkProject 9 | } from './controller'; 10 | const router = express.Router(); 11 | 12 | router.post('/',forkProject); 13 | router.param('projectId', preloadProject); 14 | router.get('/:projectId', getProjectByProjectId); 15 | router.patch('/:projectId', modifyProject); 16 | router.delete('/:projectId', deleteProject); 17 | 18 | router.use('/:projectId/files', fileRouter); 19 | 20 | export default router; 21 | -------------------------------------------------------------------------------- /api-server/src/routes/users/controller.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import { JWT_SECRET } from '../../config'; 3 | import { Project } from '../../models'; 4 | 5 | async function sendUserData(req, res) { 6 | try { 7 | const token = req.cookies.jwt; 8 | const user = await jwt.verify(token, JWT_SECRET); 9 | res.status(200).send(user); 10 | } catch (e) { 11 | res.status(401).send(); 12 | } 13 | } 14 | 15 | async function getProjectsByUsername(req, res) { 16 | Project.find({ author: req.params.username }) 17 | .then(projects => res.status(200).send(projects)) 18 | .catch(() => res.status(500).send({})); 19 | } 20 | 21 | export { sendUserData, getProjectsByUsername }; 22 | -------------------------------------------------------------------------------- /api-server/src/routes/users/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import loginRouter from './login'; 3 | import { sendUserData, getProjectsByUsername } from './controller'; 4 | 5 | const router = express.Router(); 6 | 7 | router.get('/', sendUserData); 8 | router.get('/:username/projects', getProjectsByUsername); 9 | router.use('/login', loginRouter); 10 | 11 | export default router; 12 | -------------------------------------------------------------------------------- /api-server/src/routes/users/login/controller.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | 3 | import { JWT_SECRET, COCODE_CLIENT_URI } from '../../../config'; 4 | import passport from '../../../middlewares/passport'; 5 | 6 | const STRATEGY = 'github'; 7 | const KEY_JWT = 'jwt'; 8 | 9 | function loginByGithub(req, res, next) { 10 | passport.authenticate(STRATEGY)(req, res, next); 11 | } 12 | 13 | function publishToken({ user: { username, avatar } }, res) { 14 | const payload = { username, avatar }; 15 | const expiresIn = { expiresIn: '7d' }; 16 | const token = jwt.sign(payload, JWT_SECRET, expiresIn); 17 | res.cookie(KEY_JWT, token).redirect(COCODE_CLIENT_URI); 18 | } 19 | 20 | export { loginByGithub, publishToken }; 21 | -------------------------------------------------------------------------------- /api-server/src/routes/users/login/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { loginByGithub, publishToken } from './controller'; 3 | 4 | const router = express.Router(); 5 | 6 | router.get('/', loginByGithub); 7 | router.get('/complete', loginByGithub, publishToken); 8 | 9 | export default router; -------------------------------------------------------------------------------- /api-server/src/utils/transaction.js: -------------------------------------------------------------------------------- 1 | import Transaction from 'mongoose-transactions'; 2 | 3 | const defaultFailHandler = error => console.error(error); 4 | 5 | Transaction.prototype.runAndTerminate = function({ 6 | successHandler, 7 | failHandler = defaultFailHandler 8 | }) { 9 | const transaction = this; 10 | transaction 11 | .run() 12 | .then(successHandler) 13 | .catch(error => { 14 | failHandler(error); 15 | transaction 16 | .rollback() 17 | .then(() => { 18 | transaction.clean(); 19 | res.sendStatus(500); 20 | }) 21 | .catch(() => res.sendStatus(500)); 22 | }); 23 | }; 24 | 25 | export default Transaction; 26 | -------------------------------------------------------------------------------- /cocode/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": ["@babel/plugin-transform-async-to-generator"] 4 | } 5 | -------------------------------------------------------------------------------- /cocode/.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /cocode/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | Dockerfile.dev 3 | 4 | *.md 5 | !README.md 6 | 7 | node_modules -------------------------------------------------------------------------------- /cocode/.eslintignore: -------------------------------------------------------------------------------- 1 | README.md 2 | webpack.config.js 3 | .babelrc 4 | .prettierrc 5 | .env 6 | public/* 7 | src/template/* 8 | init-script/* 9 | -------------------------------------------------------------------------------- /cocode/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier"], 3 | "env": { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "globals": { 8 | "Atomics": "readonly", 9 | "SharedArrayBuffer": "readonly" 10 | }, 11 | "parserOptions": { 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "ecmaVersion": 2018, 16 | "sourceType": "module" 17 | }, 18 | "plugins": ["react"], 19 | "rules": { 20 | "eqeqeq": "error", 21 | "no-empty-function": "error", 22 | "no-multiple-empty-lines": "error", 23 | "no-multi-spaces": "error", 24 | "quotes": ["error", "single"], 25 | "semi": "error", 26 | "switch-colon-spacing": "error", 27 | "array-bracket-spacing": ["error", "never"], 28 | "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], 29 | "keyword-spacing": ["error", { "before": true, "after": true }], 30 | "max-depth": ["error", 5], 31 | "max-len": ["error", { "code": 80 }], 32 | "no-mixed-spaces-and-tabs": "error", 33 | "no-trailing-spaces": "error" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cocode/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /cocode/.prettierignore: -------------------------------------------------------------------------------- 1 | .* 2 | public/* 3 | package-lock.json 4 | package.json 5 | README.md 6 | webpack.config.js -------------------------------------------------------------------------------- /cocode/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 4, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /cocode/.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-knobs/register'; 2 | import '@storybook/addon-actions/register'; 3 | import '@storybook/addon-links/register'; 4 | 5 | -------------------------------------------------------------------------------- /cocode/.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { addDecorator, configure } from '@storybook/react'; 2 | import { withKnobs } from '@storybook/addon-knobs'; 3 | import { ThemeDecorator } from 'stories/decorators/ThemeDecorator'; 4 | 5 | addDecorator(withKnobs); 6 | addDecorator(ThemeDecorator); 7 | configure(require.context('../src', true, /\.stories\.js$/), module); 8 | -------------------------------------------------------------------------------- /cocode/.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = async ({ config }) => { 4 | 5 | config.resolve.modules = [ 6 | ...(config.resolve.modules || []), 7 | path.resolve(path.join(__dirname), '../src'), 8 | ]; 9 | return config; 10 | }; 11 | -------------------------------------------------------------------------------- /cocode/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.13.0 as builder 2 | 3 | RUN mkdir /usr/src/app 4 | WORKDIR /usr/src/app 5 | ENV PATH /usr/src/app/node_modules/.bin:$PATH 6 | COPY package.json /usr/src/app/package.json 7 | RUN yarn install --silent 8 | RUN yarn global add react-scripts@2.1.3 --silent 9 | 10 | COPY . /usr/src/app 11 | RUN yarn init:modules 12 | 13 | RUN yarn build 14 | 15 | ### 16 | 17 | FROM nginx:1.13.9-alpine 18 | 19 | RUN rm -rf /etc/nginx/conf.d 20 | COPY nginx /etc/nginx 21 | 22 | COPY --from=builder /usr/src/app/public /usr/share/nginx/html 23 | 24 | EXPOSE 80 25 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /cocode/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:12.13.0 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | RUN yarn install 7 | RUN yarn init 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | CMD ["yarn", "start:docker"] -------------------------------------------------------------------------------- /cocode/README.md: -------------------------------------------------------------------------------- 1 | # cocode 2 | 3 | ## usage 4 | 5 | ```json 6 | "scripts": { 7 | "start": "webpack-dev-server --open", 8 | "watch": "webpack --watch", 9 | "build": "webpack --production", 10 | "build:dev": "webpack --develope", 11 | "precommit": "npm test & lint-staged", 12 | "prepush": "npm test", 13 | "test": "ls" 14 | } 15 | ``` 16 | 17 | - `start` 개발용 서버를 실행합니다. 18 | - `build` 배포용 파일을 번들링합니다. 19 | 20 | ```javascript 21 | entry : __dirname + '/src/index.js', 22 | output : { 23 | path: __dirname + '/public', 24 | filename : 'bundle.js' 25 | } 26 | ``` 27 | 28 | entry 파일은 /src/index.js 파일입니다. 해당파일에서 29 | /src/app.js 파일을 body태그에 랜더링합니다. 30 | 31 | webpack-dev-server를 실행한 경우 번들링된 파일을 생성하지 않습니다. 32 | -------------------------------------------------------------------------------- /cocode/dev.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | 3 | DEV_API_SERVER_IP= 4 | PROD_API_SERVER_IP= 5 | 6 | PROD_DEPENDENCY_SERVER_IP= 7 | DEV_DEPENDENCY_SERVER_IP= 8 | 9 | PROD_COCONUT_SERVER_IP= 10 | DEV_COCONUT_SERVER_IP= 11 | 12 | PROD_LIVE_SERVER_IP= 13 | DEV_LIVE_SERVER_IP= 14 | 15 | PROD_COCODE_SERVER_IP= 16 | DEV_COCODE_SERVER_IP= -------------------------------------------------------------------------------- /cocode/init-script/get-module-list-for-target-version.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { coerce, lte } = require('semver'); 3 | const modulesByVersions = require('./modules-by-versions.json'); 4 | 5 | module.exports = function(raw) { 6 | const corejs = coerce(String(raw)); 7 | if (corejs.major !== 3) { 8 | throw RangeError( 9 | 'This version of `core-js-compat` works only with `core-js@3`.' 10 | ); 11 | } 12 | const result = []; 13 | for (const version of Object.keys(modulesByVersions)) { 14 | if (lte(coerce(version), corejs)) { 15 | result.push(...modulesByVersions[version]); 16 | } 17 | } 18 | return result; 19 | }; 20 | -------------------------------------------------------------------------------- /cocode/init-script/index.js: -------------------------------------------------------------------------------- 1 | const { promises } = require('fs'); 2 | const path = require('path'); 3 | 4 | const fs = promises; 5 | const nodeModulesPath = path.join(__dirname, '..', '/node_modules'); 6 | const replaceFilesPath = path.join(__dirname); 7 | 8 | async function replaceNodeModules(fileName, targetPath) { 9 | try { 10 | const fileAbsolutePath = `${replaceFilesPath}${fileName}`; 11 | const fileContent = await fs.readFile(fileAbsolutePath, { 12 | encoding: 'utf8' 13 | }); 14 | 15 | const nodeModuleFileAbsolutePath = `${nodeModulesPath}${targetPath}`; 16 | await fs.writeFile(nodeModuleFileAbsolutePath, fileContent, 'utf8'); 17 | console.log(`change success ${fileName}`); 18 | } catch (error) { 19 | console.log(`change fail ${fileName}`); 20 | console.error(error); 21 | } 22 | } 23 | 24 | const fileList = [ 25 | { 26 | fileName: '/entry-plugin.js', 27 | targetPath: '/@babel/preset-env/lib/polyfills/corejs3/entry-plugin.js' 28 | }, 29 | { 30 | fileName: '/get-module-list-for-target-version.js', 31 | targetPath: '/core-js-compat/get-modules-list-for-target-version.js' 32 | }, 33 | { 34 | fileName: '/normalize-options.js', 35 | targetPath: '/@babel/preset-env/lib/normalize-options.js' 36 | }, 37 | { 38 | fileName: '/usage-plugin.js', 39 | targetPath: '/@babel/preset-env/lib/polyfills/corejs3/usage-plugin.js' 40 | } 41 | ]; 42 | 43 | fileList.forEach(({ fileName, targetPath }) => 44 | replaceNodeModules(fileName, targetPath) 45 | ); 46 | -------------------------------------------------------------------------------- /cocode/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name 127.0.0.1; 3 | listen 80; 4 | 5 | location / { 6 | root /usr/share/nginx/html; 7 | index index.html index.htm; 8 | try_files $uri $uri/ /index.html; 9 | } 10 | 11 | error_page 500 502 503 504 /50x.html; 12 | location = /50x.html { 13 | root /usr/share/nginx/html; 14 | } 15 | } -------------------------------------------------------------------------------- /cocode/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/cocode/public/favicon-16x16.png -------------------------------------------------------------------------------- /cocode/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cocode! - the SaaS IDE 5 | 11 | 12 | 16 | 17 | 18 |
19 |
20 | 21 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /cocode/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; 3 | import { ThemeProvider } from 'styled-components'; 4 | 5 | import { DEFAULT_THEME } from 'constants/theme'; 6 | import { UserContext } from 'contexts'; 7 | import useFetch from 'hooks/useFetch'; 8 | import { getUserAPICreator } from 'apis/User'; 9 | import { LiveStore } from 'stores'; 10 | 11 | import GlobalStyle from 'components/Common/GlobalStyle'; 12 | import { Home, DashBoard, Project, NotFound, Live, SignIn, Empty } from 'pages'; 13 | 14 | function App() { 15 | const [user, setUser] = useState(null); 16 | const [{ data }] = useFetch(getUserAPICreator()); 17 | 18 | useEffect(() => { 19 | setUser(data); 20 | }, [data]); 21 | 22 | return ( 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | 46 | export default App; 47 | -------------------------------------------------------------------------------- /cocode/src/App.test.js: -------------------------------------------------------------------------------- 1 | it('App.js', () => { 2 | expect(true).toBeTruthy(); 3 | }); 4 | -------------------------------------------------------------------------------- /cocode/src/actions/API.js: -------------------------------------------------------------------------------- 1 | import { API_READY, API_LOADING, API_SUCCESS, API_FAIL } from './types'; 2 | 3 | function fetchLoadActionCreator() { 4 | return { type: API_LOADING }; 5 | } 6 | 7 | function fetchSuccessActionCreator({ status, data }) { 8 | return { type: API_SUCCESS, payload: { status, data } }; 9 | } 10 | 11 | function fetchFailActionCreator(error) { 12 | return { type: API_FAIL, payload: error }; 13 | } 14 | 15 | function fetchReadyActionCreator() { 16 | return { type: API_READY }; 17 | } 18 | 19 | export { 20 | fetchReadyActionCreator, 21 | fetchLoadActionCreator, 22 | fetchSuccessActionCreator, 23 | fetchFailActionCreator 24 | }; 25 | -------------------------------------------------------------------------------- /cocode/src/actions/Dashboard.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_COCONUT_NAME, DELETE_COCONUT, FETCH_COCONUT } from './types'; 2 | 3 | function fetchCoconutActionCreator(payload) { 4 | return { type: FETCH_COCONUT, payload }; 5 | } 6 | 7 | function updateCoconutNameActionCreator(payload) { 8 | return { type: UPDATE_COCONUT_NAME, payload }; 9 | } 10 | 11 | function deleteCoconutActionCreator(payload) { 12 | return { type: DELETE_COCONUT, payload }; 13 | } 14 | 15 | export { 16 | fetchCoconutActionCreator, 17 | updateCoconutNameActionCreator, 18 | deleteCoconutActionCreator 19 | }; 20 | -------------------------------------------------------------------------------- /cocode/src/actions/Live.js: -------------------------------------------------------------------------------- 1 | import { 2 | LIVE_ON, 3 | LIVE_OFF, 4 | LIVE_JOIN_USER, 5 | LIVE_LEAVE_USER 6 | } from './types'; 7 | 8 | function liveOnActionCreator(payload) { 9 | return { type: LIVE_ON, payload }; 10 | } 11 | function liveOffActionCreator() { 12 | return { type: LIVE_OFF }; 13 | } 14 | function liveJoinUserActionCreator(payload) { 15 | return { type: LIVE_JOIN_USER, payload }; 16 | } 17 | function liveLeaveUserActionCreator(payload) { 18 | return { type: LIVE_LEAVE_USER, payload }; 19 | } 20 | 21 | export { 22 | liveOnActionCreator, 23 | liveOffActionCreator, 24 | liveJoinUserActionCreator, 25 | liveLeaveUserActionCreator 26 | }; 27 | -------------------------------------------------------------------------------- /cocode/src/actions/types.js: -------------------------------------------------------------------------------- 1 | // API 2 | const API_READY = 'API_READY'; 3 | const API_LOADING = 'API_LOADING'; 4 | const API_SUCCESS = 'API_SUCCESS'; 5 | const API_FAIL = 'API_FAILURE'; 6 | 7 | // Project 8 | const UPDATE_PROJECT_INFO = 'updateProjectInfo'; 9 | const UPDATE_CODE = 'updateCode'; 10 | const UPDATE_CODE_FROM_FILE_ID = 'updateCodeFromFileId'; 11 | const FETCH_PROJECT = 'fetchProject'; 12 | const UPDATE_FILES = 'updateFiles'; 13 | const SELECT_FILE = 'selectFile'; 14 | const CREATE_FILE = 'createFile'; 15 | const UPDATE_FILE_NAME = 'updateFileName'; 16 | const DELETE_FILE = 'deleteFile'; 17 | const MOVE_FILE = 'moveFile'; 18 | const INSTALL_DEPENDENCY = 'installDependency'; 19 | const WAITING_INSTALL_DEPENDENCY = 'waitingInstallDependency'; 20 | const SAVE_FILE = 'saveFile'; 21 | 22 | //DashBoard 23 | const FETCH_COCONUT = 'fetchCoconut'; 24 | const UPDATE_COCONUT_NAME = 'updateCoconutName'; 25 | const DELETE_COCONUT = 'deleteCoconut'; 26 | 27 | //Live 28 | const LIVE_ON = 'liveOn'; 29 | const LIVE_OFF = 'liveOff'; 30 | const LIVE_JOIN_USER = 'liveJoinUser'; 31 | const LIVE_LEAVE_USER = 'liveLeaveUser'; 32 | 33 | export { 34 | API_READY, 35 | API_LOADING, 36 | API_SUCCESS, 37 | API_FAIL, 38 | UPDATE_PROJECT_INFO, 39 | UPDATE_CODE, 40 | UPDATE_CODE_FROM_FILE_ID, 41 | FETCH_PROJECT, 42 | UPDATE_FILES, 43 | SELECT_FILE, 44 | UPDATE_FILE_NAME, 45 | CREATE_FILE, 46 | DELETE_FILE, 47 | MOVE_FILE, 48 | INSTALL_DEPENDENCY, 49 | WAITING_INSTALL_DEPENDENCY, 50 | FETCH_COCONUT, 51 | UPDATE_COCONUT_NAME, 52 | DELETE_COCONUT, 53 | LIVE_ON, 54 | LIVE_OFF, 55 | LIVE_JOIN_USER, 56 | LIVE_LEAVE_USER, 57 | SAVE_FILE 58 | }; 59 | -------------------------------------------------------------------------------- /cocode/src/apis/DashBoard.js: -------------------------------------------------------------------------------- 1 | import { API } from 'config'; 2 | 3 | function getCoconutsAPICreator(username) { 4 | return { 5 | url: `${API.users}/${username}/projects`, 6 | method: 'get' 7 | }; 8 | } 9 | 10 | function updateCoconutsAPICreator(projectId, data) { 11 | return { 12 | url: `${API.projects}/${projectId}`, 13 | method: 'patch', 14 | data 15 | }; 16 | } 17 | 18 | function deleteCoconutsAPICreator(projectId) { 19 | return { 20 | url: `${API.projects}/${projectId}`, 21 | method: 'delete' 22 | }; 23 | } 24 | 25 | export { 26 | getCoconutsAPICreator, 27 | updateCoconutsAPICreator, 28 | deleteCoconutsAPICreator 29 | }; 30 | -------------------------------------------------------------------------------- /cocode/src/apis/File.js: -------------------------------------------------------------------------------- 1 | import { API } from 'config'; 2 | 3 | function createFileAPICreator(projectId, data) { 4 | return { 5 | url: API.files(projectId), 6 | method: 'post', 7 | data 8 | }; 9 | } 10 | 11 | function deleteFileAPICreator(projectId, fileId, data) { 12 | return { 13 | url: `${API.files(projectId)}/${fileId}`, 14 | method: 'delete', 15 | data 16 | }; 17 | } 18 | 19 | function updateFileAPICreator(projectId, fileId, data) { 20 | return { 21 | url: `${API.files(projectId)}/${fileId}`, 22 | method: 'patch', 23 | data 24 | }; 25 | } 26 | 27 | export { createFileAPICreator, deleteFileAPICreator, updateFileAPICreator }; 28 | -------------------------------------------------------------------------------- /cocode/src/apis/Project.js: -------------------------------------------------------------------------------- 1 | import { API } from 'config'; 2 | 3 | function getProjectInfoAPICreator(projectId) { 4 | return { 5 | url: `${API.projects}/${projectId}`, 6 | method: 'get' 7 | }; 8 | } 9 | 10 | function forkProjectAPICreator(data) { 11 | return { 12 | url: `${API.projects}`, 13 | method: 'post', 14 | data 15 | }; 16 | } 17 | 18 | function getDenpendencyListAPICreator(name) { 19 | return { 20 | url: `${API.dependency(name)}`, 21 | method: 'get' 22 | }; 23 | } 24 | 25 | export { 26 | getProjectInfoAPICreator, 27 | forkProjectAPICreator, 28 | getDenpendencyListAPICreator 29 | }; 30 | -------------------------------------------------------------------------------- /cocode/src/apis/User.js: -------------------------------------------------------------------------------- 1 | import { API } from 'config'; 2 | 3 | function getUserAPICreator() { 4 | return { 5 | method: 'get', 6 | url: API.getUserData, 7 | }; 8 | } 9 | 10 | export { getUserAPICreator }; 11 | -------------------------------------------------------------------------------- /cocode/src/components/Common/CloseButton/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cocode/src/components/Common/CloseButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | import close from './close.svg'; 4 | 5 | function CloseButton({ onClick }) { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | export default CloseButton; 14 | -------------------------------------------------------------------------------- /cocode/src/components/Common/CloseButton/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Button = styled.button` 4 | & { 5 | opacity: 0.7; 6 | } 7 | 8 | &:hover { 9 | opacity: 1; 10 | } 11 | `; 12 | 13 | export { Button }; -------------------------------------------------------------------------------- /cocode/src/components/Common/CoconutSpinner/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import TinyCoconutLogo from 'components/Common/TinyCoconutLogo'; 5 | 6 | function Spinner() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default Spinner; 18 | -------------------------------------------------------------------------------- /cocode/src/components/Common/CoconutSpinner/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Container = styled.div` 4 | position: relative; 5 | 6 | width: 3.5rem; 7 | height: 3.5rem; 8 | `; 9 | 10 | const Content = styled.div` 11 | position: absolute; 12 | top: 1.1rem; 13 | left: 0.8rem; 14 | `; 15 | 16 | const SpinningArc = styled.div` 17 | position: absolute; 18 | width: 3.5rem; 19 | height: 3.5rem; 20 | background-color: transparent; 21 | 22 | box-sizing: content-box; 23 | border: 3px solid white; 24 | border-radius: 100%; 25 | border-bottom-color: transparent; 26 | 27 | animation: 1s linear 0s infinite normal both running spin; 28 | 29 | @keyframes spin { 30 | 0% { 31 | transform: rotate(0deg); 32 | } 33 | 34 | 100% { 35 | transform: rotate(360deg); 36 | } 37 | } 38 | `; 39 | 40 | export { Container, Content, SpinningArc }; 41 | -------------------------------------------------------------------------------- /cocode/src/components/Common/DropDownMenu/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState } from 'react'; 3 | import * as Styled from './style'; 4 | 5 | function DropDownMenu({ children, menuItems, ...props }) { 6 | const [isOpen, setIsOpen] = useState(false); 7 | 8 | const handleIsOpen = (e) => { 9 | e.stopPropagation(); 10 | setIsOpen(!isOpen); 11 | }; 12 | const handleMenuClose = (handleClick, e) => { 13 | e.stopPropagation(); 14 | setIsOpen(false); 15 | handleClick(e); 16 | }; 17 | return ( 18 | 19 | {React.cloneElement(children, { onClick: handleIsOpen })} 20 | {isOpen && ( 21 | 22 | {menuItems && 23 | menuItems.map( 24 | ({ value, handleClick, ...props }, key) => ( 25 | 33 | {value} 34 | 35 | ) 36 | )} 37 | 38 | )} 39 | 40 | ); 41 | } 42 | 43 | export default DropDownMenu; 44 | -------------------------------------------------------------------------------- /cocode/src/components/Common/DropDownMenu/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import DropDownMenu from '.'; 4 | 5 | export default { 6 | title: 'DropDownMenu' 7 | }; 8 | 9 | const Box = styled.div` 10 | display: flex; 11 | flex-direction: row; 12 | `; 13 | 14 | const Button = styled.button` 15 | padding: 2rem; 16 | width: 10rem; 17 | height: 4rem; 18 | background-color: #5b5b5b; 19 | `; 20 | 21 | export const dropDownMenu = () => { 22 | const menuItems = [ 23 | { 24 | value: 'test1', 25 | onClick: () => alert('test1') 26 | }, 27 | { 28 | value: 'test2', 29 | onClick: () => alert('test2') 30 | }, 31 | { 32 | value: 'test3', 33 | onClick: () => alert('test3') 34 | }, 35 | { 36 | value: 'test4', 37 | onClick: () => alert('test4') 38 | }, 39 | { 40 | value: 'test5', 41 | onClick: () => alert('test5') 42 | } 43 | ]; 44 | return ( 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /cocode/src/components/Common/DropDownMenu/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DROPDOWN_THEME } from 'constants/theme'; 3 | 4 | const DropDownMenu = styled.div` 5 | & { 6 | position: relative; 7 | display: flex; 8 | justify-content: flex-end; 9 | 10 | } 11 | `; 12 | 13 | const DropDownList = styled.ul` 14 | & { 15 | position: absolute; 16 | display: flex; 17 | flex-direction: column; 18 | 19 | top: 100%; 20 | 21 | margin: 0; 22 | padding: 0; 23 | z-index: 5; 24 | } 25 | `; 26 | 27 | const DropDownItem = styled.button` 28 | & { 29 | height: 2.5rem; 30 | width: 7rem; 31 | padding: 0.25rem 1rem; 32 | z-index: 1; 33 | 34 | font-size: 1rem; 35 | font-weight: 300; 36 | vertical-align: center; 37 | text-align: right; 38 | 39 | background-color: ${DROPDOWN_THEME.dropdownButtonColor}; 40 | 41 | border: none; 42 | border-top: 1px solid ${DROPDOWN_THEME.dropdownButtonBorderColor}; 43 | } 44 | &:hover { 45 | background-color: ${DROPDOWN_THEME.dropdownButtonHoverColor}; 46 | cursor: pointer; 47 | } 48 | `; 49 | 50 | export { DropDownMenu, DropDownList, DropDownItem }; 51 | -------------------------------------------------------------------------------- /cocode/src/components/Common/DropZone/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | function DropZone({ draggableComponentOverColor, ...props }) { 5 | const handleDragOver = e => { 6 | e.stopPropagation(); 7 | e.preventDefault(); 8 | props.handleDragOver(); 9 | }; 10 | 11 | const handleDragLeave = e => { 12 | e.stopPropagation(); 13 | props.handleDragLeave(e); 14 | }; 15 | 16 | const handleDrop = e => { 17 | e.stopPropagation(); 18 | const data = e.dataTransfer.getData('text'); 19 | props.handleDrop(data); 20 | }; 21 | 22 | return ( 23 | 30 | ); 31 | } 32 | 33 | export default DropZone; 34 | -------------------------------------------------------------------------------- /cocode/src/components/Common/DropZone/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const DropZone = styled.div` 4 | & { 5 | background-color: ${({ draggableComponentOverColor }) => 6 | draggableComponentOverColor 7 | ? draggableComponentOverColor 8 | : 'transparent'}; 9 | height: ${({ height }) => height}; 10 | } 11 | `; 12 | 13 | export { DropZone }; 14 | -------------------------------------------------------------------------------- /cocode/src/components/Common/ExampleButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { LinkButton, AButton, Button } from './style'; 3 | 4 | function ExampleButton({ style = {}, to = null, href = null, ...props }) { 5 | if (to) return ; 6 | 7 | if (href) return ; 8 | 9 | return ; 13 | } 14 | 15 | function hrefButton() { 16 | return ( 17 | 20 | ); 21 | } 22 | 23 | function redButton() { 24 | return ; 25 | } 26 | 27 | export { basicButton, hrefButton, redButton }; 28 | -------------------------------------------------------------------------------- /cocode/src/components/Common/ExampleButton/style.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components'; 2 | import Link from 'react-router-dom/Link'; 3 | 4 | const styles = css` 5 | border: none; 6 | outline: none; 7 | background-color: ${props => (props.red ? '#F27777' : '#66B9F4')}; 8 | 9 | border-radius: 0.1rem; 10 | 11 | box-sizing: border-box; 12 | font-size: 1.125em; 13 | text-align: center; 14 | color: white; 15 | `; 16 | 17 | const LinkButton = styled(Link)` 18 | ${styles}; 19 | `; 20 | const AButton = styled.a` 21 | ${styles}; 22 | `; 23 | const Button = styled.button` 24 | ${styles}; 25 | `; 26 | 27 | export { LinkButton, AButton, Button }; 28 | -------------------------------------------------------------------------------- /cocode/src/components/Common/LoginModalBody/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import { API } from 'config'; 5 | 6 | import Github from './github.svg'; 7 | 8 | function LoginModalBody() { 9 | const handleClickLoginButton = () => { 10 | window.location.href = API.login; 11 | localStorage.setItem('redirectURL', window.location.href); 12 | }; 13 | 14 | return ( 15 | 16 | Sign In 17 | 18 | 19 | Sign In With GitHub 20 | 21 | 22 | ); 23 | } 24 | 25 | export default LoginModalBody; 26 | -------------------------------------------------------------------------------- /cocode/src/components/Common/LoginModalBody/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Logo = styled.img` 4 | padding-right: 0.8rem; 5 | `; 6 | 7 | const LoginModalBody = styled.div` 8 | & { 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | 13 | width: 25rem; 14 | } 15 | `; 16 | 17 | const LoginModalBodyTitle = styled.h1` 18 | & { 19 | text-align: center; 20 | font-size: 3rem; 21 | } 22 | `; 23 | 24 | const LoginModalBodyButton = styled.button` 25 | & { 26 | margin: 3rem 0 1.5rem 0; 27 | padding: 0.5rem 1.5rem; 28 | 29 | font-size: 1.25rem; 30 | 31 | background-color: black; 32 | opacity: 0.85; 33 | border-radius: 0.5rem; 34 | } 35 | 36 | &:hover { 37 | opacity: 1; 38 | } 39 | `; 40 | 41 | export { Logo, LoginModalBody, LoginModalBodyTitle, LoginModalBodyButton }; 42 | -------------------------------------------------------------------------------- /cocode/src/components/Common/Logo/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import logo from './logo.svg'; 3 | 4 | function Logo() { 5 | return {'로고'}; 6 | } 7 | 8 | export default Logo; 9 | -------------------------------------------------------------------------------- /cocode/src/components/Common/Logo/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Logo from '.'; 3 | 4 | export default { 5 | title: 'Logo' 6 | }; 7 | 8 | export const logo = () => ; 9 | -------------------------------------------------------------------------------- /cocode/src/components/Common/Modal/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import CloseButton from 'components/Common/CloseButton'; 5 | 6 | function Modal({ modalBody, onClose }) { 7 | const openModal = () => { 8 | const root = document.getElementById('root'); 9 | root.style.overflow = 'hidden'; 10 | }; 11 | 12 | const closeModal = () => { 13 | const root = document.getElementById('root'); 14 | root.style.overflow = 'initial'; 15 | onClose(); 16 | }; 17 | 18 | useEffect(openModal, []); 19 | 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | {modalBody} 27 | 28 | 29 | ); 30 | } 31 | 32 | export default Modal; 33 | -------------------------------------------------------------------------------- /cocode/src/components/Common/Modal/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const ModalBackGround = styled.div` 4 | & { 5 | z-index: 10; 6 | position: fixed; 7 | top: 0; 8 | left: 0; 9 | 10 | display: flex; 11 | align-items: center; 12 | 13 | width: 100%; 14 | height: 100%; 15 | 16 | background-color: ${({ theme }) => theme.blackOpaqueColor}; 17 | } 18 | `; 19 | 20 | const Modal = styled.dialog` 21 | & { 22 | display: flex; 23 | flex-direction: column; 24 | flex-wrap: wrap; 25 | 26 | margin: auto; 27 | 28 | height: auto; 29 | 30 | background-color: white; 31 | } 32 | `; 33 | 34 | const ModalHeader = styled.header` 35 | & { 36 | padding: 1rem; 37 | 38 | display: flex; 39 | flex-direction: row; 40 | justify-content: flex-end; 41 | align-items: flex-end; 42 | } 43 | `; 44 | 45 | const ModalBody = styled.section``; 46 | 47 | export { ModalBackGround, Modal, ModalHeader, ModalBody }; 48 | -------------------------------------------------------------------------------- /cocode/src/components/Common/ModalPortal/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | 3 | const ModalPortal = ({ children }) => { 4 | const modalElement = document.getElementById('modal'); 5 | return ReactDOM.createPortal(children, modalElement); 6 | }; 7 | 8 | export default ModalPortal; 9 | -------------------------------------------------------------------------------- /cocode/src/components/Common/SplitPane/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 tomkp 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 | 23 | https://github.com/tomkp/react-split-pane -------------------------------------------------------------------------------- /cocode/src/components/Common/SplitPane/Pane.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import stylePropType from 'react-style-proptype'; 4 | 5 | class Pane extends React.PureComponent { 6 | render() { 7 | const { 8 | children, 9 | className, 10 | split, 11 | style: styleProps, 12 | size, 13 | eleRef, 14 | } = this.props; 15 | 16 | const classes = ['Pane', split, className]; 17 | 18 | let style = { 19 | flex: 1, 20 | position: 'relative', 21 | outline: 'none', 22 | }; 23 | 24 | if (size !== undefined) { 25 | if (split === 'vertical') { 26 | style.width = size; 27 | } else { 28 | style.height = size; 29 | style.display = 'flex'; 30 | } 31 | style.flex = 'none'; 32 | } 33 | 34 | style = Object.assign({}, style, styleProps || {}); 35 | 36 | return ( 37 |
38 | {children} 39 |
40 | ); 41 | } 42 | } 43 | 44 | Pane.propTypes = { 45 | className: PropTypes.string.isRequired, 46 | children: PropTypes.node.isRequired, 47 | size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 48 | split: PropTypes.oneOf(['vertical', 'horizontal']), 49 | style: stylePropType, 50 | eleRef: PropTypes.func, 51 | }; 52 | 53 | Pane.defaultProps = {}; 54 | 55 | export default Pane; 56 | -------------------------------------------------------------------------------- /cocode/src/components/Common/SplitPane/index.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | import SplitPane from './SplitPane'; 4 | import Pane from './Pane'; 5 | 6 | const SplitPaneContainer = styled(SplitPane)` 7 | .Resizer { 8 | box-sizing: border-box; 9 | background: #000; 10 | opacity: 0.5; 11 | z-index: 1; 12 | background-clip: padding-box; 13 | } 14 | 15 | .Resizer:hover { 16 | -webkit-transition: all 1s ease; 17 | transition: all 1s ease; 18 | } 19 | 20 | .Resizer.horizontal { 21 | height: 11px; 22 | margin: -5px 0; 23 | border-top: 5px solid rgba(255, 255, 255, 0); 24 | border-bottom: 5px solid rgba(255, 255, 255, 0); 25 | cursor: row-resize; 26 | width: 100%; 27 | } 28 | 29 | .Resizer.horizontal:hover { 30 | border-top: 5px solid rgba(0, 0, 0, 0.5); 31 | border-bottom: 5px solid rgba(0, 0, 0, 0.5); 32 | } 33 | 34 | .Resizer.vertical { 35 | width: 11px; 36 | margin: 0 -5px; 37 | border-left: 5px solid rgba(255, 255, 255, 0); 38 | border-right: 5px solid rgba(255, 255, 255, 0); 39 | cursor: col-resize; 40 | } 41 | 42 | .Resizer.vertical:hover { 43 | border-left: 5px solid rgba(0, 0, 0, 0.5); 44 | border-right: 5px solid rgba(0, 0, 0, 0.5); 45 | } 46 | 47 | & > * > * { 48 | height: 100%; 49 | } 50 | `; 51 | 52 | export default SplitPane; 53 | export { Pane, SplitPaneContainer }; 54 | -------------------------------------------------------------------------------- /cocode/src/components/Common/Toast/ToastContainer.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useState } from 'react'; 2 | import * as Styled from './style'; 3 | import ToastItem from './ToastItem'; 4 | 5 | function ToastContainer({ toast }) { 6 | const [toasts, setToasts] = useState([]); 7 | 8 | const updateToastItems = () => { 9 | if (toast) setToasts([...toasts, toast]); 10 | }; 11 | const close = useCallback(id => { 12 | const newToasts = toasts.filter(toast => toast.id !== id); 13 | setToasts(newToasts); 14 | }); 15 | 16 | useEffect(updateToastItems, [toast]); 17 | 18 | return ( 19 | 20 | {toasts.map(({ id, ...props }) => ( 21 | 22 | ))} 23 | 24 | ); 25 | } 26 | 27 | export default ToastContainer; 28 | -------------------------------------------------------------------------------- /cocode/src/components/Common/Toast/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import React from 'react'; 3 | import ToastContainer from './ToastContainer'; 4 | 5 | function addToast(message, options) { 6 | const toastElement = document.getElementById('toast-root'); 7 | 8 | const newToast = { 9 | id: Math.random(), 10 | type: options.type || 'info', 11 | message, 12 | ...options 13 | }; 14 | 15 | ReactDOM.render(, toastElement); 16 | } 17 | 18 | const setAddToastFunctionAtToastTypeProperty = type => 19 | (addToast[type] = (message, options) => 20 | addToast(message, { ...options, type })); 21 | 22 | const TOAST_TYPES = ['info', 'error']; 23 | TOAST_TYPES.forEach(setAddToastFunctionAtToastTypeProperty); 24 | 25 | export default addToast; 26 | -------------------------------------------------------------------------------- /cocode/src/components/Common/UserProfile/down.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /cocode/src/components/Common/UserProfile/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import DropDownMenu from 'components/Common/DropDownMenu'; 5 | import down from './down.svg'; 6 | 7 | function UserProfile({ username, avatar, menuItems }) { 8 | return ( 9 | 10 | {username} 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default UserProfile; 20 | -------------------------------------------------------------------------------- /cocode/src/components/Common/UserProfile/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Profile from '.'; 3 | import avatar from './avatar.jpeg'; 4 | 5 | export default { 6 | title: 'Profile' 7 | }; 8 | const profileData = { 9 | username: 'BasilToast', 10 | avatar: avatar, 11 | menuItems: [ 12 | { 13 | value: 'dashboard', 14 | onClick: () => console.log('dashboard') 15 | }, 16 | { 17 | value: 'sign out', 18 | onClick: () => console.log('sign out') 19 | } 20 | ] 21 | }; 22 | 23 | export const profile = () => ; 24 | -------------------------------------------------------------------------------- /cocode/src/components/Common/UserProfile/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const UserProfile = styled.div` 4 | & { 5 | display: flex; 6 | } 7 | `; 8 | 9 | const UserName = styled.div` 10 | & { 11 | align-self: center; 12 | margin-right: 2rem; 13 | font-weight: 100; 14 | font-size: 1.4rem; 15 | } 16 | `; 17 | 18 | const UserAvatar = styled.img` 19 | & { 20 | width: 3rem; 21 | height: 3rem; 22 | border-radius: 0.5rem; 23 | } 24 | `; 25 | 26 | const DownArrow = styled.img` 27 | & { 28 | width: 1rem; 29 | height: 1rem; 30 | margin: auto 0 auto 1rem; 31 | cursor: pointer; 32 | filter: invert(0.3); 33 | } 34 | 35 | &:hover { 36 | filter: invert(0); 37 | } 38 | `; 39 | 40 | export { UserProfile, UserName, UserAvatar, DownArrow }; -------------------------------------------------------------------------------- /cocode/src/components/DashBoard/CreateCoconut/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from './style'; 3 | 4 | function CreateButton({ onClick }) { 5 | return ; 6 | } 7 | 8 | export default CreateButton; 9 | -------------------------------------------------------------------------------- /cocode/src/components/DashBoard/CreateCoconut/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CreateButton from '.'; 3 | import { action } from '@storybook/addon-actions'; 4 | 5 | export default { 6 | title: 'Dashboard' 7 | }; 8 | 9 | function createButton() { 10 | return ; 11 | } 12 | 13 | export { createButton }; 14 | -------------------------------------------------------------------------------- /cocode/src/components/DashBoard/CreateCoconut/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { PROJECT_CARD_THEME } from 'constants/theme'; 3 | 4 | const Button = styled.button` 5 | height: ${PROJECT_CARD_THEME.cardHeight}; 6 | width: ${PROJECT_CARD_THEME.cardWidth}; 7 | 8 | text-align: center; 9 | font-size: ${PROJECT_CARD_THEME.buttonFontSize}; 10 | font-weight: ${PROJECT_CARD_THEME.buttonFontWight}; 11 | 12 | color: ${({ theme }) => theme.textColor}; 13 | background: ${PROJECT_CARD_THEME.buttonBackgroundColor}; 14 | 15 | border: 0.1rem dashed ${({ theme }) => theme.mainColor}; 16 | border-radius: 1rem; 17 | 18 | &:hover { 19 | opacity: 1; 20 | } 21 | `; 22 | 23 | export default Button; 24 | -------------------------------------------------------------------------------- /cocode/src/components/DashBoard/ProjectCard/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { PROJECT_CARD_THEME } from 'constants/theme'; 4 | import ProjectCard from '.'; 5 | 6 | const Contianer = styled.div` 7 | & { 8 | display: flex; 9 | height: 100vh; 10 | width: 100vw; 11 | background-color: black; 12 | justify-content: center; 13 | vertical-align: center; 14 | align-items: center; 15 | } 16 | `; 17 | 18 | export default { 19 | title: 'ProjectCard' 20 | }; 21 | 22 | const projectCard = () => { 23 | const tempData = { 24 | title: 'project', 25 | edited: 1574333501591, 26 | menuItems: [ 27 | { 28 | value: 'open', 29 | onClick: () => alert('open') 30 | }, 31 | { 32 | value: 'rename', 33 | onClick: () => alert('rename') 34 | }, 35 | { 36 | value: 'remove', 37 | onClick: () => alert('remove') 38 | } 39 | ] 40 | }; 41 | return ( 42 | 43 | 44 | 45 | ); 46 | }; 47 | 48 | export { projectCard }; 49 | -------------------------------------------------------------------------------- /cocode/src/components/Home/ReactLogo/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | import logo from './react.svg'; 4 | 5 | function ReactLogo({ className }) { 6 | return ( 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default ReactLogo; 15 | -------------------------------------------------------------------------------- /cocode/src/components/Home/ReactLogo/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Logo = styled.article` 4 | position: relative; 5 | width: 25rem; 6 | height: 25rem; 7 | `; 8 | 9 | const Image = styled.img` 10 | position: absolute; 11 | width: 25rem; 12 | height: 25rem; 13 | `; 14 | 15 | const Blur = styled.img` 16 | position: absolute; 17 | width: 25rem; 18 | height: 25rem; 19 | filter: blur(3rem); 20 | `; 21 | 22 | export { Logo, Image, Blur }; -------------------------------------------------------------------------------- /cocode/src/components/Home/ScrollDownButton/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | import React from 'react'; 3 | 4 | import * as Styled from './style'; 5 | 6 | function ScrollDownButton() { 7 | const handleScrollDown = () => window.scrollBy(0, window.innerHeight); 8 | 9 | return ( 10 | 15 | 17 | 18 | ); 19 | } 20 | 21 | export default ScrollDownButton; -------------------------------------------------------------------------------- /cocode/src/components/Home/ScrollDownButton/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Image = styled.svg` 4 | & { 5 | width: 2.5rem; 6 | height: 2rem; 7 | cursor: pointer; 8 | color: gray; 9 | position: relative; 10 | top: 60px; 11 | } 12 | 13 | &:hover { 14 | color: white; 15 | } 16 | `; 17 | 18 | export { Image }; -------------------------------------------------------------------------------- /cocode/src/components/Home/ScrollTopButton/Arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /cocode/src/components/Home/ScrollTopButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import Arrow from './Arrow.svg'; 5 | 6 | function ScrollTopButton() { 7 | const handleScrollTop = () => window.scrollTo(0, 0); 8 | 9 | return ( 10 | 11 | 12 | 13 | ); 14 | } 15 | 16 | export default ScrollTopButton; -------------------------------------------------------------------------------- /cocode/src/components/Home/ScrollTopButton/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Button = styled.button` 4 | & { 5 | border-radius: 50%; 6 | width: 3rem; 7 | height: 3rem; 8 | background-color: ${({ theme }) => theme.mainOpaqueColor}; 9 | align-self: flex-end; 10 | } 11 | 12 | &:hover { 13 | background-color: ${({ theme }) => theme.mainColor}; 14 | } 15 | `; 16 | 17 | const Image = styled.img` 18 | & { 19 | width: 2rem; 20 | height: 2rem; 21 | } 22 | `; 23 | 24 | export { Button, Image }; -------------------------------------------------------------------------------- /cocode/src/components/Live/LiveUsers/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import * as Styled from './style'; 3 | import { UserContext } from 'contexts'; 4 | 5 | function LiveUserProfile({ username, avatar }) { 6 | const { user } = useContext(UserContext); 7 | 8 | return ( 9 | 10 | 11 | {username} 12 | {user.username === username && ( 13 | (you) 14 | )} 15 | 16 | ); 17 | } 18 | 19 | function LiveUsers({ owner, participants }) { 20 | if (!participants.length) participants = []; 21 | 22 | return ( 23 | 24 | OWNERS 25 | 26 | USERS 27 | {participants.map(({ username, avatar }, index) => { 28 | return ( 29 | 34 | ); 35 | })} 36 | 37 | ); 38 | } 39 | 40 | export default LiveUsers; 41 | -------------------------------------------------------------------------------- /cocode/src/components/Live/LiveUsers/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { TAB_CONTAINER_THEME, LIVE_TAB_THEME } from 'constants/theme'; 3 | 4 | const Container = styled.div` 5 | & { 6 | margin: 1rem 0; 7 | } 8 | `; 9 | 10 | const Title = styled.h1` 11 | & { 12 | color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; 13 | font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; 14 | font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; 15 | } 16 | `; 17 | 18 | const UserProfile = styled.div` 19 | & { 20 | display: flex; 21 | margin: 0.7rem 0; 22 | } 23 | `; 24 | 25 | const UserName = styled.div` 26 | & { 27 | align-self: center; 28 | margin-left: 0.5rem; 29 | font-weight: 100; 30 | font-size: 1rem; 31 | } 32 | `; 33 | 34 | const UserAvatar = styled.img` 35 | & { 36 | width: 2rem; 37 | height: 2rem; 38 | border-radius: 0.3rem; 39 | } 40 | `; 41 | 42 | const SelfLabel = styled(UserName)` 43 | & { 44 | color: ${LIVE_TAB_THEME.liveSelfLabelColor}; 45 | } 46 | `; 47 | 48 | export { 49 | Container, 50 | Title, 51 | UserProfile, 52 | UserName, 53 | UserAvatar, 54 | SelfLabel 55 | }; 56 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/BackwardButton/backward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/BackwardButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import backward from './backward.svg'; 3 | 4 | const IMAGE_ALT = 'go backward'; 5 | 6 | function BackwardButton({ className, onClick }) { 7 | return ( 8 | 11 | ); 12 | } 13 | 14 | export default BackwardButton; 15 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/ForwardButton/forward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/ForwardButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import forward from './forward.svg'; 3 | 4 | const IMAGE_ALT = 'go forward'; 5 | 6 | function ForwardButton({ className, onClick }) { 7 | return ( 8 | 11 | ); 12 | } 13 | 14 | export default ForwardButton; 15 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/ReloadButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import reload from './reload.svg'; 3 | 4 | const IMAGE_ALT = 'reload page'; 5 | 6 | function ReloadButton({ className, onClick }) { 7 | return ( 8 | 11 | ); 12 | } 13 | 14 | export default ReloadButton; 15 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/ReloadButton/reload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { text } from '@storybook/addon-knobs'; 3 | 4 | import Browser from '.'; 5 | 6 | export default { 7 | title: 'Browser' 8 | }; 9 | 10 | function BrowserDefault() { 11 | return ; 12 | } 13 | 14 | export { BrowserDefault }; 15 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Browser/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Browser = styled.section` 4 | & { 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: stretch; 8 | 9 | height: ${({ height }) => height}; 10 | } 11 | `; 12 | 13 | const BrowserHeader = styled.header` 14 | & { 15 | display: flex; 16 | flex-direction: row; 17 | align-items: center; 18 | 19 | padding: 0.4rem 0.6rem; 20 | 21 | background-color: ${({ browserHeaderBGColor }) => browserHeaderBGColor}; 22 | } 23 | 24 | .BrowserHeader-item { 25 | margin: 0 0.5rem; 26 | } 27 | `; 28 | 29 | const AddressInput = styled.input` 30 | & { 31 | flex-grow: 2; 32 | 33 | padding: 0.5rem 1rem; 34 | 35 | background: ${({ addressInputBGColor }) => addressInputBGColor}; 36 | color: ${({ addressInputTextColor }) => addressInputTextColor}; 37 | font-size: 1rem; 38 | } 39 | `; 40 | 41 | const Iframe = styled.iframe` 42 | & { 43 | flex-grow: 2; 44 | background-color: white; 45 | } 46 | `; 47 | 48 | export { Browser, BrowserHeader, AddressInput, Iframe }; 49 | -------------------------------------------------------------------------------- /cocode/src/components/Project/BrowserV2/open.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /cocode/src/components/Project/BrowserV2/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /cocode/src/components/Project/BrowserV2/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { BROWSER_THEME } from 'constants/theme'; 3 | 4 | const Frame = styled.div` 5 | & { 6 | position: relative; 7 | } 8 | `; 9 | 10 | const BrowserV2 = styled.iframe` 11 | & { 12 | height: calc(100% - 3.1rem); 13 | width: 100%; 14 | background-color: ${BROWSER_THEME.iframeBGColor}; 15 | } 16 | `; 17 | 18 | const AddressContainer = styled.div` 19 | & { 20 | display: flex; 21 | align-items: center; 22 | height: 3.1rem; 23 | width: 100%; 24 | padding: 0.8rem; 25 | background: ${BROWSER_THEME.browserHeaderBGColor}; 26 | font-size: 1rem; 27 | } 28 | `; 29 | 30 | const AddressInput = styled.input` 31 | & { 32 | width: 100%; 33 | height: 100%; 34 | padding: 0.3rem; 35 | background: ${BROWSER_THEME.addressInputBGColor}; 36 | color: ${BROWSER_THEME.addressInputTextColor}; 37 | } 38 | `; 39 | 40 | const SearchIcon = styled.img` 41 | & { 42 | height: 100%; 43 | padding: 0.4rem 0; 44 | background: ${BROWSER_THEME.addressInputBGColor}; 45 | } 46 | `; 47 | 48 | const OpenIcon = styled.img` 49 | & { 50 | height: 100%; 51 | padding: 0.4rem 0; 52 | background: ${BROWSER_THEME.addressInputBGColor}; 53 | cursor: pointer; 54 | } 55 | `; 56 | 57 | export { 58 | Frame, 59 | BrowserV2, 60 | AddressContainer, 61 | AddressInput, 62 | SearchIcon, 63 | OpenIcon 64 | }; 65 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Dependency/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import MoreLessButton from 'components/Project/MorelessButton'; 5 | 6 | function Dependency({ title, children }) { 7 | const [toggle, setToggle] = useState(true); 8 | 9 | const handleClickToggle = () => setToggle(!toggle); 10 | 11 | return ( 12 |
13 | 14 | {title} 15 | 16 | 17 | {children} 18 |
19 | ); 20 | } 21 | 22 | export default Dependency; -------------------------------------------------------------------------------- /cocode/src/components/Project/Dependency/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { TAB_CONTAINER_THEME } from 'constants/theme'; 3 | 4 | const Header = styled.header` 5 | & { 6 | display: flex; 7 | align-items: center; 8 | background-color: ${TAB_CONTAINER_THEME.tabContainerHeaderBGColor}; 9 | } 10 | `; 11 | 12 | const Title = styled.h1` 13 | & { 14 | padding: 0.7rem 1rem; 15 | 16 | color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; 17 | font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; 18 | font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; 19 | } 20 | `; 21 | 22 | const Body = styled.div` 23 | & { 24 | display: ${({ toggle }) => (toggle ? 'block' : 'none')}; 25 | padding: 0; 26 | } 27 | `; 28 | 29 | export { Header, Title, Body }; 30 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencyItem/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencyItem/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import DependencySelector from 'components/Project/DependencySelector'; 5 | import close from './close.svg'; 6 | 7 | function DependencyItem({ name, version, versions }) { 8 | return ( 9 | 10 | {name} 11 | 12 | {/* */} 13 | ^{version} 14 | 15 | ); 16 | } 17 | 18 | export default DependencyItem; 19 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencyItem/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEPENDENCY_TAB_THEME } from 'constants/theme'; 3 | 4 | const Item = styled.li` 5 | & { 6 | padding: 1rem; 7 | font-size: 1rem; 8 | cursor: pointer; 9 | } 10 | 11 | & > select { 12 | display: none; 13 | } 14 | 15 | &:hover { 16 | background: ${DEPENDENCY_TAB_THEME.dependencyTabItemHoverColor}; 17 | 18 | & > select, & > img { 19 | display: inline; 20 | } 21 | 22 | & > span { 23 | display: none; 24 | } 25 | } 26 | `; 27 | 28 | const Version = styled.span` 29 | & { 30 | float: right; 31 | font-size: 1rem; 32 | } 33 | `; 34 | 35 | const Close = styled.img` 36 | & { 37 | display: none; 38 | float: right; 39 | width: ${DEPENDENCY_TAB_THEME.dependencyTabCloseSize}; 40 | height: ${DEPENDENCY_TAB_THEME.dependencyTabCloseSize}; 41 | margin: 0.3rem 0 0 0.2rem; 42 | } 43 | `; 44 | 45 | export { 46 | Item, 47 | Version, 48 | Close 49 | }; 50 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencyNow/dummy.js: -------------------------------------------------------------------------------- 1 | export const dependencyList = [ 2 | { 3 | name: 'react', 4 | version: '16.8.6', 5 | versions: [ 6 | '16.8.6', 7 | '16.8.5', 8 | '16.8.4', 9 | '16.8.3', 10 | '16.8.2', 11 | '16.8.1', 12 | '16.8.0', 13 | '16.7.9', 14 | '16.7.8', 15 | '16.7.7', 16 | '16.7.6', 17 | '16.7.5', 18 | '16.7.4', 19 | ] 20 | }, 21 | { 22 | name: 'react-dom', 23 | version: '16.8.6', 24 | versions: [ 25 | '16.8.6', 26 | '16.8.5', 27 | '16.8.4', 28 | '16.8.3', 29 | '16.8.2', 30 | '16.8.1', 31 | '16.8.0', 32 | '16.7.9', 33 | '16.7.8', 34 | '16.7.7', 35 | '16.7.6', 36 | '16.7.5', 37 | '16.7.4', 38 | ] 39 | } 40 | ]; -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencyNow/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import DependencyItem from 'components/Project/DependencyItem'; 5 | 6 | import { ProjectContext } from 'contexts'; 7 | 8 | function DependencyNow() { 9 | const { project } = useContext(ProjectContext); 10 | const { dependency } = project; 11 | 12 | return ( 13 | 14 | {Object.entries(dependency).map( 15 | ([key, { name, version }], index) => { 16 | return ( 17 | 24 | ); 25 | } 26 | )} 27 | 28 | ); 29 | } 30 | 31 | export default DependencyNow; 32 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencyNow/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const DependencyNow = styled.ul` 4 | & { 5 | list-style: none; 6 | } 7 | `; 8 | 9 | export { 10 | DependencyNow 11 | }; 12 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencySearch/dummy.js: -------------------------------------------------------------------------------- 1 | export const dependencySearchList = [ 2 | { 3 | name: 'lodash', 4 | latestVersion: '4.17.15', 5 | versions: [ 6 | '4.17.15', 7 | '4.17.14', 8 | '4.17.13', 9 | '4.17.12', 10 | '4.17.11', 11 | '4.17.10', 12 | '4.17.9', 13 | '4.17.8', 14 | '4.17.7', 15 | '4.17.6', 16 | '4.17.5', 17 | '4.17.4', 18 | '4.17.3', 19 | '4.17.2', 20 | ], 21 | github: 'https://github.com/lodash/lodash', 22 | npm: 'https://www.npmjs.com/package/lodash', 23 | }, 24 | { 25 | name: 'react', 26 | latestVersion: '16.8.6', 27 | versions: [ 28 | '16.8.6', 29 | '16.8.5', 30 | '16.8.4', 31 | '16.8.3', 32 | '16.8.2', 33 | '16.8.1', 34 | '16.8.0', 35 | '16.7.9', 36 | '16.7.8', 37 | '16.7.7', 38 | '16.7.6', 39 | '16.7.5', 40 | '16.7.4', 41 | ], 42 | github: 'https://github.com/reactjs', 43 | npm: 'https://www.npmjs.com/package/react', 44 | } 45 | ]; -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencySearch/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEPENDENCY_TAB_THEME } from 'constants/theme'; 3 | 4 | const SearchBar = styled.input.attrs(() => ({ 5 | type: 'text', 6 | placeholder: 'Search on enter npm dependency' 7 | }))` 8 | width: -webkit-fill-available; 9 | margin: 0.5rem 1rem; 10 | padding: 0.8rem; 11 | background-color: ${DEPENDENCY_TAB_THEME.dependencyTabInputBGColor}; 12 | font-size: 0.8rem; 13 | color: white; 14 | `; 15 | 16 | const SpinnerContainer = styled.div` 17 | & > * { 18 | margin: 1rem auto; 19 | } 20 | `; 21 | 22 | const DependencySearchList = styled.ul` 23 | & { 24 | list-style: none; 25 | overflow: scroll; 26 | height: 50vh; 27 | } 28 | `; 29 | 30 | export { SearchBar, SpinnerContainer, DependencySearchList }; 31 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencySearchItem/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import PlusImage from 'components/Project/PlusImage'; 5 | import GitHubLogo from 'components/Project/GitHubLogo'; 6 | import NpmLogo from 'components/Project/NpmLogo'; 7 | import DependencySelector from 'components/Project/DependencySelector'; 8 | 9 | import { waitingInstallDependencyActionCreator } from 'actions/Project'; 10 | import { ProjectContext } from 'contexts'; 11 | 12 | function DependencySearchItem({ name, latestVersion, github, npm }) { 13 | const { project, dispatchProject } = useContext(ProjectContext); 14 | const { dependency } = project; 15 | 16 | const handleFetchModule = () => { 17 | const moduleName = name; 18 | const moduleVersion = latestVersion; 19 | 20 | dispatchProject( 21 | waitingInstallDependencyActionCreator({ moduleName, moduleVersion }) 22 | ); 23 | }; 24 | 25 | return ( 26 | 27 | {name} 28 | 29 | {/**/} 30 | {!dependency[name] && } 31 | 32 | 33 | 34 | 35 | ); 36 | } 37 | 38 | export default DependencySearchItem; 39 | -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencySearchItem/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEPENDENCY_TAB_THEME } from 'constants/theme'; 3 | 4 | const Item = styled.li` 5 | & { 6 | padding: 1rem; 7 | font-size: 1rem; 8 | cursor: pointer; 9 | } 10 | 11 | &:hover { 12 | background: ${DEPENDENCY_TAB_THEME.dependencyTabItemHoverColor}; 13 | } 14 | `; 15 | 16 | const Description = styled.div` 17 | & { 18 | display: flex; 19 | justify-content: flex-end; 20 | align-items: center; 21 | margin-top: 0.5rem; 22 | } 23 | `; 24 | 25 | export { 26 | Item, 27 | Description, 28 | }; -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencySelector/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | function DependencySelector({ options }) { 5 | return ( 6 | 7 | {options.map((option, index) => { 8 | return ( 9 | 10 | ); 11 | })} 12 | 13 | ); 14 | } 15 | 16 | export default DependencySelector; -------------------------------------------------------------------------------- /cocode/src/components/Project/DependencySelector/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEPENDENCY_TAB_THEME } from 'constants/theme'; 3 | 4 | const Select = styled.select` 5 | & { 6 | padding: 0 0.7rem 0 0.1rem; 7 | float: right; 8 | 9 | cursor: pointer; 10 | color: ${DEPENDENCY_TAB_THEME.dependencyTextColor}; 11 | font-size: 0.8rem; 12 | 13 | background-color: ${DEPENDENCY_TAB_THEME.dependencyTabSelectBGColor}; 14 | background-image: url('https://i.imgur.com/AzGpKhN.png'); 15 | background-repeat: no-repeat; 16 | background-position: right center; 17 | background-size: 0.4rem; 18 | 19 | border-radius: 0.3rem; 20 | border: 2px solid ${DEPENDENCY_TAB_THEME.dependencyTabSelectBGColor}; 21 | border-right: 3px solid 22 | ${DEPENDENCY_TAB_THEME.dependencyTabSelectBGColor}; 23 | 24 | -webkit-appearance: none; 25 | } 26 | `; 27 | 28 | export { 29 | Select 30 | }; 31 | -------------------------------------------------------------------------------- /cocode/src/components/Project/Directory/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const FileList = styled.div` 4 | & { 5 | display: ${({ toggle }) => (toggle ? 'block' : 'none')}; 6 | } 7 | `; 8 | 9 | export { FileList }; 10 | -------------------------------------------------------------------------------- /cocode/src/components/Project/ExplorerTabIcons/DeleteIcon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from '../style'; 3 | 4 | function DeleteIcon({ onClick }) { 5 | return ( 6 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default DeleteIcon; 18 | -------------------------------------------------------------------------------- /cocode/src/components/Project/ExplorerTabIcons/EditIcon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from '../style'; 3 | 4 | function EditIcon({ onClick }) { 5 | return ( 6 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default EditIcon; 20 | -------------------------------------------------------------------------------- /cocode/src/components/Project/ExplorerTabIcons/NewFileIcon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from '../style'; 3 | 4 | function EditIcon({ onClick }) { 5 | return ( 6 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default EditIcon; 20 | -------------------------------------------------------------------------------- /cocode/src/components/Project/ExplorerTabIcons/NewFolderIcon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from '../style'; 3 | 4 | function NewFolderIcon({ onClick }) { 5 | return ( 6 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default NewFolderIcon; 20 | -------------------------------------------------------------------------------- /cocode/src/components/Project/ExplorerTabIcons/index.js: -------------------------------------------------------------------------------- 1 | import EditIcon from './EditIcon'; 2 | import DeleteIcon from './DeleteIcon'; 3 | import NewFolderIcon from './NewFolderIcon'; 4 | import NewFileIcon from './NewFileIcon'; 5 | 6 | export { EditIcon, DeleteIcon, NewFolderIcon, NewFileIcon }; 7 | -------------------------------------------------------------------------------- /cocode/src/components/Project/ExplorerTabIcons/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { EXPLORER_TAB_CONTAINER_THEME } from 'constants/theme'; 3 | 4 | const { 5 | explorerTabContainerIconColor, 6 | explorerTabContainerIconHoverColor 7 | } = EXPLORER_TAB_CONTAINER_THEME; 8 | 9 | const Svg = styled.svg` 10 | & { 11 | vertical-align: middle; 12 | 13 | fill: ${explorerTabContainerIconColor}; 14 | } 15 | 16 | &:hover { 17 | fill: ${explorerTabContainerIconHoverColor}; 18 | } 19 | `; 20 | 21 | export { Svg }; 22 | -------------------------------------------------------------------------------- /cocode/src/components/Project/FileTab/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /cocode/src/components/Project/FileTab/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | import Close from './close.svg'; 4 | 5 | function FileTab({ 6 | index, 7 | fileName, 8 | icon, 9 | type, 10 | clicked, 11 | onClick, 12 | onCloseClick 13 | }) { 14 | const handleTabClick = () => onClick(index); 15 | const handleCloseClick = (e) => onCloseClick(e, index); 16 | 17 | return ( 18 | 19 | 20 | {fileName} 21 | 27 | 28 | ); 29 | } 30 | 31 | export default FileTab; 32 | -------------------------------------------------------------------------------- /cocode/src/components/Project/FileTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { FILE_TAB_THEME } from 'constants/theme'; 3 | 4 | const Tab = styled.li` 5 | & { 6 | padding: 0.8rem; 7 | display: inline-flex; 8 | background-color: ${({ clicked }) => 9 | clicked 10 | ? FILE_TAB_THEME.fileTabClickedBGColor 11 | : FILE_TAB_THEME.fileTabDefaultBGColor}; 12 | font-size: ${FILE_TAB_THEME.fileTabFontSize}; 13 | cursor: pointer; 14 | } 15 | 16 | &:hover { 17 | & > img { 18 | visibility: visible; 19 | } 20 | } 21 | `; 22 | 23 | const Icon = styled.img` 24 | & { 25 | width: 1.2rem; 26 | height: 1.2rem; 27 | margin-right: 0.3rem; 28 | } 29 | `; 30 | 31 | const Close = styled.img` 32 | & { 33 | width: ${FILE_TAB_THEME.fileTabCloseButtonSize}; 34 | height: ${FILE_TAB_THEME.fileTabCloseButtonSize}; 35 | margin-top: 0.18rem; 36 | margin-left: 0.5rem; 37 | visibility: ${({ clicked }) => (clicked ? 'visible' : 'hidden')}; 38 | } 39 | `; 40 | 41 | export { Icon, Tab, Close }; 42 | -------------------------------------------------------------------------------- /cocode/src/components/Project/FileTabBar/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { FILE_TAB_THEME} from 'constants/theme'; 3 | 4 | const TabBar = styled.ul` 5 | & { 6 | width: 100%; 7 | list-style: none; 8 | display: inline-flex; 9 | overflow: scroll; 10 | min-height: 3.1rem; 11 | background-color: ${FILE_TAB_THEME.fileTabDefaultBGColor}; 12 | } 13 | `; 14 | 15 | export { TabBar }; -------------------------------------------------------------------------------- /cocode/src/components/Project/GitHubLogo/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEPENDENCY_TAB_THEME } from 'constants/theme'; 3 | 4 | const Logo = styled.svg` 5 | & { 6 | width: ${DEPENDENCY_TAB_THEME.dependencyTabIconSize}; 7 | height: ${DEPENDENCY_TAB_THEME.dependencyTabIconSize}; 8 | margin-right: 0.5rem; 9 | fill: ${DEPENDENCY_TAB_THEME.dependencyTabIconColor}; 10 | } 11 | 12 | &:hover { 13 | fill: ${DEPENDENCY_TAB_THEME.dependencyTabIconHoverColor}; 14 | } 15 | `; 16 | 17 | export { 18 | Logo, 19 | }; -------------------------------------------------------------------------------- /cocode/src/components/Project/MonacoEditor/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ControlledEditor } from '@monaco-editor/react'; 3 | import * as Styled from './style'; 4 | 5 | function MonacoEditor({ 6 | isFilesEmpty, 7 | code, 8 | handleEditorDidMount, 9 | handleUpdateCode, 10 | ...props 11 | }) { 12 | return ( 13 | 14 | 24 | 25 | ); 26 | } 27 | 28 | export default MonacoEditor; 29 | -------------------------------------------------------------------------------- /cocode/src/components/Project/MonacoEditor/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import MonacoEditor from '.'; 4 | 5 | export default { 6 | title: 'MonacoEditor' 7 | }; 8 | 9 | function Editor() { 10 | return ; 11 | } 12 | 13 | export { Editor }; 14 | -------------------------------------------------------------------------------- /cocode/src/components/Project/MonacoEditor/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { MONACO_THEME } from 'constants/theme'; 3 | 4 | const MonacoEditor = styled.div` 5 | & { 6 | visibility: ${({ isFilesEmpty }) => 7 | isFilesEmpty ? 'hidden' : 'visible'}; 8 | } 9 | 10 | .monaco-editor-background, 11 | .margin { 12 | background-color: ${MONACO_THEME.editorMainColor} !important; 13 | } 14 | 15 | .scroll-decoration { 16 | box-shadow: none !important; 17 | } 18 | 19 | .monaco-editor .view-overlays .current-line { 20 | border: ${MONACO_THEME.editorCurrentLineColor}; 21 | background-color: ${MONACO_THEME.editorCurrentLineColor}; 22 | } 23 | `; 24 | 25 | export { MonacoEditor }; 26 | -------------------------------------------------------------------------------- /cocode/src/components/Project/MorelessButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | function MoreLessButton({ onClick, toggle }) { 5 | const handleClickButton = () => onClick(); 6 | 7 | return ( 8 | 14 | 16 | 17 | ); 18 | } 19 | 20 | export default MoreLessButton; -------------------------------------------------------------------------------- /cocode/src/components/Project/MorelessButton/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Image = styled.svg` 4 | & { 5 | width: 1rem; 6 | height: 1rem; 7 | cursor: pointer; 8 | color: gray; 9 | transition: .2s; 10 | transform: ${({ toggle }) => toggle ? 'rotate(0)' : 'rotate(-90deg)'}; 11 | } 12 | 13 | &:hover { 14 | color: white; 15 | } 16 | `; 17 | 18 | export { 19 | Image 20 | }; -------------------------------------------------------------------------------- /cocode/src/components/Project/NpmLogo/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | function NpmLogo({ href }) { 5 | const handleClickLogo = () => { 6 | window.open(href, '_blank'); 7 | }; 8 | 9 | return ( 10 | 11 | 13 | 14 | ); 15 | } 16 | 17 | export default NpmLogo; -------------------------------------------------------------------------------- /cocode/src/components/Project/NpmLogo/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEPENDENCY_TAB_THEME } from 'constants/theme'; 3 | 4 | const Logo = styled.svg` 5 | & { 6 | width: ${DEPENDENCY_TAB_THEME.dependencyTabIconSize}; 7 | height: ${DEPENDENCY_TAB_THEME.dependencyTabIconSize}; 8 | fill: ${DEPENDENCY_TAB_THEME.dependencyTabIconColor} 9 | } 10 | 11 | &:hover { 12 | fill: ${DEPENDENCY_TAB_THEME.dependencyTabIconHoverColor} 13 | } 14 | `; 15 | 16 | export { 17 | Logo, 18 | }; -------------------------------------------------------------------------------- /cocode/src/components/Project/PlusImage/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | function PlusImage({ onClick }) { 5 | return ( 6 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default PlusImage; 18 | -------------------------------------------------------------------------------- /cocode/src/components/Project/PlusImage/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEPENDENCY_TAB_THEME } from 'constants/theme'; 3 | 4 | const Image = styled.svg` 5 | & { 6 | width: ${DEPENDENCY_TAB_THEME.dependencyTabIconSize}; 7 | height: ${DEPENDENCY_TAB_THEME.dependencyTabIconSize}; 8 | margin-right: 0.5rem; 9 | fill: ${DEPENDENCY_TAB_THEME.dependencyTabIconColor} 10 | } 11 | 12 | &:hover { 13 | fill: ${DEPENDENCY_TAB_THEME.dependencyTabIconHoverColor} 14 | } 15 | `; 16 | 17 | export { 18 | Image 19 | }; -------------------------------------------------------------------------------- /cocode/src/components/Project/ProjectIcon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | function Project({ fillColor }) { 5 | return ( 6 | 12 | 19 | 20 | 21 | 29 | 35 | 36 | 37 | 38 | 39 | ); 40 | } 41 | 42 | export default Project; 43 | -------------------------------------------------------------------------------- /cocode/src/components/Project/ProjectIcon/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Svg = styled.svg` 4 | .Target { 5 | fill: ${({ fillColor }) => fillColor}; 6 | stroke: ${({ fillColor }) => fillColor}; 7 | } 8 | `; 9 | 10 | export { Svg }; 11 | -------------------------------------------------------------------------------- /cocode/src/components/Project/TabIcon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | function TabIcon({ theme, index, name, icon, onClick, clicked }) { 5 | const handleTabClick = () => onClick(index); 6 | 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default TabIcon; 15 | -------------------------------------------------------------------------------- /cocode/src/components/Project/TabIcon/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Button = styled.button` 4 | & { 5 | width: 2.5rem; 6 | height: 2.5rem; 7 | background: ${({ clicked, theme }) => 8 | clicked ? theme.tabBarSelectedItemBGColor : theme.tabBarBGColor}; 9 | padding: 0.5rem; 10 | border-radius: 0.6rem; 11 | margin: 0.5rem; 12 | } 13 | 14 | &:hover { 15 | background: ${({ theme }) => theme.tabBarSelectedItemBGColor}; 16 | } 17 | `; 18 | 19 | const Icon = styled.img` 20 | & { 21 | width: 1.5rem; 22 | height: 1.5rem; 23 | } 24 | `; 25 | 26 | export { Button, Icon }; 27 | -------------------------------------------------------------------------------- /cocode/src/config/index.js: -------------------------------------------------------------------------------- 1 | const API_SERVER = 2 | process.env.NODE_ENV === 'production' 3 | ? process.env.PROD_API_SERVER_IP 4 | : process.env.DEV_API_SERVER_IP; 5 | 6 | const COCONUT_SERVER = 7 | process.env.NODE_ENV === 'production' 8 | ? process.env.PROD_COCONUT_SERVER_IP 9 | : process.env.DEV_COCONUT_SERVER_IP; 10 | 11 | const LIVE_SERVER = 12 | process.env.NODE_ENV === 'production' 13 | ? process.env.PROD_LIVE_SERVER_IP 14 | : process.env.DEV_LIVE_SERVER_IP; 15 | 16 | const COCODE_SERVER = 17 | process.env.NODE_ENV === 'production' 18 | ? process.env.PROD_COCODE_SERVER_IP 19 | : process.env.DEV_COCODE_SERVER_IP; 20 | 21 | const DEFAULT_REQUEST_OPTION = { 22 | headers: {'Cache-Control': 'no-cache'}, 23 | withCredentials: true, 24 | mode: 'cors', 25 | credentials: 'include', 26 | }; 27 | 28 | const API = { 29 | getUserData: `${API_SERVER}/users`, 30 | login: `${API_SERVER}/users/login`, 31 | users: `${API_SERVER}/users`, 32 | projects: `${API_SERVER}/projects`, 33 | files: projectId => `${API_SERVER}/projects/${projectId}/files`, 34 | dependency: name => `${API_SERVER}/dependency/search?name=${name}` 35 | }; 36 | 37 | export { 38 | DEFAULT_REQUEST_OPTION, 39 | API, 40 | COCONUT_SERVER, 41 | LIVE_SERVER, 42 | COCODE_SERVER 43 | }; 44 | -------------------------------------------------------------------------------- /cocode/src/constants/cookie.js: -------------------------------------------------------------------------------- 1 | const DELETE_COOKIE_VALUE = '=; expires=Thu, 01 Jan 1999 00:00:10 GMT;'; 2 | 3 | export { 4 | DELETE_COOKIE_VALUE 5 | }; -------------------------------------------------------------------------------- /cocode/src/constants/cursorColors.js: -------------------------------------------------------------------------------- 1 | const colors = [ 2 | '#CC000080', 3 | '#FF650F80', 4 | '#FFBA0280', 5 | '#458d5380', 6 | '#006b0480', 7 | '#2E588D80', 8 | '#52338D80', 9 | '#74155180', 10 | '#13133C80', 11 | '#220D3880', 12 | '#00000080' 13 | ]; 14 | 15 | export { colors }; -------------------------------------------------------------------------------- /cocode/src/constants/fileImagesSrc.js: -------------------------------------------------------------------------------- 1 | const FILE_IMAGES_SRC = { 2 | file: 'https://codesandbox.io/static/media/file.6cbc0ce8.svg', 3 | directory: 'https://codesandbox.io/static/media/folder.30a30d83.svg', 4 | directoryOpen: 'https://codesandbox.io/static/media/folder-open.df474ba4.svg', 5 | js: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/javascript.svg', 6 | css: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/css.svg', 7 | html: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/html.svg', 8 | npm: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/npm.svg' 9 | }; 10 | 11 | export default FILE_IMAGES_SRC; 12 | -------------------------------------------------------------------------------- /cocode/src/constants/keyCode.js: -------------------------------------------------------------------------------- 1 | const KEY_CODE_ENTER = 13; 2 | const KEY_CODE_S = 83; 3 | 4 | export { KEY_CODE_ENTER, KEY_CODE_S }; 5 | -------------------------------------------------------------------------------- /cocode/src/constants/statusCode.js: -------------------------------------------------------------------------------- 1 | const CREATED = 201; 2 | const CONFLICT = 409; 3 | 4 | export { CREATED, CONFLICT }; 5 | -------------------------------------------------------------------------------- /cocode/src/containers/Common/Header/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '.'; 3 | 4 | export default { 5 | title: 'Header' 6 | }; 7 | 8 | export const header = () =>
; 9 | -------------------------------------------------------------------------------- /cocode/src/containers/Common/Header/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Header = styled.header` 4 | & { 5 | display: flex; 6 | flex-direction: row; 7 | justify-content: space-between; 8 | align-items: center; 9 | 10 | height: ${({ theme, isMinHeight }) => 11 | isMinHeight ? theme.headerMinHeight : theme.headerHeight}; 12 | 13 | background-color: ${({ theme }) => theme.backgroundColor}; 14 | padding: 2rem 2.3rem; 15 | } 16 | `; 17 | 18 | const ProjectName = styled.div` 19 | & { 20 | font-size: 1.3rem; 21 | } 22 | `; 23 | 24 | const SignInButton = styled.button` 25 | & { 26 | font-size: 1.4rem; 27 | font-weight: 100; 28 | } 29 | 30 | &:hover { 31 | color: ${({ theme }) => theme.mainColor}; 32 | } 33 | `; 34 | 35 | export { Header, SignInButton, ProjectName }; 36 | -------------------------------------------------------------------------------- /cocode/src/containers/Common/LoadingSpinner/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | import CoconutSpinner from 'components/Common/CoconutSpinner'; 4 | 5 | function LoadingSpinner({ message }) { 6 | return ( 7 | 8 | 9 | {message} 10 | 11 | ); 12 | } 13 | 14 | export default LoadingSpinner; 15 | -------------------------------------------------------------------------------- /cocode/src/containers/Common/LoadingSpinner/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const LoadingDisplay = styled.div` 4 | & { 5 | position: absolute; 6 | z-index: 1; 7 | 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: center; 11 | align-items: center; 12 | 13 | height: 100%; 14 | width: 100%; 15 | 16 | background-color: rgba(0, 0, 0, 0.7); 17 | } 18 | `; 19 | 20 | const LoadingPhrase = styled.p` 21 | & { 22 | margin-top: 2rem; 23 | 24 | font-size: 3rem; 25 | font-weight: 200; 26 | } 27 | `; 28 | 29 | export { LoadingDisplay, LoadingPhrase }; 30 | -------------------------------------------------------------------------------- /cocode/src/containers/DashBoard/ProjectCardList/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import * as Style from './style'; 4 | import ProjectCard from 'components/DashBoard/ProjectCard'; 5 | import CreateButton from 'components/DashBoard/CreateCoconut'; 6 | import { PROJECT_CARD_THEME } from 'constants/theme'; 7 | import { DashBoardContext } from 'contexts'; 8 | 9 | function ProjectCardList() { 10 | const { coconuts } = useContext(DashBoardContext); 11 | return ( 12 | 13 | 14 | Coconuts 15 | {coconuts.length} 16 | 17 | 18 | 19 | 20 | 21 | {coconuts.map((coconut, index) => ( 22 | 27 | ))} 28 | 29 | 30 | ); 31 | } 32 | 33 | export default ProjectCardList; 34 | -------------------------------------------------------------------------------- /cocode/src/containers/DashBoard/ProjectCardList/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Main = styled.main` 4 | height: 88vh; 5 | padding: 3rem; 6 | `; 7 | 8 | const Title = styled.h2` 9 | color: ${({ theme }) => theme.textColor}; 10 | font-size: 2.5rem; 11 | font-weight: 300; 12 | padding-left: 3.5rem; 13 | padding-bottom: 1rem; 14 | `; 15 | 16 | const CoconutCount = styled.span` 17 | color: ${({ theme }) => theme.mainColor}; 18 | padding: 1rem; 19 | `; 20 | 21 | const CardList = styled.section` 22 | & { 23 | display: flex; 24 | flex-flow: row wrap; 25 | align-content: flex-start; 26 | justify-content: flex-start; 27 | height: 80%; 28 | width: 100%; 29 | padding-bottom: 3rem; 30 | overflow: auto; 31 | } 32 | & > * { 33 | margin: 3rem; 34 | } 35 | `; 36 | 37 | export { Main, Title, CoconutCount, CardList }; 38 | -------------------------------------------------------------------------------- /cocode/src/containers/History/CocodeHistory/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import * as Styled from './style'; 4 | 5 | const VERSION_HISTORIES = [ 6 | { 7 | title: 'Version 1', 8 | link: '/history/version1', 9 | descriptions: ['하나의 React File을 build 할 수 있습니다'] 10 | } 11 | ]; 12 | 13 | function HistoryItem({ title, link, descriptions }) { 14 | return ( 15 | 16 | 17 | {title} 18 | 19 | 20 | {descriptions.map((description, index) => ( 21 |
  • {description}
  • 22 | ))} 23 |
    24 |
    25 | ); 26 | } 27 | 28 | function CocodeHistory() { 29 | return ( 30 | 31 | {VERSION_HISTORIES.map((history, index) => { 32 | return ; 33 | })} 34 | 35 | ); 36 | } 37 | 38 | export default CocodeHistory; 39 | -------------------------------------------------------------------------------- /cocode/src/containers/History/CocodeHistory/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const CocodeHistory = styled.main` 4 | text-align: center; 5 | `; 6 | 7 | const Version = styled.section` 8 | padding: 5rem; 9 | `; 10 | 11 | const VersionTitle = styled.h1` 12 | font-size: 4rem; 13 | font-weight: normal; 14 | `; 15 | 16 | const VersionDesctiption = styled.ul` 17 | margin-top: 1rem; 18 | 19 | list-style-type: none; 20 | 21 | font-size: 2rem; 22 | font-weight: lighter; 23 | 24 | li { 25 | margin: 0.5rem 0; 26 | } 27 | `; 28 | 29 | export { CocodeHistory, Version, VersionTitle, VersionDesctiption }; 30 | -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutCocode/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import ScrollTopButton from 'components/Home/ScrollTopButton'; 5 | import ScrollDownButton from 'components/Home/ScrollDownButton'; 6 | 7 | function AboutCocode() { 8 | return ( 9 | 10 | 11 | Cocode 12 | is an online editor 13 |
    14 | that helps you create web applications based on 15 | React 16 |
    17 | 18 | 19 |
    20 | ); 21 | } 22 | 23 | export default AboutCocode; 24 | -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutCocode/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const AboutCocode = styled.section` 4 | & { 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | 10 | height: 100vh; 11 | padding: 5rem; 12 | } 13 | `; 14 | 15 | const DescriptionPhrase = styled.h1` 16 | & { 17 | text-align: center; 18 | 19 | font-size: 2.8rem; 20 | font-weight: lighter; 21 | 22 | flex: 1; 23 | display: flex; 24 | align-items: center; 25 | justify-content: center; 26 | } 27 | 28 | .DescriptionPhrase-emphasis { 29 | color: ${({ theme }) => theme.mainColor}; 30 | font-weight: 400; 31 | display: contents; 32 | } 33 | `; 34 | 35 | export { AboutCocode, DescriptionPhrase }; 36 | -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutUs/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import profiles from './profiles'; 5 | import ScrollTopButton from 'components/Home/ScrollTopButton'; 6 | 7 | function AboutUsProfileCard({ name, nickName, src }) { 8 | return ( 9 | 10 | 16 | 17 | {name} 18 | 19 | 23 | @{nickName} 24 | 25 | 26 | ); 27 | } 28 | 29 | function AboutUs() { 30 | return ( 31 | 32 | 33 | 34 | Who's making 35 | cocode? 36 | 37 | 38 | {profiles.map((profile, index) => ( 39 | 40 | ))} 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | 48 | export default AboutUs; 49 | -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutUs/profiles/basiltoast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/cocode/src/containers/Home/AboutUs/profiles/basiltoast.png -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutUs/profiles/hzoou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/cocode/src/containers/Home/AboutUs/profiles/hzoou.png -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutUs/profiles/index.js: -------------------------------------------------------------------------------- 1 | import basiltoast from './basiltoast.png'; 2 | import lallaheeee from './lallaheeee.png'; 3 | import hzoou from './hzoou.png'; 4 | import yukjisoo from './yukjisoo.png'; 5 | 6 | const profileData = [ 7 | { 8 | name: '김준표', 9 | nickName: 'BasilToast', 10 | src: basiltoast 11 | }, 12 | { 13 | name: '김희라', 14 | nickName: 'lallaheeee', 15 | src: lallaheeee 16 | }, 17 | { 18 | name: '우혜주', 19 | nickName: 'hzoou', 20 | src: hzoou 21 | }, 22 | { 23 | name: '육지수', 24 | nickName: 'yukjisoo', 25 | src: yukjisoo 26 | } 27 | ]; 28 | 29 | export default profileData; 30 | -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutUs/profiles/lallaheeee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/cocode/src/containers/Home/AboutUs/profiles/lallaheeee.png -------------------------------------------------------------------------------- /cocode/src/containers/Home/AboutUs/profiles/yukjisoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/cocode/src/containers/Home/AboutUs/profiles/yukjisoo.png -------------------------------------------------------------------------------- /cocode/src/containers/Home/Main/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | import ReactLogo from 'components/Home/ReactLogo'; 6 | import ScrollDownButton from 'components/Home/ScrollDownButton'; 7 | 8 | function Title() { 9 | return ( 10 | <> 11 | 12 | co 13 | code 14 | 15 | 16 | The online code editor for 17 | React 18 | 19 | 20 | ); 21 | } 22 | 23 | function OpenButton() { 24 | return OPEN REACT; 25 | } 26 | 27 | function Description() { 28 | return ( 29 | 30 | 31 | <Link to="/project/new"> 32 | <OpenButton /> 33 | </Link> 34 | </Styled.Description> 35 | ); 36 | } 37 | 38 | function Main() { 39 | return ( 40 | <Styled.Main> 41 | <Styled.Content> 42 | <Description /> 43 | <ReactLogo className="Logo-floatRight Logo-alignCenter" /> 44 | </Styled.Content> 45 | <ScrollDownButton /> 46 | </Styled.Main> 47 | ); 48 | } 49 | 50 | export default Main; 51 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/DependencyTab/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import CoconutSpinner from 'components/Common/CoconutSpinner'; 5 | import Dependency from 'components/Project/Dependency'; 6 | import DependencyNow from 'components/Project/DependencyNow'; 7 | import DependencySearch from 'components/Project/DependencySearch'; 8 | 9 | import { ProjectContext } from 'contexts'; 10 | 11 | const TabTitleFirst = 'DEPENDENCIES'; 12 | const TabTitleSecond = 'SEARCH DEPENDENCY'; 13 | 14 | function InstallingDisplay() { 15 | return ( 16 | <Styled.InstallingDisplay> 17 | <CoconutSpinner /> 18 | <Styled.InstallPhrase> 19 | Please wait to install module... 20 | </Styled.InstallPhrase> 21 | </Styled.InstallingDisplay> 22 | ); 23 | } 24 | 25 | function DependencyTab() { 26 | const { project } = useContext(ProjectContext); 27 | const { dependencyInstalling } = project; 28 | 29 | return ( 30 | <Styled.Frame> 31 | {dependencyInstalling && <InstallingDisplay />} 32 | <Styled.DependencyArea> 33 | <Dependency title={TabTitleFirst}> 34 | <DependencyNow /> 35 | </Dependency> 36 | <Dependency title={TabTitleSecond}> 37 | <DependencySearch /> 38 | </Dependency> 39 | </Styled.DependencyArea> 40 | </Styled.Frame> 41 | ); 42 | } 43 | 44 | export default DependencyTab; 45 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/DependencyTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Frame = styled.div` 4 | & { 5 | position: relative; 6 | 7 | height: 100%; 8 | } 9 | 10 | & > * { 11 | width: 100%; 12 | } 13 | `; 14 | 15 | const InstallingDisplay = styled.div` 16 | & { 17 | position: absolute; 18 | z-index: 1; 19 | 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: center; 23 | align-items: center; 24 | 25 | height: 100%; 26 | width: 100%; 27 | 28 | background-color: rgba(0, 0, 0, 0.7); 29 | } 30 | `; 31 | 32 | const InstallPhrase = styled.p` 33 | & { 34 | margin-top: 2rem; 35 | 36 | font-size: 1rem; 37 | font-weight: lighter; 38 | } 39 | `; 40 | 41 | const DependencyArea = styled.div` 42 | & { 43 | position: absolute; 44 | } 45 | `; 46 | 47 | export { Frame, InstallingDisplay, InstallPhrase, DependencyArea }; 48 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/Editor/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Editor = styled.section` 4 | & { 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | 9 | .Stretch-width { 10 | flex-grow: 2; 11 | } 12 | `; 13 | 14 | export { Editor }; 15 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/ExplorerTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { 3 | TAB_CONTAINER_THEME, 4 | EXPLORER_TAB_CONTAINER_THEME 5 | } from 'constants/theme'; 6 | 7 | const { 8 | explorerTabContainerSelectedFileBGColor, 9 | } = EXPLORER_TAB_CONTAINER_THEME; 10 | 11 | const { 12 | tabContainerHeaderBGColor, 13 | tabContainerTitleColor, 14 | tabContainerTitleSize, 15 | tabContainerTitleWeight 16 | } = TAB_CONTAINER_THEME; 17 | 18 | const TabBody = styled.div` 19 | & { 20 | height: 100%; 21 | } 22 | 23 | .Is-selected-file { 24 | background-color: ${explorerTabContainerSelectedFileBGColor}; 25 | } 26 | `; 27 | 28 | const TabHeader = styled.header` 29 | & { 30 | display: flex; 31 | flex-direction: row; 32 | background-color: ${tabContainerHeaderBGColor}; 33 | } 34 | 35 | .Tab-header-Side-icons { 36 | margin: auto 0; 37 | margin-left: auto; 38 | margin-right: 1rem; 39 | } 40 | `; 41 | 42 | const Title = styled.h1` 43 | & { 44 | padding: 0.7rem 1rem; 45 | 46 | color: ${tabContainerTitleColor}; 47 | font-size: ${tabContainerTitleSize}; 48 | font-weight: ${tabContainerTitleWeight}; 49 | } 50 | `; 51 | 52 | const SideIcons = styled.span` 53 | & { 54 | display: flex; 55 | flex-direction: row; 56 | 57 | margin-left: auto; 58 | } 59 | 60 | & > svg { 61 | margin: 0 0.2rem; 62 | 63 | cursor: pointer; 64 | } 65 | `; 66 | 67 | export { TabHeader, TabBody, Title, SideIcons }; 68 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/LiveOnTab/close.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" class="svg-inline--fa fa-times fa-w-11" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512"> 2 | <path fill="white" 3 | d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Live/LiveOnTab/copy.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> 2 | <path fill="white" 3 | d="M464 0H144c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h320c26.51 0 48-21.49 48-48v-48h48c26.51 0 48-21.49 48-48V48c0-26.51-21.49-48-48-48zM362 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h42v224c0 26.51 21.49 48 48 48h224v42a6 6 0 0 1-6 6zm96-96H150a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h308a6 6 0 0 1 6 6v308a6 6 0 0 1-6 6z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Live/LiveTab/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import LiveOnTab from 'containers/Live/LiveOnTab'; 5 | 6 | const TAB_TITLE = 'LIVE'; 7 | 8 | function LiveTab() { 9 | return ( 10 | <> 11 | <Styled.Title>{TAB_TITLE}</Styled.Title> 12 | <div> 13 | <LiveOnTab /> 14 | </div> 15 | </> 16 | ); 17 | } 18 | 19 | export default LiveTab; 20 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/LiveTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { TAB_CONTAINER_THEME } from 'constants/theme'; 3 | 4 | const Title = styled.h1` 5 | & { 6 | padding: 0.7rem 1rem; 7 | color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; 8 | font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; 9 | font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; 10 | background-color: ${TAB_CONTAINER_THEME.tabContainerHeaderBGColor}; 11 | } 12 | `; 13 | 14 | export { 15 | Title 16 | }; 17 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/TabBar/dependency.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> 2 | <path fill="currentColor" 3 | d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Live/TabBar/explorer.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"> 2 | <path fill="currentColor" 3 | d="M224 136V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zm160-14.1v6.1H256V0h6.1c6.4 0 12.5 2.5 17 7l97.9 98c4.5 4.5 7 10.6 7 16.9z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Live/TabBar/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import { ProjectContext } from 'contexts'; 5 | import TabIcon from 'components/Project/TabIcon'; 6 | import Explorer from './explorer.svg'; 7 | import Live from './live.svg'; 8 | 9 | function TabBar({ theme }) { 10 | const { clickedTabIndex, setClickedTabIndex } = useContext(ProjectContext); 11 | 12 | const handleSetClickedIndex = index => setClickedTabIndex(index); 13 | 14 | const tabIcons = [ 15 | { 16 | name: 'explorer', 17 | icon: Explorer 18 | }, 19 | { 20 | name: 'live', 21 | icon: Live, 22 | } 23 | ]; 24 | 25 | return ( 26 | <Styled.TabBar tabBarBGColor={theme.tabBarBGColor}> 27 | {tabIcons.map(({ name, icon }, index) => { 28 | return ( 29 | <TabIcon 30 | theme={theme} 31 | key={index} 32 | index={index} 33 | name={name} 34 | icon={icon} 35 | onClick={handleSetClickedIndex} 36 | clicked={index === clickedTabIndex} 37 | /> 38 | ); 39 | })} 40 | </Styled.TabBar> 41 | ); 42 | } 43 | 44 | export default TabBar; 45 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/TabBar/live.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"> 2 | <path fill="currentColor" 3 | d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Live/TabBar/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const TabBar = styled.nav` 4 | & { 5 | min-width: 4rem; 6 | display: inline-flex; 7 | flex-direction: column; 8 | justify-content: flex-start; 9 | align-items: center; 10 | 11 | background-color: ${({ tabBarBGColor }) => tabBarBGColor}; 12 | } 13 | `; 14 | 15 | export { TabBar }; 16 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/TabContainer/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import { ProjectContext } from 'contexts'; 5 | import ExplorerTab from '../ExplorerTab'; 6 | import LiveTab from '../LiveTab'; 7 | 8 | function TabContainer() { 9 | const { clickedTabIndex } = useContext(ProjectContext); 10 | 11 | const tapMapping = { 12 | 0: <ExplorerTab />, 13 | 1: <LiveTab /> 14 | }; 15 | 16 | const renderTab = () => tapMapping[clickedTabIndex]; 17 | 18 | useEffect(() => { 19 | renderTab(); 20 | }, [clickedTabIndex]); 21 | 22 | return <Styled.Container>{renderTab()}</Styled.Container>; 23 | } 24 | 25 | export default TabContainer; 26 | -------------------------------------------------------------------------------- /cocode/src/containers/Live/TabContainer/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { TAB_CONTAINER_THEME } from 'constants/theme'; 3 | 4 | const Container = styled.section` 5 | & { 6 | background-color: ${TAB_CONTAINER_THEME.tabContainerBGColor}; 7 | } 8 | `; 9 | 10 | export { Container }; 11 | -------------------------------------------------------------------------------- /cocode/src/containers/NotFound/WeAreSorry/coconut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/cocode/src/containers/NotFound/WeAreSorry/coconut.png -------------------------------------------------------------------------------- /cocode/src/containers/NotFound/WeAreSorry/index.js: -------------------------------------------------------------------------------- 1 | //https://pixabay.com/illustrations/coconut-coconut-oil-coconut-milk-2327882/ 2 | import React from 'react'; 3 | import { Link } from 'react-router-dom'; 4 | import * as Styled from './style'; 5 | import coconut from './coconut.png'; 6 | 7 | function WeAreSorry() { 8 | return ( 9 | <Styled.WeAreSorry> 10 | <Styled.CoconutImage src={coconut} alt={'coconut'} /> 11 | <Styled.Title> 12 | This page isn't available. Sorry about that. 13 | </Styled.Title> 14 | <Styled.Description> 15 | If you want start over, go to the <Link to={'/'}>homepage</Link> 16 | </Styled.Description> 17 | </Styled.WeAreSorry> 18 | ); 19 | } 20 | 21 | export default WeAreSorry; 22 | -------------------------------------------------------------------------------- /cocode/src/containers/NotFound/WeAreSorry/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DEFAULT_THEME } from 'constants/theme'; 3 | 4 | const WeAreSorry = styled.section` 5 | & { 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | align-items: center; 10 | 11 | height: 70vh; 12 | padding: 5rem; 13 | } 14 | `; 15 | 16 | const Title = styled.h1` 17 | & { 18 | text-align: center; 19 | 20 | font-size: 2.8rem; 21 | font-weight: lighter; 22 | 23 | flex: 0 1; 24 | } 25 | `; 26 | 27 | const Description = styled.p` 28 | & { 29 | text-align: center; 30 | 31 | font-size: 1.8rem; 32 | font-weight: lighter; 33 | 34 | flex: 0 1; 35 | 36 | a { 37 | color: ${DEFAULT_THEME.mainColor}; 38 | } 39 | a:hover { 40 | color: ${DEFAULT_THEME.mainOpaqueColor}; 41 | } 42 | } 43 | `; 44 | 45 | const CoconutImage = styled.img` 46 | & { 47 | height: 9.8rem; 48 | width: 9.8rem; 49 | 50 | padding: 0.5rem; 51 | margin-bottom: 1.3rem; 52 | } 53 | `; 54 | export { WeAreSorry, Title, Description, CoconutImage }; 55 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/DependencyTab/dummy.js: -------------------------------------------------------------------------------- 1 | const dependencyList = [ 2 | { 3 | name: 'react', 4 | version: '16.8.6', 5 | }, 6 | { 7 | name: 'react-dom', 8 | version: '16.8.6', 9 | } 10 | ]; 11 | 12 | const dependencySearchList = [ 13 | { 14 | name: 'lodash', 15 | latestVersion: '4.17.15', 16 | github: 'https://github.com/lodash/lodash', 17 | npm: 'https://www.npmjs.com/package/lodash' 18 | }, 19 | { 20 | name: 'react', 21 | latestVersion: '16.8.6', 22 | github: 'https://github.com/reactjs', 23 | npm: 'https://www.npmjs.com/package/react' 24 | } 25 | ]; 26 | 27 | export { 28 | dependencyList, 29 | dependencySearchList 30 | }; -------------------------------------------------------------------------------- /cocode/src/containers/Project/DependencyTab/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import CoconutSpinner from 'components/Common/CoconutSpinner'; 5 | import Dependency from 'components/Project/Dependency'; 6 | import DependencyNow from 'components/Project/DependencyNow'; 7 | import DependencySearch from 'components/Project/DependencySearch'; 8 | 9 | import { ProjectContext } from 'contexts'; 10 | 11 | const TabTitleFirst = 'DEPENDENCIES'; 12 | const TabTitleSecond = 'SEARCH DEPENDENCY'; 13 | 14 | function InstallingDisplay() { 15 | return ( 16 | <Styled.InstallingDisplay> 17 | <CoconutSpinner /> 18 | <Styled.InstallPhrase> 19 | Please wait to install module... 20 | </Styled.InstallPhrase> 21 | </Styled.InstallingDisplay> 22 | ); 23 | } 24 | 25 | function DependencyTab() { 26 | const { project } = useContext(ProjectContext); 27 | const { dependencyInstalling } = project; 28 | 29 | return ( 30 | <Styled.Frame> 31 | {dependencyInstalling && <InstallingDisplay />} 32 | <Styled.DependencyArea> 33 | <Dependency title={TabTitleFirst}> 34 | <DependencyNow /> 35 | </Dependency> 36 | <Dependency title={TabTitleSecond}> 37 | <DependencySearch /> 38 | </Dependency> 39 | </Styled.DependencyArea> 40 | </Styled.Frame> 41 | ); 42 | } 43 | 44 | export default DependencyTab; 45 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/DependencyTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Frame = styled.div` 4 | & { 5 | position: relative; 6 | 7 | height: 100%; 8 | } 9 | 10 | & > * { 11 | width: 100%; 12 | } 13 | `; 14 | 15 | const InstallingDisplay = styled.div` 16 | & { 17 | position: absolute; 18 | z-index: 1; 19 | 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: center; 23 | align-items: center; 24 | 25 | height: 100%; 26 | width: 100%; 27 | 28 | background-color: rgba(0, 0, 0, 0.7); 29 | } 30 | `; 31 | 32 | const InstallPhrase = styled.p` 33 | & { 34 | margin-top: 2rem; 35 | 36 | font-size: 1rem; 37 | font-weight: lighter; 38 | } 39 | `; 40 | 41 | const DependencyArea = styled.div` 42 | & { 43 | position: absolute; 44 | } 45 | `; 46 | 47 | export { Frame, InstallingDisplay, InstallPhrase, DependencyArea }; 48 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/Editor/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Editor = styled.section` 4 | & { 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | 9 | .Stretch-width { 10 | flex-grow: 2; 11 | } 12 | `; 13 | 14 | export { Editor }; 15 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/ExplorerTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { 3 | TAB_CONTAINER_THEME, 4 | EXPLORER_TAB_CONTAINER_THEME 5 | } from 'constants/theme'; 6 | 7 | const TabBody = styled.div` 8 | & { 9 | height: 100%; 10 | } 11 | 12 | .Is-selected-file { 13 | background-color: ${EXPLORER_TAB_CONTAINER_THEME.explorerTabContainerSelectedFileBGColor}; 14 | } 15 | `; 16 | 17 | const TabHeader = styled.header` 18 | & { 19 | display: flex; 20 | flex-direction: row; 21 | background-color: ${TAB_CONTAINER_THEME.tabContainerHeaderBGColor}; 22 | } 23 | 24 | .Tab-header-Side-icons { 25 | margin: auto 0; 26 | margin-left: auto; 27 | margin-right: 1rem; 28 | } 29 | `; 30 | 31 | const Title = styled.h1` 32 | & { 33 | padding: 0.7rem 1rem; 34 | 35 | color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; 36 | font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; 37 | font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; 38 | } 39 | `; 40 | 41 | const SideIcons = styled.span` 42 | & { 43 | display: flex; 44 | flex-direction: row; 45 | 46 | margin-left: auto; 47 | } 48 | 49 | & > svg { 50 | margin: 0 0.2rem; 51 | 52 | cursor: pointer; 53 | } 54 | `; 55 | 56 | export { TabHeader, TabBody, Title, SideIcons }; 57 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/InfoTab/modify.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 40 40"> 2 | <path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"/> 3 | </svg> 4 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/LiveOffTab/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { useParams, useHistory } from 'react-router-dom'; 3 | import * as Styled from './style'; 4 | import { ProjectContext } from 'contexts'; 5 | 6 | const OFF_BUTTON_LABEL = 'Go Live'; 7 | const OFF_DESCRIPTION = 8 | 'Invite others to live edit this coconut with you. We’re doing it live!'; 9 | 10 | function LiveOffTab() { 11 | const history = useHistory(); 12 | const { projectId } = useParams(); 13 | const { forkCoconut } = useContext(ProjectContext); 14 | const handleConnectSocket = () => { 15 | const idOfNewProject = forkCoconut({ live: true }); 16 | if (idOfNewProject) return; 17 | 18 | history.replace(`../live/${projectId}`); 19 | }; 20 | 21 | return ( 22 | <Styled.Container> 23 | <Styled.Description>{OFF_DESCRIPTION}</Styled.Description> 24 | <Styled.Button onClick={handleConnectSocket}> 25 | <Styled.Circle /> 26 | {OFF_BUTTON_LABEL} 27 | </Styled.Button> 28 | </Styled.Container> 29 | ); 30 | } 31 | 32 | export default LiveOffTab; 33 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/LiveOffTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { LIVE_TAB_THEME } from 'constants/theme'; 3 | 4 | const Container = styled.div` 5 | & { 6 | margin: 0.7rem 1rem; 7 | } 8 | `; 9 | 10 | const Description = styled.div` 11 | & { 12 | color: ${LIVE_TAB_THEME.liveFontColor}; 13 | font-size: 1rem; 14 | font-weight: 100; 15 | margin-bottom: 1rem; 16 | } 17 | `; 18 | 19 | const Button = styled.button` 20 | & { 21 | display: flex; 22 | align-items: center; 23 | justify-content: center; 24 | width: -webkit-fill-available; 25 | padding: 0.7rem 2.2rem; 26 | border-radius: 0.7rem; 27 | background-color: ${LIVE_TAB_THEME.liveButtonBGColor}; 28 | font-size: 1rem; 29 | font-weight: 400; 30 | } 31 | 32 | &:hover { 33 | background-color: ${LIVE_TAB_THEME.liveButtonBGColorHover}; 34 | } 35 | `; 36 | 37 | const Circle = styled.div` 38 | & { 39 | width: 0.5rem; 40 | height: 0.5rem; 41 | margin-right: 0.5rem; 42 | background-color: ${LIVE_TAB_THEME.liveCircleBGColor}; 43 | border-radius: 50%; 44 | } 45 | `; 46 | 47 | export { 48 | Container, 49 | Description, 50 | Button, 51 | Circle 52 | }; 53 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/LiveTab/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import LiveOffTab from 'containers/Project/LiveOffTab'; 5 | 6 | const TAB_TITLE = 'LIVE'; 7 | 8 | function LiveTab() { 9 | return ( 10 | <> 11 | <Styled.Title>{TAB_TITLE}</Styled.Title> 12 | <div> 13 | <LiveOffTab /> 14 | </div> 15 | </> 16 | ); 17 | } 18 | 19 | export default LiveTab; 20 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/LiveTab/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { TAB_CONTAINER_THEME } from 'constants/theme'; 3 | 4 | const Title = styled.h1` 5 | & { 6 | padding: 0.7rem 1rem; 7 | color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; 8 | font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; 9 | font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; 10 | background-color: ${TAB_CONTAINER_THEME.tabContainerHeaderBGColor}; 11 | } 12 | `; 13 | 14 | export { 15 | Title 16 | }; 17 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabBar/dependency.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> 2 | <path fill="currentColor" 3 | d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabBar/explorer.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"> 2 | <path fill="currentColor" 3 | d="M224 136V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zm160-14.1v6.1H256V0h6.1c6.4 0 12.5 2.5 17 7l97.9 98c4.5 4.5 7 10.6 7 16.9z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabBar/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import { ProjectContext } from 'contexts'; 5 | 6 | import TabIcon from 'components/Project/TabIcon'; 7 | 8 | import Info from './info.svg'; 9 | import Explorer from './explorer.svg'; 10 | import Dependency from './dependency.svg'; 11 | import Live from './live.svg'; 12 | 13 | function TabBar({ theme }) { 14 | const { clickedTabIndex, setClickedTabIndex } = useContext(ProjectContext); 15 | 16 | const handleSetClickedIndex = index => setClickedTabIndex(index); 17 | 18 | const tabIcons = [ 19 | { 20 | name: 'info', 21 | icon: Info, 22 | }, 23 | { 24 | name: 'explorer', 25 | icon: Explorer 26 | }, 27 | { 28 | name: 'dependency', 29 | icon: Dependency 30 | }, 31 | { 32 | name: 'live', 33 | icon: Live, 34 | } 35 | ]; 36 | 37 | return ( 38 | <Styled.TabBar tabBarBGColor={theme.tabBarBGColor}> 39 | {tabIcons.map(({ name, icon }, index) => { 40 | return ( 41 | <TabIcon 42 | theme={theme} 43 | key={index} 44 | index={index} 45 | name={name} 46 | icon={icon} 47 | onClick={handleSetClickedIndex} 48 | clicked={index === clickedTabIndex} 49 | /> 50 | ); 51 | })} 52 | </Styled.TabBar> 53 | ); 54 | } 55 | 56 | export default TabBar; 57 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabBar/info.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"> 2 | <path fill="currentColor" 3 | d="M20 424.229h20V279.771H20c-11.046 0-20-8.954-20-20V212c0-11.046 8.954-20 20-20h112c11.046 0 20 8.954 20 20v212.229h20c11.046 0 20 8.954 20 20V492c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20v-47.771c0-11.046 8.954-20 20-20zM96 0C56.235 0 24 32.235 24 72s32.235 72 72 72 72-32.235 72-72S135.764 0 96 0z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabBar/live.svg: -------------------------------------------------------------------------------- 1 | <svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"> 2 | <path fill="currentColor" 3 | d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"></path> 4 | </svg> -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabBar/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const TabBar = styled.nav` 4 | & { 5 | min-width: 4rem; 6 | display: inline-flex; 7 | flex-direction: column; 8 | justify-content: flex-start; 9 | align-items: center; 10 | 11 | background-color: ${({ tabBarBGColor }) => tabBarBGColor}; 12 | } 13 | `; 14 | 15 | export { TabBar }; 16 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabContainer/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useContext } from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import { ProjectContext } from 'contexts'; 5 | 6 | import InfoTab from '../InfoTab'; 7 | import ExplorerTab from '../ExplorerTab'; 8 | import DependencyTab from '../DependencyTab'; 9 | import LiveTab from '../LiveTab'; 10 | 11 | function TabContainer() { 12 | const { clickedTabIndex } = useContext(ProjectContext); 13 | 14 | const tapMapping = { 15 | 0: <InfoTab />, 16 | 1: <ExplorerTab />, 17 | 2: <DependencyTab />, 18 | 3: <LiveTab/> 19 | }; 20 | 21 | const renderTab = () => tapMapping[clickedTabIndex]; 22 | 23 | useEffect(() => { 24 | renderTab(); 25 | }, [clickedTabIndex]); 26 | 27 | return <Styled.Container>{renderTab()}</Styled.Container>; 28 | } 29 | 30 | export default TabContainer; 31 | -------------------------------------------------------------------------------- /cocode/src/containers/Project/TabContainer/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { TAB_CONTAINER_THEME } from 'constants/theme'; 3 | 4 | const Container = styled.section` 5 | & { 6 | background-color: ${TAB_CONTAINER_THEME.tabContainerBGColor}; 7 | } 8 | `; 9 | 10 | export { Container }; 11 | -------------------------------------------------------------------------------- /cocode/src/containers/SignIn/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | import Github from 'components/Common/LoginModalBody/github.svg'; 4 | import { API } from 'config'; 5 | 6 | const SIGN_IN_TITLE = 'Sorry, This service requires a login.'; 7 | 8 | function SignIn() { 9 | const handleClickLoginButton = () => (window.location.href = API.login); 10 | 11 | return ( 12 | <Styled.Wrapper> 13 | <Styled.Title>{SIGN_IN_TITLE}</Styled.Title> 14 | <Styled.LoginButton onClick={handleClickLoginButton}> 15 | <Styled.Logo src={Github} /> 16 | Sign In With GitHub 17 | </Styled.LoginButton> 18 | </Styled.Wrapper> 19 | ); 20 | } 21 | 22 | export default SignIn; 23 | -------------------------------------------------------------------------------- /cocode/src/containers/SignIn/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { SIGN_IN_THEME } from 'constants/theme'; 3 | 4 | const Logo = styled.img` 5 | height: 1.2rem; 6 | margin-right: 0.8rem; 7 | filter: invert(1); 8 | `; 9 | 10 | const Wrapper = styled.div` 11 | & { 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | align-items: center; 16 | 17 | height: 70vh; 18 | padding: 5rem; 19 | } 20 | `; 21 | 22 | const Title = styled.h1` 23 | & { 24 | text-align: center; 25 | font-size: 3rem; 26 | font-weight: 100; 27 | } 28 | `; 29 | 30 | const LoginButton = styled.button` 31 | & { 32 | display: flex; 33 | justify-content: center; 34 | align-items: center; 35 | margin: 2rem 0 1.5rem 0; 36 | padding: 1rem 2.5rem; 37 | 38 | font-size: 1.5rem; 39 | 40 | background-color: ${SIGN_IN_THEME.signInButtonBGColor}; 41 | color: ${SIGN_IN_THEME.signInButtonTextColor}; 42 | border-radius: 0.5rem; 43 | } 44 | 45 | &:hover { 46 | background-color: ${SIGN_IN_THEME.signInButtonBGHoverColor}; 47 | } 48 | `; 49 | 50 | export { Logo, Wrapper, LoginButton, Title }; 51 | -------------------------------------------------------------------------------- /cocode/src/contexts/DashBoardContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const DashBoardContext = createContext(); 4 | 5 | export default DashBoardContext; 6 | -------------------------------------------------------------------------------- /cocode/src/contexts/LiveContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const LiveContext = createContext(); 4 | 5 | export default LiveContext; 6 | -------------------------------------------------------------------------------- /cocode/src/contexts/ProjectContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const ProjectContext = createContext(); 4 | 5 | export default ProjectContext; 6 | -------------------------------------------------------------------------------- /cocode/src/contexts/UserContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const UserContext = createContext(); 4 | 5 | export default UserContext; 6 | -------------------------------------------------------------------------------- /cocode/src/contexts/index.js: -------------------------------------------------------------------------------- 1 | import DashBoardContext from './DashBoardContext'; 2 | import ProjectContext from './ProjectContext'; 3 | import UserContext from './UserContext'; 4 | import LiveContext from './LiveContext'; 5 | 6 | export { DashBoardContext, ProjectContext, UserContext, LiveContext }; 7 | -------------------------------------------------------------------------------- /cocode/src/hooks/useFetch.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useReducer } from 'react'; 2 | import axios from 'axios'; 3 | import { DEFAULT_REQUEST_OPTION } from 'config'; 4 | import { APIReducer } from 'reducers'; 5 | import { 6 | fetchReadyActionCreator, 7 | fetchLoadActionCreator, 8 | fetchSuccessActionCreator, 9 | fetchFailActionCreator 10 | } from 'actions/API'; 11 | 12 | const API = axios.create(DEFAULT_REQUEST_OPTION); 13 | 14 | function useFetch({ method, url, data = {} }) { 15 | const [request, setRequest] = useState({ method, url, data }); 16 | const [state, dispatchFetchState] = useReducer(APIReducer, { 17 | data: false, 18 | loading: false, 19 | error: false, 20 | status: false 21 | }); 22 | 23 | const requestToServer = () => { 24 | dispatchFetchState(fetchLoadActionCreator()); 25 | API(request) 26 | .then(res => dispatchFetchState(fetchSuccessActionCreator(res))) 27 | .catch(error => dispatchFetchState(fetchFailActionCreator(error))); 28 | }; 29 | 30 | useEffect(() => { 31 | if (!request.url) return; 32 | 33 | requestToServer(); 34 | return () => dispatchFetchState(fetchReadyActionCreator()); 35 | }, [request]); 36 | 37 | return [state, setRequest]; 38 | } 39 | 40 | export default useFetch; 41 | -------------------------------------------------------------------------------- /cocode/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | const rootElement = document.getElementById('root'); 6 | ReactDOM.render(<App />, rootElement); 7 | -------------------------------------------------------------------------------- /cocode/src/pages/Empty/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Header from 'containers/Common/Header'; 3 | 4 | function Empty() { 5 | useEffect(() => { 6 | const redirectURL = localStorage.getItem('redirectURL'); 7 | if (redirectURL) { 8 | window.location.href = redirectURL; 9 | localStorage.removeItem('redirectURL'); 10 | } 11 | }, []); 12 | 13 | return ( 14 | <Header /> 15 | ); 16 | } 17 | 18 | export default Empty; -------------------------------------------------------------------------------- /cocode/src/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Header from 'containers/Common/Header'; 3 | import Main from 'containers/Home/Main'; 4 | import AboutCocode from 'containers/Home/AboutCocode'; 5 | import AboutUs from 'containers/Home/AboutUs'; 6 | 7 | import addWheelEvent from 'utils/addWheelEvent'; 8 | 9 | function Home() { 10 | useEffect(addWheelEvent, []); 11 | 12 | return ( 13 | <> 14 | <Header /> 15 | <Main /> 16 | <AboutCocode /> 17 | <AboutUs /> 18 | </> 19 | ); 20 | } 21 | 22 | export default Home; 23 | -------------------------------------------------------------------------------- /cocode/src/pages/Live/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Main = styled.main` 4 | & { 5 | display: flex; 6 | flex-direction: row; 7 | 8 | height: 91vh; 9 | 10 | .Project-main-stretch { 11 | flex-grow: 2; 12 | } 13 | } 14 | `; 15 | 16 | export { Main }; 17 | -------------------------------------------------------------------------------- /cocode/src/pages/NotFound/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from 'containers/Common/Header'; 3 | import WeAreSorry from 'containers/NotFound/WeAreSorry'; 4 | 5 | function NotFound() { 6 | return ( 7 | <> 8 | <Header /> 9 | <WeAreSorry /> 10 | </> 11 | ); 12 | } 13 | 14 | export default NotFound; 15 | -------------------------------------------------------------------------------- /cocode/src/pages/Project/getUpdatedPackageJSON.js: -------------------------------------------------------------------------------- 1 | function getUpdatedPackageJSON(files, root, dependency) { 2 | const childOfRoot = files[root].child; 3 | const packageJSON = childOfRoot 4 | .map(id => files[id]) 5 | .filter(({ name }) => name === 'package.json')[0]; 6 | const dependencies = JSON.parse(packageJSON.contents).dependencies; 7 | const newDependencies = { 8 | ...dependencies, 9 | [dependency.name]: dependency.version 10 | }; 11 | 12 | const newPackageJSONContents = JSON.stringify( 13 | { 14 | ...JSON.parse(packageJSON.contents), 15 | dependencies: newDependencies 16 | }, 17 | undefined, 18 | 4 19 | ); 20 | const packageJSONFileId = packageJSON._id; 21 | 22 | return { newPackageJSONContents, packageJSONFileId }; 23 | } 24 | 25 | export default getUpdatedPackageJSON; 26 | -------------------------------------------------------------------------------- /cocode/src/pages/Project/parseProject.js: -------------------------------------------------------------------------------- 1 | import copyProject from 'template/copyProject'; 2 | 3 | const parseProject = (project, username) => { 4 | const { dependency, entry, name, root, _id } = project; 5 | const projectInfo = JSON.parse( 6 | JSON.stringify({ dependency, entry, name, root, _id }) 7 | ); 8 | const files = []; 9 | Object.entries(project.files).forEach(([_, file]) => { 10 | const { child, name, projectId, type, _id, contents } = file; 11 | files.push({ child, name, projectId, type, _id, contents }); 12 | }); 13 | const parsedProject = copyProject({ ...projectInfo, files }); 14 | 15 | parsedProject.author = username; 16 | 17 | return parsedProject; 18 | }; 19 | 20 | export default parseProject; 21 | -------------------------------------------------------------------------------- /cocode/src/pages/Project/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Main = styled.main` 4 | & { 5 | display: flex; 6 | flex-direction: row; 7 | 8 | height: 91vh; 9 | 10 | .Project-main-stretch { 11 | flex-grow: 2; 12 | } 13 | } 14 | `; 15 | 16 | export { Main }; 17 | -------------------------------------------------------------------------------- /cocode/src/pages/SignIn/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { useHistory } from 'react-router-dom'; 3 | import Header from 'containers/Common/Header'; 4 | import SignInContainer from 'containers/SignIn'; 5 | import { UserContext } from 'contexts'; 6 | 7 | function SignIn() { 8 | const { user } = useContext(UserContext); 9 | const history = useHistory(); 10 | 11 | if (user) history.replace('../'); 12 | 13 | return ( 14 | <> 15 | <Header /> 16 | <SignInContainer /> 17 | </> 18 | ); 19 | } 20 | 21 | export default SignIn; 22 | -------------------------------------------------------------------------------- /cocode/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Home from './Home'; 2 | import DashBoard from './DashBoard'; 3 | import Project from './Project'; 4 | import NotFound from './NotFound'; 5 | import Live from './Live'; 6 | import SignIn from './SignIn'; 7 | import Empty from './Empty'; 8 | 9 | export { Home, DashBoard, Project, NotFound, Live, SignIn, Empty }; 10 | -------------------------------------------------------------------------------- /cocode/src/reducers/APIReducer.js: -------------------------------------------------------------------------------- 1 | import { API_READY, API_LOADING, API_SUCCESS, API_FAIL } from 'actions/types'; 2 | 3 | const ready = () => ({ 4 | data: false, 5 | loading: false, 6 | error: false, 7 | status: false 8 | }); 9 | 10 | const loading = () => ({ 11 | data: undefined, 12 | loading: true, 13 | error: false, 14 | status: false 15 | }); 16 | 17 | const success = (state, { data, status }) => ({ 18 | ...state, 19 | data, 20 | status, 21 | loading: false 22 | }); 23 | 24 | const fail = state => ({ 25 | ...state, 26 | loading: false, 27 | error: true 28 | }); 29 | 30 | function APIReducer(state, { type, payload }) { 31 | const reducers = { 32 | [API_READY]: ready, 33 | [API_LOADING]: loading, 34 | [API_SUCCESS]: success, 35 | [API_FAIL]: fail 36 | }; 37 | 38 | const reducer = reducers[type]; 39 | return reducer ? reducer(state, payload) : state; 40 | } 41 | 42 | export default APIReducer; 43 | -------------------------------------------------------------------------------- /cocode/src/reducers/DashBoardReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCH_COCONUT, 3 | UPDATE_COCONUT_NAME, 4 | DELETE_COCONUT 5 | } from 'actions/types'; 6 | 7 | const fetchCoconut = (_, coconuts) => coconuts; 8 | 9 | const updateCoconutName = (coconuts, newCoconut) => 10 | coconuts.map(coconut => 11 | coconut._id !== newCoconut._id ? coconut : newCoconut 12 | ); 13 | 14 | const deleteCoconut = (coconuts, _id) => 15 | coconuts.filter(coconut => coconut._id !== _id); 16 | 17 | function DashBoardReducer(state, { type, payload }) { 18 | const reducers = { 19 | [FETCH_COCONUT]: fetchCoconut, 20 | [UPDATE_COCONUT_NAME]: updateCoconutName, 21 | [DELETE_COCONUT]: deleteCoconut 22 | }; 23 | 24 | const reducer = reducers[type]; 25 | return reducer ? reducer(state, payload) : state; 26 | } 27 | 28 | export default DashBoardReducer; 29 | -------------------------------------------------------------------------------- /cocode/src/reducers/LiveReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | LIVE_ON, 3 | LIVE_OFF, 4 | LIVE_JOIN_USER, 5 | LIVE_LEAVE_USER 6 | } from 'actions/types'; 7 | 8 | const liveOn = (state, { url, socket, project, owner }) => { 9 | return ({ 10 | ...state, 11 | url, 12 | socket, 13 | project, 14 | owner, 15 | participants: [] 16 | }); 17 | }; 18 | 19 | const liveOff = (state) => ({ 20 | ...state, 21 | socket: null, 22 | owner: undefined, 23 | participants: [] 24 | }); 25 | 26 | const joinUser = (state, { participants }) => ({ 27 | ...state, 28 | participants, 29 | }); 30 | 31 | const leaveUser = (state, { participants }) => ({ 32 | ...state, 33 | participants, 34 | }); 35 | 36 | function LiveReducer(state, { type, payload }) { 37 | const reducers = { 38 | [LIVE_ON]: liveOn, 39 | [LIVE_OFF]: liveOff, 40 | [LIVE_JOIN_USER]: joinUser, 41 | [LIVE_LEAVE_USER]: leaveUser 42 | }; 43 | 44 | const reducer = reducers[type]; 45 | return reducer ? reducer(state, payload) : state; 46 | } 47 | 48 | export default LiveReducer; 49 | -------------------------------------------------------------------------------- /cocode/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import APIReducer from './APIReducer'; 2 | import DashBoardReducer from './DashBoardReducer'; 3 | import LiveReducer from './LiveReducer'; 4 | import ProjectReducer from './ProjectReducer'; 5 | 6 | export { APIReducer, DashBoardReducer, LiveReducer, ProjectReducer }; -------------------------------------------------------------------------------- /cocode/src/stores/LiveStore.js: -------------------------------------------------------------------------------- 1 | import React, { useReducer } from 'react'; 2 | import { LiveReducer } from 'reducers'; 3 | import { LiveContext } from 'contexts'; 4 | import { LIVE_SERVER } from 'config'; 5 | 6 | function LiveStore({ children }) { 7 | const initialValue = { 8 | liveServer: LIVE_SERVER, 9 | url: '', 10 | project: {}, 11 | socket: null, 12 | owner: undefined, 13 | participants: [] 14 | }; 15 | 16 | const [live, dispatchLive] = useReducer(LiveReducer, initialValue); 17 | const { liveServer, url, socket, participants, owner } = live; 18 | 19 | return ( 20 | <LiveContext.Provider 21 | value={{ 22 | liveServer, 23 | url, 24 | socket, 25 | participants, 26 | owner, 27 | dispatchLive 28 | }} 29 | > 30 | {children} 31 | </LiveContext.Provider> 32 | ); 33 | } 34 | 35 | export default LiveStore; 36 | -------------------------------------------------------------------------------- /cocode/src/stores/index.js: -------------------------------------------------------------------------------- 1 | import LiveStore from './LiveStore'; 2 | 3 | export { LiveStore }; 4 | -------------------------------------------------------------------------------- /cocode/src/stories/decorators/ThemeDecorator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from 'styled-components'; 3 | 4 | import { DEFAULT_THEME } from 'constants/theme'; 5 | 6 | export const ThemeDecorator = story => ( 7 | <ThemeProvider theme={DEFAULT_THEME}>{story()}</ThemeProvider> 8 | ); 9 | -------------------------------------------------------------------------------- /cocode/src/stories/decorators/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/cocode/src/stories/decorators/index.js -------------------------------------------------------------------------------- /cocode/src/template/copyProject.js: -------------------------------------------------------------------------------- 1 | import ObjectID from 'bson-objectid'; 2 | 3 | function copyProject(project) { 4 | const deepCopyProject = JSON.parse(JSON.stringify(project)); 5 | 6 | const idMap = objectIdMapping(deepCopyProject); 7 | const projectInfo = updateProjectInfo(idMap, deepCopyProject); 8 | const files = mappingNewIdAtProjectFiles(idMap, deepCopyProject); 9 | 10 | return Object.assign(deepCopyProject, { ...projectInfo, files }); 11 | } 12 | 13 | function objectIdMapping({ _id, files }) { 14 | const idMap = { [_id]: ObjectID().str }; 15 | 16 | files.forEach(({ _id }) => { 17 | idMap[_id] = ObjectID().str; 18 | }); 19 | 20 | return idMap; 21 | } 22 | 23 | function mappingNewIdAtProjectFiles(idMap, { _id, files }) { 24 | const projectId = idMap[_id]; 25 | 26 | const newFiles = files.map(file => { 27 | const { _id, child } = file; 28 | const source = { _id: idMap[_id], projectId }; 29 | 30 | if (child) source.child = child.map(_id => idMap[_id]); 31 | return Object.assign(file, source); 32 | }); 33 | 34 | return newFiles; 35 | } 36 | 37 | function updateProjectInfo(idMap, project) { 38 | const { _id, root, entry } = project; 39 | return { 40 | _id: idMap[_id], 41 | root: idMap[root], 42 | entry: idMap[entry] 43 | }; 44 | } 45 | 46 | export default copyProject; 47 | -------------------------------------------------------------------------------- /cocode/src/utils/addWheelEvent.js: -------------------------------------------------------------------------------- 1 | let isScrollable = false; 2 | 3 | function handleWheelScroll(e) { 4 | if (isScrollable) return; 5 | isScrollable = true; 6 | if (e.deltaY < 0) window.scrollBy(0, -window.innerHeight); 7 | else window.scrollBy(0, window.innerHeight); 8 | setTimeout(handleWheelAble, 1000); 9 | } 10 | function handleWheelAble() { 11 | isScrollable = false; 12 | } 13 | 14 | function addWheelEvent() { 15 | window.addEventListener('wheel', handleWheelScroll); 16 | } 17 | 18 | export default addWheelEvent; 19 | -------------------------------------------------------------------------------- /cocode/src/utils/controlCookie.js: -------------------------------------------------------------------------------- 1 | import { DELETE_COOKIE_VALUE } from 'constants/cookie'; 2 | 3 | function deleteCookie(key) { 4 | document.cookie = key + DELETE_COOKIE_VALUE; 5 | } 6 | 7 | //참고: https://cofs.tistory.com/363 8 | function getCookie(name) { 9 | const value = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); 10 | return value ? value[2] : null; 11 | } 12 | 13 | export { deleteCookie, getCookie }; 14 | -------------------------------------------------------------------------------- /cocode/src/utils/domControl.js: -------------------------------------------------------------------------------- 1 | function selectAllTextAboutFocusedDom() { 2 | document.execCommand('selectAll', false, null); 3 | } 4 | 5 | function changeDivEditable(node, status) { 6 | node.contentEditable = status; 7 | if (status) node.focus(); 8 | } 9 | 10 | function copyToClipboard(node) { 11 | const range = document.createRange(); 12 | range.selectNode(node); 13 | window.getSelection().removeAllRanges(); 14 | window.getSelection().addRange(range); 15 | document.execCommand('copy'); 16 | } 17 | 18 | export { selectAllTextAboutFocusedDom, changeDivEditable, copyToClipboard }; 19 | -------------------------------------------------------------------------------- /cocode/src/utils/index.js: -------------------------------------------------------------------------------- 1 | function fileHasNoExtension(fileName) { 2 | const fileNameSplited = fileName.split('.'); 3 | return fileNameSplited.length === 1; 4 | } 5 | 6 | function getFileExtension(fileName) { 7 | if (fileHasNoExtension(fileName)) return 'file'; 8 | return fileName.split('.').pop(); 9 | } 10 | 11 | export { getFileExtension }; 12 | -------------------------------------------------------------------------------- /cocode/src/utils/keyDownEvent.js: -------------------------------------------------------------------------------- 1 | import { KEY_CODE_S } from 'constants/keyCode'; 2 | 3 | //출처: https://michilehr.de/overwrite-cmds-and-ctrls-in-javascript/ 4 | const isPressCtrlAndS = e => 5 | (window.navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey) && 6 | e.which === KEY_CODE_S; 7 | 8 | export { isPressCtrlAndS }; 9 | -------------------------------------------------------------------------------- /cocode/test_setup/__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /coconut/.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /coconut/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | Dockerfile.dev 3 | 4 | *.md 5 | !README.md 6 | 7 | node_modules -------------------------------------------------------------------------------- /coconut/.eslintignore: -------------------------------------------------------------------------------- 1 | README.md 2 | webpack.config.js 3 | .babelrc 4 | .prettierrc 5 | .env 6 | public/* 7 | src/template/* 8 | init-script/* 9 | -------------------------------------------------------------------------------- /coconut/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier"], 3 | "env": { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "globals": { 8 | "Atomics": "readonly", 9 | "SharedArrayBuffer": "readonly" 10 | }, 11 | "parserOptions": { 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "ecmaVersion": 2018, 16 | "sourceType": "module" 17 | }, 18 | "plugins": ["react"], 19 | "rules": { 20 | "eqeqeq": "error", 21 | "no-empty-function": "error", 22 | "no-multiple-empty-lines": "error", 23 | "no-multi-spaces": "error", 24 | "quotes": ["error", "single"], 25 | "semi": "error", 26 | "switch-colon-spacing": "error", 27 | "array-bracket-spacing": ["error", "never"], 28 | "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], 29 | "keyword-spacing": ["error", { "before": true, "after": true }], 30 | "max-depth": ["error", 5], 31 | "max-len": ["error", { "code": 80 }], 32 | "no-mixed-spaces-and-tabs": "error", 33 | "no-trailing-spaces": "error" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /coconut/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /coconut/.prettierignore: -------------------------------------------------------------------------------- 1 | .* 2 | public/* 3 | package-lock.json 4 | package.json 5 | README.md 6 | webpack.config.js -------------------------------------------------------------------------------- /coconut/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 4, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /coconut/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.13.0 as builder 2 | 3 | RUN mkdir /usr/src/app 4 | WORKDIR /usr/src/app 5 | ENV PATH /usr/src/app/node_modules/.bin:$PATH 6 | COPY package.json /usr/src/app/package.json 7 | RUN yarn install --silent 8 | RUN yarn global add react-scripts@2.1.3 --silent 9 | 10 | COPY . /usr/src/app 11 | 12 | RUN yarn build 13 | 14 | ### 15 | 16 | FROM nginx:1.13.9-alpine 17 | 18 | RUN rm -rf /etc/nginx/conf.d 19 | COPY nginx /etc/nginx 20 | 21 | COPY --from=builder /usr/src/app/build /usr/share/nginx/html 22 | 23 | EXPOSE 8080:8080 24 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /coconut/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:12.13.0 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | RUN yarn install 7 | 8 | COPY . . 9 | 10 | EXPOSE 3000 11 | CMD ["yarn", "start:docker"] -------------------------------------------------------------------------------- /coconut/config-overrides.js: -------------------------------------------------------------------------------- 1 | module.exports = function override(config, env) { 2 | config.module.rules.push({ 3 | test: /\.worker\.js$/, 4 | use: { loader: 'worker-loader' } 5 | }); 6 | return config; 7 | }; 8 | -------------------------------------------------------------------------------- /coconut/dev.env: -------------------------------------------------------------------------------- 1 | NODE_PATH=src/ 2 | PORT=3010 3 | SKIP_PREFLIGHT_CHECK=true 4 | 5 | REACT_APP_DEV_API_SERVER_IP= 6 | REACT_APP_PROD_API_SERVER_IP= 7 | 8 | REACT_APP_PROD_DEPENDENCY_SERVER_IP= 9 | REACT_APP_DEV_DEPENDENCY_SERVER_IP= 10 | -------------------------------------------------------------------------------- /coconut/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name 127.0.0.1; 3 | listen 8080; 4 | 5 | location / { 6 | root /usr/share/nginx/html; 7 | index index.html index.htm; 8 | try_files $uri $uri/ /index.html; 9 | } 10 | 11 | error_page 500 502 503 504 /50x.html; 12 | location = /50x.html { 13 | root /usr/share/nginx/html; 14 | } 15 | } -------------------------------------------------------------------------------- /coconut/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/coconut/public/favicon.ico -------------------------------------------------------------------------------- /coconut/public/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <link rel="icon" href="favicon.ico" /> 5 | <title>Coconut 6 | 7 | 15 | 16 |
    17 | 18 | 19 | -------------------------------------------------------------------------------- /coconut/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 3 | 4 | import Coconut from 'components/Coconut'; 5 | 6 | function App() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /coconut/src/App.test.js: -------------------------------------------------------------------------------- 1 | it('App.js', () => { 2 | expect(true).toBeTruthy(); 3 | }); 4 | -------------------------------------------------------------------------------- /coconut/src/actions/API.js: -------------------------------------------------------------------------------- 1 | import { API_READY, API_LOADING, API_SUCCESS, API_FAIL } from './types'; 2 | 3 | function fetchLoadActionCreator() { 4 | return { type: API_LOADING }; 5 | } 6 | 7 | function fetchSuccessActionCreator({ status, data }) { 8 | return { type: API_SUCCESS, payload: { status, data } }; 9 | } 10 | 11 | function fetchFailActionCreator(error) { 12 | return { type: API_FAIL, payload: error }; 13 | } 14 | 15 | function fetchReadyActionCreator() { 16 | return { type: API_READY }; 17 | } 18 | 19 | export { 20 | fetchReadyActionCreator, 21 | fetchLoadActionCreator, 22 | fetchSuccessActionCreator, 23 | fetchFailActionCreator 24 | }; 25 | -------------------------------------------------------------------------------- /coconut/src/actions/Project.js: -------------------------------------------------------------------------------- 1 | import { FETCH_PROJECT, CLONE_PROJECT } from './types'; 2 | 3 | function fetchProjectActionCreator(payload) { 4 | return { type: FETCH_PROJECT, payload }; 5 | } 6 | 7 | function cloneProjectActionCreator(payload) { 8 | return { type: CLONE_PROJECT, payload }; 9 | } 10 | 11 | export { fetchProjectActionCreator, cloneProjectActionCreator }; 12 | -------------------------------------------------------------------------------- /coconut/src/actions/types.js: -------------------------------------------------------------------------------- 1 | // API 2 | const API_READY = 'API_READY'; 3 | const API_LOADING = 'API_LOADING'; 4 | const API_SUCCESS = 'API_SUCCESS'; 5 | const API_FAIL = 'API_FAILURE'; 6 | 7 | // Project 8 | const FETCH_PROJECT = 'fetchProject'; 9 | const CLONE_PROJECT = 'cloneProject'; 10 | 11 | export { 12 | API_READY, 13 | API_LOADING, 14 | API_SUCCESS, 15 | API_FAIL, 16 | FETCH_PROJECT, 17 | CLONE_PROJECT 18 | }; 19 | -------------------------------------------------------------------------------- /coconut/src/apis/Dependency.js: -------------------------------------------------------------------------------- 1 | import { DEPENDENCY } from 'config'; 2 | 3 | function getModule(moduleName, moduleVersion) { 4 | return { 5 | url: `${DEPENDENCY.modules}`, 6 | method: 'post', 7 | data: { 8 | moduleName, 9 | moduleVersion 10 | } 11 | }; 12 | } 13 | 14 | export { getModule }; 15 | -------------------------------------------------------------------------------- /coconut/src/apis/Project.js: -------------------------------------------------------------------------------- 1 | import { API } from 'config'; 2 | 3 | function getProjectInfoAPICreator(projectId) { 4 | return { 5 | url: `${API.projects}/${projectId}`, 6 | method: 'get' 7 | }; 8 | } 9 | 10 | export { getProjectInfoAPICreator }; 11 | -------------------------------------------------------------------------------- /coconut/src/bundler/core.js: -------------------------------------------------------------------------------- 1 | import * as babel from '@babel/core'; 2 | import presetEnv from '@babel/preset-env'; 3 | import presetReact from '@babel/preset-react'; 4 | import pluginRegenerator from '@babel/plugin-transform-regenerator'; 5 | import { pathStack } from './global'; 6 | 7 | function transformCode(code) { 8 | try { 9 | const result = babel.transform(code, { 10 | presets: [presetEnv, presetReact], 11 | plugins: [pluginRegenerator], 12 | compact: true 13 | }); 14 | return { 15 | state: true, 16 | value: result.code 17 | }; 18 | } catch (error) { 19 | return { 20 | state: false, 21 | value: error.message 22 | }; 23 | } 24 | } 25 | 26 | function init() { 27 | while (pathStack.length) pathStack.pop(); 28 | pathStack.push('/root/src/'); 29 | } 30 | 31 | export { transformCode, init }; 32 | -------------------------------------------------------------------------------- /coconut/src/bundler/global.js: -------------------------------------------------------------------------------- 1 | const pathStack = []; 2 | 3 | export { pathStack }; 4 | -------------------------------------------------------------------------------- /coconut/src/bundler/index.js: -------------------------------------------------------------------------------- 1 | import { transformCode, init } from './core'; 2 | import { pathStack } from './global'; 3 | import { require } from './require'; 4 | import { pathParser } from './pathParser'; 5 | 6 | export { transformCode, init, pathStack, require, pathParser }; 7 | -------------------------------------------------------------------------------- /coconut/src/bundler/pathParser.js: -------------------------------------------------------------------------------- 1 | /* eslint no-restricted-globals: 0 */ 2 | import { pathStack } from './global'; 3 | import path from 'path'; 4 | 5 | const DEPENDENCY_PATH = '/node_modules/'; 6 | 7 | function pathParser(param) { 8 | const moduleName = param; 9 | 10 | if (param[0] !== '.' && param[0] !== '/') { 11 | param = `${DEPENDENCY_PATH}${param}`; 12 | } 13 | param = path.resolve(pathStack[pathStack.length - 1], param); 14 | const extension = param.split('.'); 15 | 16 | if (extension[extension.length - 1] === 'js') { 17 | return [param, path.dirname(param)]; 18 | } 19 | if (self.fileSystem[`${param}.js`]) { 20 | return [`${param}.js`, path.dirname(param)]; 21 | } 22 | if (self.fileSystem[`${param}/index.js`]) { 23 | return [`${param}/index.js`, param]; 24 | } 25 | 26 | throw Error(`Module not found: '${moduleName}'`); 27 | } 28 | 29 | export { pathParser }; 30 | -------------------------------------------------------------------------------- /coconut/src/bundler/pathParserInMain.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | const DEPENDENCY_PATH = '/node_modules/'; 4 | 5 | function pathParser(param) { 6 | const moduleName = param; 7 | 8 | if (param[0] !== '.' && param[0] !== '/') { 9 | param = `${DEPENDENCY_PATH}${param}`; 10 | } 11 | 12 | param = path.resolve(window.pathStack[window.pathStack.length - 1], param); 13 | const extension = param.split('.'); 14 | 15 | if (extension[extension.length - 1] === 'js') { 16 | return [param, path.dirname(param)]; 17 | } 18 | if (window.fileSystem[`${param}.js`]) { 19 | return [`${param}.js`, path.dirname(param)]; 20 | } 21 | if (window.fileSystem[`${param}/index.js`]) { 22 | return [`${param}/index.js`, param]; 23 | } 24 | 25 | throw Error(`Module not found: '${moduleName}'`); 26 | } 27 | 28 | export { pathParser }; 29 | -------------------------------------------------------------------------------- /coconut/src/bundler/require.js: -------------------------------------------------------------------------------- 1 | /* eslint no-restricted-globals: 0 */ 2 | import { pathStack } from './global'; 3 | import { pathParser } from './pathParser'; 4 | import { transformCode } from './core'; 5 | import { scriptCodeTemplate, executeCodeTemplate } from './codeTemplate'; 6 | 7 | function require(path) { 8 | if (path === '.' || path === './') 9 | throw Error('Recursive path parsing error'); 10 | const [newPath, newPathParent] = pathParser(path); 11 | 12 | if (self.exports[newPath]) return self.exports[newPath]; 13 | 14 | pathStack.push(newPathParent); 15 | const code = transformCode(self.fileSystem[newPath].contents).value; 16 | 17 | let result = null; 18 | let stackLength = 0; 19 | try { 20 | stackLength = pathStack.length; 21 | result = eval(executeCodeTemplate(code)); 22 | } catch (error) { 23 | self.exports[newPath] = {}; 24 | while (stackLength < pathStack.length) pathStack.pop(); 25 | result = eval(executeCodeTemplate(code)); 26 | } 27 | 28 | self.bundledCode = ` 29 | ${self.bundledCode}\n${scriptCodeTemplate(code, newPath, newPathParent)}`; 30 | 31 | self.exports[newPath] = result; 32 | pathStack.pop(); 33 | 34 | return result; 35 | } 36 | 37 | export { require }; 38 | -------------------------------------------------------------------------------- /coconut/src/bundler/requireInMain.js: -------------------------------------------------------------------------------- 1 | import { pathStack } from './global'; 2 | import { pathParser } from './pathParserInMain'; 3 | import { transformCode } from './core'; 4 | import { executeCodeTemplate } from './codeTemplate'; 5 | 6 | function requireInMain(path) { 7 | if (path === '.' || path === './') 8 | throw Error('Recursive path parsing error'); 9 | const [newPath, newPathParent] = pathParser(path); 10 | 11 | if (window.exports[newPath]) return window.exports[newPath]; 12 | 13 | pathStack.push(newPathParent); 14 | const code = transformCode(window.fileSystem[newPath].contents).value; 15 | 16 | let result = null; 17 | let stackLength = 0; 18 | try { 19 | stackLength = pathStack.length; 20 | result = eval(executeCodeTemplate(code)); 21 | } catch (error) { 22 | while (stackLength < pathStack.length) pathStack.pop(); 23 | result = eval(executeCodeTemplate(code)); 24 | } 25 | 26 | window.exports[newPath] = result; 27 | pathStack.pop(); 28 | 29 | return result; 30 | } 31 | 32 | export { requireInMain }; 33 | -------------------------------------------------------------------------------- /coconut/src/components/Coconut/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const BuildStateOverlay = styled.div` 4 | & { 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | 14 | overflow: scroll; 15 | 16 | width: 100%; 17 | height: 100%; 18 | 19 | background-color: black; 20 | color: white; 21 | 22 | p { 23 | margin: 1rem; 24 | 25 | font-size: 2rem; 26 | font-weight: lighter; 27 | } 28 | 29 | .Is-build-error { 30 | font-size: 1rem; 31 | } 32 | } 33 | `; 34 | 35 | export { BuildStateOverlay }; 36 | -------------------------------------------------------------------------------- /coconut/src/components/CoconutSpinner/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as Styled from './style'; 3 | 4 | import TinyCoconutLogo from 'components/TinyCoconutLogo'; 5 | 6 | function Spinner() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default Spinner; 18 | -------------------------------------------------------------------------------- /coconut/src/components/CoconutSpinner/style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Container = styled.div` 4 | position: relative; 5 | 6 | width: 3.5rem; 7 | height: 3.5rem; 8 | `; 9 | 10 | const Content = styled.div` 11 | position: absolute; 12 | top: 1.1rem; 13 | left: 0.8rem; 14 | `; 15 | 16 | const SpinningArc = styled.div` 17 | position: absolute; 18 | width: 3.5rem; 19 | height: 3.5rem; 20 | background-color: transparent; 21 | 22 | box-sizing: content-box; 23 | border: 3px solid white; 24 | border-radius: 100%; 25 | border-bottom-color: transparent; 26 | 27 | animation: 1s linear 0s infinite normal both running spin; 28 | 29 | @keyframes spin { 30 | 0% { 31 | transform: rotate(0deg); 32 | } 33 | 34 | 100% { 35 | transform: rotate(360deg); 36 | } 37 | } 38 | `; 39 | 40 | export { Container, Content, SpinningArc }; 41 | -------------------------------------------------------------------------------- /coconut/src/components/Logo/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import logo from './logo.svg'; 3 | 4 | function Logo() { 5 | return {'로고'}; 6 | } 7 | 8 | export default Logo; 9 | -------------------------------------------------------------------------------- /coconut/src/config/index.js: -------------------------------------------------------------------------------- 1 | const API_SERVER = 2 | process.env.NODE_ENV === 'production' 3 | ? process.env.REACT_APP_PROD_API_SERVER_IP 4 | : process.env.REACT_APP_DEV_API_SERVER_IP; 5 | 6 | const DEPENDENCY_SERVER = 7 | process.env.NODE_ENV === 'production' 8 | ? process.env.REACT_APP_PROD_DEPENDENCY_SERVER_IP 9 | : process.env.REACT_APP_DEV_DEPENDENCY_SERVER_IP; 10 | 11 | const DEFAULT_REQUEST_OPTION = { 12 | withCredentials: true, 13 | mode: 'cors', 14 | credentials: 'include' 15 | }; 16 | 17 | const API = { 18 | projects: `${API_SERVER}/projects`, 19 | dependency: name => `${API_SERVER}/dependency/search?name=${name}` 20 | }; 21 | 22 | const DEPENDENCY = { 23 | modules: `${DEPENDENCY_SERVER}/modules` 24 | }; 25 | 26 | export { DEFAULT_REQUEST_OPTION, API, DEPENDENCY }; 27 | -------------------------------------------------------------------------------- /coconut/src/hooks/index.js: -------------------------------------------------------------------------------- 1 | import useFetch from 'hooks/useFetch'; 2 | import useConnectToIDB from 'hooks/useConnectToIDB'; 3 | import useUpdateProject from 'hooks/useUpdateProject'; 4 | import useUpdateDependency from 'hooks/useUpdateDependency'; 5 | import useBuildProject from 'hooks/useBuildProject'; 6 | import useCommunicateCocode from 'hooks/useCommunicateCocode'; 7 | 8 | export { 9 | useFetch, 10 | useConnectToIDB, 11 | useUpdateProject, 12 | useUpdateDependency, 13 | useBuildProject, 14 | useCommunicateCocode 15 | }; 16 | -------------------------------------------------------------------------------- /coconut/src/hooks/useBuildProject.js: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from 'react'; 2 | 3 | const DISPLAY_LOADING_SPINNER_DELAY = 1500; 4 | let isProgressingBuild = false; 5 | 6 | function useBuildProject() { 7 | const [buildResult, setBuildResult] = useState(undefined); 8 | 9 | const buildProject = (project, worker, displayBuildLoading) => { 10 | const { files, root, selectedFileId } = project; 11 | 12 | const rootPath = files[root].path; 13 | fileParser(project, rootPath, root); 14 | 15 | const updatedFilePath = files[selectedFileId].path; 16 | worker.postMessage({ fileSystem: window.fileSystem, updatedFilePath }); 17 | 18 | isProgressingBuild = true; 19 | setTimeout(() => { 20 | if (isProgressingBuild) displayBuildLoading(); 21 | }, DISPLAY_LOADING_SPINNER_DELAY); 22 | 23 | worker.onmessage = ({ data: { error, bundledCode } }) => { 24 | isProgressingBuild = false; 25 | if (!error) setBuildResult({ bundledCode }); 26 | else setBuildResult({ error }); 27 | }; 28 | }; 29 | 30 | const fileParser = useCallback((project, path, id) => { 31 | const { files } = project; 32 | 33 | if (files[id].type !== 'directory') { 34 | window.fileSystem[path] = { 35 | contents: files[id].contents 36 | }; 37 | delete window.exports[path]; 38 | } else if (files[id].child) { 39 | files[id].child.forEach(id => { 40 | const path = files[id].path; 41 | fileParser(project, path, id); 42 | }); 43 | } 44 | }, []); 45 | 46 | return [buildResult, buildProject]; 47 | } 48 | 49 | export default useBuildProject; 50 | -------------------------------------------------------------------------------- /coconut/src/hooks/useCommunicateCocode.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useCallback } from 'react'; 2 | 3 | import { cloneProjectActionCreator } from 'actions/Project'; 4 | 5 | function useCommunicateCocode(dispatchProject) { 6 | const sendToCocode = message => window.parent.postMessage(message, '*'); 7 | 8 | const handleAddListener = () => 9 | window.addEventListener('message', receiveMsgFromCocode); 10 | 11 | const updateProject = useCallback( 12 | messageFromCocode => { 13 | window.fileSystem = {}; 14 | 15 | const { project } = messageFromCocode; 16 | 17 | const cloneProjectAction = cloneProjectActionCreator({ project }); 18 | dispatchProject(cloneProjectAction); 19 | }, 20 | [dispatchProject] 21 | ); 22 | 23 | const receiveMsgFromCocode = useCallback( 24 | ({ data }) => { 25 | const { command } = data; 26 | if (!command) return; 27 | 28 | const coconutActions = { updateProject }; 29 | coconutActions[command] && coconutActions[command](data); 30 | }, 31 | [updateProject] 32 | ); 33 | 34 | useEffect(handleAddListener, []); 35 | 36 | return [sendToCocode]; 37 | } 38 | 39 | export default useCommunicateCocode; 40 | -------------------------------------------------------------------------------- /coconut/src/hooks/useFetch.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useReducer } from 'react'; 2 | import axios from 'axios'; 3 | import { DEFAULT_REQUEST_OPTION } from 'config'; 4 | import APIReducer from 'reducers/APIReducer'; 5 | import { 6 | fetchReadyActionCreator, 7 | fetchLoadActionCreator, 8 | fetchSuccessActionCreator, 9 | fetchFailActionCreator 10 | } from 'actions/API'; 11 | 12 | const API = axios.create(DEFAULT_REQUEST_OPTION); 13 | 14 | function useFetch({ method, url, data = {} }) { 15 | const [request, setRequest] = useState({ method, url, data }); 16 | const [state, dispatchFetchState] = useReducer(APIReducer, { 17 | data: false, 18 | loading: false, 19 | error: false, 20 | status: false 21 | }); 22 | 23 | useEffect(() => { 24 | if (!request.url) return; 25 | 26 | const requestToServer = () => { 27 | dispatchFetchState(fetchLoadActionCreator()); 28 | API(request) 29 | .then(res => dispatchFetchState(fetchSuccessActionCreator(res))) 30 | .catch(error => 31 | dispatchFetchState(fetchFailActionCreator(error)) 32 | ); 33 | }; 34 | 35 | requestToServer(); 36 | return () => dispatchFetchState(fetchReadyActionCreator()); 37 | }, [request]); 38 | 39 | return [state, setRequest]; 40 | } 41 | 42 | export default useFetch; 43 | -------------------------------------------------------------------------------- /coconut/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /coconut/src/reducers/APIReducer.js: -------------------------------------------------------------------------------- 1 | import { API_READY, API_LOADING, API_SUCCESS, API_FAIL } from 'actions/types'; 2 | 3 | const ready = () => ({ 4 | data: false, 5 | loading: false, 6 | error: false, 7 | status: false 8 | }); 9 | 10 | const loading = () => ({ 11 | data: undefined, 12 | loading: true, 13 | error: false, 14 | status: false 15 | }); 16 | 17 | const success = (state, { data, status }) => ({ 18 | ...state, 19 | data, 20 | status, 21 | loading: false 22 | }); 23 | 24 | const fail = state => ({ 25 | ...state, 26 | loading: false, 27 | error: true 28 | }); 29 | 30 | function APIReducer(state, { type, payload }) { 31 | const reducers = { 32 | [API_READY]: ready, 33 | [API_LOADING]: loading, 34 | [API_SUCCESS]: success, 35 | [API_FAIL]: fail 36 | }; 37 | 38 | const reducer = reducers[type]; 39 | return reducer ? reducer(state, payload) : state; 40 | } 41 | 42 | export default APIReducer; 43 | -------------------------------------------------------------------------------- /coconut/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /coconut/src/utils/getUpdatedPackageJSON.js: -------------------------------------------------------------------------------- 1 | function getUpdatedPackageJSON(files, root, dependency) { 2 | const childOfRoot = files[root].child; 3 | const packageJSON = childOfRoot 4 | .map(id => files[id]) 5 | .filter(({ name }) => name === 'package.json')[0]; 6 | const dependencies = JSON.parse(packageJSON.contents).dependencies; 7 | const newDependencies = { 8 | ...dependencies, 9 | [dependency.name]: dependency.version 10 | }; 11 | 12 | const newPackageJSONContents = JSON.stringify( 13 | { 14 | ...JSON.parse(packageJSON.contents), 15 | dependencies: newDependencies 16 | }, 17 | undefined, 18 | 4 19 | ); 20 | const packageJSONFileId = packageJSON._id; 21 | 22 | return { newPackageJSONContents, packageJSONFileId }; 23 | } 24 | 25 | export default getUpdatedPackageJSON; 26 | -------------------------------------------------------------------------------- /coconut/src/worker/build.worker.js: -------------------------------------------------------------------------------- 1 | /* eslint no-restricted-globals: 0 */ 2 | import * as bundler from 'bundler'; 3 | 4 | self.process = {}; 5 | self.process.env = {}; 6 | self.process.env.NODE_MODULE = 'development'; 7 | 8 | self.exports = {}; 9 | 10 | self.addEventListener('message', ({ data }) => { 11 | const { fileSystem, updatedFilePath } = data; 12 | 13 | self.exports[updatedFilePath] = undefined; 14 | self.fileSystem = fileSystem; 15 | self.bundledCode = ''; 16 | 17 | buildProject(); 18 | }); 19 | 20 | function buildProject() { 21 | try { 22 | bundler.init(); 23 | bundler.require('./index.js'); 24 | 25 | buildRemainModule(); 26 | self.postMessage({ bundledCode: self.bundledCode }); 27 | } catch (error) { 28 | self.postMessage({ error: error.stack }); 29 | } 30 | } 31 | 32 | function buildRemainModule() { 33 | const bundledModules = Object.keys(self.exports); 34 | let remainModules = Object.keys(self.fileSystem).filter( 35 | path => !bundledModules.includes(path) 36 | ); 37 | 38 | const installList = ['axios']; 39 | 40 | remainModules = remainModules 41 | .filter(path => installList.includes(path.split('/')[2])) 42 | .forEach(path => bundler.require(path)); 43 | } 44 | -------------------------------------------------------------------------------- /database/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.3" 2 | 3 | services: 4 | database: 5 | container_name: cocode-mongodb 6 | image: mongo:latest 7 | restart: always 8 | volumes: 9 | - data:/data/db 10 | ports: 11 | - "27017:27017" 12 | 13 | volumes: 14 | data: 15 | driver: local 16 | -------------------------------------------------------------------------------- /dependency-packager/.eslintignore: -------------------------------------------------------------------------------- 1 | README.md 2 | webpack.config.js 3 | .babelrc 4 | .prettierrc 5 | .env 6 | public/* -------------------------------------------------------------------------------- /dependency-packager/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .env -------------------------------------------------------------------------------- /dependency-packager/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 4, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /dependency-packager/database/index.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const { package } = require('./schema'); 3 | 4 | mongoose.connect(process.env.DEV_DATABASE_URI, { useNewUrlParser: true }); 5 | 6 | const db = mongoose.connection; 7 | db.on('error', console.error.bind(console, 'connection error:')); 8 | db.once('open', () => {}); 9 | 10 | const Package = mongoose.model('Package', package); 11 | 12 | module.exports = { Package }; 13 | -------------------------------------------------------------------------------- /dependency-packager/database/schema/index.js: -------------------------------------------------------------------------------- 1 | const package = require('./package.js'); 2 | 3 | module.exports = { package }; 4 | -------------------------------------------------------------------------------- /dependency-packager/database/schema/package.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const package = new Schema({ 5 | module: String, 6 | version: String, 7 | data: String 8 | }); 9 | 10 | module.exports = package; 11 | -------------------------------------------------------------------------------- /dependency-packager/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@babel/core": "^7.7.4", 4 | "@babel/preset-env": "^7.7.4", 5 | "@babel/preset-react": "^7.7.4", 6 | "dotenv": "^8.2.0", 7 | "express": "^4.17.1", 8 | "mongoose": "^5.7.13", 9 | "morgan": "^1.9.1", 10 | "react": "^16.12.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dependency-packager/packager/index.js: -------------------------------------------------------------------------------- 1 | const packager = require('./packager'); 2 | 3 | module.exports = packager; 4 | -------------------------------------------------------------------------------- /dependency-packager/packager/package-template.js: -------------------------------------------------------------------------------- 1 | const packageTemplate = `{ 2 | "name": "dependency", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT" 6 | } 7 | `; 8 | 9 | module.exports = packageTemplate; 10 | -------------------------------------------------------------------------------- /dependency-packager/packager/regex.js: -------------------------------------------------------------------------------- 1 | const requireRegexp = /require[(]{1}['"]{1}[@a-zA-Z-/.0-9_-]*['"]{1}[)]{1}/g; 2 | const pathRegexp = /['"]{1}[@a-zA-Z0-9./_-]+['"]{1}/; 3 | const maskQuotationRegexp = /['"]{1}/g; 4 | 5 | module.exports = { requireRegexp, pathRegexp, maskQuotationRegexp }; 6 | -------------------------------------------------------------------------------- /dependency-packager/packager/transpiler.js: -------------------------------------------------------------------------------- 1 | const babel = require('@babel/core'); 2 | const presetEnv = require('@babel/preset-env'); 3 | const presetReact = require('@babel/preset-react'); 4 | 5 | function transpileCode(code) { 6 | return babel.transform(code, { 7 | presets: [presetEnv, presetReact] 8 | }).code; 9 | } 10 | 11 | module.exports = transpileCode; 12 | -------------------------------------------------------------------------------- /dependency-packager/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const packager = require('./packager'); 3 | const { Package } = require('./database'); 4 | const express = require('express'); 5 | 6 | const PORT = process.env.DEPENDENCY_SERVER_PORT; 7 | const app = express(); 8 | app.use(express.json()); 9 | app.use(express.urlencoded({ extended: false })); 10 | app.post('/', (req, res) => { 11 | const { moduleName, moduleVersion } = req.body; 12 | try { 13 | Package.findOne( 14 | { module: moduleName, version: moduleVersion }, 15 | (err, result) => { 16 | if (!result || err) { 17 | const [Bundle, version] = packager( 18 | moduleName, 19 | moduleVersion 20 | ); 21 | const data = new Package({ 22 | module: moduleName, 23 | version: moduleVersion, 24 | data: JSON.stringify(Bundle) 25 | }); 26 | data.save(); 27 | res.send(data); 28 | } else { 29 | res.send(result); 30 | } 31 | } 32 | ); 33 | } catch (e) { 34 | res.send('error'); 35 | } 36 | }); 37 | 38 | app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`)); 39 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | api-server: 5 | container_name: api-server 6 | build: 7 | dockerfile: Dockerfile.dev 8 | context: ./api-server 9 | ports: 10 | - "3030:3030" 11 | volumes: 12 | - ./api-server/:/usr/src/app 13 | - ./api-server/node_modules:/usr/src/app/node_modules 14 | networks: 15 | - cocode-backend 16 | 17 | cocode-client: 18 | container_name: cocode-client 19 | build: 20 | dockerfile: Dockerfile.dev 21 | context: ./cocode 22 | ports: 23 | - "3000:3000" 24 | volumes: 25 | - ./cocode/:/usr/src/app 26 | - ./cocode/node_modules:/usr/src/app/node_modules 27 | networks: 28 | - cocode-backend 29 | 30 | mongo: 31 | container_name: cocode-mongodb 32 | image: mongo:4.0.4 33 | restart: always 34 | volumes: 35 | - data:/data/db 36 | ports: 37 | - "27017:27017" 38 | networks: 39 | - cocode-backend 40 | 41 | networks: 42 | cocode-backend: 43 | driver: bridge 44 | 45 | volumes: 46 | data: 47 | driver: local 48 | -------------------------------------------------------------------------------- /docs/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## 간단한 요약 설명 2 | 3 | 4 | ## 관련 이슈 5 | * close #11 6 | * close #12 7 | * close #33 8 | 9 | ### 셀프 리뷰 10 | - [ ] PR에 관련없는 변경 사항이 포함되어 있지 않는가 (1 PR should do 1 thing) 11 | - [ ] 코딩 스타일 가이드를 따르고 있는가 12 | - [ ] 변수나 함수의 이름이 적절한가 13 | - [ ] 리뷰를 요청하기 전에 직접 코드를 리뷰했는가 14 | - [ ] 코드가 요구 사항을 충족하는가 15 | - [ ] 테스트에 통과하는가 16 | - [ ] 불필요한 주석이 없는가 17 | - [ ] 매직 넘버가 없는가 -------------------------------------------------------------------------------- /env.tar.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-04/9e4bf4b6a5a15ae6ee70a51d0d17bc523da603e7/env.tar.enc -------------------------------------------------------------------------------- /live-server/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 4, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /live-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "live-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon app.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "diff": "^4.0.1", 14 | "socket.io": "^2.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.13.9-alpine 2 | COPY ./nginx.conf /etc/nginx/nginx.conf 3 | 4 | EXPOSE 8686 5 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /script/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | SERVICE="$1" 3 | PORT="$2" 4 | echo "deploy service: ${SERVICE}" 5 | echo "on port: ${PORT}" 6 | 7 | docker pull wltn3231/${SERVICE} 8 | docker stop ${SERVICE} 9 | docker rm ${SERVICE} 10 | docker run --name=${SERVICE} -d -p ${PORT}:${PORT} wltn3231/${SERVICE} 11 | -------------------------------------------------------------------------------- /script/update-env-files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | tar cvf env.tar cocode/.env coconut/.env api-server/.env 3 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | --------------------------------------------------------------------------------