├── lambda ├── config ├── .gitignore ├── package.json ├── api │ ├── get.js │ ├── themes.js │ ├── create.js │ └── examples.js └── serverless.yml ├── postcss.config.js ├── public ├── robots.txt ├── favicon.png ├── humans.txt ├── examples │ ├── es2017 │ │ ├── trailing-function-commas.js │ │ ├── async-using-fetch.js │ │ └── async-function.js │ ├── es2015 │ │ ├── binary-octal.js │ │ ├── object-literals.js │ │ ├── symbols.js │ │ ├── let-const.js │ │ ├── tail-calls.js │ │ ├── promises.js │ │ ├── arrows.js │ │ ├── default-rest-spread.js │ │ ├── for-of.js │ │ ├── string-templates.js │ │ ├── destructuring.js │ │ ├── math-number-string-object.js │ │ ├── classes.js │ │ └── generators.js │ └── es2016 │ │ └── exponentiation-operator.js └── logo.svg ├── .gitignore ├── src ├── utils │ ├── index.js │ └── log-formatters.js ├── styles │ ├── _variables.scss │ ├── variables │ │ ├── _colors.scss │ │ └── _fonts.scss │ ├── themes │ │ └── default │ │ │ └── assets │ │ │ ├── fonts │ │ │ ├── icons.eot │ │ │ ├── icons.otf │ │ │ ├── icons.ttf │ │ │ ├── icons.woff │ │ │ └── icons.woff2 │ │ │ └── images │ │ │ └── flags.png │ ├── _base.scss │ ├── core.scss │ └── console.css ├── components │ ├── Editor │ │ ├── index.js │ │ ├── utils.js │ │ ├── theme.scss │ │ ├── Editor.scss │ │ └── Editor.js │ ├── Menu │ │ ├── index.js │ │ ├── Menu.js │ │ └── Menu.scss │ ├── Console │ │ ├── index.js │ │ ├── styles │ │ │ ├── gutter-icons.png │ │ │ ├── meslo │ │ │ │ ├── MesloLGSDZ-Bold.woff │ │ │ │ ├── MesloLGSDZ-Italic.woff │ │ │ │ ├── MesloLGSDZ-Regular.woff │ │ │ │ └── MesloLGSDZ-BoldItalic.woff │ │ │ └── statusbarButtonGlyphs_2x.png │ │ ├── Console.js │ │ ├── Console.scss │ │ └── jsconsole.js │ ├── Header │ │ ├── index.js │ │ ├── Header.scss │ │ └── Header.js │ ├── Loading │ │ ├── index.js │ │ ├── Loading.scss │ │ ├── Loading.js │ │ └── semantic.scss │ └── Toolbar │ │ ├── Toolbar.scss │ │ └── index.js ├── layouts │ └── CoreLayout │ │ ├── index.js │ │ ├── CoreLayout.scss │ │ └── CoreLayout.js ├── routes │ ├── Home │ │ ├── index.js │ │ └── components │ │ │ ├── HomeView.scss │ │ │ └── HomeView.js │ └── index.js ├── store │ ├── ducks │ │ ├── index.js │ │ ├── console.js │ │ ├── babelPresets.js │ │ ├── editorConfig.js │ │ ├── loading.js │ │ ├── compilers.js │ │ └── editors.js │ ├── reducers.js │ ├── location.js │ ├── middleware │ │ └── localStorage.js │ ├── themes.js │ ├── actionTypes.js │ ├── examples.js │ ├── createStore.js │ └── ide.js ├── compilers │ ├── typescript.js │ ├── base.js │ ├── babel5.js │ ├── babel6.js │ ├── index.js │ ├── prepack.js │ ├── regenerator.js │ └── traceur.js ├── index.html ├── containers │ └── AppContainer.js ├── main.js └── sandbox.js ├── design └── logos.sketch ├── jsconfig.json ├── tests ├── .eslintrc ├── routes │ ├── Home │ │ ├── index.spec.js │ │ └── components │ │ │ └── HomeView.spec.js │ └── Counter │ │ ├── index.spec.js │ │ ├── components │ │ └── Counter.spec.js │ │ └── modules │ │ └── counter.spec.js ├── store │ ├── createStore.spec.js │ └── location.spec.js ├── layouts │ └── CoreLayout.spec.js ├── components │ └── Header │ │ └── Header.spec.js └── test-bundler.js ├── bin ├── dev-server.js ├── compile.js └── deploy.js ├── .eslintrc.json ├── todo.md ├── LICENSE ├── CONTRIBUTING.md ├── config ├── environments.config.js ├── karma.config.js ├── project.config.js └── webpack.config.js ├── server └── main.js ├── package.json └── README.md /lambda/config: -------------------------------------------------------------------------------- 1 | ../config -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | static/dist 3 | static/style 4 | dist 5 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | export { complexFormatter } from './log-formatters'; -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | @import 'variables/colors'; 2 | @import 'variables/fonts'; -------------------------------------------------------------------------------- /design/logos.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/design/logos.sketch -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/public/favicon.png -------------------------------------------------------------------------------- /src/components/Editor/index.js: -------------------------------------------------------------------------------- 1 | import Editor from './Editor'; 2 | 3 | export default Editor; -------------------------------------------------------------------------------- /src/components/Menu/index.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | 3 | export default Menu; 4 | -------------------------------------------------------------------------------- /src/styles/variables/_colors.scss: -------------------------------------------------------------------------------- 1 | $primary-color: #feca00; 2 | 3 | $secondary-color: #457abb; -------------------------------------------------------------------------------- /src/styles/variables/_fonts.scss: -------------------------------------------------------------------------------- 1 | $font-family: 'Open Sans', sans-serif; 2 | $font-size: 14px; -------------------------------------------------------------------------------- /src/components/Console/index.js: -------------------------------------------------------------------------------- 1 | import Console from './Console'; 2 | 3 | export default Console; -------------------------------------------------------------------------------- /src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /src/components/Loading/index.js: -------------------------------------------------------------------------------- 1 | import Loading from './Loading'; 2 | 3 | export default Loading; -------------------------------------------------------------------------------- /src/layouts/CoreLayout/index.js: -------------------------------------------------------------------------------- 1 | import CoreLayout from './CoreLayout'; 2 | 3 | export default CoreLayout; 4 | -------------------------------------------------------------------------------- /lambda/.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | jspm_packages 4 | 5 | # Serverless directories 6 | .serverless -------------------------------------------------------------------------------- /src/components/Console/styles/gutter-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/components/Console/styles/gutter-icons.png -------------------------------------------------------------------------------- /src/styles/themes/default/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/styles/themes/default/assets/fonts/icons.eot -------------------------------------------------------------------------------- /src/styles/themes/default/assets/fonts/icons.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/styles/themes/default/assets/fonts/icons.otf -------------------------------------------------------------------------------- /src/styles/themes/default/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/styles/themes/default/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /src/styles/themes/default/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/styles/themes/default/assets/fonts/icons.woff -------------------------------------------------------------------------------- /src/styles/themes/default/assets/images/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/styles/themes/default/assets/images/flags.png -------------------------------------------------------------------------------- /src/styles/themes/default/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/styles/themes/default/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /src/routes/Home/index.js: -------------------------------------------------------------------------------- 1 | import HomeView from './components/HomeView'; 2 | 3 | // Sync route definition 4 | export default { 5 | component : HomeView 6 | }; 7 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6" 4 | }, 5 | "exclude": [ 6 | "node_modules", 7 | "dist" 8 | ] 9 | } -------------------------------------------------------------------------------- /public/humans.txt: -------------------------------------------------------------------------------- 1 | # Check it out: http://humanstxt.org/ 2 | 3 | # TEAM 4 | 5 | -- -- 6 | 7 | # THANKS 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/Console/styles/meslo/MesloLGSDZ-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/components/Console/styles/meslo/MesloLGSDZ-Bold.woff -------------------------------------------------------------------------------- /src/components/Console/styles/meslo/MesloLGSDZ-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/components/Console/styles/meslo/MesloLGSDZ-Italic.woff -------------------------------------------------------------------------------- /src/components/Console/styles/meslo/MesloLGSDZ-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/components/Console/styles/meslo/MesloLGSDZ-Regular.woff -------------------------------------------------------------------------------- /src/components/Console/styles/statusbarButtonGlyphs_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/components/Console/styles/statusbarButtonGlyphs_2x.png -------------------------------------------------------------------------------- /src/components/Console/styles/meslo/MesloLGSDZ-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthisk/es6console/HEAD/src/components/Console/styles/meslo/MesloLGSDZ-BoldItalic.woff -------------------------------------------------------------------------------- /public/examples/es2017/trailing-function-commas.js: -------------------------------------------------------------------------------- 1 | function clownPuppiesEverywhere( 2 | param1, 3 | param2, 4 | ) { /* ... */ } 5 | 6 | clownPuppiesEverywhere( 7 | 'foo', 8 | 'bar', 9 | ); -------------------------------------------------------------------------------- /public/examples/es2015/binary-octal.js: -------------------------------------------------------------------------------- 1 | // ES6 introduces two new types of numeric literals 2 | // Binary and octal notation: 3 | 4 | console.log(0b111110111 === 503); 5 | console.log(0o767 === 503); 6 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends" : "../.eslintrc", 3 | "env" : { 4 | "mocha" : true 5 | }, 6 | "globals" : { 7 | "expect" : false, 8 | "should" : false, 9 | "sinon" : false 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /bin/dev-server.js: -------------------------------------------------------------------------------- 1 | const project = require('../config/project.config') 2 | const server = require('../server/main') 3 | const debug = require('debug')('app:bin:dev-server') 4 | 5 | server.listen(project.server_port) 6 | debug(`Server is now running at http://localhost:${project.server_port}.`) 7 | -------------------------------------------------------------------------------- /public/examples/es2016/exponentiation-operator.js: -------------------------------------------------------------------------------- 1 | // x ** y 2 | 3 | let squared = 2 ** 2; 4 | // same as: 2 * 2 5 | 6 | let cubed = 2 ** 3; 7 | // same as: 2 * 2 * 2 8 | 9 | 10 | // x **= y 11 | 12 | let a = 2; 13 | a **= 2; 14 | // same as: a = a * a; 15 | 16 | let b = 3; 17 | b **= 3; 18 | // same as: b = b * b * b; -------------------------------------------------------------------------------- /public/examples/es2015/object-literals.js: -------------------------------------------------------------------------------- 1 | var obj = { 2 | // __proto__ 3 | __proto__: theProtoObj, 4 | // Shorthand for ‘handler: handler’ 5 | handler, 6 | // Methods 7 | toString() { 8 | // Super calls 9 | return "d " + super.toString(); 10 | }, 11 | // Computed (dynamic) property names 12 | [ 'prop_' + (() => 42)() ]: 42 13 | 14 | }; 15 | -------------------------------------------------------------------------------- /public/examples/es2015/symbols.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // module scoped symbol 4 | var key = Symbol("key"); 5 | 6 | function MyClass(privateData) { 7 | this[key] = privateData; 8 | } 9 | 10 | MyClass.prototype = { 11 | doStuff: function() { 12 | console.log( this[key] ); 13 | } 14 | }; 15 | 16 | })(); 17 | 18 | var c = new MyClass("hello") 19 | c["key"] === undefined -------------------------------------------------------------------------------- /public/examples/es2015/let-const.js: -------------------------------------------------------------------------------- 1 | // Declaration declared through 'var' are hoisted and function 2 | // scoped. Variables declared through either 'let' or 'const' 3 | // are block-scoped an not available in the temporal dead-zone. 4 | 5 | function f() { 6 | let x = 'n'; 7 | { 8 | // okay, block scoped name 9 | const x = "sneaky"; 10 | } 11 | 12 | console.log(x); 13 | } 14 | 15 | f(); 16 | -------------------------------------------------------------------------------- /src/store/ducks/index.js: -------------------------------------------------------------------------------- 1 | import babelPresetsReducer from './babelPresets'; 2 | import compilersReducer from './compilers'; 3 | import consoleReducer from './console'; 4 | import editorConfigReducer from './editorConfig'; 5 | import editorsReducer from './editors'; 6 | 7 | export { 8 | babelPresetsReducer, 9 | compilersReducer, 10 | consoleReducer, 11 | editorConfigReducer, 12 | editorsReducer, 13 | }; 14 | -------------------------------------------------------------------------------- /public/examples/es2015/tail-calls.js: -------------------------------------------------------------------------------- 1 | // Recursive tail-calls can fill the stack rapidly while 2 | // it is possible to optimize this call. ES6 introduces 3 | // such a tail-call optimization 4 | function factorial(n, acc = 1) { 5 | 'use strict'; 6 | if (n <= 1) return acc; 7 | return factorial(n - 1, n * acc); 8 | } 9 | 10 | // Stack overflow in most implementations today, 11 | // but safe on arbitrary inputs in eS6 12 | console.log( factorial(100000) ); 13 | -------------------------------------------------------------------------------- /tests/routes/Home/index.spec.js: -------------------------------------------------------------------------------- 1 | import HomeRoute from 'routes/Home' 2 | 3 | describe('(Route) Home', () => { 4 | let _component 5 | 6 | beforeEach(() => { 7 | _component = HomeRoute.component() 8 | }) 9 | 10 | it('Should return a route configuration object', () => { 11 | expect(typeof HomeRoute).to.equal('object') 12 | }) 13 | 14 | it('Should define a route component', () => { 15 | expect(_component.type).to.equal('div') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /tests/routes/Counter/index.spec.js: -------------------------------------------------------------------------------- 1 | import CounterRoute from 'routes/Counter' 2 | 3 | describe('(Route) Counter', () => { 4 | let _route 5 | 6 | beforeEach(() => { 7 | _route = CounterRoute({}) 8 | }) 9 | 10 | it('Should return a route configuration object', () => { 11 | expect(typeof _route).to.equal('object') 12 | }) 13 | 14 | it('Configuration should contain path `counter`', () => { 15 | expect(_route.path).to.equal('counter') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/styles/_base.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Application Settings Go Here 3 | ------------------------------------ 4 | This file acts as a bundler for all variables/mixins/themes, so they 5 | can easily be swapped out without `core.scss` ever having to know. 6 | 7 | For example: 8 | 9 | @import './variables/colors'; 10 | @import './variables/components'; 11 | @import './themes/default'; 12 | */ 13 | 14 | @import url('https://fonts.googleapis.com/css?family=Open+Sans'); 15 | 16 | @import 'variables'; -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "experimentalObjectRestSpread": true, 11 | "jsx": true 12 | }, 13 | "sourceType": "module" 14 | }, 15 | "plugins": ["react"], 16 | "rules": { 17 | "indent": ["error", 2], 18 | "linebreak-style": ["error", "unix"], 19 | "quotes": ["error", "single"], 20 | "semi": ["error", "always"] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/store/ducks/console.js: -------------------------------------------------------------------------------- 1 | import * as actionTypes from 'store/actionTypes'; 2 | 3 | const initialState = { 4 | display: false, 5 | logBuffer: [], 6 | }; 7 | export default function reducer(state = initialState, action) { 8 | switch (action.type) { 9 | case actionTypes.TOGGLE_CONSOLE_DISPLAY: 10 | return { 11 | ...state, 12 | display: !state.display, 13 | }; 14 | 15 | case actionTypes.FLUSH_BUFFER: 16 | return { 17 | ...state, 18 | logBuffer: [], 19 | }; 20 | 21 | default: 22 | return state; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Loading/Loading.scss: -------------------------------------------------------------------------------- 1 | .loading__viewport { 2 | position: absolute; 3 | top: 50px; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | background: rgba(255, 255, 255, 0.8); 8 | z-index: 9999; 9 | opacity: 0; 10 | 11 | transition: opacity .5s ease-in-out; 12 | 13 | &-wrapper { 14 | position: relative; 15 | top: 50%; 16 | left: 50%; 17 | transform: translate(-50%, -50%); 18 | } 19 | 20 | &.visible { 21 | opacity: 1; 22 | } 23 | 24 | &.hidden { 25 | display: none; 26 | } 27 | } -------------------------------------------------------------------------------- /src/styles/core.scss: -------------------------------------------------------------------------------- 1 | @import 'base'; 2 | @import '~normalize.css/normalize'; 3 | @import "icons"; 4 | 5 | html { 6 | box-sizing: border-box; 7 | overflow: hidden; 8 | height: 100%; 9 | 10 | font-family: $font-family; 11 | font-size: $font-size; 12 | } 13 | 14 | html, 15 | body { 16 | margin: 0; 17 | padding: 0; 18 | height: 100%; 19 | } 20 | 21 | body { 22 | height: 100%; 23 | overflow: auto; 24 | } 25 | 26 | *, 27 | *:before, 28 | *:after { 29 | box-sizing: inherit; 30 | } 31 | 32 | /** 33 | * LINKS 34 | */ 35 | 36 | a { 37 | text-decoration: none; 38 | } -------------------------------------------------------------------------------- /src/compilers/typescript.js: -------------------------------------------------------------------------------- 1 | import Base from './base'; 2 | 3 | export default class TypeScript extends Base { 4 | loadCompiler() { 5 | require.ensure(['typescript'],require => { 6 | this.compiler = require('typescript'); 7 | this.resolveFuture(); 8 | }); 9 | } 10 | 11 | compile( input ) { 12 | this._checkIfCompilerIsLoaded(); 13 | 14 | let code = '', 15 | errors = []; 16 | 17 | code = this.compiler.transpile( input, { module: this.compiler.ModuleKind.CommonJS }); 18 | 19 | return { 20 | code, 21 | errors 22 | }; 23 | } 24 | } -------------------------------------------------------------------------------- /public/examples/es2015/promises.js: -------------------------------------------------------------------------------- 1 | // Promises have been the standard for async programming 2 | // with ES6 they are integrated in the standard library and 3 | // we no longer need external libraries to implement them 4 | function timeout(duration = 0) { 5 | return new Promise((resolve, reject) => { 6 | setTimeout(resolve, duration); 7 | }) 8 | } 9 | 10 | var p = timeout(1000).then(() => { 11 | return timeout(2000); 12 | }).then(() => { 13 | console.log("then"); 14 | throw new Error("hmm"); 15 | }).catch(err => { 16 | console.log(err); 17 | return Promise.all([timeout(100), timeout(200)]); 18 | }) 19 | -------------------------------------------------------------------------------- /src/utils/log-formatters.js: -------------------------------------------------------------------------------- 1 | export function complexFormatter(...args) { 2 | let res = ''; 3 | if (typeof args[0] === 'string' && args[0].search('%s') !== -1) { 4 | let s = args[0], 5 | search, 6 | i = 1; 7 | 8 | while (i < args.length && (search = s.search(/%s|%d|%i|%f]/)) !== -1) { 9 | let replace = s[search] + s[search + 1]; 10 | s = s.replace(replace, args[i]); 11 | i++; 12 | } 13 | res = s; 14 | } else { 15 | args.forEach(arg => { 16 | if (typeof arg === 'object') res += `${JSON.stringify(arg)} `; 17 | else res += `${arg} `; 18 | }); 19 | } 20 | return res; 21 | } -------------------------------------------------------------------------------- /lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6console-aws", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Matthisk (http://matthisk.nl/)", 10 | "license": "ISC", 11 | "dependencies": { 12 | "aws-sdk": "~2.62.0", 13 | "debug": "~2.6.8", 14 | "ip": "~1.1.5", 15 | "serverless-dynamodb-local": "~0.2.22", 16 | "serverless-offline": "~3.14.1", 17 | "yargs": "~8.0.1" 18 | }, 19 | "devDependencies": { 20 | "klaw": "~1.3.1", 21 | "serverless-s3-local": "~0.2.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/examples/es2015/arrows.js: -------------------------------------------------------------------------------- 1 | var evens = [2,4,6,8,10], 2 | fives = []; 3 | 4 | // Expression bodies 5 | var odds = evens.map(v => v + 1); 6 | var nums = evens.map((v, i) => v + i); 7 | 8 | console.log(odds); 9 | console.log(nums); 10 | 11 | // Statement bodies 12 | nums.forEach(v => { 13 | if (v % 5 === 0) fives.push(v); 14 | }); 15 | 16 | // This is bound to its lexical enclosing scope's this 17 | function thisBinding() { 18 | return () => console.log(this.string); 19 | } 20 | 21 | thisBinding.call({ string: 'bound' })(); 22 | 23 | // The same goes for arguments 24 | function args() { 25 | return () => console.log(arguments); 26 | } 27 | 28 | args('arg1','arg2')(); 29 | -------------------------------------------------------------------------------- /public/examples/es2017/async-using-fetch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ES2017 introduces a special function type, async functions. 3 | * Inside these functions a special keyword `async` can be 4 | * used to wait for a promise to resolve. It is required that 5 | * the expression following the `await` keyword is resolved to 6 | * a Promise. 7 | * 8 | * In this example we use the fetch API (which returns Promise 9 | * objects) to load the current example javascript async from 10 | * es6console.com 11 | */ 12 | async function getThisExample() { 13 | let response = await fetch('/examples/es2017/async-function.js'); 14 | let text = await response.text(); 15 | 16 | console.log(text); 17 | } 18 | 19 | getThisExample(); -------------------------------------------------------------------------------- /src/compilers/base.js: -------------------------------------------------------------------------------- 1 | export default class Base { 2 | constructor() { 3 | this.compiler = undefined; 4 | this.future = new Promise(resolve => { 5 | this.resolveFuture = resolve; 6 | }); 7 | } 8 | 9 | _checkIfCompilerIsLoaded() { 10 | if( ! this.compiler ) { 11 | throw new Error([ 12 | 'Compiler is not initialized call', 13 | '`loadCompiler` before invoking this function.', 14 | ].join(' ')); 15 | } 16 | } 17 | 18 | initCompiler() { 19 | if (! this.isInitialized()) { 20 | this.loadCompiler(); 21 | } 22 | 23 | return this.future; 24 | } 25 | 26 | isInitialized() { 27 | return this.compiler !== undefined; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/routes/Home/components/HomeView.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { HomeView } from 'routes/Home/components/HomeView' 3 | import { render } from 'enzyme' 4 | 5 | describe('(View) Home', () => { 6 | let _component 7 | 8 | beforeEach(() => { 9 | _component = render() 10 | }) 11 | 12 | it('Renders a welcome message', () => { 13 | const welcome = _component.find('h4') 14 | expect(welcome).to.exist 15 | expect(welcome.text()).to.match(/Welcome!/) 16 | }) 17 | 18 | it('Renders an awesome duck image', () => { 19 | const duck = _component.find('img') 20 | expect(duck).to.exist 21 | expect(duck.attr('alt')).to.match(/This is a duck, because Redux!/) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /src/compilers/babel5.js: -------------------------------------------------------------------------------- 1 | import Base from './base'; 2 | 3 | import $script from 'scriptjs'; 4 | 5 | export default class Babel5 extends Base { 6 | loadCompiler() { 7 | $script(['/dist/compilers/browser.js'],() => { 8 | this.compiler = window.babel; 9 | this.resolveFuture(); 10 | }); 11 | } 12 | 13 | compile( input, options = {} ) { 14 | this._checkIfCompilerIsLoaded(); 15 | 16 | let code = '', 17 | errors = []; 18 | try { 19 | code = this.compiler.transform( input, { 20 | babelrc: false, 21 | filename: 'repl', 22 | }).code; 23 | } catch( e ) { 24 | errors = [e]; 25 | } 26 | 27 | return { 28 | code, 29 | errors 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ES6 Console - try JavaScript compilers 5 | 6 | 7 | 8 | 9 |
10 | 11 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/compilers/babel6.js: -------------------------------------------------------------------------------- 1 | import Base from './base'; 2 | 3 | import $script from 'scriptjs'; 4 | 5 | export default class Babel extends Base { 6 | loadCompiler() { 7 | $script(['https://unpkg.com/babel-standalone@6.15.0/babel.min.js'],() => { 8 | this.compiler = window.Babel; 9 | this.resolveFuture(); 10 | }); 11 | } 12 | 13 | compile( input, options ) { 14 | this._checkIfCompilerIsLoaded(); 15 | 16 | let code = '', 17 | errors = []; 18 | try { 19 | code = this.compiler.transform( input, { 20 | babelrc: false, 21 | filename: 'repl', 22 | ...options 23 | } ).code; 24 | } catch( e ) { 25 | errors = [e]; 26 | } 27 | 28 | return { 29 | code, 30 | errors 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/store/createStore.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | default as createStore 3 | } from 'store/createStore' 4 | 5 | describe('(Store) createStore', () => { 6 | let store 7 | 8 | before(() => { 9 | store = createStore() 10 | }) 11 | 12 | it('should have an empty asyncReducers object', () => { 13 | expect(store.asyncReducers).to.be.an('object') 14 | expect(store.asyncReducers).to.be.empty 15 | }) 16 | 17 | describe('(Location)', () => { 18 | it('store should be initialized with Location state', () => { 19 | const location = { 20 | pathname : '/echo' 21 | } 22 | store.dispatch({ 23 | type : 'LOCATION_CHANGE', 24 | payload : location 25 | }) 26 | expect(store.getState().location).to.deep.equal(location) 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /public/examples/es2015/default-rest-spread.js: -------------------------------------------------------------------------------- 1 | // When an argument is not passed (or passed as undefined) 2 | // in a function call the default value 3 | function f(x, y=12) { 4 | // y is 12 if not passed (or passed as undefined) 5 | return x + y; 6 | } 7 | 8 | console.log( f(3) ); 9 | 10 | // The remaining passed arguments can be bound through the rest argument (...)Id 11 | function g(x, ...y) { 12 | // y is an Array 13 | return x * y.length; 14 | } 15 | 16 | console.log( g(3, "hello", true) ); 17 | 18 | function h(x, y, z) { 19 | return x + y + z; 20 | } 21 | 22 | // The spread operator can be used to pass each 23 | // element of an array as a separate argument 24 | console.log( h(...[1,2,3],4,5,6) ); 25 | // The spread operator can also be used with Strings 26 | console.log( Math.max( ..."1234" ) ); 27 | -------------------------------------------------------------------------------- /public/examples/es2015/for-of.js: -------------------------------------------------------------------------------- 1 | // JavaScript already has several looping constructs 2 | // ES6 introduces a new type of loop the for-of loop. 3 | // This loop iterates over Iterable objects (e.g. Array, 4 | // Map, Set, arguments). 5 | let fibonacci = { 6 | [Symbol.iterator]() { 7 | let pre = 0, cur = 1; 8 | return { 9 | next() { 10 | [pre, cur] = [cur, pre + cur]; 11 | return { done: false, value: cur } 12 | } 13 | } 14 | } 15 | } 16 | 17 | // With the for of loop we can loop over an iterators values 18 | for (var n of fibonacci) { 19 | // truncate the sequence at 1000 20 | if (n > 1000) 21 | break; 22 | console.log(n); 23 | } 24 | 25 | // The for of loop also works on array literals 26 | for(var x of [1,2,3,4,5,6,7,8,9,10]) { 27 | console.log(x); 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Header/Header.scss: -------------------------------------------------------------------------------- 1 | .route--active { 2 | font-weight: bold; 3 | text-decoration: underline; 4 | } 5 | 6 | .header { 7 | background: white; 8 | box-shadow: 0 0 5px rgba(57,70,78,.2); 9 | 10 | height: 50px; 11 | display: flex; 12 | padding: 0 10px; 13 | 14 | position: relative; 15 | z-index: 9999; 16 | 17 | display: flex; 18 | } 19 | 20 | .header__logo { 21 | margin-right: 25px; 22 | width: 125px; 23 | height: 50px; 24 | flex: 1; 25 | } 26 | 27 | .header__title { 28 | color: #fff; 29 | line-height: 50px; 30 | font-size: 1.5rem; 31 | 32 | > span.colored { 33 | color: rgb(186, 224, 255); 34 | } 35 | 36 | &:hover, 37 | &:active, 38 | &:focus { 39 | color: #fff; 40 | } 41 | } -------------------------------------------------------------------------------- /src/compilers/index.js: -------------------------------------------------------------------------------- 1 | import Babel from './babel6'; 2 | import Babel5 from './babel5'; 3 | import Traceur from './traceur'; 4 | import TypeScript from './typescript'; 5 | import Regenerator from './regenerator'; 6 | import Prepack from './prepack'; 7 | 8 | 9 | export const DEFAULT_COMPILER = 'Babel (6)'; 10 | 11 | export function getCompiler(name) { 12 | if (!compilers.hasOwnProperty(name)) { 13 | throw new ReferenceError(`Unexpected compiler naem ${name} please pick one of ${Object.keys(compilers)}`); 14 | } 15 | 16 | return compilers[name]; 17 | } 18 | 19 | const compilers = { 20 | 'Babel (6)': new Babel(), 21 | 'Babel (5)': new Babel5(), 22 | 'Traceur': new Traceur(), 23 | 'TypeScript': new TypeScript(), 24 | 'Regenerator': new Regenerator(), 25 | 'Prepack': new Prepack(), 26 | }; 27 | 28 | export default compilers; 29 | -------------------------------------------------------------------------------- /public/examples/es2015/string-templates.js: -------------------------------------------------------------------------------- 1 | // JavaScript string literals have some limitations 2 | // i.e. they do not allow for new-line characters. 3 | // or the interleaving of variables. String-Templates 4 | // do have these features: 5 | 6 | // Basic literal string creation 7 | console.log(`In JavaScript '\n' is a line-feed.`); 8 | 9 | // Multiline strings 10 | console.log(`In JavaScript this is 11 | not legal.`); 12 | 13 | // Construct a DOM query 14 | var name = "Bob", time = "today"; 15 | console.log(`Hello ${name}, how are you ${time}?`); 16 | 17 | // Construct an HTTP request prefix is used to interpret the replacements and construction 18 | GET`http://foo.org/bar?a=${a}&b=${b} 19 | Content-Type: application/json 20 | X-Credentials: ${credentials} 21 | { "foo": ${foo}, 22 | "bar": ${bar}}`(myOnReadyStateChangeHandler); 23 | -------------------------------------------------------------------------------- /src/store/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import locationReducer from './location'; 3 | import ideReducer from './ide'; 4 | import examplesReducer from './examples'; 5 | import themeReducer from './themes'; 6 | import loadingReducer from './ducks/loading'; 7 | 8 | export const makeRootReducer = (asyncReducers) => { 9 | return combineReducers({ 10 | location: locationReducer, 11 | ide: ideReducer, 12 | examples: examplesReducer, 13 | themes: themeReducer, 14 | loading: loadingReducer, 15 | ...asyncReducers 16 | }); 17 | }; 18 | 19 | export const injectReducer = (store, { key, reducer }) => { 20 | if (Object.hasOwnProperty.call(store.asyncReducers, key)) return; 21 | 22 | store.asyncReducers[key] = reducer; 23 | store.replaceReducer(makeRootReducer(store.asyncReducers)); 24 | }; 25 | 26 | export default makeRootReducer; -------------------------------------------------------------------------------- /src/store/ducks/babelPresets.js: -------------------------------------------------------------------------------- 1 | import { loadPersistedState} from 'store/middleware/localStorage'; 2 | 3 | import * as actionTypes from 'store/actionTypes'; 4 | 5 | let initialState = loadPersistedState('ide', 'babelPresets') || { 6 | 'es2015': { checked: true }, 7 | 'es2016': { checked: false }, 8 | 'es2017': { checked: false }, 9 | 'react': { checked: false }, 10 | 'stage-0': { checked: false }, 11 | 'stage-1': { checked: false }, 12 | 'stage-2': { checked: false }, 13 | 'stage-3': { checked: false }, 14 | }; 15 | 16 | export default function reducer(state = initialState, action) { 17 | switch(action.type) { 18 | 19 | case actionTypes.TOGGLE_COMPILER_PRESET: 20 | return { 21 | ...state, 22 | [action.payload] : { 23 | checked: ! state[action.payload].checked, 24 | }, 25 | }; 26 | 27 | default: 28 | return state; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /tests/layouts/CoreLayout.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TestUtils from 'react-addons-test-utils' 3 | import CoreLayout from 'layouts/CoreLayout/CoreLayout' 4 | 5 | function shallowRender (component) { 6 | const renderer = TestUtils.createRenderer() 7 | 8 | renderer.render(component) 9 | return renderer.getRenderOutput() 10 | } 11 | 12 | function shallowRenderWithProps (props = {}) { 13 | return shallowRender() 14 | } 15 | 16 | describe('(Layout) Core', function () { 17 | let _component 18 | let _props 19 | let _child 20 | 21 | beforeEach(function () { 22 | _child =

Child

23 | _props = { 24 | children : _child 25 | } 26 | 27 | _component = shallowRenderWithProps(_props) 28 | }) 29 | 30 | it('Should render as a
.', function () { 31 | expect(_component.type).to.equal('div') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /public/examples/es2015/destructuring.js: -------------------------------------------------------------------------------- 1 | // Destructuring assignment lets you extract values from an 2 | // object or an array in a single assignment Expression or 3 | // Statement. 4 | 5 | // list matching 6 | var [a,,b] = [1,2,3]; 7 | 8 | console.log(a,b); 9 | 10 | // Destructuring of a function parameter 11 | function f( [a,b] ) { 12 | return a + b; 13 | } 14 | 15 | console.log( f([1,2]) ); 16 | 17 | // Computed properties (see object literal) can also be used 18 | // in object destructuring 19 | var qux = "corge"; 20 | var { [qux] : corge } = { corge: "graphly" }; 21 | 22 | console.log( corge ); 23 | 24 | // Similar to function parameters, destructuring bindings can 25 | // have a default value (used when the bound identifier does 26 | // not exist in the target object or array, or when it is 27 | // undefined) 28 | var { a = '100' } = {b:0}; 29 | var [c,d,e = '100'] = [0,1]; 30 | 31 | console.log(a,e); 32 | -------------------------------------------------------------------------------- /src/compilers/prepack.js: -------------------------------------------------------------------------------- 1 | import Base from './base'; 2 | 3 | export default class Prepack extends Base { 4 | static makeError( { lineNumber, description, column, endColumn = NaN } ) { 5 | let l = lineNumber - 1; 6 | 7 | return { 8 | loc : { 9 | line : l, 10 | column : column, 11 | offset : () => { return { line: l,column:endColumn}; } 12 | }, 13 | message : description 14 | }; 15 | } 16 | 17 | loadCompiler() { 18 | require.ensure(['prepack'],require => { 19 | this.compiler = require('prepack'); 20 | this.resolveFuture(); 21 | }); 22 | } 23 | 24 | compile( input ) { 25 | this._checkIfCompilerIsLoaded(); 26 | 27 | let code = '', 28 | errors = []; 29 | 30 | try { 31 | code = this.compiler.prepack(input).code; 32 | } catch (e) { 33 | // errors = [e]; 34 | } 35 | 36 | return { 37 | code, 38 | errors 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/examples/es2015/math-number-string-object.js: -------------------------------------------------------------------------------- 1 | // ES6 introduces new standard library functions: 2 | console.log( Number.EPSILON ); 3 | console.log( Number.isInteger(Infinity) ); // false 4 | console.log( Number.isNaN("NaN") ); // false 5 | 6 | console.log( Math.acosh(3) ); // 1.762747174039086 7 | console.log( Math.hypot(3, 4) );// 5 8 | console.log( Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) ); // 2 9 | 10 | console.log( "abc".repeat(3) ); // "abcabcabc" 11 | 12 | console.log( Array.from(document.querySelectorAll('*')) ); // Returns a real Array 13 | console.log( Array.of(1, 2, 3) ); // Similar to new Array(...), but without special one-arg behavior 14 | console.log( [0, 0, 0].fill(7, 1) ); // [0,7,7] 15 | console.log( [1,2,3].findIndex(x => x == 2) ); // 1 16 | console.log( ["a", "b", "c"].entries() ); // iterator [0, "a"], [1,"b"], [2,"c"] 17 | console.log( ["a", "b", "c"].keys() ); // iterator 0, 1, 2 18 | console.log( ["a", "b", "c"].values() ); // iterator "a", "b", "c" 19 | -------------------------------------------------------------------------------- /lambda/api/get.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const dynamo = require('./dynamodb'); 3 | 4 | module.exports.handler = (event, context, callback) => { 5 | const params = { 6 | TableName: process.env.DYNAMODB_TABLE, 7 | Key: { 8 | id: { 9 | S: event.pathParameters.id, 10 | } 11 | } 12 | }; 13 | 14 | dynamo.getItem(params, (err, data) => { 15 | if (err) return callback(err); 16 | 17 | if (! Object.hasOwnProperty.call(data, 'Item')) { 18 | return callback(null, { statusCode: 404 }); 19 | } 20 | 21 | const response = { 22 | statusCode: 200, 23 | headers: { 24 | 'Access-Control-Allow-Origin': '*', 25 | }, 26 | body: JSON.stringify({ 27 | message: 'Retrieved snippet', 28 | snippet: data.Item.code.S, 29 | }), 30 | }; 31 | 32 | callback(null, response); 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /src/compilers/regenerator.js: -------------------------------------------------------------------------------- 1 | import Base from './base'; 2 | 3 | export default class Regenerator extends Base { 4 | static makeError( { lineNumber, description, column, endColumn = NaN } ) { 5 | let l = lineNumber - 1; 6 | 7 | return { 8 | loc : { 9 | line : l, 10 | column : column, 11 | offset : () => { return { line: l,column:endColumn}; } 12 | }, 13 | message : description 14 | }; 15 | } 16 | 17 | loadCompiler() { 18 | require.ensure(['regenerator'],require => { 19 | this.compiler = require('regenerator'); 20 | this.resolveFuture(); 21 | }); 22 | } 23 | 24 | compile( input ) { 25 | this._checkIfCompilerIsLoaded(); 26 | 27 | let code = '', 28 | errors = []; 29 | 30 | try { 31 | code = this.compiler.compile( input ).code; 32 | } catch (e) { 33 | errors = [Regenerator.makeError( e )]; 34 | } 35 | 36 | return { 37 | code, 38 | errors 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/store/location.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------ 2 | // Constants 3 | // ------------------------------------ 4 | export const LOCATION_CHANGE = 'LOCATION_CHANGE'; 5 | 6 | // ------------------------------------ 7 | // Actions 8 | // ------------------------------------ 9 | export function locationChange (location = '/') { 10 | return { 11 | type : LOCATION_CHANGE, 12 | payload : location 13 | }; 14 | } 15 | 16 | // ------------------------------------ 17 | // Specialized Action Creator 18 | // ------------------------------------ 19 | export const updateLocation = ({ dispatch }) => { 20 | return (nextLocation) => dispatch(locationChange(nextLocation)); 21 | }; 22 | 23 | // ------------------------------------ 24 | // Reducer 25 | // ------------------------------------ 26 | const initialState = null; 27 | export default function locationReducer (state = initialState, action) { 28 | return action.type === LOCATION_CHANGE 29 | ? action.payload 30 | : state; 31 | } 32 | -------------------------------------------------------------------------------- /src/store/ducks/editorConfig.js: -------------------------------------------------------------------------------- 1 | import * as actionTypes from 'store/actionTypes'; 2 | 3 | import { loadPersistedState} from 'store/middleware/localStorage'; 4 | 5 | let initialState = loadPersistedState('ide', 'editorConfig') || { 6 | lineWrapping: true, 7 | matchBrackets: true, 8 | lineNumbers: true, 9 | continueComments: true, 10 | indentUnit: 2, 11 | theme : 'default', 12 | }; 13 | 14 | if (initialState.theme !== 'default') { 15 | var ss = document.createElement('link'); 16 | ss.type = 'text/css'; 17 | ss.rel = 'stylesheet'; 18 | ss.href = `${S3_SERVER_HOST}codemirror/theme/${initialState.theme}.css`; 19 | document.getElementsByTagName('head')[0].appendChild(ss); 20 | } 21 | 22 | export default function reducer(state = initialState, action) { 23 | switch(action.type) { 24 | case actionTypes.UPDATE_EDITOR_CONFIG: 25 | return { 26 | ...state, 27 | [action.payload.key]: action.payload.value, 28 | }; 29 | 30 | default: 31 | return state; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /public/examples/es2015/classes.js: -------------------------------------------------------------------------------- 1 | // The classes syntax is syntactic sugar for prototypal inheritance 2 | // it now gives JavaScript a unified way to define inheritance 3 | // including a super object to represent a classes parent 4 | 5 | class Mesh { 6 | constructor(geometry) { 7 | this.geometry = geometry; 8 | } 9 | 10 | update( y = 12 ) { 11 | console.log("update"); 12 | } 13 | }; 14 | 15 | class SkinnedMesh extends Mesh { 16 | constructor(geometry, materials) { 17 | super(geometry, materials); 18 | 19 | this.idMatrix = SkinnedMesh.defaultMatrix(); 20 | this.bones = []; 21 | this.boneMatrices = []; 22 | //... 23 | } 24 | update(camera) { 25 | //... 26 | super.update(); 27 | } 28 | static defaultMatrix() { 29 | return new THREE.Matrix4(); 30 | } 31 | } 32 | 33 | // Classes can be either defined in a Statement or an Expression 34 | var c = class C {}; 35 | 36 | // The class to be extended can also be an Expression 37 | var d = class D extends (class E {}) {}; 38 | 39 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | ### Todo list 2 | 3 | [*] Editor Settings: 4 | - Wrap lines 5 | - show line numbers 6 | - select theme 7 | - indent unit 8 | [*] Keyboard shortcuts 9 | - [*] Cmd-Enter 10 | - [*] Cmd-B 11 | - [*] Cmd-S 12 | [*] Console better formatting for log lines 13 | [*] Help text when you open console 14 | [*] Localstorage saving 15 | [*] Saving to psql 16 | [*] Loading from psql 17 | [*] Examples for es2016 and es2017 presets 18 | [*] Loading screen 19 | [] Draggable editor sizes 20 | [*] Add link to my Twitter 21 | 22 | ###Ops 23 | [] Create test deploy to dokku 24 | [] Create real deploy to dokku 25 | 26 | ### Bugs 27 | 28 | [] (re)load theme on page reload 29 | [*] Console prints multiple times on run 30 | [] Catch errors on iframe (sandbox) 31 | [*] Saving from a snippet page doesn't seem to work 32 | [] Async examples will not log all statements to the console 33 | [] CompletionValue is not logged to console 34 | [*] locationChange should load snippet 35 | [] Shortcuts do not 36 | 37 | ### Future 38 | 39 | [] Add Linter 40 | [] Wiki -------------------------------------------------------------------------------- /src/store/middleware/localStorage.js: -------------------------------------------------------------------------------- 1 | let localStorage = window.localStorage; 2 | 3 | if (! window.localStorage) { 4 | localStorage = { 5 | getItem(name, def) { 6 | return def; 7 | }, 8 | 9 | setItem(name, value) { 10 | return; 11 | } 12 | }; 13 | } 14 | 15 | export default store => next => action => { 16 | let result = next(action); 17 | let state = JSON.stringify(store.getState()); 18 | 19 | localStorage.setItem('state', state); 20 | 21 | return result; 22 | }; 23 | 24 | function _load() { 25 | let state; 26 | 27 | if (window.localStorage) { 28 | state = localStorage.getItem('state', ''); 29 | 30 | try { 31 | state = JSON.parse(state); 32 | } catch (e) { 33 | console.error('Unable to parse persisted state', e); 34 | } 35 | } 36 | 37 | return state; 38 | } 39 | 40 | export function loadPersistedState(...keys) { 41 | let state = _load(); 42 | 43 | for (let key of keys) { 44 | if (state) state = state[key]; 45 | else return; 46 | } 47 | 48 | return state; 49 | } -------------------------------------------------------------------------------- /src/store/themes.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | import { CALL_API } from 'redux-api-middleware'; 3 | 4 | import { updateCode, transformCode } from 'store/ide'; 5 | import * as actionTypes from 'store/actionTypes'; 6 | 7 | // ------------------------------------ 8 | // Actions 9 | // ------------------------------------ 10 | export function loadThemes() { 11 | return { 12 | [CALL_API]: { 13 | endpoint : `${API_SERVER_HOST}themes/`, 14 | method : 'GET', 15 | types : [actionTypes.THEMES_REQUEST, 16 | actionTypes.THEMES_SUCCESS, 17 | actionTypes.THEMES_FAILURE], 18 | } 19 | }; 20 | } 21 | 22 | // ------------------------------------ 23 | // Reducer 24 | // ------------------------------------ 25 | const initialState = { 26 | available: [], 27 | }; 28 | export default function reducer(state = initialState, action) { 29 | switch(action.type) { 30 | case actionTypes.THEMES_SUCCESS: 31 | return { 32 | ...state, 33 | available: action.payload.themes, 34 | }; 35 | default: 36 | return state; 37 | } 38 | } -------------------------------------------------------------------------------- /tests/components/Header/Header.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Header } from 'components/Header/Header' 3 | import { IndexLink, Link } from 'react-router' 4 | import { shallow } from 'enzyme' 5 | 6 | describe('(Component) Header', () => { 7 | let _wrapper 8 | 9 | beforeEach(() => { 10 | _wrapper = shallow(
) 11 | }) 12 | 13 | it('Renders a welcome message', () => { 14 | const welcome = _wrapper.find('h1') 15 | expect(welcome).to.exist 16 | expect(welcome.text()).to.match(/React Redux Starter Kit/) 17 | }) 18 | 19 | describe('Navigation links...', () => { 20 | it('Should render a Link to Home route', () => { 21 | expect(_wrapper.contains( 22 | 23 | Home 24 | 25 | )).to.be.true 26 | }) 27 | 28 | it('Should render a Link to Counter route', () => { 29 | expect(_wrapper.contains( 30 | 31 | Counter 32 | 33 | )).to.be.true 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /src/store/ducks/loading.js: -------------------------------------------------------------------------------- 1 | import { CALL_API } from 'redux-api-middleware'; 2 | import * as actionTypes from 'store/actionTypes'; 3 | 4 | import { 5 | loadSnippet, 6 | } from 'store/ide'; 7 | 8 | import { 9 | loadExamples 10 | } from 'store/examples'; 11 | 12 | import { 13 | loadThemes 14 | } from 'store/themes'; 15 | 16 | function getTypeIfObject(typeOrObject) { 17 | if (typeof typeOrObject === 'object') { 18 | return typeOrObject.type; 19 | } 20 | 21 | return typeOrObject; 22 | } 23 | 24 | const loadingTypes = [ 25 | loadSnippet('')[CALL_API].types.map(getTypeIfObject), 26 | loadExamples()[CALL_API].types.map(getTypeIfObject), 27 | loadThemes()[CALL_API].types.map(getTypeIfObject), 28 | ]; 29 | 30 | const initialState = 0; 31 | export default function reducer(state = initialState, action) { 32 | for (let [start, finish, failure] of loadingTypes) { 33 | switch (action.type) { 34 | case start: 35 | return state + 1; 36 | case finish: 37 | return state - 1; 38 | case failure: 39 | return state - 1; 40 | } 41 | } 42 | 43 | return state; 44 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Zukowski 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 | -------------------------------------------------------------------------------- /lambda/api/themes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const aws = require('aws-sdk'); 3 | const project = require('../config/project.config.js'); 4 | 5 | const options = { 6 | region: 'eu-west-1', 7 | }; 8 | 9 | function trimItem(key) { 10 | const pattern = /codemirror\/theme\/([^.]*)\.css$/; 11 | return key.match(pattern)[1]; 12 | } 13 | 14 | function makeBody(data) { 15 | return { 16 | themes: data.Contents.map((item) => trimItem(item.Key)), 17 | }; 18 | } 19 | 20 | module.exports.handler = (event, context, callback) => { 21 | const s3 = new aws.S3(options); 22 | 23 | const params = { 24 | Bucket: project.bucket_name, 25 | Delimiter: '/', 26 | Prefix: 'codemirror/theme/', 27 | }; 28 | 29 | s3.listObjects(params, (err, data) => { 30 | if (err) return callback(err); 31 | 32 | const content = makeBody(data); 33 | const response = { 34 | statusCode: 200, 35 | headers: { 36 | 'Access-Control-Allow-Origin': '*', 37 | }, 38 | body: JSON.stringify(content), 39 | }; 40 | 41 | callback(null, response); 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /src/store/ducks/compilers.js: -------------------------------------------------------------------------------- 1 | import * as actionTypes from 'store/actionTypes'; 2 | 3 | import Compilers from 'compilers'; 4 | 5 | export function getSelectedCompiler(compilers) { 6 | for (let key of Object.keys(compilers)) { 7 | if (compilers[key].selected) { 8 | return key; 9 | } 10 | } 11 | } 12 | 13 | const initialState = (() => { 14 | const result = {}; 15 | const keys = Object.keys(Compilers); 16 | 17 | for (let key of keys) { 18 | result[key] = { 19 | loading: false, 20 | initialized: false, 21 | selected: false, 22 | }; 23 | } 24 | 25 | return result; 26 | })(); 27 | export default function reducer(state = initialState, action) { 28 | switch(action.type) { 29 | case actionTypes.LOADED_COMPILER: 30 | return { 31 | ...state, 32 | [action.payload]: { 33 | loading: false, 34 | initialized: true, 35 | }, 36 | }; 37 | 38 | case actionTypes.SELECT_COMPILER: 39 | return { 40 | ...state, 41 | [action.payload]: { 42 | selected: true, 43 | loading: true, 44 | initialized: false, 45 | }, 46 | }; 47 | 48 | default: 49 | return state; 50 | } 51 | } -------------------------------------------------------------------------------- /src/compilers/traceur.js: -------------------------------------------------------------------------------- 1 | import Base from './base'; 2 | 3 | import $script from 'scriptjs'; 4 | 5 | export default class Traceur extends Base { 6 | constructor() { 7 | super(); 8 | } 9 | 10 | loadCompiler() { 11 | $script(['/dist/compilers/traceur.js'],() => { 12 | this.compiler = new traceur.Compiler({ 13 | modules: 'inline' 14 | }); 15 | this.resolveFuture(); 16 | }); 17 | } 18 | 19 | static makeErrorFromMsg( msg ) { 20 | let sm = msg.split(':'), 21 | line = parseInt( sm[1], 10 ), 22 | column = parseInt( sm[2], 10 ) - 1; 23 | 24 | return { 25 | loc : { 26 | line, 27 | column, 28 | offset : () => { return { line:line,column:NaN }; } 29 | }, 30 | message : msg 31 | }; 32 | } 33 | 34 | compile( input ) { 35 | this._checkIfCompilerIsLoaded(); 36 | 37 | let code = '', 38 | errors = []; 39 | 40 | try { 41 | code = this.compiler.compile( input ); 42 | } catch(errs) { 43 | errs.forEach(error => errors.push( Traceur.makeErrorFromMsg( error ) ) ); 44 | } 45 | 46 | return { 47 | code, 48 | errors 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/examples/es2017/async-function.js: -------------------------------------------------------------------------------- 1 | // When an async function is called, it returns a Promise. 2 | // When the async function returns a value, the Promise will 3 | // be resolved with the returned value. When the async 4 | // function throws an exception or some value, the Promise will 5 | // be rejected with the thrown value. 6 | 7 | // An async function can contain an await expression, that pauses 8 | // the execution of the async function and waits for the 9 | // passed Promise's resolution, and then resumes the async 10 | // function's execution and returns the resolved value. 11 | 12 | function resolveAfter2Seconds(x) { 13 | return new Promise(resolve => { 14 | setTimeout(() => { 15 | resolve(x); 16 | }, 2000); 17 | }); 18 | } 19 | 20 | async function add1(x) { 21 | var a = resolveAfter2Seconds(20); 22 | var b = resolveAfter2Seconds(30); 23 | return x + await a + await b; 24 | } 25 | 26 | add1(10).then(v => { 27 | console.log(v); // prints 60 after 2 seconds. 28 | }); 29 | 30 | async function add2(x) { 31 | var a = await resolveAfter2Seconds(20); 32 | var b = await resolveAfter2Seconds(30); 33 | return x + a + b; 34 | } 35 | 36 | add2(10).then(v => { 37 | console.log(v); // prints 60 after 4 seconds. 38 | }); -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | // We only need to import the modules necessary for initial render 2 | import CoreLayout from '../layouts/CoreLayout'; 3 | import Home from './Home'; 4 | 5 | /* Note: Instead of using JSX, we recommend using react-router 6 | PlainRoute objects to build route definitions. */ 7 | 8 | export const createRoutes = (store) => ({ 9 | path : '/', 10 | component : CoreLayout, 11 | indexRoute : Home, 12 | childRoutes : [ 13 | { 14 | path : ':id', 15 | indexRoute : Home, 16 | } 17 | ] 18 | }); 19 | 20 | /* Note: childRoutes can be chunked or otherwise loaded programmatically 21 | using getChildRoutes with the following signature: 22 | 23 | getChildRoutes (location, cb) { 24 | require.ensure([], (require) => { 25 | cb(null, [ 26 | // Remove imports! 27 | require('./Counter').default(store) 28 | ]) 29 | }) 30 | } 31 | 32 | However, this is not necessary for code-splitting! It simply provides 33 | an API for async route definitions. Your code splitting should occur 34 | inside the route `getComponent` function, since it is only invoked 35 | when the route exists and matches. 36 | */ 37 | 38 | export default createRoutes; 39 | -------------------------------------------------------------------------------- /src/store/ducks/editors.js: -------------------------------------------------------------------------------- 1 | import * as actionTypes from 'store/actionTypes'; 2 | 3 | import { loadPersistedState } from 'store/middleware/localStorage'; 4 | 5 | let initialState = { 6 | 'es6': { 7 | errors: [], 8 | code: loadPersistedState('ide', 'editors', 'es6', 'code') || '', 9 | display: true, 10 | }, 11 | 'es5': { 12 | errors: [], 13 | code: loadPersistedState('ide', 'editors', 'es6', 'code') || '', 14 | display: false, 15 | }, 16 | }; 17 | 18 | export default function reducer(state = initialState, action) { 19 | switch (action.type) { 20 | case actionTypes.LOAD_SUCCESS: 21 | return { 22 | ...state, 23 | 'es6': { 24 | ...state['es6'], 25 | code: action.payload, 26 | } 27 | }; 28 | 29 | case actionTypes.UPDATE_CODE: 30 | return { 31 | ...state, 32 | [action.editor]: { 33 | ...state[action.editor], 34 | code: action.payload.code, 35 | }, 36 | }; 37 | 38 | case actionTypes.TOGGLE_EDITOR_DISPLAY: 39 | return { 40 | ...state, 41 | [action.payload]: { 42 | ...state[action.payload], 43 | display: !state[action.payload].display, 44 | } 45 | }; 46 | 47 | default: 48 | return state; 49 | } 50 | } -------------------------------------------------------------------------------- /src/components/Menu/Menu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Menu.scss'; 3 | 4 | import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon'; 5 | 6 | const Menu = ({ children, title, helpHref = false }) => ( 7 |
8 |
9 | { title } 10 | { helpHref ? 11 | 14 | help 15 | : 16 | null 17 | } 18 |
19 |
    20 | { children } 21 |
22 |
23 | ); 24 | 25 | Menu.Item = ({ icon, children, shortcut, onClick, loading=false, active=false }) => ( 26 |
  • 27 |
    28 |
    { children }
    29 |
    30 | { 31 | loading ? 32 | : 33 | shortcut ? 34 | shortcut : 35 | (active ? 36 | : 37 | null) 38 | }
    39 |
  • 40 | ); 41 | 42 | export default Menu; -------------------------------------------------------------------------------- /src/components/Loading/Loading.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { findDOMNode } from 'react-dom' 3 | 4 | import className from 'classnames' 5 | 6 | import './semantic.scss' 7 | import './Loading.scss' 8 | 9 | export default class Loading extends Component { 10 | state = { 11 | 'hidden': false, 12 | }; 13 | 14 | componentDidMount() { 15 | let node = findDOMNode(this.refs['viewport']); 16 | 17 | node.addEventListener('transitionend', this.onHidden.bind(this)); 18 | } 19 | 20 | componetWillUpdate(nextProps, nextState) { 21 | if (!this.props.visible && nextProps.visible) { 22 | this.setState({ hidden: false }); 23 | } 24 | } 25 | 26 | onHidden(event) { 27 | this.setState({ hidden: ! this.props.visible }); 28 | } 29 | 30 | render() { 31 | let { 32 | visible 33 | } = this.props; 34 | 35 | return ( 36 |
    41 |
    42 |
    Loading
    43 |
    44 |
    45 | ); 46 | } 47 | } -------------------------------------------------------------------------------- /src/containers/AppContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { browserHistory, Router } from 'react-router' 3 | import { Provider } from 'react-redux' 4 | 5 | import { DEFAULT_COMPILER } from 'compilers' 6 | import { 7 | selectCompiler, 8 | runCode, 9 | } from 'store/ide' 10 | import { loadExamples } from 'store/examples' 11 | import { loadThemes } from 'store/themes' 12 | 13 | class AppContainer extends Component { 14 | static propTypes = { 15 | routes: PropTypes.object.isRequired, 16 | store: PropTypes.object.isRequired 17 | } 18 | 19 | componentDidMount() { 20 | const compiler = this.props.store.getState().ide.selectedCompiler; 21 | const selectCompilerAction = selectCompiler(compiler); 22 | const loadExamplesAction = loadExamples(); 23 | const loadThemesAction = loadThemes(); 24 | 25 | this.props.store.dispatch(selectCompilerAction); 26 | this.props.store.dispatch(loadExamplesAction); 27 | this.props.store.dispatch(loadThemesAction); 28 | } 29 | 30 | shouldComponentUpdate() { 31 | return false; 32 | } 33 | 34 | render() { 35 | const { routes, store } = this.props 36 | 37 | return ( 38 | 39 |
    40 | 41 |
    42 |
    43 | ) 44 | } 45 | } 46 | 47 | export default AppContainer 48 | -------------------------------------------------------------------------------- /tests/test-bundler.js: -------------------------------------------------------------------------------- 1 | // --------------------------------------- 2 | // Test Environment Setup 3 | // --------------------------------------- 4 | import sinon from 'sinon' 5 | import chai from 'chai' 6 | import sinonChai from 'sinon-chai' 7 | import chaiAsPromised from 'chai-as-promised' 8 | import chaiEnzyme from 'chai-enzyme' 9 | 10 | chai.use(sinonChai) 11 | chai.use(chaiAsPromised) 12 | chai.use(chaiEnzyme()) 13 | 14 | global.chai = chai 15 | global.sinon = sinon 16 | global.expect = chai.expect 17 | global.should = chai.should() 18 | 19 | // --------------------------------------- 20 | // Require Tests 21 | // --------------------------------------- 22 | // for use with karma-webpack-with-fast-source-maps 23 | const __karmaWebpackManifest__ = []; // eslint-disable-line 24 | const inManifest = (path) => ~__karmaWebpackManifest__.indexOf(path) 25 | 26 | // require all `tests/**/*.spec.js` 27 | const testsContext = require.context('./', true, /\.spec\.js$/) 28 | 29 | // only run tests that have changed after the first pass. 30 | const testsToRun = testsContext.keys().filter(inManifest) 31 | ;(testsToRun.length ? testsToRun : testsContext.keys()).forEach(testsContext) 32 | 33 | // require all `src/**/*.js` except for `main.js` (for isparta coverage reporting) 34 | if (__COVERAGE__) { 35 | const componentsContext = require.context('../src/', true, /^((?!main|reducers).)*\.js$/) 36 | componentsContext.keys().forEach(componentsContext) 37 | } 38 | -------------------------------------------------------------------------------- /lambda/api/create.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const AWS = require('aws-sdk'); 3 | const project = require('../config/project.config'); 4 | 5 | const options = { 6 | region: 'eu-west-1', 7 | }; 8 | 9 | // if (process.env.IS_OFFLINE) { 10 | // options.s3ForcePathStyle = true; 11 | // options.endpoint = new AWS.Endpoint(project.s3_server_host); 12 | // } 13 | 14 | const S3 = new AWS.S3(options); 15 | 16 | module.exports.handler = (event, context, callback) => { 17 | let body; 18 | 19 | try { 20 | body = JSON.parse(event.body); 21 | } catch (error) { 22 | console.error(error); 23 | return callback(error); 24 | } 25 | 26 | const now = Date.now() 27 | const snippetId = parseInt(now, 10).toString(36); 28 | 29 | const params = { 30 | Bucket: process.env.SNIPPET_BUCKET_NAME, 31 | Key: snippetId, 32 | Body: new Buffer(body.code), 33 | ACL: 'public-read', 34 | }; 35 | 36 | S3.upload(params, (err, data) => { 37 | if (err) { 38 | console.error(err); 39 | return callback(err); 40 | } 41 | 42 | const response = { 43 | statusCode: 201, 44 | headers: { 45 | 'Access-Control-Allow-Origin': '*', 46 | }, 47 | body: JSON.stringify({ 48 | message: 'Saved snippet', 49 | saved: true, 50 | id: snippetId, 51 | }), 52 | }; 53 | 54 | callback(null, response); 55 | }); 56 | }; 57 | -------------------------------------------------------------------------------- /public/examples/es2015/generators.js: -------------------------------------------------------------------------------- 1 | // ES6 introduces a new type of function, the generator function 2 | // these functions do not have normal control flow of input and 3 | // return. The 'yield' expression can be used to pause the function 4 | // an emit an intermediate result. The function can be reentered 5 | // where it left off with last yield expression. 6 | // Want to read more about generators visit: 7 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* 8 | var fibonacci = { 9 | [Symbol.iterator]: function*() { 10 | var pre = 0, cur = 1; 11 | for (;;) { 12 | var temp = pre; 13 | pre = cur; 14 | cur += temp; 15 | yield cur; 16 | } 17 | } 18 | } 19 | 20 | // Generators can be used in coherence with iterators so 21 | // they can be looped over by a for-of loop 22 | for (var n of fibonacci) { 23 | // truncate the sequence at 1000 24 | if (n > 1000) 25 | break; 26 | console.log(n); 27 | } 28 | 29 | function *generator() { 30 | return (yield (yield 10) + 'world'); 31 | } 32 | 33 | var gen = generator(); 34 | 35 | // The argument supplied to generator.next is fed into the generator 36 | // on reentering (at the position of the last yield expression 37 | console.log(gen.next('hello').value); 38 | console.log(gen.next(0).value); 39 | console.log(gen.next().value); 40 | 41 | // Generators can be composed with the use of the yield* expression 42 | function *compose() { 43 | yield* generator(); 44 | } 45 | 46 | console.log(compose().next().value); 47 | -------------------------------------------------------------------------------- /src/store/actionTypes.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------ 2 | // Action Types 3 | // ------------------------------------ 4 | export const RUN_CODE = 'ide/RUN_CODE'; 5 | export const SELECT_COMPILER = 'ide/SELECT_COMPILER'; 6 | export const LOADED_COMPILER = 'ide/LOADED_COMPILER'; 7 | export const TRANSFORMED_CODE = 'ide/TRANSFORMED_CODE'; 8 | export const TRANSFORM_ON_TYPE = 'ide/TRANSFORM_ON_TYPE'; 9 | export const UPDATE_CODE = 'ide/UPDATE_CODE'; 10 | export const TOGGLE_EDITOR_DISPLAY = 'ide/TOGGLE_EDITOR_DISPLAY'; 11 | export const TOGGLE_CONSOLE_DISPLAY = 'ide/TOGGLE_CONSOLE_DISPLAY'; 12 | export const FLUSH_BUFFER = 'ide/FLUSH_BUFFER'; 13 | export const TOGGLE_COMPILER_PRESET = 'ide/TOGGLE_COMPILER_PRESET'; 14 | export const UPDATE_EDITOR_CONFIG = 'ide/UPDATE_EDITOR_CONFIG'; 15 | export const SAVE_SUCCESS = 'ide/SAVE_SUCCESS'; 16 | export const SAVE_FAILURE = 'ide/SAVE_FAILURE'; 17 | export const SAVE_REQUEST = 'ide/SAVE_REQUEST'; 18 | export const LOAD_SUCCESS = 'ide/LOAD_SUCCESS'; 19 | export const LOAD_FAILURE = 'ide/LOAD_FAILURE'; 20 | export const LOAD_REQUEST = 'ide/LOAD_REQUEST'; 21 | 22 | export const EXAMPLES_REQUEST = 'examples/EXAMPLES_REQUEST'; 23 | export const EXAMPLES_SUCCESS = 'examples/EXAMPLES_SUCCESS'; 24 | export const EXAMPLES_FAILURE = 'examples/EXAMPLES_FAILURE'; 25 | export const FETCH_EXAMPLE = 'examples/FETCH_EXAMPLE'; 26 | 27 | export const THEMES_REQUEST = 'examples/THEMES_REQUEST'; 28 | export const THEMES_SUCCESS = 'examples/THEMES_SUCCESS'; 29 | export const THEMES_FAILURE = 'examples/THEMES_FAILURE'; 30 | -------------------------------------------------------------------------------- /src/components/Menu/Menu.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .sidebar__header { 4 | text-transform: uppercase; 5 | border-bottom: 1px solid rgb(235, 235, 235); 6 | margin: 0 0 10px 10px; 7 | padding-bottom: 3px; 8 | 9 | font-size: .9rem; 10 | font-weight: 700; 11 | color: darken($primary-color, 5%); 12 | } 13 | 14 | .sidebar__header-help { 15 | position: absolute; 16 | right: 20px; 17 | text-transform: lowercase; 18 | text-decoration: underline; 19 | color: #000; 20 | font-size: .8rem; 21 | } 22 | 23 | .sidebar__menu { 24 | list-style-type: none; 25 | margin: 0 0 15px 0; 26 | padding: 0; 27 | 28 | > li { 29 | transition: background-color .2s; 30 | border-bottom-right-radius: 4px; 31 | border-top-right-radius: 4px; 32 | padding: 6px 10px 6px 20px; 33 | cursor: pointer; 34 | text-overflow: ellipses; 35 | white-space: nowrap; 36 | display: flex; 37 | flex-direction: row; 38 | overflow: hidden; 39 | text-transform: lowercase; 40 | 41 | > div { 42 | display: inline-block; 43 | } 44 | 45 | &.active { 46 | font-weight: 700; 47 | color: rgb(10, 10, 10); 48 | } 49 | } 50 | 51 | > li:hover { 52 | background-color: rgb(235, 235, 235); 53 | border-right: 3px solid $primary-color; 54 | } 55 | 56 | &-icon { 57 | margin-right: 5px; 58 | } 59 | 60 | &-shortcut { 61 | flex: 1 0 auto; 62 | text-align: right; 63 | padding-left: 5px; 64 | text-transform: none; 65 | font-size: .8rem; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/components/Editor/utils.js: -------------------------------------------------------------------------------- 1 | import CodeMirror from 'codemirror'; 2 | 3 | export function rm(el) { 4 | if(el.parentNode) el.parentNode.removeChild(el); 5 | } 6 | 7 | export function showTooltip(e, msg) { 8 | var tt = document.createElement('div'); 9 | tt.className = 'compiler-error-tooltip'; 10 | tt.appendChild(document.createTextNode(msg)); 11 | document.body.appendChild(tt); 12 | 13 | function position(e) { 14 | if(!tt.parentNode) return CodeMirror.off(document, 'mousemove', position); 15 | tt.style.top = Math.max (0, e.clientY - tt.offsetHeight - 5) + 'px'; 16 | tt.style.left = (e.clientX + 5) + 'px'; 17 | } 18 | 19 | CodeMirror.on(document, 'mousemove', position); 20 | 21 | position(e); 22 | 23 | if(tt.style.opacity != null) tt.style.opacity = 1; 24 | 25 | return tt; 26 | } 27 | 28 | export function hideTooltip(tt) { 29 | if(!tt.parentNode) return; 30 | if(tt.style.opacity == null) rm(tt); 31 | tt.style.opacity = 0; 32 | setTimeout(()=>rm(tt),600); 33 | } 34 | 35 | export function showTooltipFor( e, msg, marker ) { 36 | var tooltip = showTooltip(e, msg); 37 | 38 | function hide() { 39 | CodeMirror.off(marker, 'mouseout', hide); 40 | 41 | if(tooltip) { 42 | hideTooltip(tooltip); 43 | tooltip = null; 44 | } 45 | } 46 | 47 | CodeMirror.on(marker, 'mouseout', hide); 48 | } 49 | 50 | export function makeMarker( msg ) { 51 | msg = msg.split('\n')[0]; 52 | 53 | var marker = document.createElement('i'); 54 | 55 | marker.classList.add('compiler-error-marker', 56 | 'icon', 57 | 'remove'); 58 | 59 | CodeMirror.on(marker, 'mouseover', function(e) { 60 | showTooltipFor(e, msg, marker); 61 | }); 62 | 63 | return marker; 64 | } -------------------------------------------------------------------------------- /src/components/Editor/theme.scss: -------------------------------------------------------------------------------- 1 | .cm-s-default .cm-header { 2 | color: #00f 3 | } 4 | .cm-s-default .cm-quote { 5 | color: #36ac3b 6 | } 7 | .cm-negative { 8 | color: #FF4D4D 9 | } 10 | .cm-positive { 11 | color: #36ac3b 12 | } 13 | .cm-em { 14 | font-style: italic 15 | } 16 | .cm-link { 17 | text-decoration: underline 18 | } 19 | .cm-strikethrough { 20 | text-decoration: line-through 21 | } 22 | .cm-s-default .cm-keyword { 23 | color: #a151d2 24 | } 25 | .cm-s-default .cm-atom { 26 | color: #219 27 | } 28 | .cm-s-default .cm-number { 29 | color: #ED5C65 30 | } 31 | .cm-s-default .cm-def { 32 | color: #f18c16 33 | } 34 | .cm-s-default .cm-variable, 35 | .cm-s-default .cm-variable-2 { 36 | color: #2795EE 37 | } 38 | .cm-s-default .cm-variable-3 { 39 | color: #249D7F 40 | } 41 | .cm-s-default .cm-comment { 42 | color: #A0A1A7 43 | } 44 | .cm-s-default .cm-string { 45 | color: #249D7F 46 | } 47 | .cm-s-default .cm-string-2 { 48 | color: #ED5C65 49 | } 50 | .cm-s-default .cm-meta { 51 | color: #39464E 52 | } 53 | .cm-s-default .cm-builtin, 54 | .cm-s-default .cm-qualifier { 55 | color: #f18c16 56 | } 57 | .cm-s-default .cm-tag { 58 | color: #2795EE 59 | } 60 | .cm-s-default .cm-bracket { 61 | color: #39464E 62 | } 63 | .cm-s-default .cm-attribute { 64 | color: #f18c16 65 | } 66 | .cm-s-default .cm-hr { 67 | color: #999 68 | } 69 | .cm-s-default .cm-link { 70 | color: #2795EE 71 | } 72 | .cm-invalidchar, 73 | .cm-s-default .cm-error, 74 | 75 | div.CodeMirror span.CodeMirror-nonmatchingbracket { 76 | background: #ED5C65; 77 | color: #fff; 78 | border-radius: 2px 79 | } 80 | 81 | div.CodeMirror span.CodeMirror-matchingbracket { 82 | color: inherit; 83 | border-bottom: solid 2px rgba(36, 157, 127, .8) 84 | } -------------------------------------------------------------------------------- /src/store/examples.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | import { CALL_API } from 'redux-api-middleware'; 3 | 4 | import { updateCode, transformCode } from 'store/ide'; 5 | 6 | import * as actionTypes from 'store/actionTypes'; 7 | 8 | // ------------------------------------ 9 | // Actions 10 | // ------------------------------------ 11 | export function loadExamples() { 12 | return { 13 | [CALL_API]: { 14 | endpoint : `${API_SERVER_HOST}examples/`, 15 | method : 'GET', 16 | types : [actionTypes.EXAMPLES_REQUEST, 17 | actionTypes.EXAMPLES_SUCCESS, 18 | actionTypes.EXAMPLES_FAILURE], 19 | } 20 | }; 21 | } 22 | 23 | export function showExample(group, example) { 24 | return dispatch => { 25 | fetch(`${S3_SERVER_HOST}examples/${group}/${example}`) 26 | .then(res => res.text()) 27 | .then(code => { 28 | dispatch({ 29 | type : actionTypes.FETCH_EXAMPLE, 30 | key : example, 31 | payload : code, 32 | }); 33 | 34 | dispatch(updateCode('es6', code)); 35 | 36 | dispatch(transformCode()); 37 | }); 38 | }; 39 | } 40 | 41 | // ------------------------------------ 42 | // Reducer 43 | // ------------------------------------ 44 | const initialState = { 45 | available: {}, 46 | examples: {}, 47 | selected: null, 48 | }; 49 | export default function reducer(state = initialState, action) { 50 | switch(action.type) { 51 | case actionTypes.EXAMPLES_SUCCESS: 52 | return { 53 | ...state, 54 | available: action.payload.examples, 55 | }; 56 | case actionTypes.FETCH_EXAMPLE: 57 | return { 58 | ...state, 59 | selected: action.key, 60 | examples: { 61 | ...state.examples, 62 | [action.key]: action.payload, 63 | }, 64 | }; 65 | default: 66 | return state; 67 | } 68 | } -------------------------------------------------------------------------------- /src/components/Toolbar/Toolbar.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | $height: 50px; 4 | 5 | .toolbar { 6 | list-style-type: none; 7 | margin: 0; 8 | padding: 0; 9 | line-height: $height; 10 | 11 | > li { 12 | position: relative; 13 | display: inline-block; 14 | padding: 0 10px; 15 | height: $height; 16 | width: auto; 17 | cursor: pointer; 18 | 19 | > a { 20 | display: block; 21 | } 22 | } 23 | 24 | > li:hover { 25 | border-bottom: 3px solid $primary-color; 26 | } 27 | } 28 | 29 | .toolbar.right { 30 | margin-left: auto; 31 | } 32 | 33 | .dropdown { 34 | display: none; 35 | position: absolute; 36 | left: -205px; 37 | top: 55px; 38 | 39 | line-height: initial; 40 | width: 300px; 41 | 42 | background: #fff; 43 | border: 1px solid rgb(245, 245, 245); 44 | border-radius: 2px; 45 | 46 | &__wrapper { 47 | padding: 15px; 48 | box-shadow: 0 0 1px rgba(57,70,78,.15), 0 20px 55px -8px rgba(57,70,78,.25); 49 | } 50 | 51 | &.open { 52 | display: block; 53 | } 54 | 55 | p { 56 | margin: 0; 57 | line-height: 2rem; 58 | } 59 | 60 | input[type="checkbox"] { 61 | margin-right: 15px; 62 | } 63 | } 64 | 65 | .dropdown__wrapper:after { 66 | top: -10px; 67 | border-left: 8px solid transparent; 68 | border-right: 8px solid transparent; 69 | border-bottom: 10px solid #fff; 70 | border-style: none double solid; 71 | } 72 | 73 | .dropdown__wrapper:before { 74 | top: -11px; 75 | border-left: 8px solid transparent; 76 | border-right: 8px solid transparent; 77 | border-bottom: 10px solid rgba(57,70,78,.15); 78 | border-style: none double solid; 79 | } 80 | 81 | .dropdown__wrapper:before, 82 | .dropdown__wrapper:after { 83 | display: block; 84 | position: absolute; 85 | right: 10px; 86 | vertical-align: middle; 87 | content: ""; 88 | width: 0; 89 | height: 0; 90 | } -------------------------------------------------------------------------------- /bin/compile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const webpack = require('webpack') 3 | const debug = require('debug')('app:bin:compile') 4 | const webpackConfig = require('../config/webpack.config') 5 | const project = require('../config/project.config') 6 | 7 | // Wrapper around webpack to promisify its compiler and supply friendly logging 8 | const webpackCompiler = (webpackConfig) => 9 | new Promise((resolve, reject) => { 10 | const compiler = webpack(webpackConfig) 11 | 12 | compiler.run((err, stats) => { 13 | if (err) { 14 | debug('Webpack compiler encountered a fatal error.', err) 15 | return reject(err) 16 | } 17 | 18 | const jsonStats = stats.toJson() 19 | debug('Webpack compile completed.') 20 | debug(stats.toString(project.compiler_stats)) 21 | 22 | if (jsonStats.errors.length > 0) { 23 | debug('Webpack compiler encountered errors.') 24 | debug(jsonStats.errors.join('\n')) 25 | return reject(new Error('Webpack compiler encountered errors')) 26 | } else if (jsonStats.warnings.length > 0) { 27 | debug('Webpack compiler encountered warnings.') 28 | debug(jsonStats.warnings.join('\n')) 29 | } else { 30 | debug('No errors or warnings encountered.') 31 | } 32 | resolve(jsonStats) 33 | }) 34 | }) 35 | 36 | const compile = () => { 37 | debug('Starting compiler.') 38 | return Promise.resolve() 39 | .then(() => webpackCompiler(webpackConfig)) 40 | .then(stats => { 41 | if (stats.warnings.length && project.compiler_fail_on_warning) { 42 | throw new Error('Config set to fail on warning, exiting with status code "1".') 43 | } 44 | debug('Copying static assets to dist folder.') 45 | fs.copySync(project.paths.public(), project.paths.dist()) 46 | }) 47 | .then(() => { 48 | debug('Compilation completed successfully.') 49 | }) 50 | .catch((err) => { 51 | debug('Compiler encountered an error.', err) 52 | process.exit(1) 53 | }) 54 | } 55 | 56 | compile() 57 | -------------------------------------------------------------------------------- /lambda/serverless.yml: -------------------------------------------------------------------------------- 1 | service: es6console 2 | 3 | plugins: 4 | - serverless-s3-local 5 | - serverless-offline 6 | 7 | custom: 8 | s3: 9 | start: 10 | port: 8001 11 | buckets: 12 | - ${self:provider.environment.SNIPPET_BUCKET_NAME} 13 | directory: /tmp 14 | cors: true 15 | 16 | provider: 17 | name: aws 18 | runtime: nodejs6.10 19 | stage: dev 20 | region: eu-west-1 21 | environment: 22 | NODE_ENV: ${self:provider.stage} 23 | SNIPPET_BUCKET_NAME: ${self:service}-${opt:stage, self:provider.stage}-snippets 24 | iamRoleStatements: 25 | - Effect: Allow 26 | Action: 27 | - s3:Get* 28 | - s3:List* 29 | Resource: "arn:aws:s3:::staging.es6console.com" 30 | - Effect: Allow 31 | Action: 32 | - s3:Put* 33 | Resource: arn:aws:s3:::${self:provider.environment.SNIPPET_BUCKET_NAME} 34 | - Effect: Allow 35 | Action: 36 | - s3:Put* 37 | Resource: arn:aws:s3:::${self:provider.environment.SNIPPET_BUCKET_NAME}/* 38 | 39 | functions: 40 | create: 41 | handler: api/create.handler 42 | events: 43 | - http: 44 | path: snippet/save 45 | method: post 46 | cors: true 47 | listExamples: 48 | handler: api/examples.handler 49 | events: 50 | - http: 51 | path: examples 52 | method: get 53 | cors: true 54 | listThemes: 55 | handler: api/themes.handler 56 | events: 57 | - http: 58 | path: themes 59 | method: get 60 | cors: true 61 | 62 | resources: 63 | Resources: 64 | snippetsBucket: 65 | Type: AWS::S3::Bucket 66 | Properties: 67 | BucketName: ${self:provider.environment.SNIPPET_BUCKET_NAME} 68 | CorsConfiguration: 69 | CorsRules: 70 | - AllowedHeaders: 71 | - Authorization 72 | AllowedMethods: 73 | - GET 74 | - HEAD 75 | AllowedOrigins: 76 | - 'staging.es6console.com' 77 | - 'es6console.com' 78 | -------------------------------------------------------------------------------- /src/store/createStore.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, compose, createStore } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import { apiMiddleware } from 'redux-api-middleware'; 4 | import createLogger from 'redux-logger'; 5 | import { browserHistory } from 'react-router'; 6 | import makeRootReducer from './reducers'; 7 | import { updateLocation } from './location'; 8 | import localStorage from './middleware/localStorage'; 9 | 10 | export default (initialState = {}) => { 11 | // ====================================================== 12 | // Middleware Configuration 13 | // ====================================================== 14 | const middleware = [thunk, apiMiddleware, localStorage]; 15 | 16 | // ====================================================== 17 | // Store Enhancers 18 | // ====================================================== 19 | const enhancers = []; 20 | 21 | let composeEnhancers = compose; 22 | 23 | if (__DEV__) { 24 | const composeWithDevToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; 25 | if (typeof composeWithDevToolsExtension === 'function') { 26 | composeEnhancers = composeWithDevToolsExtension; 27 | } 28 | } 29 | 30 | if (__DEV__) { 31 | const logger = createLogger(); 32 | middleware.push(logger); 33 | } 34 | 35 | // ====================================================== 36 | // Store Instantiation and HMR Setup 37 | // ====================================================== 38 | const store = createStore( 39 | makeRootReducer(), 40 | initialState, 41 | composeEnhancers( 42 | applyMiddleware(...middleware), 43 | ...enhancers 44 | ) 45 | ); 46 | store.asyncReducers = {}; 47 | 48 | // To unsubscribe, invoke `store.unsubscribeHistory()` anytime 49 | store.unsubscribeHistory = browserHistory.listen(updateLocation(store)); 50 | 51 | if (module.hot) { 52 | module.hot.accept('./reducers', () => { 53 | const reducers = require('./reducers').default; 54 | store.replaceReducer(reducers(store.asyncReducers)); 55 | }); 56 | } 57 | 58 | return store; 59 | }; 60 | -------------------------------------------------------------------------------- /lambda/api/examples.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const aws = require('aws-sdk'); 3 | const project = require('../config/project.config.js'); 4 | 5 | const options = { 6 | region: 'eu-west-1', 7 | }; 8 | 9 | function listObjects(s3, params) { 10 | return new Promise((resolve, reject) => { 11 | s3.listObjects(params, (err, data) => { 12 | if (err) return reject(err); 13 | return resolve(data); 14 | }); 15 | }); 16 | } 17 | 18 | function trimPrefix(prefix) { 19 | const pattern = /examples\/(\w+)\//; 20 | return prefix.match(pattern)[1]; 21 | } 22 | 23 | function trimItem(key) { 24 | const pattern = /examples\/\w+\/(.*)/; 25 | return key.match(pattern)[1]; 26 | } 27 | 28 | function makeBody(data) { 29 | const result = {}; 30 | 31 | data.forEach((set) => { 32 | result[trimPrefix(set.Prefix)] = set.Contents.map((item) => trimItem(item.Key)); 33 | }); 34 | 35 | return { 36 | examples: result, 37 | }; 38 | } 39 | 40 | module.exports.handler = (event, context, callback) => { 41 | const s3 = new aws.S3(options); 42 | 43 | const params = { 44 | Bucket: project.bucket_name, 45 | Delimiter: '/', 46 | Prefix: 'examples/', 47 | }; 48 | 49 | s3.listObjects(params, (err, data) => { 50 | if (err) return callback(err); 51 | 52 | const prefixes = data.CommonPrefixes.map((item) => item.Prefix); 53 | 54 | Promise.all(prefixes.map((prefix) => 55 | listObjects(s3, { 56 | Bucket: project.bucket_name, 57 | Delimiter: '/', 58 | Prefix: prefix, 59 | }) 60 | )).then((data) => { 61 | const content = makeBody(data); 62 | const response = { 63 | statusCode: 200, 64 | headers: { 65 | 'Access-Control-Allow-Origin': '*', 66 | }, 67 | body: JSON.stringify(content), 68 | }; 69 | 70 | return callback(null, response); 71 | }).catch((err) => { 72 | return callback(err); 73 | }); 74 | 75 | }); 76 | }; 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Some basic conventions for contributing to this project. 4 | 5 | ### General 6 | 7 | Please make sure that there aren't existing pull requests attempting to address the issue mentioned. Likewise, please check for issues related to update, as someone else may be working on the issue in a branch or fork. 8 | 9 | * Non-trivial changes should be discussed in an issue first 10 | * Develop in a topic branch, not master 11 | * Squash your commits 12 | 13 | ### Linting 14 | 15 | Please check your code using `npm run lint` before submitting your pull requests, as the CI build will fail if `eslint` fails. 16 | 17 | ### Commit Message Format 18 | 19 | Each commit message should include a **type**, a **scope** and a **subject**: 20 | 21 | ``` 22 | (): 23 | ``` 24 | 25 | Lines should not exceed 100 characters. This allows the message to be easier to read on github as well as in various git tools and produces a nice, neat commit log ie: 26 | 27 | ``` 28 | #271 feat(standard): add style config and refactor to match 29 | #270 fix(config): only override publicPath when served by webpack 30 | #269 feat(eslint-config-defaults): replace eslint-config-airbnb 31 | #268 feat(config): allow user to configure webpack stats output 32 | ``` 33 | 34 | #### Type 35 | 36 | Must be one of the following: 37 | 38 | * **feat**: A new feature 39 | * **fix**: A bug fix 40 | * **docs**: Documentation only changes 41 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing 42 | semi-colons, etc) 43 | * **refactor**: A code change that neither fixes a bug or adds a feature 44 | * **test**: Adding missing tests 45 | * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation 46 | generation 47 | 48 | #### Scope 49 | 50 | The scope could be anything specifying place of the commit change. For example `webpack`, 51 | `babel`, `redux` etc... 52 | 53 | #### Subject 54 | 55 | The subject contains succinct description of the change: 56 | 57 | * use the imperative, present tense: "change" not "changed" nor "changes" 58 | * don't capitalize first letter 59 | * no dot (.) at the end 60 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import createStore from './store/createStore'; 6 | import AppContainer from './containers/AppContainer'; 7 | 8 | import Raven from 'raven-js'; 9 | 10 | // ======================================================== 11 | // Store Instantiation 12 | // ======================================================== 13 | if (__PROD__) { 14 | Raven 15 | .config('https://830240a30b9d4229add8932a16a17096@sentry.io/101285') 16 | .install(); 17 | } 18 | 19 | // ======================================================== 20 | // Store Instantiation 21 | // ======================================================== 22 | const initialState = window.___INITIAL_STATE__; 23 | const store = createStore(initialState); 24 | 25 | // ======================================================== 26 | // Render Setup 27 | // ======================================================== 28 | const MOUNT_NODE = document.getElementById('root'); 29 | 30 | let render = () => { 31 | const routes = require('./routes/index').default(store); 32 | 33 | ReactDOM.render( 34 | , 35 | MOUNT_NODE 36 | ); 37 | }; 38 | 39 | // This code is excluded from production bundle 40 | if (__DEV__) { 41 | if (module.hot) { 42 | // Development render functions 43 | const renderApp = render; 44 | const renderError = (error) => { 45 | const RedBox = require('redbox-react').default; 46 | 47 | ReactDOM.render(, MOUNT_NODE); 48 | }; 49 | 50 | // Wrap render in try/catch 51 | render = () => { 52 | try { 53 | renderApp(); 54 | } catch (error) { 55 | console.error(error); 56 | renderError(error); 57 | } 58 | }; 59 | 60 | // Setup hot module replacement 61 | module.hot.accept('./routes/index', () => 62 | setImmediate(() => { 63 | ReactDOM.unmountComponentAtNode(MOUNT_NODE); 64 | render(); 65 | }) 66 | ); 67 | } 68 | } 69 | 70 | // ======================================================== 71 | // Go! 72 | // ======================================================== 73 | render(); 74 | -------------------------------------------------------------------------------- /config/environments.config.js: -------------------------------------------------------------------------------- 1 | // Here is where you can define configuration overrides based on the execution environment. 2 | // Supply a key to the default export matching the NODE_ENV that you wish to target, and 3 | // the base configuration will apply your overrides before exporting itself. 4 | module.exports = { 5 | // ====================================================== 6 | // Overrides when NODE_ENV === 'development' 7 | // ====================================================== 8 | // NOTE: In development, we use an explicit public path when the assets 9 | // are served webpack by to fix this issue: 10 | // http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809 11 | development : (config) => ({ 12 | compiler_public_path : `http://${config.server_host}:${config.server_port}/`, 13 | }), 14 | 15 | staging: (config) => ({ 16 | bucket_name : 'staging.es6console.com', 17 | api_server_host : 'https://zl9ct7ehp9.execute-api.eu-west-1.amazonaws.com/dev/', 18 | s3_server_host : 'http://staging.es6console.com.s3-website-eu-west-1.amazonaws.com/', 19 | snippet_bucket_url: 'https://s3-eu-west-1.amazonaws.com/es6console-prod-snippets/', 20 | 21 | compiler_public_path : '/', 22 | compiler_fail_on_warning : false, 23 | compiler_hash_type : 'chunkhash', 24 | compiler_devtool : 'source-map', 25 | compiler_stats : { 26 | chunks : true, 27 | chunkModules : true, 28 | colors : true 29 | } 30 | }), 31 | 32 | // ====================================================== 33 | // Overrides when NODE_ENV === 'production' 34 | // ====================================================== 35 | production : (config) => ({ 36 | bucket_name : 'es6console.com', 37 | api_server_host : 'https://api.es6console.com/v1/', 38 | s3_server_host : 'https://es6console.com/', 39 | snippet_bucket_url: 'https://s3-eu-west-1.amazonaws.com/es6console-prod-snippets/', 40 | 41 | compiler_public_path : '/', 42 | compiler_fail_on_warning : false, 43 | compiler_hash_type : 'chunkhash', 44 | compiler_devtool : 'source-map', 45 | compiler_stats : { 46 | chunks : true, 47 | chunkModules : true, 48 | colors : true 49 | } 50 | }) 51 | }; 52 | -------------------------------------------------------------------------------- /src/routes/Home/components/HomeView.scss: -------------------------------------------------------------------------------- 1 | .ReactCodeMirror { 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | left: 25px; 6 | right: 0; 7 | } 8 | 9 | .ReactCodeMirror .CodeMirror { 10 | height: 100% !important; 11 | width: 100%; 12 | } 13 | 14 | .ide { 15 | height: 100%; 16 | display: flex; 17 | flex-direction: column; 18 | } 19 | 20 | .ide__viewport { 21 | display: flex; 22 | flex-direction: row; 23 | flex: 1; 24 | } 25 | 26 | .ide__column { 27 | flex: 1; 28 | position: relative; 29 | padding-left: 25px; 30 | transition: flex .3s; 31 | min-height: 0px; 32 | overflow: hidden; 33 | background: #fff; 34 | } 35 | 36 | .ide__column-gutter { 37 | position: absolute; 38 | z-index: 5000; 39 | top: 0; left: 0; 40 | bottom: 0; 41 | width: 25px; 42 | 43 | cursor: pointer; 44 | 45 | font-size: 0.8571rem; 46 | background: rgb(245, 245, 245); 47 | border-right: 1px solid rgb(200, 200, 200); 48 | 49 | 50 | &.second { 51 | border-left: 1px solid rgb(220, 220, 220); 52 | } 53 | 54 | .title { 55 | position: absolute; 56 | transform: rotate(90deg); 57 | transform-origin: left top; 58 | left: 18px; 59 | top: 10px; 60 | } 61 | } 62 | 63 | .ide__console { 64 | transition: flex .5s; 65 | flex: 0 0 250px; 66 | display: flex; 67 | flex-direction: column; 68 | overflow: hidden; 69 | 70 | &.collapsed { 71 | flex: 0 0 25px; 72 | } 73 | } 74 | 75 | .ide__console-title { 76 | display: flex; 77 | padding: 0 10px; 78 | line-height: 23px; 79 | cursor: pointer; 80 | flex: 0 0 25px; 81 | 82 | background: rgb(245, 245, 245); 83 | border-top: 1px solid rgb(200, 200, 200); 84 | border-bottom: 1px solid rgb(220, 220, 220); 85 | font-size: 0.8571rem; 86 | color: rgb(80, 80, 80); 87 | 88 | > span { 89 | overflow: hidden; 90 | text-overflow: ellipses; 91 | } 92 | 93 | & > i { 94 | text-align: right; 95 | flex: 1; 96 | } 97 | } 98 | 99 | .ide__console-console { 100 | background: white; 101 | flex: 1 1 auto; 102 | overflow-y: scroll; 103 | min-height: 0; 104 | cursor: text; 105 | } 106 | -------------------------------------------------------------------------------- /src/components/Console/Console.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { findDOMNode } from 'react-dom'; 3 | import { connect } from 'react-redux'; 4 | 5 | import * as actionCreators from 'store/ide'; 6 | 7 | import JSConsole from './jsconsole'; 8 | 9 | import './Console.scss'; 10 | 11 | const CTRL_KEY = 'Ctrl'; 12 | const HELP_TEXT = [ 13 | '`Welcome to the interactive ES6 console', 14 | ' :help - Show this help text', 15 | '', 16 | ' Console commands:', 17 | ' Up & Down - Navigate command history', 18 | ' Enter - Execute code', 19 | ` ${CTRL_KEY}-L - Clear console`, 20 | ` ${CTRL_KEY}-C - Cancel current command`, 21 | ` ${CTRL_KEY}-S - Save snippet\``, 22 | 23 | ].join('\n'); 24 | 25 | class _Console extends Component { 26 | componentDidMount() { 27 | let node = findDOMNode(this.refs['console-viewport']); 28 | 29 | this.jsconsole = new JSConsole(node, { 30 | mode: 'javascript', 31 | theme: 'default', 32 | evaluate: this.props.runCode, 33 | commands: { 34 | 'help': function() { 35 | this.print(HELP_TEXT); 36 | }, 37 | } 38 | }); 39 | 40 | setTimeout(() => { 41 | this.jsconsole.input.refresh(); 42 | this.jsconsole.output.refresh(); 43 | }, 0); 44 | 45 | this.jsconsole.print(HELP_TEXT); 46 | } 47 | 48 | componentDidUpdate(prevProps, prevState) { 49 | let buffer = this.props.logBuffer; 50 | 51 | if (buffer.length > 0) { 52 | let print = this.jsconsole.print.bind(this.jsconsole); 53 | 54 | buffer.forEach(l => print(l)); 55 | 56 | // Remove these log items from the state 57 | this.props.flushBuffer(); 58 | } 59 | 60 | this.jsconsole.output.refresh(); 61 | } 62 | 63 | componentWillUnmount() { 64 | 65 | } 66 | 67 | focus(e) { 68 | if(e) { 69 | e.stopPropagation(); 70 | e.preventDefault(); 71 | } 72 | 73 | this.jsconsole.input.focus(); 74 | } 75 | 76 | render() { 77 | return ( 78 |
    81 | 82 |
    83 | ); 84 | } 85 | } 86 | 87 | function mapStateToProps(state) { 88 | return state.ide.console; 89 | } 90 | 91 | export default connect(mapStateToProps, actionCreators)(_Console); -------------------------------------------------------------------------------- /config/karma.config.js: -------------------------------------------------------------------------------- 1 | const argv = require('yargs').argv 2 | const project = require('./project.config') 3 | const webpackConfig = require('./webpack.config') 4 | const debug = require('debug')('app:config:karma') 5 | 6 | debug('Creating configuration.') 7 | const karmaConfig = { 8 | basePath : '../', // project root in relation to bin/karma.js 9 | files : [ 10 | { 11 | pattern : `./${project.dir_test}/test-bundler.js`, 12 | watched : false, 13 | served : true, 14 | included : true 15 | } 16 | ], 17 | singleRun : !argv.watch, 18 | frameworks : ['mocha'], 19 | reporters : ['mocha'], 20 | preprocessors : { 21 | [`${project.dir_test}/test-bundler.js`] : ['webpack'] 22 | }, 23 | browsers : ['PhantomJS'], 24 | webpack : { 25 | devtool : 'cheap-module-source-map', 26 | resolve : Object.assign({}, webpackConfig.resolve, { 27 | alias : Object.assign({}, webpackConfig.resolve.alias, { 28 | sinon : 'sinon/pkg/sinon.js' 29 | }) 30 | }), 31 | plugins : webpackConfig.plugins, 32 | module : { 33 | noParse : [ 34 | /\/sinon\.js/ 35 | ], 36 | loaders : webpackConfig.module.loaders.concat([ 37 | { 38 | test : /sinon(\\|\/)pkg(\\|\/)sinon\.js/, 39 | loader : 'imports?define=>false,require=>false' 40 | } 41 | ]) 42 | }, 43 | // Enzyme fix, see: 44 | // https://github.com/airbnb/enzyme/issues/47 45 | externals : Object.assign({}, webpackConfig.externals, { 46 | 'react/addons' : true, 47 | 'react/lib/ExecutionEnvironment' : true, 48 | 'react/lib/ReactContext' : 'window' 49 | }), 50 | sassLoader : webpackConfig.sassLoader 51 | }, 52 | webpackMiddleware : { 53 | noInfo : true 54 | }, 55 | coverageReporter : { 56 | reporters : project.coverage_reporters 57 | } 58 | } 59 | 60 | if (project.globals.__COVERAGE__) { 61 | karmaConfig.reporters.push('coverage') 62 | karmaConfig.webpack.module.preLoaders = [{ 63 | test : /\.(js|jsx)$/, 64 | include : new RegExp(project.dir_client), 65 | exclude : /node_modules/, 66 | loader : 'babel', 67 | query : Object.assign({}, project.compiler_babel, { 68 | plugins : (project.compiler_babel.plugins || []).concat('istanbul') 69 | }) 70 | }] 71 | } 72 | 73 | module.exports = (cfg) => cfg.set(karmaConfig) 74 | -------------------------------------------------------------------------------- /src/layouts/CoreLayout/CoreLayout.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .core-layout__viewport { 4 | height: 100%; 5 | width: 100%; 6 | background-color: rgb(235, 235, 235); 7 | 8 | > nav { 9 | position: absolute; 10 | width: 270px; 11 | top: 50px; 12 | left: 0; 13 | bottom: 0; 14 | 15 | border-right: 1px solid #fff; 16 | } 17 | 18 | > .content { 19 | position: absolute; 20 | left: 270px; 21 | top: 50px; 22 | bottom: 0; 23 | right: 0; 24 | } 25 | } 26 | 27 | .sidebar-uno { 28 | width: 50px; 29 | height: 100%; 30 | position: relative; 31 | float: left; 32 | padding: 10px 0; 33 | } 34 | 35 | .sidebar-dos { 36 | height: 100%; 37 | overflow-y: scroll; 38 | margin-left: 50px; 39 | padding-right: 10px; 40 | padding-top: 10px; 41 | position: relative; 42 | background-color: rgb(247, 247, 247); 43 | } 44 | 45 | .sidebar-uno__bottom { 46 | position: absolute; 47 | bottom: 0; 48 | left: 50%; 49 | transform: translateX(-50%); 50 | 51 | .button-round:hover, 52 | .button-round:active, 53 | .button-round:focus { 54 | background: rgb(64, 120, 192) !important; 55 | 56 | > i { 57 | color: #fff; 58 | } 59 | } 60 | } 61 | 62 | .sidebar-uno__bottom-two { 63 | @extend .sidebar-uno__bottom; 64 | bottom: 45px; 65 | } 66 | 67 | .button-round { 68 | width: 34px; 69 | height: 34px; 70 | margin: 0 8px 8px; 71 | background-color: rgb(210, 210, 210); 72 | border-radius: 34px; 73 | position: relative; 74 | cursor: pointer; 75 | transition: background .3s; 76 | 77 | > i { 78 | color: rgb(99, 99, 99); 79 | position: absolute; 80 | top: 50%; 81 | left: 50%; 82 | transform: translate(-50%,-50%); 83 | margin: 0; 84 | font-size: 17px; 85 | display: inline-block; 86 | text-rendering: auto; 87 | transition: color .1s; 88 | } 89 | 90 | &.active { 91 | // background-color: rgb(99, 99, 99); 92 | background: $primary-color !important; 93 | 94 | > i { 95 | color: #000; 96 | } 97 | } 98 | 99 | &:hover, 100 | &:active, 101 | &:focus { 102 | background: #ccc; 103 | } 104 | } -------------------------------------------------------------------------------- /src/components/Editor/Editor.scss: -------------------------------------------------------------------------------- 1 | $font-family: 'Lato', sans-serif; 2 | $font-family-mono: "Monaco", "Andale Mono", "Lucida Console", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; 3 | $font-family-linenumber: Helvetica; 4 | 5 | $divider-color: #f1f1f1; 6 | $divider-sec-color: #d1d1d1; 7 | $divider-ter-color: #a1a1a1; 8 | 9 | $gutter-bg-color: rgba(240,240,240,.3); 10 | $tooltip-bg-color: rgba(242,242,242,.8); 11 | 12 | $error-color: #d9534f; 13 | 14 | $mono-line-height: 1.5rem; 15 | $mono-font-size: .8rem; 16 | $linenumber-font-size: .6rem; 17 | 18 | .ReactCodeMirror .CodeMirror-gutters { 19 | background-color: $gutter-bg-color; 20 | } 21 | 22 | .CodeMirror { 23 | line-height: 1.2rem; 24 | } 25 | 26 | body .CodeMirror * { 27 | -webkit-font-smoothing: subpixel-antialiased; 28 | } 29 | 30 | .ReactCodeMirror .CodeMirror-linenumber { 31 | font-family: $font-family-mono; 32 | font-size: $linenumber-font-size; 33 | color: #CCC; 34 | } 35 | 36 | .ReactCodeMirror .compiler-error-marker { 37 | font-size: $mono-font-size ; 38 | color: $error-color; 39 | margin-right: 0 !important; 40 | } 41 | 42 | .CodeMirror .compiler-markers { 43 | width: 15px; 44 | } 45 | 46 | .CodeMirror .CodeMirror-linenumbers { 47 | width: 15px; 48 | } 49 | 50 | .ReactCodeMirror.no-errors .CodeMirror .compiler-markers { 51 | width: 0px; 52 | } 53 | 54 | .compiler-error-tooltip { 55 | background-color: $tooltip-bg-color; 56 | color: #c7254e; 57 | opacity: 0; 58 | 59 | border: 1px solid $divider-color; 60 | border-radius: 2px; 61 | 62 | font-family: $font-family-mono; 63 | font-size: .7rem; 64 | line-height: 1rem; 65 | overflow: hidden; 66 | 67 | max-width: 600px; 68 | padding: 2px 5px; 69 | 70 | position: fixed; 71 | white-space: pre; 72 | white-space: pre-wrap; 73 | 74 | z-index: 1000; 75 | transition: opacity .4s; 76 | -moz-transition: opacity .4s; 77 | -webkit-transition: opacity .4s; 78 | -o-transition: opacity .4s; 79 | -ms-transition: opacity .4s; 80 | } 81 | 82 | .ReactCodeMirror .compiler-error-mark { 83 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg=="); 84 | background-position: left bottom; 85 | background-repeat: repeat-x; 86 | } -------------------------------------------------------------------------------- /src/components/Toolbar/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { findDOMNode } from 'react-dom' 3 | 4 | import className from 'classnames' 5 | import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon' 6 | 7 | import './Toolbar.scss' 8 | 9 | const Toolbar = ({ children, right = false }) => ( 10 |
      14 | { children } 15 |
    16 | ); 17 | 18 | const DropDown = ({ 19 | children, 20 | open = false, 21 | onMouseDown, 22 | onMouseUp, 23 | }) => ( 24 |
    30 |
    31 | { children } 32 |
    33 |
    34 | ); 35 | 36 | const Item = ({ icon, children, onClick }) => ( 37 |
  • 38 | 39 |   40 | { children } 41 |
  • 42 | ); 43 | 44 | class DropDownItem extends Component { 45 | state = { 46 | open: false, 47 | }; 48 | 49 | componentDidMount() { 50 | this.mouseDown = this.mouseDown.bind(this); 51 | 52 | document.addEventListener("click", this.mouseDown, false); 53 | } 54 | 55 | componentWillUnmount() { 56 | document.removeEventListener("click", this.mouseDown, false); 57 | } 58 | 59 | onToggle(event) { 60 | this.setState({ 61 | open: ! this.state.open, 62 | }); 63 | } 64 | 65 | mouseDown(event) { 66 | if(findDOMNode(this).contains(event.target)) { 67 | return; 68 | } 69 | 70 | this.setState({ 71 | open: false 72 | }); 73 | } 74 | 75 | render() { 76 | let { 77 | icon, 78 | children, 79 | title, 80 | } = this.props; 81 | 82 | let { 83 | open, 84 | } = this.state; 85 | 86 | return
  • 87 | 88 | 89 |   90 | { title } 91 | 92 | 93 | { children } 94 | 95 |
  • ; 96 | } 97 | } 98 | 99 | Toolbar.Item = Item; 100 | Toolbar.DropDownItem = DropDownItem; 101 | 102 | export default Toolbar; -------------------------------------------------------------------------------- /tests/routes/Counter/components/Counter.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { bindActionCreators } from 'redux' 3 | import { Counter } from 'routes/Counter/components/Counter' 4 | import { shallow } from 'enzyme' 5 | 6 | describe('(Component) Counter', () => { 7 | let _props, _spies, _wrapper 8 | 9 | beforeEach(() => { 10 | _spies = {} 11 | _props = { 12 | counter : 5, 13 | ...bindActionCreators({ 14 | doubleAsync : (_spies.doubleAsync = sinon.spy()), 15 | increment : (_spies.increment = sinon.spy()) 16 | }, _spies.dispatch = sinon.spy()) 17 | } 18 | _wrapper = shallow() 19 | }) 20 | 21 | it('Should render as a
    .', () => { 22 | expect(_wrapper.is('div')).to.equal(true) 23 | }) 24 | 25 | it('Should render with an

    that includes Sample Counter text.', () => { 26 | expect(_wrapper.find('h2').text()).to.match(/Counter:/) 27 | }) 28 | 29 | it('Should render props.counter at the end of the sample counter

    .', () => { 30 | expect(_wrapper.find('h2').text()).to.match(/5$/) 31 | _wrapper.setProps({ counter: 8 }) 32 | expect(_wrapper.find('h2').text()).to.match(/8$/) 33 | }) 34 | 35 | it('Should render exactly two buttons.', () => { 36 | expect(_wrapper.find('button')).to.have.length(2) 37 | }) 38 | 39 | describe('An increment button...', () => { 40 | let _button 41 | 42 | beforeEach(() => { 43 | _button = _wrapper.find('button').filterWhere(a => a.text() === 'Increment') 44 | }) 45 | 46 | it('has bootstrap classes', () => { 47 | expect(_button.hasClass('btn btn-default')).to.be.true 48 | }) 49 | 50 | it('Should dispatch a `increment` action when clicked', () => { 51 | _spies.dispatch.should.have.not.been.called 52 | 53 | _button.simulate('click') 54 | 55 | _spies.dispatch.should.have.been.called 56 | _spies.increment.should.have.been.called 57 | }) 58 | }) 59 | 60 | describe('A Double (Async) button...', () => { 61 | let _button 62 | 63 | beforeEach(() => { 64 | _button = _wrapper.find('button').filterWhere(a => a.text() === 'Double (Async)') 65 | }) 66 | 67 | it('has bootstrap classes', () => { 68 | expect(_button.hasClass('btn btn-default')).to.be.true 69 | }) 70 | 71 | it('Should dispatch a `doubleAsync` action when clicked', () => { 72 | _spies.dispatch.should.have.not.been.called 73 | 74 | _button.simulate('click') 75 | 76 | _spies.dispatch.should.have.been.called 77 | _spies.doubleAsync.should.have.been.called 78 | }) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /tests/store/location.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | LOCATION_CHANGE, 3 | locationChange, 4 | updateLocation, 5 | default as locationReducer 6 | } from 'store/location' 7 | 8 | describe('(Internal Module) Location', () => { 9 | it('Should export a constant LOCATION_CHANGE.', () => { 10 | expect(LOCATION_CHANGE).to.equal('LOCATION_CHANGE') 11 | }) 12 | 13 | describe('(Reducer)', () => { 14 | it('Should be a function.', () => { 15 | expect(locationReducer).to.be.a('function') 16 | }) 17 | 18 | it('Should initialize with a state of null.', () => { 19 | expect(locationReducer(undefined, {})).to.equal(null) 20 | }) 21 | 22 | it('Should return the previous state if an action was not matched.', () => { 23 | let state = locationReducer(undefined, {}) 24 | expect(state).to.equal(null) 25 | state = locationReducer(state, { type: '@@@@@@@' }) 26 | expect(state).to.equal(null) 27 | 28 | const locationState = { pathname: '/yup' } 29 | state = locationReducer(state, locationChange(locationState)) 30 | expect(state).to.equal(locationState) 31 | state = locationReducer(state, { type: '@@@@@@@' }) 32 | expect(state).to.equal(locationState) 33 | }) 34 | }) 35 | 36 | describe('(Action Creator) locationChange', () => { 37 | it('Should be exported as a function.', () => { 38 | expect(locationChange).to.be.a('function') 39 | }) 40 | 41 | it('Should return an action with type "LOCATION_CHANGE".', () => { 42 | expect(locationChange()).to.have.property('type', LOCATION_CHANGE) 43 | }) 44 | 45 | it('Should assign the first argument to the "payload" property.', () => { 46 | const locationState = { pathname: '/yup' } 47 | expect(locationChange(locationState)).to.have.property('payload', locationState) 48 | }) 49 | 50 | it('Should default the "payload" property to "/" if not provided.', () => { 51 | expect(locationChange()).to.have.property('payload', '/') 52 | }) 53 | }) 54 | 55 | describe('(Specialized Action Creator) updateLocation', () => { 56 | let _globalState 57 | let _dispatchSpy 58 | 59 | beforeEach(() => { 60 | _globalState = { 61 | location : locationReducer(undefined, {}) 62 | } 63 | _dispatchSpy = sinon.spy((action) => { 64 | _globalState = { 65 | ..._globalState, 66 | location : locationReducer(_globalState.location, action) 67 | } 68 | }) 69 | }) 70 | 71 | it('Should be exported as a function.', () => { 72 | expect(updateLocation).to.be.a('function') 73 | }) 74 | 75 | it('Should return a function (is a thunk).', () => { 76 | expect(updateLocation({ dispatch: _dispatchSpy })).to.be.a('function') 77 | }) 78 | 79 | it('Should call dispatch exactly once.', () => { 80 | updateLocation({ dispatch: _dispatchSpy })('/') 81 | expect(_dispatchSpy.should.have.been.calledOnce) 82 | }) 83 | }) 84 | }) 85 | -------------------------------------------------------------------------------- /server/main.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const debug = require('debug')('app:server') 3 | const path = require('path') 4 | const webpack = require('webpack') 5 | const webpackConfig = require('../config/webpack.config') 6 | const project = require('../config/project.config') 7 | const compress = require('compression') 8 | 9 | const app = express() 10 | 11 | // Apply gzip compression 12 | app.use(compress()) 13 | 14 | if (project.env === 'development') { 15 | // Debug toolbar middleware 16 | debug('Enabling error handling middleware') 17 | app.use(function(err, req, res, next) { 18 | debug(err.stack) 19 | return next(err); 20 | }); 21 | } 22 | 23 | // ------------------------------------ 24 | // Apply Webpack HMR Middleware 25 | // ------------------------------------ 26 | if (project.env === 'development') { 27 | const compiler = webpack(webpackConfig) 28 | 29 | debug('Enabling webpack dev and HMR middleware') 30 | app.use(require('webpack-dev-middleware')(compiler, { 31 | publicPath : webpackConfig.output.publicPath, 32 | contentBase : project.paths.client(), 33 | hot : true, 34 | quiet : project.compiler_quiet, 35 | noInfo : project.compiler_quiet, 36 | lazy : false, 37 | stats : project.compiler_stats 38 | })) 39 | app.use(require('webpack-hot-middleware')(compiler, { 40 | path: '/__webpack_hmr' 41 | })) 42 | 43 | // Serve static assets from ~/public since Webpack is unaware of 44 | // these files. This middleware doesn't need to be enabled outside 45 | // of development since this directory will be copied into ~/dist 46 | // when the application is compiled. 47 | app.use(express.static(project.paths.public())) 48 | 49 | // This rewrites all routes requests to the root /index.html file 50 | // (ignoring file requests). If you want to implement universal 51 | // rendering, you'll want to remove this middleware. 52 | app.use('*', function (req, res, next) { 53 | const filename = path.join(compiler.outputPath, 'index.html') 54 | compiler.outputFileSystem.readFile(filename, (err, result) => { 55 | if (err) { 56 | return next(err) 57 | } 58 | res.set('content-type', 'text/html') 59 | res.send(result) 60 | res.end() 61 | }) 62 | }) 63 | } else { 64 | debug( 65 | 'Server is being run outside of live development mode, meaning it will ' + 66 | 'only serve the compiled application bundle in ~/dist. Generally you ' + 67 | 'do not need an application server for this and can instead use a web ' + 68 | 'server such as nginx to serve your static files. See the "deployment" ' + 69 | 'section in the README for more information on deployment strategies.' 70 | ) 71 | 72 | // Serving ~/dist by default. Ideally these files should be served by 73 | // the web server and not the app server, but this helps to demo the 74 | // server in production. 75 | app.use(express.static(project.paths.dist())) 76 | } 77 | 78 | module.exports = app 79 | -------------------------------------------------------------------------------- /src/routes/Home/components/HomeView.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import './HomeView.scss'; 4 | 5 | import * as actionCreators from 'store/ide'; 6 | 7 | import className from 'classnames'; 8 | 9 | import Editor from 'components/Editor'; 10 | 11 | import Console from 'components/Console'; 12 | import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon'; 13 | 14 | 15 | class _REPLConsole extends Component { 16 | 17 | render() { 18 | let { 19 | display, 20 | toggleConsoleDisplay, 21 | } = this.props; 22 | 23 | let cn = className({ 24 | 'ide__console': true, 25 | 'collapsed': !display, 26 | }); 27 | 28 | return ( 29 |
    30 |
    32 | Console 33 | {!display ? 34 | : 35 | 36 | } 37 |
    38 | 39 | 40 |
    41 | ); 42 | } 43 | } 44 | 45 | function mapStateToProps2(state) { 46 | return state.ide.console; 47 | } 48 | 49 | const REPLConsole = connect(mapStateToProps2, actionCreators)(_REPLConsole); 50 | 51 | class _IDEViewPort extends Component { 52 | columnStyle() { 53 | return { 54 | flex: this.props['es5'].display ? '1' : '0 0 25px', 55 | }; 56 | } 57 | 58 | render() { 59 | return ( 60 |
    61 |
    62 |
    63 |
    64 | ES6 65 |
    66 |
    67 | 69 |
    70 | 71 |
    73 |
    75 |
    76 | ES5 77 |
    78 |
    79 | 80 |
    81 |
    82 | ); 83 | } 84 | } 85 | 86 | function mapStateToProps(state, ownProps) { 87 | return state.ide.editors; 88 | } 89 | 90 | const IDEViewPort = connect(mapStateToProps, actionCreators)(_IDEViewPort); 91 | 92 | export const HomeView = () => ( 93 |
    94 | 95 | 96 | 97 |
    98 | ); 99 | 100 | export default HomeView; -------------------------------------------------------------------------------- /src/sandbox.js: -------------------------------------------------------------------------------- 1 | import Compilers from 'compilers'; 2 | 3 | import { logCall } from 'store/ide'; 4 | import { complexFormatter } from 'utils'; 5 | 6 | // Disable modules so we do not insert use-strict 7 | // on the first line. 8 | const ES2015 = [ 9 | 'es2015', 10 | { 11 | modules: false, 12 | }, 13 | ]; 14 | 15 | import runtimeScript from 'raw-loader!babel-polyfill/dist/polyfill.js'; 16 | 17 | class LogBuffer { 18 | constructor() { 19 | this.buffer = []; 20 | } 21 | 22 | flush() { 23 | let buffer = [...this.buffer]; 24 | 25 | this.buffer.length = 0; 26 | 27 | return buffer; 28 | } 29 | 30 | append(...args) { 31 | this.buffer.push(...args); 32 | } 33 | } 34 | 35 | export default class SandBox { 36 | constructor() { 37 | this.logBuffer = new LogBuffer(); 38 | 39 | var body = document.getElementsByTagName('body')[0]; 40 | this.frame = document.createElement('iframe'); 41 | this.frame.setAttribute('style', 'display: none'); 42 | this.frame.onerror = this._handleError.bind(this); 43 | body.appendChild(this.frame); 44 | 45 | // this.cnsl.wrapLog(this.frame.contentWindow.console); 46 | 47 | this._wrapLog(this.frame.contentWindow.console); 48 | 49 | // Todo: Load runtimeScript async 50 | var script = this.frame.contentDocument.createElement('script'); 51 | script.type = 'text/javascript'; 52 | script.text = runtimeScript; 53 | this.frame.contentDocument.body.appendChild(script); 54 | } 55 | 56 | _handleError(e) { 57 | debugger; 58 | } 59 | 60 | _wrapLog(console) { 61 | var origConsoleLog = console.log.bind(console); 62 | 63 | console.log = (...args) => { 64 | const formattedLog = complexFormatter(...args); 65 | 66 | this.logBuffer.append(formattedLog); 67 | 68 | origConsoleLog(...args); 69 | }; 70 | 71 | return (this.origConsoleLog = origConsoleLog); 72 | } 73 | 74 | updateUserCode(code, onlyUpdate = false) { 75 | if (onlyUpdate && this.userCode && this.userCode.innerHTML == code) { 76 | return; 77 | } 78 | 79 | if (this.userCode) { 80 | this.frame.contentDocument.body.removeChild(this.userCode); 81 | } 82 | 83 | try { 84 | this.userCode = this.frame.contentDocument.createElement('script'); 85 | this.userCode.type = 'text/javascript'; 86 | this.frame.contentDocument.body.appendChild(this.userCode); 87 | 88 | this.userCode.innerHTML = code; 89 | } catch (e) { 90 | console.error(['Error in user code:', e.message].join('\n')); 91 | } 92 | 93 | // We are not interested in log statements 94 | // originated from updating the runtime 95 | this.logBuffer.flush(); 96 | } 97 | 98 | runCode(code) { 99 | let out = { 100 | logBuffer: [], 101 | completionValue: null, 102 | }; 103 | let compiler = Compilers['Babel (6)']; 104 | 105 | try { 106 | code = compiler.compile(code, { 107 | presets: [ES2015], 108 | }).code; 109 | out.completionValue = this.frame.contentWindow.eval.call(null, code); 110 | } catch (e) { 111 | out.error = true; 112 | if (e instanceof this.frame.contentWindow.Error) { 113 | out.completionValue = window[e.name](e.message); 114 | // e is an instance of an Error object from the 115 | // frames window object 116 | } else { 117 | out.completionValue = new Error(e); 118 | } 119 | out.recoverable = 120 | (e instanceof SyntaxError || 121 | e instanceof this.frame.contentWindow.SyntaxError) && 122 | e.message.match('Unexpected (token|end)'); 123 | } 124 | 125 | out.logBuffer = this.logBuffer.flush(); 126 | 127 | return out; 128 | } 129 | } 130 | 131 | export const sandbox = new SandBox(); 132 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { IndexLink, Link } from 'react-router'; 4 | import './Header.scss'; 5 | 6 | import * as actionCreators from 'store/ide'; 7 | 8 | import Select from 'react-select'; 9 | import Toolbar from 'components/Toolbar'; 10 | 11 | import 'react-select/dist/react-select.css'; 12 | 13 | export const Header = ({ 14 | code, 15 | transformCode, 16 | runCode, 17 | editorConfig, 18 | updateEditorConfig, 19 | availableThemes, 20 | saveSnippet, 21 | onChangeSelect = (key, fn) => (({ value }) => fn(key, value)), 22 | onChange = (key, fn) => ((event) => fn(key, event.target.checked)), 23 | }) => ( 24 |
    25 | 26 | ES6console.com 29 | 30 | 31 | 32 | runCode()}> 34 | Run 35 | 36 | 38 | Save 39 | 40 | 42 | Transform 43 | 44 | 45 | 46 | 47 | 48 |

    49 | 55 |

    56 | 57 |

    58 | 64 |

    65 | 66 |
    67 | 68 | 71 | 101 |
    102 |
    103 |
    104 | ); 105 | 106 | function mapStateToProps(state, ownProps) { 107 | return { 108 | editorConfig: state.ide.editorConfig, 109 | code: state.ide.editors['es6'].code, 110 | availableThemes: ['default'].concat(state.themes.available), 111 | }; 112 | } 113 | 114 | export default connect(mapStateToProps, actionCreators)(Header); 115 | -------------------------------------------------------------------------------- /tests/routes/Counter/modules/counter.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | COUNTER_INCREMENT, 3 | increment, 4 | doubleAsync, 5 | default as counterReducer 6 | } from 'routes/Counter/modules/counter' 7 | 8 | describe('(Redux Module) Counter', () => { 9 | it('Should export a constant COUNTER_INCREMENT.', () => { 10 | expect(COUNTER_INCREMENT).to.equal('COUNTER_INCREMENT') 11 | }) 12 | 13 | describe('(Reducer)', () => { 14 | it('Should be a function.', () => { 15 | expect(counterReducer).to.be.a('function') 16 | }) 17 | 18 | it('Should initialize with a state of 0 (Number).', () => { 19 | expect(counterReducer(undefined, {})).to.equal(0) 20 | }) 21 | 22 | it('Should return the previous state if an action was not matched.', () => { 23 | let state = counterReducer(undefined, {}) 24 | expect(state).to.equal(0) 25 | state = counterReducer(state, { type: '@@@@@@@' }) 26 | expect(state).to.equal(0) 27 | state = counterReducer(state, increment(5)) 28 | expect(state).to.equal(5) 29 | state = counterReducer(state, { type: '@@@@@@@' }) 30 | expect(state).to.equal(5) 31 | }) 32 | }) 33 | 34 | describe('(Action Creator) increment', () => { 35 | it('Should be exported as a function.', () => { 36 | expect(increment).to.be.a('function') 37 | }) 38 | 39 | it('Should return an action with type "COUNTER_INCREMENT".', () => { 40 | expect(increment()).to.have.property('type', COUNTER_INCREMENT) 41 | }) 42 | 43 | it('Should assign the first argument to the "payload" property.', () => { 44 | expect(increment(5)).to.have.property('payload', 5) 45 | }) 46 | 47 | it('Should default the "payload" property to 1 if not provided.', () => { 48 | expect(increment()).to.have.property('payload', 1) 49 | }) 50 | }) 51 | 52 | describe('(Action Creator) doubleAsync', () => { 53 | let _globalState 54 | let _dispatchSpy 55 | let _getStateSpy 56 | 57 | beforeEach(() => { 58 | _globalState = { 59 | counter : counterReducer(undefined, {}) 60 | } 61 | _dispatchSpy = sinon.spy((action) => { 62 | _globalState = { 63 | ..._globalState, 64 | counter : counterReducer(_globalState.counter, action) 65 | } 66 | }) 67 | _getStateSpy = sinon.spy(() => { 68 | return _globalState 69 | }) 70 | }) 71 | 72 | it('Should be exported as a function.', () => { 73 | expect(doubleAsync).to.be.a('function') 74 | }) 75 | 76 | it('Should return a function (is a thunk).', () => { 77 | expect(doubleAsync()).to.be.a('function') 78 | }) 79 | 80 | it('Should return a promise from that thunk that gets fulfilled.', () => { 81 | return doubleAsync()(_dispatchSpy, _getStateSpy).should.eventually.be.fulfilled 82 | }) 83 | 84 | it('Should call dispatch and getState exactly once.', () => { 85 | return doubleAsync()(_dispatchSpy, _getStateSpy) 86 | .then(() => { 87 | _dispatchSpy.should.have.been.calledOnce 88 | _getStateSpy.should.have.been.calledOnce 89 | }) 90 | }) 91 | 92 | it('Should produce a state that is double the previous state.', () => { 93 | _globalState = { counter: 2 } 94 | 95 | return doubleAsync()(_dispatchSpy, _getStateSpy) 96 | .then(() => { 97 | _dispatchSpy.should.have.been.calledOnce 98 | _getStateSpy.should.have.been.calledOnce 99 | expect(_globalState.counter).to.equal(4) 100 | return doubleAsync()(_dispatchSpy, _getStateSpy) 101 | }) 102 | .then(() => { 103 | _dispatchSpy.should.have.been.calledTwice 104 | _getStateSpy.should.have.been.calledTwice 105 | expect(_globalState.counter).to.equal(8) 106 | }) 107 | }) 108 | }) 109 | 110 | // NOTE: if you have a more complex state, you will probably want to verify 111 | // that you did not mutate the state. In this case our state is just a number 112 | // (which cannot be mutated). 113 | describe('(Action Handler) COUNTER_INCREMENT', () => { 114 | it('Should increment the state by the action payload\'s "value" property.', () => { 115 | let state = counterReducer(undefined, {}) 116 | expect(state).to.equal(0) 117 | state = counterReducer(state, increment(1)) 118 | expect(state).to.equal(1) 119 | state = counterReducer(state, increment(2)) 120 | expect(state).to.equal(3) 121 | state = counterReducer(state, increment(-3)) 122 | expect(state).to.equal(0) 123 | }) 124 | }) 125 | }) 126 | -------------------------------------------------------------------------------- /src/styles/console.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Meslo'; 3 | src: url('meslo/MesloLGSDZ-Regular.woff') format('woff'); 4 | } 5 | @font-face { 6 | font-family: 'Meslo'; 7 | src: url('meslo/MesloLGSDZ-Bold.woff') format('woff'); 8 | font-weight:bold; 9 | } 10 | @font-face { 11 | font-family: 'Meslo'; 12 | src: url('meslo/MesloLGSDZ-BoldItalic.woff') format('woff'); 13 | font-weight:bold; 14 | font-style:italic; 15 | } 16 | @font-face { 17 | font-family: 'Meslo'; 18 | src: url('meslo/MesloLGSDZ-Italic.woff') format('woff'); 19 | font-style:italic; 20 | } 21 | 22 | .jsconsole .CodeMirror-gutter-elt .gutter-icon:before { 23 | position: absolute; 24 | display: block; 25 | content: ""; 26 | margin-top:1px; 27 | width: 8px; 28 | height: 10px; 29 | left:4px; 30 | top:2px; 31 | -webkit-user-select: none; 32 | background-image: url(gutter-icons.png); 33 | background-size: 46px 10px; 34 | background-position: 0 0; 35 | } 36 | 37 | .jsconsole .CodeMirror-gutter-elt .prompt:before { 38 | left:5px; 39 | background-position: -10px 0px; 40 | } 41 | 42 | .jsconsole .CodeMirror-gutter-elt .completion-value:before { 43 | background-position: -18px 0px; 44 | } 45 | 46 | .jsconsole .CodeMirror-gutter-elt .prev-command:before { 47 | background-position: -73px 0px; 48 | } 49 | 50 | .jsconsole .CodeMirror-gutter-elt .console-error:before { 51 | top: 3px; 52 | left: 2px; 53 | background-position: -81px 0px; 54 | width: 12px; 55 | } 56 | 57 | .jsconsole .CodeMirror-gutter-elt .prompt-continuation:before { 58 | content : '..'; 59 | background:none; 60 | left:4px; 61 | top:0px; 62 | } 63 | 64 | .jsconsole .CodeMirror-code pre { 65 | padding:3px 4px 2px 4px; 66 | } 67 | 68 | .jsconsole .CodeMirror-gutter.repl { 69 | min-width: 2em; 70 | } 71 | 72 | .jsconsole .CodeMirror { 73 | line-height: 1em; 74 | font-family: Meslo, monospace; 75 | font-size:11px; 76 | 77 | } 78 | 79 | /* don't know what's causing this to be necessary, there's a 4px offset to the selection*/ 80 | .jsconsole .CodeMirror-selected { 81 | background:Highlight !important; 82 | } 83 | 84 | .jsconsole .CodeMirror-gutters { 85 | border-right: 0; 86 | background-color: transparent; 87 | } 88 | 89 | .jsconsole-output .CodeMirror-cursor { visibility: hidden; } 90 | 91 | .jsconsole-input { 92 | margin-top:-8px; 93 | } 94 | .jsconsole .CodeMirror { 95 | height: auto; 96 | } 97 | .jsconsole .CodeMirror-code .prev-command span { 98 | font-weight:normal 99 | } 100 | 101 | /* eclipse theme */ 102 | 103 | .jsconsole.eclipse .CodeMirror-gutter-elt .prompt-continuation:before { 104 | color:#3d80f1; 105 | } 106 | .jsconsole.eclipse .jsconsole-output .CodeMirror-code .type-error span { 107 | color: red; 108 | } 109 | .jsconsole.eclipse .jsconsole-output .CodeMirror-code .type-undefined span { 110 | color: hsl(0, 0%, 58%); 111 | } 112 | 113 | .jsconsole.eclipse .CodeMirror-code .prev-command span { 114 | color: #0080ff; 115 | } 116 | 117 | .jsconsole.eclipse .CodeMirror .completion-value-end { 118 | border-bottom: 1px solid hsl(0, 0%, 94%); 119 | } 120 | 121 | /* base16-dark theme */ 122 | 123 | .jsconsole.base16-dark { 124 | background-color:#151515; 125 | } 126 | 127 | .jsconsole.base16-dark .CodeMirror-gutter-elt .prompt-continuation:before { 128 | color:#3d80f1; 129 | } 130 | .jsconsole.base16-dark .jsconsole-output .CodeMirror-code .type-error span { 131 | color: red; 132 | } 133 | .jsconsole.base16-dark .jsconsole-output .CodeMirror-code .type-undefined span { 134 | color: hsl(0, 0%, 58%); 135 | } 136 | 137 | .jsconsole.base16-dark .CodeMirror-code .prev-command span { 138 | color: hsl(210, 100%, 65%);; 139 | } 140 | 141 | .jsconsole.base16-dark .CodeMirror .completion-value-end { 142 | border-bottom: 1px solid hsl(0, 0%, 20%); 143 | } 144 | 145 | /* dark theme */ 146 | 147 | .cm-s-dark.CodeMirror {background: #151515; color: #fff;} 148 | .cm-s-dark .CodeMirror-cursor {border-left: 1px solid #b0b0b0 !important;} 149 | 150 | .jsconsole.dark { 151 | background-color:#151515; 152 | } 153 | 154 | .jsconsole.dark .CodeMirror-gutter-elt .prompt-continuation:before { 155 | color:#3d80f1; 156 | } 157 | .jsconsole.dark .jsconsole-output .CodeMirror-code .type-error span { 158 | color: #c20000; 159 | } 160 | .jsconsole.dark .jsconsole-output .CodeMirror-code .type-undefined span { 161 | color: hsl(0, 0%, 83%); 162 | } 163 | 164 | .jsconsole.dark .CodeMirror-code .prev-command span { 165 | color: hsl(0, 0%, 83%); 166 | } 167 | 168 | .jsconsole.dark .CodeMirror .completion-value-end { 169 | border-bottom: 1px solid hsl(0, 0%, 20%); 170 | } 171 | -------------------------------------------------------------------------------- /bin/deploy.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | 3 | const fs = require('fs'); 4 | const klaw = require('klaw'); 5 | const path = require('path'); 6 | const debug = require('debug')('app:bin:deploy'); 7 | const crypto = require('crypto'); 8 | const project = require('../config/project.config'); 9 | const through2 = require('through2'); 10 | const mime = require('mime-types'); 11 | const argv = require('yargs').argv 12 | 13 | const EXCLUDES = ['.DS_Store']; 14 | 15 | const s3 = new AWS.S3({ 16 | region: 'eu-west-1', 17 | }); 18 | 19 | const cf = new AWS.CloudFront({}); 20 | 21 | function promisify(fn, thisScope) { 22 | return function decorator(...args) { 23 | return new Promise((resolve, reject) => { 24 | args.push((err, data) => { 25 | if (err) return reject(err); 26 | return resolve(data); 27 | }); 28 | 29 | fn.apply(thisScope, args); 30 | }); 31 | }; 32 | } 33 | 34 | const readFile = promisify(fs.readFile); 35 | const s3HeadObject = promisify(s3.headObject, s3); 36 | const s3Upload = promisify(s3.upload, s3); 37 | 38 | function stripPath(inputPath, absPathPrefix) { 39 | return inputPath.slice(absPathPrefix.length + 1); 40 | } 41 | 42 | function uploadFile(objectKey, fileData) { 43 | if (EXCLUDES.indexOf(objectKey) != -1) { 44 | debug('Not uploading file', objectKey); 45 | } 46 | 47 | debug('Uploading file', objectKey); 48 | 49 | return s3Upload({ 50 | Bucket: project.bucket_name, 51 | Key: objectKey, 52 | Body: fileData, 53 | ContentType: mime.contentType(objectKey), 54 | }); 55 | } 56 | 57 | function checkMD5AndUpload(absPathPrefix, item) { 58 | const objectKey = stripPath(item.path, absPathPrefix); 59 | 60 | let fileMD5, fileData; 61 | 62 | return readFile(item.path) 63 | .then( 64 | (fd) => { 65 | fileData = fd; 66 | fileMD5 = crypto.createHash('md5').update(fileData).digest('hex'); 67 | 68 | return s3HeadObject({ 69 | Bucket: project.bucket_name, 70 | Key: objectKey, 71 | }); 72 | }, 73 | (err) => { 74 | debug('Unable to read file', item.path, err); 75 | } 76 | ) 77 | .then( 78 | (data) => { 79 | if (! data) return; 80 | 81 | const etag = JSON.parse(data['ETag']); 82 | 83 | if (!argv.all && fileMD5 !== etag) { 84 | return uploadFile(objectKey, fileData); 85 | } 86 | }, 87 | (err) => { 88 | if (err.code === 'NotFound') 89 | return uploadFile(objectKey, fileData); 90 | debug('ERROR S3', objectKey, err); 91 | } 92 | ) 93 | .catch( 94 | (err) => { 95 | debug('Uncaught Error', err); 96 | } 97 | ); 98 | } 99 | 100 | const deploy = (directory, absPathPrefix) => { 101 | absPathPrefix = absPathPrefix || directory; 102 | 103 | debug('Performing Deploy', directory); 104 | debug('NODE_ENV', project.env); 105 | 106 | const upload = through2.obj(function(item, enc, next) { 107 | const result = checkMD5AndUpload(absPathPrefix, item); 108 | this.push(result); 109 | next(); 110 | }); 111 | 112 | const excludeDirFilter = through2.obj(function(item, enc, next) { 113 | if (! item.stats.isDirectory()) this.push(item); 114 | next(); 115 | }); 116 | 117 | const waitForCompletion = through2.obj(function(item, enc, next) { 118 | item.then(() => next(), () => next()); 119 | }); 120 | 121 | return klaw(directory) 122 | .pipe(excludeDirFilter) 123 | .pipe(upload) 124 | .pipe(waitForCompletion) 125 | .on('data', () => { 126 | debug('DONE'); 127 | }) 128 | }; 129 | 130 | const dist = path.join(__dirname, '../dist'); 131 | const themes = path.join(__dirname, '../node_modules/codemirror/theme'); 132 | 133 | deploy(dist) 134 | .on('end', () => { 135 | deploy(themes, path.join(__dirname, '../node_modules')) 136 | .on('end', () => { 137 | const params = { 138 | DistributionId: 'EDV35IT95U5Q7', 139 | InvalidationBatch: { 140 | CallerReference: `${Date.now()}`, 141 | Paths: { 142 | Quantity: 1, 143 | Items: ['/index.html'], 144 | } 145 | } 146 | }; 147 | 148 | cf.createInvalidation(params, (err, data) => { 149 | if (err) return debug('Failed invalidation:', err.message); 150 | 151 | debug('Succesfully invalidated index.html'); 152 | }); 153 | }); 154 | }); 155 | -------------------------------------------------------------------------------- /src/components/Editor/Editor.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import * as actionCreators from 'store/ide'; 5 | 6 | import CodeMirrorInstance from 'codemirror'; 7 | import CodeMirror from 'react-codemirror'; 8 | import * as util from './utils'; 9 | 10 | import 'codemirror/lib/codemirror.css'; 11 | import 'codemirror/mode/javascript/javascript'; 12 | import 'codemirror/addon/edit/matchbrackets'; 13 | import 'codemirror/addon/comment/continuecomment'; 14 | 15 | import './Editor.scss'; 16 | import './theme.scss'; 17 | 18 | const GUTTER_ID = 'compiler-markers'; 19 | const KEYS = CodeMirrorInstance.keyNames; 20 | 21 | class _Editor extends Component { 22 | 23 | /** 24 | * CONSTRUCTOR 25 | */ 26 | 27 | constructor(...args) { 28 | super(...args); 29 | 30 | this.markers = []; 31 | } 32 | 33 | /** 34 | * GETTERS SETTERS 35 | */ 36 | 37 | get editor() { 38 | return this.refs['editor'].getCodeMirror(); 39 | } 40 | 41 | /** 42 | * LIFE CYCLE METHODS 43 | */ 44 | 45 | componentDidMount() { 46 | setTimeout(() => { 47 | this.editor.refresh(); 48 | }, 0); 49 | 50 | // Terrible hack to force Firefox into displaying the editor: 51 | setTimeout(() => { 52 | this.editor.refresh(); 53 | }, 250); 54 | } 55 | 56 | componentDidUpdate(prevProps, prevState) { 57 | this.clearErrorMarkers(); 58 | this.setErrorMarkers(this.props.errors); 59 | } 60 | 61 | /** 62 | * EVENT HANDLERS 63 | */ 64 | 65 | onChange(...args) { 66 | this.props.updateCode(this.props.name, ...args); 67 | 68 | if (this.props.onChange) { 69 | this.props.onChange(...args); 70 | } 71 | } 72 | 73 | /** 74 | * METHODS 75 | */ 76 | 77 | clearErrorMarkers() { 78 | this.editor.clearGutter(GUTTER_ID); 79 | this.markers.forEach(marker => marker.clear()); 80 | this.markers = []; 81 | } 82 | 83 | setErrorMarkers(errors = []) { 84 | errors.forEach(error => this.setErrorMarker( error )); 85 | } 86 | 87 | setErrorMarker(error) { 88 | var loc = error.loc; 89 | 90 | if (!loc) { 91 | throw new Error(['Trying to set an error marker for', 92 | 'an error marker that has no loc prop'] 93 | .join(' ')); 94 | } 95 | 96 | this.editor.setGutterMarker(error.loc.line - 1, 97 | GUTTER_ID, 98 | util.makeMarker(error.message)); 99 | 100 | var offset; 101 | if (typeof loc.offset !== 'function') { 102 | offset = { 103 | line: loc.line, 104 | }; 105 | } else { 106 | offset = error.loc.offset(); 107 | } 108 | 109 | var mark = this.editor.markText({ 110 | line: loc.line-1, 111 | ch:loc.column 112 | }, 113 | { 114 | line: offset.line-1, 115 | ch:isNaN(offset.column) ? null : offset.column 116 | }, 117 | { 118 | className: 'compiler-error-mark' 119 | }); 120 | 121 | this.markers.push(mark); 122 | } 123 | 124 | /** 125 | * RENDER METHOD 126 | */ 127 | 128 | render() { 129 | let { 130 | runCode, 131 | transformCode, 132 | saveSnippet, 133 | } = this.props; 134 | 135 | let options = { 136 | ...this.props.options, 137 | defaultValue: ' ', 138 | mode: 'javascript', 139 | viewportMargin : Infinity, 140 | gutters : [GUTTER_ID,'CodeMirror-linenumbers'], 141 | extraKeys: { 142 | 'Ctrl-Enter': cm => runCode(), 143 | 'Ctrl-B': cm => transformCode(), 144 | 'Ctrl-S': cm => { 145 | const code = cm.getValue(); 146 | saveSnippet(code); 147 | } 148 | } 149 | }; 150 | 151 | let code = this.props.code; 152 | if (typeof this.props.code !== 'string') { 153 | console.warn(`Expected code property to be of type 'string' and not '${typeof this.props.code}'`); 154 | code = ''; 155 | } 156 | 157 | return ( 158 | 168 | ); 169 | } 170 | } 171 | 172 | function mapStateToProps(state, ownProps) { 173 | const editorName = ownProps.name; 174 | const editors = state.ide.editors; 175 | 176 | if (! Object.prototype.hasOwnProperty.call(editors, editorName)) { 177 | throw new Error(`Unexpected editor name ${editorName}`); 178 | } 179 | 180 | return { 181 | ...editors[editorName], 182 | options: state.ide.editorConfig, 183 | }; 184 | } 185 | 186 | const Editor = connect(mapStateToProps, actionCreators)(_Editor); 187 | 188 | export default Editor; -------------------------------------------------------------------------------- /src/components/Console/Console.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Meslo'; 3 | src: url('styles/meslo/MesloLGSDZ-Regular.woff') format('woff'); 4 | } 5 | @font-face { 6 | font-family: 'Meslo'; 7 | src: url('styles/meslo/MesloLGSDZ-Bold.woff') format('woff'); 8 | font-weight: bold; 9 | } 10 | @font-face { 11 | font-family: 'Meslo'; 12 | src: url('styles/meslo/MesloLGSDZ-BoldItalic.woff') format('woff'); 13 | font-weight: bold; 14 | font-style: italic; 15 | } 16 | @font-face { 17 | font-family: 'Meslo'; 18 | src: url('styles/meslo/MesloLGSDZ-Italic.woff') format('woff'); 19 | font-style: italic; 20 | } 21 | 22 | .jsconsole .CodeMirror { 23 | line-height: 1.2rem !important; 24 | } 25 | 26 | .jsconsole-output:not(.hidden) ~ .jsconsole-input { 27 | margin-top: -3px; 28 | 29 | .CodeMirror-scroll { 30 | height: auto; 31 | overflow-x: hidden; 32 | overflow-y: auto; 33 | } 34 | } 35 | 36 | .jsconsole-output.hidden { 37 | display: none; 38 | } 39 | 40 | .jsconsole-output .CodeMirror-code pre { 41 | border-bottom: 1px solid rgb(230, 230, 230); 42 | } 43 | 44 | .jsconsole-output .CodeMirror-gutter-elt { 45 | border-bottom: 1px solid rgb(230, 230, 230); 46 | top: -1px; 47 | } 48 | 49 | .jsconsole .CodeMirror-gutter-elt { 50 | width: 20px; 51 | height: 22px; 52 | } 53 | 54 | .jsconsole-input .CodeMirror-gutter-elt { 55 | top: -3px; 56 | } 57 | 58 | .jsconsole .CodeMirror-gutter-elt .gutter-icon:before { 59 | display: block; 60 | content: ""; 61 | 62 | width: 8px; 63 | height: 10px; 64 | margin: 7px auto; 65 | 66 | -webkit-user-select: none; 67 | 68 | background-image: url(styles/gutter-icons.png); 69 | background-size: 46px 10px; 70 | background-position: 0 0; 71 | } 72 | 73 | .jsconsole .CodeMirror-gutter-elt .prompt:before { 74 | left: 5px; 75 | background-position: -10px 0px; 76 | } 77 | 78 | .jsconsole .CodeMirror-gutter-elt .completion-value:before { 79 | background-position: -18px 0px; 80 | } 81 | 82 | .jsconsole .CodeMirror-gutter-elt .prev-command:before { 83 | background-position: -73px 0px; 84 | } 85 | 86 | .jsconsole .CodeMirror-gutter-elt .console-error:before { 87 | top: 3px; 88 | left: 2px; 89 | background-position: -81px 0px; 90 | width: 12px; 91 | } 92 | 93 | .jsconsole .CodeMirror-gutter-elt .prompt-continuation:before { 94 | content: '..'; 95 | background: none; 96 | left: 4px; 97 | top: 0px; 98 | } 99 | 100 | .jsconsole .CodeMirror-code pre { 101 | padding: 3px 4px 2px 4px; 102 | } 103 | 104 | .jsconsole .CodeMirror-gutter.repl { 105 | min-width: 20px; 106 | } 107 | 108 | .jsconsole .CodeMirror { 109 | line-height: 1em; 110 | font-family: Meslo, monospace; 111 | font-size: 11px; 112 | } 113 | 114 | /* don't know what's causing this to be necessary, there's a 4px offset to the selection*/ 115 | .jsconsole .CodeMirror-selected { 116 | background: Highlight !important; 117 | } 118 | 119 | .jsconsole .CodeMirror-gutters { 120 | border-right: 0; 121 | background-color: transparent; 122 | } 123 | 124 | .jsconsole-output .CodeMirror-cursor { 125 | visibility: hidden; 126 | } 127 | 128 | .jsconsole .CodeMirror { 129 | height: auto; 130 | } 131 | .jsconsole .CodeMirror-code .prev-command span { 132 | font-weight: normal; 133 | } 134 | 135 | /* eclipse theme */ 136 | 137 | .jsconsole.eclipse .CodeMirror-gutter-elt .prompt-continuation:before { 138 | color: #3d80f1; 139 | } 140 | .jsconsole.eclipse .jsconsole-output .CodeMirror-code .type-error span { 141 | color: red; 142 | } 143 | .jsconsole.eclipse .jsconsole-output .CodeMirror-code .type-undefined span { 144 | color: hsl(0, 0%, 58%); 145 | } 146 | 147 | .jsconsole.eclipse .CodeMirror-code .prev-command span { 148 | color: #0080ff; 149 | } 150 | 151 | .jsconsole.eclipse .CodeMirror .completion-value-end { 152 | border-bottom: 1px solid hsl(0, 0%, 94%); 153 | } 154 | 155 | /* base16-dark theme */ 156 | 157 | .jsconsole.base16-dark { 158 | background-color: #151515; 159 | } 160 | 161 | .jsconsole.base16-dark .CodeMirror-gutter-elt .prompt-continuation:before { 162 | color: #3d80f1; 163 | } 164 | .jsconsole.base16-dark .jsconsole-output .CodeMirror-code .type-error span { 165 | color: red; 166 | } 167 | .jsconsole.base16-dark .jsconsole-output .CodeMirror-code .type-undefined span { 168 | color: hsl(0, 0%, 58%); 169 | } 170 | 171 | .jsconsole.base16-dark .CodeMirror-code .prev-command span { 172 | color: hsl(210, 100%, 65%); 173 | } 174 | 175 | .jsconsole.base16-dark .CodeMirror .completion-value-end { 176 | border-bottom: 1px solid hsl(0, 0%, 20%); 177 | } 178 | 179 | /* dark theme */ 180 | 181 | .cm-s-dark.CodeMirror { 182 | background: #151515; 183 | color: #fff; 184 | } 185 | .cm-s-dark .CodeMirror-cursor { 186 | border-left: 1px solid #b0b0b0 !important; 187 | } 188 | 189 | .jsconsole.dark { 190 | background-color: #151515; 191 | } 192 | 193 | .jsconsole.dark .CodeMirror-gutter-elt .prompt-continuation:before { 194 | color: #3d80f1; 195 | } 196 | .jsconsole.dark .jsconsole-output .CodeMirror-code .type-error span { 197 | color: #c20000; 198 | } 199 | .jsconsole.dark .jsconsole-output .CodeMirror-code .type-undefined span { 200 | color: hsl(0, 0%, 83%); 201 | } 202 | 203 | .jsconsole.dark .CodeMirror-code .prev-command span { 204 | color: hsl(0, 0%, 83%); 205 | } 206 | 207 | .jsconsole.dark .CodeMirror .completion-value-end { 208 | border-bottom: 1px solid hsl(0, 0%, 20%); 209 | } 210 | -------------------------------------------------------------------------------- /config/project.config.js: -------------------------------------------------------------------------------- 1 | /* eslint key-spacing:0 spaced-comment:0 */ 2 | const path = require('path') 3 | const debug = require('debug')('app:config:project') 4 | const argv = require('yargs').argv 5 | const ip = require('ip') 6 | 7 | debug('Creating default configuration.') 8 | // ======================================================== 9 | // Default Configuration 10 | // ======================================================== 11 | const config = { 12 | env : process.env.NODE_ENV || 'development', 13 | 14 | // ---------------------------------- 15 | // Project Structure 16 | // ---------------------------------- 17 | path_base : path.resolve(__dirname, '..'), 18 | dir_client : 'src', 19 | dir_dist : 'dist', 20 | dir_public : 'public', 21 | dir_server : 'server', 22 | dir_test : 'tests', 23 | 24 | // ---------------------------------- 25 | // Server Configuration 26 | // ---------------------------------- 27 | server_host : 'localhost', // ip.address(), // use string 'localhost' to prevent exposure on local network 28 | server_port : process.env.PORT || 8000, 29 | 30 | // ---------------------------------- 31 | // API Server Configuration 32 | // ---------------------------------- 33 | api_server_host : `http://localhost:3000/`, // use string 'localhost' to prevent exposure on local network 34 | s3_server_host : 'http://staging.es6console.com/', 35 | 36 | // ---------------------------------- 37 | // AWS Configuration 38 | // ---------------------------------- 39 | bucket_name : 'staging.es6console.com', 40 | snippet_bucket_url: 'https://s3-eu-west-1.amazonaws.com/es6console-prod-snippets/', 41 | 42 | // ---------------------------------- 43 | // Compiler Configuration 44 | // ---------------------------------- 45 | compiler_babel : { 46 | cacheDirectory : true, 47 | plugins : ['transform-runtime'], 48 | presets : ['es2015', 'react', 'stage-0'] 49 | }, 50 | compiler_devtool : 'source-map', 51 | compiler_hash_type : 'hash', 52 | compiler_fail_on_warning : false, 53 | compiler_quiet : false, 54 | compiler_public_path : '/', 55 | compiler_stats : { 56 | chunks : false, 57 | chunkModules : false, 58 | colors : true 59 | }, 60 | compiler_vendors : [ 61 | 'react', 62 | 'react-redux', 63 | 'react-router', 64 | 'redux' 65 | ], 66 | 67 | // ---------------------------------- 68 | // Test Configuration 69 | // ---------------------------------- 70 | coverage_reporters : [ 71 | { type : 'text-summary' }, 72 | { type : 'lcov', dir : 'coverage' } 73 | ] 74 | } 75 | 76 | /************************************************ 77 | ------------------------------------------------- 78 | 79 | All Internal Configuration Below 80 | Edit at Your Own Risk 81 | 82 | ------------------------------------------------- 83 | ************************************************/ 84 | 85 | // ------------------------------------ 86 | // Validate Vendor Dependencies 87 | // ------------------------------------ 88 | const pkg = require('../package.json') 89 | 90 | config.compiler_vendors = config.compiler_vendors 91 | .filter((dep) => { 92 | if (pkg.dependencies[dep]) return true 93 | 94 | debug( 95 | `Package "${dep}" was not found as an npm dependency in package.json; ` + 96 | `it won't be included in the webpack vendor bundle. 97 | Consider removing it from \`compiler_vendors\` in ~/config/index.js` 98 | ) 99 | }) 100 | 101 | // ------------------------------------ 102 | // Utilities 103 | // ------------------------------------ 104 | function base () { 105 | const args = [config.path_base].concat([].slice.call(arguments)) 106 | return path.resolve.apply(path, args) 107 | } 108 | 109 | config.paths = { 110 | base : base, 111 | client : base.bind(null, config.dir_client), 112 | public : base.bind(null, config.dir_public), 113 | dist : base.bind(null, config.dir_dist) 114 | } 115 | 116 | // ======================================================== 117 | // Environment Configuration 118 | // ======================================================== 119 | debug(`Looking for environment overrides for NODE_ENV "${config.env}".`) 120 | const environments = require('./environments.config') 121 | let overrides = environments[config.env] 122 | 123 | if (config.env === 'dev') { 124 | debug('Found serverless environment dev which translates to staging AWS env'); 125 | overrides = environments['staging']; 126 | } 127 | 128 | if (overrides) { 129 | debug('Found overrides, applying to default configuration.') 130 | Object.assign(config, overrides(config)) 131 | } else { 132 | debug('No environment overrides found, defaults will be used.') 133 | } 134 | 135 | // ------------------------------------ 136 | // Environment 137 | // ------------------------------------ 138 | // N.B.: globals added here must _also_ be added to .eslintrc 139 | config.globals = { 140 | 'process.env' : { 141 | 'NODE_ENV' : JSON.stringify(config.env) 142 | }, 143 | 'NODE_ENV' : config.env, 144 | '__DEV__' : config.env === 'development', 145 | '__PROD__' : config.env === 'production', 146 | '__TEST__' : config.env === 'test', 147 | '__COVERAGE__' : !argv.watch && config.env === 'test', 148 | '__BASENAME__' : JSON.stringify(process.env.BASENAME || ''), 149 | 'API_SERVER_HOST' : JSON.stringify(config.api_server_host), 150 | 'S3_SERVER_HOST': JSON.stringify(config.s3_server_host), 151 | 'SNIPPET_BUCKET_URL': JSON.stringify(config.snippet_bucket_url), 152 | } 153 | 154 | module.exports = config 155 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6console", 3 | "version": "2.0.0", 4 | "description": "Try out ES6 JavaScript transpilers", 5 | "main": "src/main.js", 6 | "engines": { 7 | "node": ">=7.0.0", 8 | "npm": "^4.0.0" 9 | }, 10 | "scripts": { 11 | "clean": "rimraf dist", 12 | "compile": "better-npm-run compile", 13 | "lint": "eslint bin build config server src tests", 14 | "lint:fix": "npm run lint -- --fix", 15 | "start": "better-npm-run start", 16 | "dev": "better-npm-run dev", 17 | "debug": "better-npm-run debug", 18 | "test": "better-npm-run test", 19 | "test:dev": "npm run test -- --watch", 20 | "deploy": "better-npm-run deploy", 21 | "deploy:staging": "better-npm-run deploy:staging", 22 | "deploy:prod": "better-npm-run deploy:prod", 23 | "lambda_deploy:staging": "better-npm-run lambda_deploy:staging", 24 | "lambda_deploy:prod": "better-npm-run lambda_deploy:prod", 25 | "sync": "better-npm-run sync", 26 | "codecov": "cat coverage/*/lcov.info | codecov", 27 | "postinstall": "better-npm-run compile" 28 | }, 29 | "betterScripts": { 30 | "compile": { 31 | "command": "node bin/compile", 32 | "env": { 33 | "DEBUG": "app:*" 34 | } 35 | }, 36 | "dev": { 37 | "command": "nodemon bin/dev-server -w server -w config -w bin", 38 | "env": { 39 | "NODE_ENV": "development", 40 | "DEBUG": "app:*" 41 | } 42 | }, 43 | "debug": { 44 | "command": "node debug bin/dev-server.js", 45 | "env": { 46 | "NODE_ENV": "development", 47 | "DEBUG": "app:*" 48 | } 49 | }, 50 | "sync": { 51 | "command": "node bin/deploy.js", 52 | "env": { 53 | "DEBUG": "app:*" 54 | } 55 | }, 56 | "lambda_deploy:staging": { 57 | "command": "cd lambda/storage; sls deploy -s dev", 58 | "env": { 59 | "DEBUG": "app:*" 60 | } 61 | }, 62 | "lambda_deploy:prod": { 63 | "command": "cd lambda/storage; sls deploy -s prod", 64 | "env": { 65 | "DEBUG": "app:*" 66 | } 67 | }, 68 | "deploy": { 69 | "command": "npm run clean && npm run compile && npm run sync", 70 | "env": { 71 | "DEBUG": "app:*" 72 | } 73 | }, 74 | "deploy:staging": { 75 | "command": "npm run deploy", 76 | "env": { 77 | "NODE_ENV": "staging", 78 | "DEBUG": "app:*" 79 | } 80 | }, 81 | "deploy:prod": { 82 | "command": "npm run deploy", 83 | "env": { 84 | "NODE_ENV": "production", 85 | "DEBUG": "app:*" 86 | } 87 | }, 88 | "start": { 89 | "command": "node bin/dev-server", 90 | "env": { 91 | "DEBUG": "app:*" 92 | } 93 | }, 94 | "test": { 95 | "command": "node ./node_modules/karma/bin/karma start config/karma.config", 96 | "env": { 97 | "NODE_ENV": "test", 98 | "DEBUG": "app:*" 99 | } 100 | } 101 | }, 102 | "repository": { 103 | "type": "git", 104 | "url": "git+https://github.com/davezuko/react-redux-starter-kit.git" 105 | }, 106 | "author": "Matthisk Heimensen (http://matthisk.nl)", 107 | "license": "MIT", 108 | "dependencies": { 109 | "babel-core": "^6.17.0", 110 | "babel-eslint": "^7.1.0", 111 | "babel-loader": "^6.2.5", 112 | "babel-plugin-istanbul": "^3.0.0", 113 | "babel-plugin-transform-runtime": "^6.15.0", 114 | "babel-polyfill": "~6.22.0", 115 | "babel-preset-es2015": "^6.14.0", 116 | "babel-preset-react": "^6.11.1", 117 | "babel-preset-stage-0": "^6.3.13", 118 | "babel-runtime": "^6.11.6", 119 | "better-npm-run": "0.0.13", 120 | "body-parser": "~1.16.1", 121 | "classnames": "~2.2.5", 122 | "codemirror": "~5.27.0", 123 | "compression": "^1.6.2", 124 | "css-loader": "^0.26.0", 125 | "cssnano": "^3.7.4", 126 | "debug": "^2.2.0", 127 | "express": "^4.14.0", 128 | "extract-text-webpack-plugin": "^1.0.0", 129 | "file-loader": "^0.9.0", 130 | "fs-extra": "^1.0.0", 131 | "html-webpack-plugin": "^2.22.0", 132 | "imports-loader": "^0.7.0", 133 | "ip": "^1.1.2", 134 | "json-loader": "^0.5.4", 135 | "node-sass": "^4.0.0", 136 | "normalize.css": "^5.0.0", 137 | "pg": "~6.1.2", 138 | "postcss-loader": "^1.1.0", 139 | "prepack": "~0.2.0", 140 | "raven-js": "~3.15.0", 141 | "raw-loader": "~0.5.1", 142 | "react": "^15.0.0", 143 | "react-codemirror": "~0.3.0", 144 | "react-dom": "^15.0.0", 145 | "react-redux": "^5.0.1", 146 | "react-router": "^3.0.0", 147 | "react-select": "~1.0.0-rc.3", 148 | "redux": "^3.6.0", 149 | "redux-api-middleware": "~1.0.3", 150 | "redux-logger": "~2.8.1", 151 | "redux-thunk": "^2.0.0", 152 | "regenerator": "~0.8.26", 153 | "sass-loader": "^4.0.0", 154 | "scriptjs": "~2.5.8", 155 | "semantic-ui-react": "~0.64.7", 156 | "style-loader": "^0.13.1", 157 | "typescript": "~1.5.0", 158 | "url-loader": "^0.5.6", 159 | "webpack": "^1.12.14", 160 | "webpack-dev-middleware": "^1.6.1", 161 | "webpack-hot-middleware": "^2.12.2", 162 | "yargs": "^6.3.0" 163 | }, 164 | "devDependencies": { 165 | "chai": "^3.4.1", 166 | "chai-as-promised": "^6.0.0", 167 | "chai-enzyme": "^0.6.1", 168 | "cheerio": "^0.22.0", 169 | "codecov": "^1.0.1", 170 | "enzyme": "^2.0.0", 171 | "eslint": "^3.0.1", 172 | "eslint-config-standard": "^6.0.0", 173 | "eslint-config-standard-react": "^4.0.0", 174 | "eslint-plugin-babel": "^4.0.0", 175 | "eslint-plugin-promise": "^3.0.0", 176 | "eslint-plugin-react": "^6.10.3", 177 | "eslint-plugin-standard": "^2.0.0", 178 | "karma": "^1.0.0", 179 | "karma-coverage": "^1.0.0", 180 | "karma-mocha": "^1.0.1", 181 | "karma-mocha-reporter": "^2.0.0", 182 | "karma-phantomjs-launcher": "^1.0.2", 183 | "karma-webpack-with-fast-source-maps": "^1.9.2", 184 | "klaw": "~1.3.1", 185 | "mime-types": "~2.1.15", 186 | "mocha": "^3.0.1", 187 | "nodemon": "~1.11.0", 188 | "phantomjs-prebuilt": "^2.1.12", 189 | "react-addons-test-utils": "^15.0.0", 190 | "redbox-react": "^1.2.10", 191 | "rimraf": "^2.5.4", 192 | "sinon": "^1.17.5", 193 | "sinon-chai": "^2.8.0", 194 | "through2": "~2.0.3" 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/layouts/CoreLayout/CoreLayout.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { connect } from 'react-redux' 3 | import className from 'classnames' 4 | 5 | import * as actionCreators from 'store/ide' 6 | import * as exampleActionCreators from 'store/examples' 7 | 8 | import compilers from 'compilers' 9 | import Menu from 'components/Menu' 10 | import Header from 'components/Header' 11 | import Loading from 'components/Loading' 12 | import './CoreLayout.scss' 13 | import 'styles/core.scss' 14 | 15 | import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon' 16 | 17 | 18 | export const _Examples = ({ 19 | examples, 20 | selected, 21 | showExample, 22 | }) => ( 23 |
    24 | { Object.keys(examples).map(group => 25 | 26 | { examples[group].map(example => 27 | 31 | { example } 32 | 33 | ) } 34 | 35 | )} 36 |
    37 | ) 38 | 39 | function mapStateToProps2(state) { 40 | return { 41 | examples: state.examples.available, 42 | selected: state.examples.selected, 43 | }; 44 | } 45 | 46 | const Examples = connect(mapStateToProps2, exampleActionCreators)(_Examples); 47 | 48 | export const _Toolbar = ({ 49 | selectedCompiler, 50 | compilers, 51 | editors, 52 | babelPresets, 53 | selectCompiler, 54 | transformCode, 55 | runCode, 56 | saveSnippet, 57 | toggleBabelPreset, 58 | }) => ( 59 |
    60 | 61 | 66 | save code 67 | 68 | runCode()}> 71 | run 72 | 73 | 76 | transform 77 | 78 | 79 | 80 | 81 | { Object.keys(compilers).map(key => ( 82 | 88 | { key } 89 | 90 | )) 91 | } 92 | 93 | 94 | { selectedCompiler == 'Babel (6)' ? 95 | 97 | { Object.keys(babelPresets).map(key => 98 | 106 | { key } 107 | 108 | )} 109 | : null 110 | } 111 |
    112 | ) 113 | 114 | 115 | function mapStateToProps(state, ownProps) { 116 | return state.ide; 117 | } 118 | 119 | const Toolbar = connect(mapStateToProps, actionCreators)(_Toolbar); 120 | 121 | 122 | class CoreLayout extends Component { 123 | state = { 124 | selected: 'toolbar', 125 | }; 126 | 127 | componentDidMount() { 128 | let { 129 | id = null, 130 | } = this.props.params; 131 | 132 | if (id) { 133 | console.log("Load snippet", id); 134 | this.props.loadSnippet(id); 135 | } 136 | } 137 | 138 | componentWillUpdate(nextProps, nextState) { 139 | if (this.props.params.id !== nextProps.params.id && nextProps.params.id) { 140 | this.props.loadSnippet(nextProps.params.id); 141 | } 142 | } 143 | 144 | render() { 145 | let { 146 | children, 147 | loading, 148 | } = this.props; 149 | 150 | return ( 151 | // Start JSX 152 |
    153 | 0} /> 154 | 155 |
    156 | 198 |
    199 | { children } 200 |
    201 |
    202 | ); 203 | } 204 | } 205 | 206 | CoreLayout.propTypes = { 207 | children : React.PropTypes.element.isRequired 208 | } 209 | 210 | export default connect(state => ({ loading: state.loading }), actionCreators)(CoreLayout) 211 | -------------------------------------------------------------------------------- /config/webpack.config.js: -------------------------------------------------------------------------------- 1 | const argv = require('yargs').argv 2 | const webpack = require('webpack') 3 | const cssnano = require('cssnano') 4 | const HtmlWebpackPlugin = require('html-webpack-plugin') 5 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 6 | const project = require('./project.config') 7 | const debug = require('debug')('app:config:webpack') 8 | 9 | const __DEV__ = project.globals.__DEV__ 10 | const __PROD__ = project.globals.__PROD__ 11 | const __TEST__ = project.globals.__TEST__ 12 | 13 | debug('Creating configuration.') 14 | const webpackConfig = { 15 | name : 'client', 16 | target : 'web', 17 | devtool : project.compiler_devtool, 18 | resolve : { 19 | root : project.paths.client(), 20 | extensions : ['', '.js', '.jsx', '.json'] 21 | }, 22 | node: { 23 | fs: "empty", 24 | }, 25 | module : {} 26 | } 27 | // ------------------------------------ 28 | // Entry Points 29 | // ------------------------------------ 30 | const APP_ENTRY = project.paths.client('main.js') 31 | 32 | webpackConfig.entry = { 33 | app : __DEV__ 34 | ? [APP_ENTRY].concat(`webpack-hot-middleware/client?path=${project.compiler_public_path}__webpack_hmr`) 35 | : [APP_ENTRY], 36 | vendor : project.compiler_vendors 37 | } 38 | 39 | // ------------------------------------ 40 | // Bundle Output 41 | // ------------------------------------ 42 | webpackConfig.output = { 43 | filename : `[name].[${project.compiler_hash_type}].js`, 44 | path : project.paths.dist(), 45 | publicPath : project.compiler_public_path 46 | } 47 | 48 | // ------------------------------------ 49 | // Externals 50 | // ------------------------------------ 51 | webpackConfig.externals = {} 52 | webpackConfig.externals['react/lib/ExecutionEnvironment'] = true 53 | webpackConfig.externals['react/lib/ReactContext'] = true 54 | webpackConfig.externals['react/addons'] = true 55 | 56 | // ------------------------------------ 57 | // Plugins 58 | // ------------------------------------ 59 | webpackConfig.plugins = [ 60 | new webpack.DefinePlugin(project.globals), 61 | new HtmlWebpackPlugin({ 62 | template : project.paths.client('index.html'), 63 | hash : false, 64 | favicon : project.paths.public('favicon.png'), 65 | filename : 'index.html', 66 | inject : 'body', 67 | minify : { 68 | collapseWhitespace : true 69 | } 70 | }) 71 | ] 72 | 73 | // Ensure that the compiler exits on errors during testing so that 74 | // they do not get skipped and misreported. 75 | if (__TEST__ && !argv.watch) { 76 | webpackConfig.plugins.push(function () { 77 | this.plugin('done', function (stats) { 78 | if (stats.compilation.errors.length) { 79 | // Pretend no assets were generated. This prevents the tests 80 | // from running making it clear that there were warnings. 81 | throw new Error( 82 | stats.compilation.errors.map(err => err.message || err) 83 | ) 84 | } 85 | }) 86 | }) 87 | } 88 | 89 | if (__DEV__) { 90 | debug('Enabling plugins for live development (HMR, NoErrors).') 91 | webpackConfig.plugins.push( 92 | new webpack.HotModuleReplacementPlugin(), 93 | new webpack.NoErrorsPlugin() 94 | ) 95 | } else if (__PROD__) { 96 | debug('Enabling plugins for production (OccurenceOrder, Dedupe & UglifyJS).') 97 | webpackConfig.plugins.push( 98 | new webpack.optimize.OccurrenceOrderPlugin(), 99 | new webpack.optimize.DedupePlugin(), 100 | new webpack.optimize.UglifyJsPlugin({ 101 | compress : { 102 | unused : true, 103 | dead_code : true, 104 | warnings : false 105 | } 106 | }), 107 | new webpack.optimize.AggressiveMergingPlugin() 108 | ) 109 | } 110 | 111 | // Don't split bundles during testing, since we only want import one bundle 112 | if (!__TEST__) { 113 | webpackConfig.plugins.push( 114 | new webpack.optimize.CommonsChunkPlugin({ 115 | names : ['vendor'] 116 | }) 117 | ) 118 | } 119 | 120 | // ------------------------------------ 121 | // Loaders 122 | // ------------------------------------ 123 | // JavaScript / JSON 124 | webpackConfig.module.loaders = [{ 125 | test : /\.(js|jsx)$/, 126 | exclude : /node_modules/, 127 | loader : 'babel', 128 | query : project.compiler_babel 129 | }, { 130 | test : /\.json$/, 131 | loader : 'json' 132 | }] 133 | 134 | // ------------------------------------ 135 | // Style Loaders 136 | // ------------------------------------ 137 | // We use cssnano with the postcss loader, so we tell 138 | // css-loader not to duplicate minimization. 139 | const BASE_CSS_LOADER = 'css?sourceMap&-minimize' 140 | 141 | webpackConfig.module.loaders.push({ 142 | test : /\.scss$/, 143 | exclude : null, 144 | loaders : [ 145 | 'style', 146 | BASE_CSS_LOADER, 147 | 'postcss', 148 | 'sass?sourceMap' 149 | ] 150 | }) 151 | webpackConfig.module.loaders.push({ 152 | test : /\.css$/, 153 | exclude : null, 154 | loaders : [ 155 | 'style', 156 | BASE_CSS_LOADER, 157 | 'postcss' 158 | ] 159 | }) 160 | 161 | webpackConfig.sassLoader = { 162 | includePaths : project.paths.client('styles'), 163 | } 164 | 165 | webpackConfig.postcss = [ 166 | cssnano({ 167 | autoprefixer : { 168 | add : true, 169 | remove : true, 170 | browsers : ['last 2 versions'] 171 | }, 172 | discardComments : { 173 | removeAll : true 174 | }, 175 | discardUnused : false, 176 | mergeIdents : false, 177 | reduceIdents : false, 178 | safe : true, 179 | sourcemap : true 180 | }) 181 | ] 182 | 183 | // File loaders 184 | /* eslint-disable */ 185 | webpackConfig.module.loaders.push( 186 | { test: /\.woff(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff' }, 187 | { test: /\.woff2(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2' }, 188 | { test: /\.otf(\?.*)?$/, loader: 'file?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype' }, 189 | { test: /\.ttf(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream' }, 190 | { test: /\.eot(\?.*)?$/, loader: 'file?prefix=fonts/&name=[path][name].[ext]' }, 191 | { test: /\.svg(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml' }, 192 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192' } 193 | ) 194 | /* eslint-enable */ 195 | 196 | // ------------------------------------ 197 | // Finalize Configuration 198 | // ------------------------------------ 199 | // when we don't know the public path (we know it only when HMR is enabled [in development]) we 200 | // need to use the extractTextPlugin to fix this issue: 201 | // http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809 202 | if (!__DEV__) { 203 | debug('Applying ExtractTextPlugin to CSS loaders.') 204 | webpackConfig.module.loaders.filter((loader) => 205 | loader.loaders && loader.loaders.find((name) => /css/.test(name.split('?')[0])) 206 | ).forEach((loader) => { 207 | const first = loader.loaders[0] 208 | const rest = loader.loaders.slice(1) 209 | loader.loader = ExtractTextPlugin.extract(first, rest.join('!')) 210 | delete loader.loaders 211 | }) 212 | 213 | webpackConfig.plugins.push( 214 | new ExtractTextPlugin('[name].[contenthash].css', { 215 | allChunks : true 216 | }) 217 | ) 218 | } 219 | 220 | module.exports = webpackConfig 221 | -------------------------------------------------------------------------------- /src/store/ide.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import fetch from 'isomorphic-fetch'; 4 | import { CALL_API } from 'redux-api-middleware'; 5 | 6 | import Compilers, { getCompiler, DEFAULT_COMPILER } from 'compilers'; 7 | import { sandbox } from 'sandbox'; 8 | 9 | import { locationChange } from 'store/location'; 10 | import { browserHistory } from 'react-router'; 11 | import { loadPersistedState } from 'store/middleware/localStorage'; 12 | 13 | import * as actionTypes from './actionTypes'; 14 | 15 | import { 16 | babelPresetsReducer, 17 | compilersReducer, 18 | consoleReducer, 19 | editorConfigReducer, 20 | editorsReducer, 21 | } from 'store/ducks'; 22 | 23 | // ------------------------------------ 24 | // Actions 25 | // ------------------------------------ 26 | export function runCode(code) { 27 | return { 28 | type: actionTypes.RUN_CODE, 29 | payload: code, 30 | }; 31 | } 32 | 33 | export function selectCompiler(key) { 34 | if (!Object.prototype.hasOwnProperty.call(Compilers, key)) { 35 | throw new Error(`Unexpected compiler ${key}`); 36 | } 37 | 38 | return dispatch => { 39 | dispatch({ 40 | type: actionTypes.SELECT_COMPILER, 41 | payload: key, 42 | }); 43 | 44 | Compilers[key].future.then(() => { 45 | dispatch({ 46 | type: actionTypes.LOADED_COMPILER, 47 | payload: key, 48 | }); 49 | }); 50 | 51 | Compilers[key].initCompiler(); 52 | }; 53 | } 54 | 55 | export function transformCode() { 56 | return { 57 | type: actionTypes.TRANSFORMED_CODE, 58 | }; 59 | } 60 | 61 | export function transformOnType() { 62 | return { 63 | type: actionTypes.TRANSFORM_ON_TYPE, 64 | }; 65 | } 66 | 67 | export function updateCode(editorName, code) { 68 | return { 69 | type: actionTypes.UPDATE_CODE, 70 | editor: editorName, 71 | payload: { 72 | code: code 73 | } 74 | }; 75 | } 76 | 77 | export function toggleEditorDisplay(editorName) { 78 | return { 79 | type: actionTypes.TOGGLE_EDITOR_DISPLAY, 80 | payload: editorName, 81 | }; 82 | } 83 | 84 | export function toggleConsoleDisplay() { 85 | return { 86 | type: actionTypes.TOGGLE_CONSOLE_DISPLAY, 87 | }; 88 | } 89 | 90 | export function flushBuffer() { 91 | return { 92 | type: actionTypes.FLUSH_BUFFER, 93 | }; 94 | } 95 | 96 | export function toggleBabelPreset(key) { 97 | return dispatch => { 98 | dispatch({ 99 | type: actionTypes.TOGGLE_COMPILER_PRESET, 100 | payload: key, 101 | }); 102 | 103 | dispatch(transformCode()); 104 | }; 105 | } 106 | 107 | export function updateEditorConfig(key, value) { 108 | if (key === 'theme') { 109 | var ss = document.createElement('link'); 110 | ss.type = 'text/css'; 111 | ss.rel = 'stylesheet'; 112 | ss.href = `${S3_SERVER_HOST}codemirror/theme/${value}.css`; 113 | document.getElementsByTagName('head')[0].appendChild(ss); 114 | } 115 | 116 | return { 117 | type: actionTypes.UPDATE_EDITOR_CONFIG, 118 | payload: { 119 | key, value 120 | } 121 | }; 122 | } 123 | 124 | export function loadSnippet(id) { 125 | return { 126 | [CALL_API]: { 127 | endpoint: `${SNIPPET_BUCKET_URL}${id}`, 128 | method: 'GET', 129 | types: [actionTypes.LOAD_REQUEST, 130 | { 131 | type: actionTypes.LOAD_SUCCESS, 132 | payload: (action, state, res) => { 133 | return res.text(); 134 | } 135 | }, 136 | actionTypes.LOAD_FAILURE], 137 | } 138 | }; 139 | } 140 | 141 | export function saveSnippet(code) { 142 | let body = JSON.stringify({ code: code }); 143 | 144 | return dispatch => { 145 | dispatch({ 146 | [CALL_API]: { 147 | endpoint: `${API_SERVER_HOST}snippet/save/`, 148 | method: 'POST', 149 | headers: { 150 | 'Content-Type': 'application/json', 151 | }, 152 | body: body, 153 | types: [actionTypes.SAVE_REQUEST, 154 | { 155 | type: actionTypes.SAVE_SUCCESS, 156 | payload: (action, state, res) => { 157 | return res.json() 158 | .then(res => { 159 | browserHistory.push(`/${res.id}/`); 160 | return res; 161 | }); 162 | } 163 | }, 164 | actionTypes.SAVE_FAILURE], 165 | } 166 | }); 167 | }; 168 | } 169 | 170 | // ------------------------------------ 171 | // Reducer 172 | // ------------------------------------ 173 | let initialCompiler = loadPersistedState('selectedCompiler') || DEFAULT_COMPILER; 174 | export function selectedCompilerReducer(state = initialCompiler, action) { 175 | switch (action.type) { 176 | case actionTypes.SELECT_COMPILER: 177 | return action.payload; 178 | default: 179 | return state; 180 | } 181 | } 182 | 183 | function transformReducer(state, action) { 184 | let compiler = getCompiler(state.selectedCompiler); 185 | 186 | if (!compiler) { 187 | throw new Error(`Unexpected compiler ${state.selectedCompiler}`); 188 | } 189 | 190 | let presets = Object.keys(state.babelPresets) 191 | .map(p => [p, state.babelPresets[p].checked]); 192 | let options = { 193 | presets: presets.filter(([p, c]) => c) 194 | .map(([p, c]) => p), 195 | }; 196 | 197 | let code = compiler.compile(state.editors['es6'].code, options); 198 | 199 | if (code.errors.length == 0) { 200 | sandbox.updateUserCode(code.code, true); 201 | } 202 | 203 | return code; 204 | } 205 | 206 | function transformCodeReducer(state, action) { 207 | let { errors = [], 208 | code = '' } = transformReducer(state, action); 209 | 210 | let { editors } = state; 211 | 212 | return { 213 | ...editors, 214 | 'es6': { 215 | ...editors['es6'], 216 | errors: errors, 217 | }, 218 | 'es5': { 219 | ...editors['es5'], 220 | code: code, 221 | display: true, 222 | }, 223 | }; 224 | } 225 | 226 | function transformOnTypeReducer(state, action) { 227 | let { 228 | errors = [], 229 | code = '' 230 | } = transformReducer(state, action); 231 | 232 | let { editors } = state; 233 | 234 | return { 235 | ...editors, 236 | 'es6': { 237 | ...editors['es6'], 238 | errors: errors, 239 | }, 240 | 'es5': { 241 | ...editors['es5'], 242 | code: code, 243 | }, 244 | }; 245 | } 246 | 247 | function runCodeReducer(state, action) { 248 | let out; 249 | let { console } = state; 250 | 251 | if (action.payload) { 252 | out = sandbox.runCode(action.payload); 253 | } else { 254 | out = sandbox.runCode(state.editors['es6'].code); 255 | } 256 | 257 | return { 258 | ...console, 259 | display: true, 260 | completionValue: out.completionValue, 261 | logBuffer: out.logBuffer, 262 | }; 263 | } 264 | 265 | const baseReducer = combineReducers({ 266 | editors: editorsReducer, 267 | babelPresets: babelPresetsReducer, 268 | console: consoleReducer, 269 | editorConfig: editorConfigReducer, 270 | compilers: compilersReducer, 271 | selectedCompiler: selectedCompilerReducer, 272 | }); 273 | 274 | export default function reducer(state, action) { 275 | state = baseReducer(state, action); 276 | 277 | switch (action.type) { 278 | case actionTypes.RUN_CODE: 279 | return { 280 | ...state, 281 | console: runCodeReducer(state, action) 282 | }; 283 | 284 | case actionTypes.TRANSFORM_ON_TYPE: 285 | return { 286 | ...state, 287 | editors: transformOnTypeReducer(state, action), 288 | }; 289 | 290 | case actionTypes.TRANSFORMED_CODE: 291 | return { 292 | ...state, 293 | editors: transformCodeReducer(state, action), 294 | }; 295 | 296 | default: 297 | return state; 298 | } 299 | } -------------------------------------------------------------------------------- /src/components/Loading/semantic.scss: -------------------------------------------------------------------------------- 1 | i/******************************* 2 | Loader 3 | *******************************/ 4 | 5 | /* Standard Size */ 6 | 7 | .ui.loader { 8 | display: none; 9 | position: absolute; 10 | top: 50%; 11 | left: 50%; 12 | margin: 0px; 13 | text-align: center; 14 | z-index: 1000; 15 | -webkit-transform: translateX(-50%) translateY(-50%); 16 | transform: translateX(-50%) translateY(-50%); 17 | } 18 | 19 | /* Static Shape */ 20 | 21 | .ui.loader:before { 22 | position: absolute; 23 | content: ''; 24 | top: 0%; 25 | left: 50%; 26 | width: 100%; 27 | height: 100%; 28 | border-radius: 500rem; 29 | border: 0.2em solid rgba(0, 0, 0, 0.1); 30 | } 31 | 32 | /* Active Shape */ 33 | 34 | .ui.loader:after { 35 | position: absolute; 36 | content: ''; 37 | top: 0%; 38 | left: 50%; 39 | width: 100%; 40 | height: 100%; 41 | -webkit-animation: loader 0.6s linear; 42 | animation: loader 0.6s linear; 43 | -webkit-animation-iteration-count: infinite; 44 | animation-iteration-count: infinite; 45 | border-radius: 500rem; 46 | border-color: #767676 transparent transparent; 47 | border-style: solid; 48 | border-width: 0.2em; 49 | box-shadow: 0px 0px 0px 1px transparent; 50 | } 51 | 52 | /* Active Animation */ 53 | 54 | @-webkit-keyframes loader { 55 | from { 56 | -webkit-transform: rotate(0deg); 57 | transform: rotate(0deg); 58 | } 59 | 60 | to { 61 | -webkit-transform: rotate(360deg); 62 | transform: rotate(360deg); 63 | } 64 | } 65 | 66 | @keyframes loader { 67 | from { 68 | -webkit-transform: rotate(0deg); 69 | transform: rotate(0deg); 70 | } 71 | 72 | to { 73 | -webkit-transform: rotate(360deg); 74 | transform: rotate(360deg); 75 | } 76 | } 77 | 78 | /* Sizes */ 79 | 80 | .ui.mini.loader:before, 81 | .ui.mini.loader:after { 82 | width: 1rem; 83 | height: 1rem; 84 | margin: 0em 0em 0em -0.5rem; 85 | } 86 | 87 | .ui.tiny.loader:before, 88 | .ui.tiny.loader:after { 89 | width: 1.14285714rem; 90 | height: 1.14285714rem; 91 | margin: 0em 0em 0em -0.57142857rem; 92 | } 93 | 94 | .ui.small.loader:before, 95 | .ui.small.loader:after { 96 | width: 1.71428571rem; 97 | height: 1.71428571rem; 98 | margin: 0em 0em 0em -0.85714286rem; 99 | } 100 | 101 | .ui.loader:before, 102 | .ui.loader:after { 103 | width: 2.28571429rem; 104 | height: 2.28571429rem; 105 | margin: 0em 0em 0em -1.14285714rem; 106 | } 107 | 108 | .ui.large.loader:before, 109 | .ui.large.loader:after { 110 | width: 3.42857143rem; 111 | height: 3.42857143rem; 112 | margin: 0em 0em 0em -1.71428571rem; 113 | } 114 | 115 | .ui.big.loader:before, 116 | .ui.big.loader:after { 117 | width: 3.71428571rem; 118 | height: 3.71428571rem; 119 | margin: 0em 0em 0em -1.85714286rem; 120 | } 121 | 122 | .ui.huge.loader:before, 123 | .ui.huge.loader:after { 124 | width: 4.14285714rem; 125 | height: 4.14285714rem; 126 | margin: 0em 0em 0em -2.07142857rem; 127 | } 128 | 129 | .ui.massive.loader:before, 130 | .ui.massive.loader:after { 131 | width: 4.57142857rem; 132 | height: 4.57142857rem; 133 | margin: 0em 0em 0em -2.28571429rem; 134 | } 135 | 136 | /*------------------- 137 | Coupling 138 | --------------------*/ 139 | 140 | /* Show inside active dimmer */ 141 | 142 | .ui.dimmer .loader { 143 | display: block; 144 | } 145 | 146 | /* Black Dimmer */ 147 | 148 | .ui.dimmer .ui.loader { 149 | color: rgba(255, 255, 255, 0.9); 150 | } 151 | 152 | .ui.dimmer .ui.loader:before { 153 | border-color: rgba(255, 255, 255, 0.15); 154 | } 155 | 156 | .ui.dimmer .ui.loader:after { 157 | border-color: #FFFFFF transparent transparent; 158 | } 159 | 160 | /* White Dimmer (Inverted) */ 161 | 162 | .ui.inverted.dimmer .ui.loader { 163 | color: rgba(0, 0, 0, 0.87); 164 | } 165 | 166 | .ui.inverted.dimmer .ui.loader:before { 167 | border-color: rgba(0, 0, 0, 0.1); 168 | } 169 | 170 | .ui.inverted.dimmer .ui.loader:after { 171 | border-color: #767676 transparent transparent; 172 | } 173 | 174 | /******************************* 175 | Types 176 | *******************************/ 177 | 178 | /*------------------- 179 | Text 180 | --------------------*/ 181 | 182 | .ui.text.loader { 183 | width: auto !important; 184 | height: auto !important; 185 | text-align: center; 186 | font-style: normal; 187 | } 188 | 189 | /******************************* 190 | States 191 | *******************************/ 192 | 193 | .ui.indeterminate.loader:after { 194 | -webkit-animation-direction: reverse; 195 | animation-direction: reverse; 196 | -webkit-animation-duration: 1.2s; 197 | animation-duration: 1.2s; 198 | } 199 | 200 | .ui.loader.active, 201 | .ui.loader.visible { 202 | display: block; 203 | } 204 | 205 | .ui.loader.disabled, 206 | .ui.loader.hidden { 207 | display: none; 208 | } 209 | 210 | /******************************* 211 | Variations 212 | *******************************/ 213 | 214 | /*------------------- 215 | Sizes 216 | --------------------*/ 217 | 218 | /* Loader */ 219 | 220 | .ui.inverted.dimmer .ui.mini.loader, 221 | .ui.mini.loader { 222 | width: 1rem; 223 | height: 1rem; 224 | font-size: 0.78571429em; 225 | } 226 | 227 | .ui.inverted.dimmer .ui.tiny.loader, 228 | .ui.tiny.loader { 229 | width: 1.14285714rem; 230 | height: 1.14285714rem; 231 | font-size: 0.85714286em; 232 | } 233 | 234 | .ui.inverted.dimmer .ui.small.loader, 235 | .ui.small.loader { 236 | width: 1.71428571rem; 237 | height: 1.71428571rem; 238 | font-size: 0.92857143em; 239 | } 240 | 241 | .ui.inverted.dimmer .ui.loader, 242 | .ui.loader { 243 | width: 2.28571429rem; 244 | height: 2.28571429rem; 245 | font-size: 1em; 246 | } 247 | 248 | .ui.inverted.dimmer .ui.large.loader, 249 | .ui.large.loader { 250 | width: 3.42857143rem; 251 | height: 3.42857143rem; 252 | font-size: 1.14285714em; 253 | } 254 | 255 | .ui.inverted.dimmer .ui.big.loader, 256 | .ui.big.loader { 257 | width: 3.71428571rem; 258 | height: 3.71428571rem; 259 | font-size: 1.28571429em; 260 | } 261 | 262 | .ui.inverted.dimmer .ui.huge.loader, 263 | .ui.huge.loader { 264 | width: 4.14285714rem; 265 | height: 4.14285714rem; 266 | font-size: 1.42857143em; 267 | } 268 | 269 | .ui.inverted.dimmer .ui.massive.loader, 270 | .ui.massive.loader { 271 | width: 4.57142857rem; 272 | height: 4.57142857rem; 273 | font-size: 1.71428571em; 274 | } 275 | 276 | /* Text Loader */ 277 | 278 | .ui.mini.text.loader { 279 | min-width: 1rem; 280 | padding-top: 1.78571429rem; 281 | } 282 | 283 | .ui.tiny.text.loader { 284 | min-width: 1.14285714rem; 285 | padding-top: 1.92857143rem; 286 | } 287 | 288 | .ui.small.text.loader { 289 | min-width: 1.71428571rem; 290 | padding-top: 2.5rem; 291 | } 292 | 293 | .ui.text.loader { 294 | min-width: 2.28571429rem; 295 | padding-top: 3.07142857rem; 296 | } 297 | 298 | .ui.large.text.loader { 299 | min-width: 3.42857143rem; 300 | padding-top: 4.21428571rem; 301 | } 302 | 303 | .ui.big.text.loader { 304 | min-width: 3.71428571rem; 305 | padding-top: 4.5rem; 306 | } 307 | 308 | .ui.huge.text.loader { 309 | min-width: 4.14285714rem; 310 | padding-top: 4.92857143rem; 311 | } 312 | 313 | .ui.massive.text.loader { 314 | min-width: 4.57142857rem; 315 | padding-top: 5.35714286rem; 316 | } 317 | 318 | /*------------------- 319 | Inverted 320 | --------------------*/ 321 | 322 | .ui.inverted.loader { 323 | color: rgba(255, 255, 255, 0.9); 324 | } 325 | 326 | .ui.inverted.loader:before { 327 | border-color: rgba(255, 255, 255, 0.15); 328 | } 329 | 330 | .ui.inverted.loader:after { 331 | border-top-color: #FFFFFF; 332 | } 333 | 334 | /*------------------- 335 | Inline 336 | --------------------*/ 337 | 338 | .ui.inline.loader { 339 | position: relative; 340 | vertical-align: middle; 341 | margin: 0em; 342 | left: 0em; 343 | top: 0em; 344 | -webkit-transform: none; 345 | transform: none; 346 | } 347 | 348 | .ui.inline.loader.active, 349 | .ui.inline.loader.visible { 350 | display: inline-block; 351 | } 352 | 353 | /* Centered Inline */ 354 | 355 | .ui.centered.inline.loader.active, 356 | .ui.centered.inline.loader.visible { 357 | display: block; 358 | margin-left: auto; 359 | margin-right: auto; 360 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ES6Console.com 2 | 3 | This is the main repository of es6console.com. A tool for playing around with ECMAScript 6 to 5 transformers. 4 | 5 | ## Table of Contents 6 | 1. [Features](#features) 7 | 1. [Requirements](#requirements) 8 | 1. [Application Structure](#application-structure) 9 | 1. [Development](#development) 10 | 1. [Developer Tools](#developer-tools) 11 | 1. [Routing](#routing) 12 | 1. [Testing](#testing) 13 | 1. [Deployment](#deployment) 14 | 1. [Build System](#build-system) 15 | 1. [Configuration](#configuration) 16 | 1. [Globals](#globals) 17 | 1. [Styles](#styles) 18 | 1. [Server](#server) 19 | 1. [Production Optimization](#production-optimization) 20 | 21 | ## Features 22 | * [serverless](http://serverless.com) 23 | * [react](https://github.com/facebook/react) 24 | * [redux](https://github.com/rackt/redux) 25 | * [react-router](https://github.com/rackt/react-router) 26 | * [webpack](https://github.com/webpack/webpack) 27 | * [babel](https://github.com/babel/babel) 28 | * [eslint](http://eslint.org) 29 | 30 | ## Requirements 31 | * node `^4.5.0` 32 | * yarn `^0.17.0` or npm `^3.0.0` 33 | 34 | ### Install from source 35 | 36 | First, clone the project: 37 | 38 | ```bash 39 | $ git clone https://github.com/matthisk/es6console.git 40 | $ cd 41 | ``` 42 | 43 | Then install dependencies and check to see it works. It is recommended that you use [Yarn](https://yarnpkg.com/) for deterministic installs, but `npm install` will work just as well. 44 | 45 | ```bash 46 | $ npm install # Install project dependencies 47 | $ npm run dev # Compile and launch (same as `npm start`) 48 | ``` 49 | 50 | While developing, you will probably rely mostly on `npm dev`; however, there are additional scripts at your disposal: 51 | 52 | |`npm run