├── .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 |
--------------------------------------------------------------------------------
/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 ;
10 | }
11 |
12 | export { ExampleButton as Button };
13 |
--------------------------------------------------------------------------------
/cocode/src/components/Common/ExampleButton/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { action } from '@storybook/addon-actions';
4 | import { text, boolean } from '@storybook/addon-knobs';
5 | import { Button } from '.';
6 |
7 | export default {
8 | title: 'Button'
9 | };
10 |
11 | function basicButton() {
12 | 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/cocode/src/components/Project/BrowserV2/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
32 |
33 |
34 |
35 | );
36 | }
37 |
38 | function Main() {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
46 |
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 |
17 |
18 |
19 | Please wait to install module...
20 |
21 |
22 | );
23 | }
24 |
25 | function DependencyTab() {
26 | const { project } = useContext(ProjectContext);
27 | const { dependencyInstalling } = project;
28 |
29 | return (
30 |
31 | {dependencyInstalling && }
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
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 |
--------------------------------------------------------------------------------
/cocode/src/containers/Live/LiveOnTab/copy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 | {TAB_TITLE}
12 |
13 |
14 |
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 |
--------------------------------------------------------------------------------
/cocode/src/containers/Live/TabBar/explorer.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
27 | {tabIcons.map(({ name, icon }, index) => {
28 | return (
29 |
38 | );
39 | })}
40 |
41 | );
42 | }
43 |
44 | export default TabBar;
45 |
--------------------------------------------------------------------------------
/cocode/src/containers/Live/TabBar/live.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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: ,
13 | 1:
14 | };
15 |
16 | const renderTab = () => tapMapping[clickedTabIndex];
17 |
18 | useEffect(() => {
19 | renderTab();
20 | }, [clickedTabIndex]);
21 |
22 | return {renderTab()};
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 |
10 |
11 |
12 | This page isn't available. Sorry about that.
13 |
14 |
15 | If you want start over, go to the homepage
16 |
17 |
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 |
17 |
18 |
19 | Please wait to install module...
20 |
21 |
22 | );
23 | }
24 |
25 | function DependencyTab() {
26 | const { project } = useContext(ProjectContext);
27 | const { dependencyInstalling } = project;
28 |
29 | return (
30 |
31 | {dependencyInstalling && }
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
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 |
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 |
23 | {OFF_DESCRIPTION}
24 |
25 |
26 | {OFF_BUTTON_LABEL}
27 |
28 |
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 | {TAB_TITLE}
12 |
13 |
14 |
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 |
--------------------------------------------------------------------------------
/cocode/src/containers/Project/TabBar/explorer.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
39 | {tabIcons.map(({ name, icon }, index) => {
40 | return (
41 |
50 | );
51 | })}
52 |
53 | );
54 | }
55 |
56 | export default TabBar;
57 |
--------------------------------------------------------------------------------
/cocode/src/containers/Project/TabBar/info.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cocode/src/containers/Project/TabBar/live.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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: ,
16 | 1: ,
17 | 2: ,
18 | 3:
19 | };
20 |
21 | const renderTab = () => tapMapping[clickedTabIndex];
22 |
23 | useEffect(() => {
24 | renderTab();
25 | }, [clickedTabIndex]);
26 |
27 | return {renderTab()};
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 |
13 | {SIGN_IN_TITLE}
14 |
15 |
16 | Sign In With GitHub
17 |
18 |
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(, 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 |
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 |
15 |
16 |
17 |
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 |
9 |
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 |
16 |
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 |
30 | {children}
31 |
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 | {story()}
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 |
2 |
3 |
4 |
5 | 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 |
--------------------------------------------------------------------------------