├── .dockerignore ├── .gitignore ├── .prettierrc ├── Dockerfile ├── LICENSE ├── README.md ├── package.json ├── public ├── CNAME ├── favicon.ico ├── index.html └── manifest.json ├── src ├── App.js ├── App.module.css ├── CodeMirror.js ├── CodeMirrorPanel.js ├── CodeMirrorPanel.module.css ├── Header.js ├── Header.module.css ├── OptionsSection.js ├── OptionsSection.module.css ├── Repl.js ├── Repl.module.css ├── Repl.test.js ├── ReplOptions.js ├── ReplOptions.module.css ├── Svg.js ├── Svg.module.css ├── index.css ├── index.js ├── lib │ ├── helpers.js │ └── terser-options.js ├── paraiso-dark.css ├── paraiso-light.css └── serviceWorker.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "semi": true, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | 3 | WORKDIR /app 4 | 5 | EXPOSE 3000 35729 6 | 7 | COPY package.json /app 8 | COPY yarn.lock /app 9 | 10 | RUN yarn install 11 | 12 | COPY . /app 13 | 14 | CMD ["yarn", "start"] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Rogério Vicente 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # repl 2 | 3 | ## About 4 | 5 | This is the repo for terser's online repl available at [try.terser.org](https://try.terser.org). 6 | 7 | ### Screenshot 8 | 9 | ![Terser REPL screenshot](https://user-images.githubusercontent.com/38240/63511165-5afcae80-c4d8-11e9-9011-acfb7d58484d.png) 10 | 11 | ## Development 12 | 13 | In the project directory, you can run: 14 | 15 | ### Start 16 | `yarn start` 17 | 18 | Runs the app in the development mode. 19 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 20 | 21 | ### Test 22 | `yarn test` 23 | 24 | Runs tests with Jest. 25 | 26 | ### Build 27 | `npm run build` 28 | 29 | Builds the app for production to the `build` folder. 30 | 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "try.terser.org", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "classnames": "^2.3.1", 7 | "codemirror": "^5.65.2", 8 | "lodash-es": "^4.17.21", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "5.0.0" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject", 18 | "predeploy": "yarn build", 19 | "deploy": "gh-pages -d build" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | }, 36 | "devDependencies": { 37 | "gh-pages": "^2.1.1" 38 | }, 39 | "homepage": "https://try.terser.org" 40 | } 41 | -------------------------------------------------------------------------------- /public/CNAME: -------------------------------------------------------------------------------- 1 | try.terser.org 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terser/repl/0d37a136073d1bae81dbb79443d61caf2301eeeb/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 25 | Terser REPL 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Terser REPL", 3 | "name": "Terser Online REPL", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Repl from './Repl' 4 | import Header from './Header'; 5 | 6 | import styles from './App.module.css' 7 | 8 | export default class App extends React.Component { 9 | state = { 10 | error: null, 11 | } 12 | componentDidMount() { 13 | if (!window.Terser) { 14 | this.setState({ 15 | error: new Error('Could not load Terser from jsdelivr') 16 | }) 17 | } 18 | } 19 | render() { 20 | const { error } = this.state 21 | 22 | const body = (() => { 23 | if (error) { 24 | return ( 25 |
26 | {error?.message || 'An error has occurred'} 27 |
28 | ) 29 | } else { 30 | return 31 | } 32 | })() 33 | 34 | return ( 35 |
36 |
37 | {body} 38 |
39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/App.module.css: -------------------------------------------------------------------------------- 1 | .message { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | flex: 1 0 auto; 6 | } 7 | 8 | .error { 9 | color: lightred; 10 | font-weight: bold; 11 | } 12 | 13 | .container { 14 | min-height: 100vh; 15 | display: flex; 16 | flex-direction: column; 17 | } 18 | -------------------------------------------------------------------------------- /src/CodeMirror.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import CodeMirror from 'codemirror'; 4 | 5 | require('codemirror/lib/codemirror.css'); 6 | require('./paraiso-dark.css'); 7 | require('./paraiso-light.css'); 8 | require('codemirror/mode/xml/xml'); 9 | require('codemirror/mode/javascript/javascript'); 10 | require('codemirror/keymap/sublime'); 11 | 12 | const DEFAULT_CODE_MIRROR_OPTIONS = { 13 | autoCloseBrackets: true, 14 | keyMap: 'sublime', 15 | lineNumbers: true, 16 | matchBrackets: true, 17 | mode: 'javascript', 18 | showCursorWhenSelecting: true, 19 | styleActiveLine: true, 20 | tabWidth: 2 21 | }; 22 | 23 | class CodeMirrorReact extends Component { 24 | static propTypes = { 25 | autoFocus: PropTypes.bool, 26 | preserveScrollPosition: PropTypes.bool, 27 | options: PropTypes.object, 28 | placeholder: PropTypes.string, 29 | value: PropTypes.string, 30 | theme: PropTypes.string 31 | }; 32 | 33 | static defaultProps = { 34 | autoFocus: false, 35 | preserveScrollPosition: false, 36 | onChange: null, 37 | options: {}, 38 | placeholder: null, 39 | value: null, 40 | theme: 'default' 41 | }; 42 | 43 | state = { 44 | isFocused: false 45 | }; 46 | 47 | _codeMirror = null; 48 | _textAreaRef = null; 49 | 50 | componentDidMount() { 51 | this._codeMirror = CodeMirror.fromTextArea(this._textAreaRef, { 52 | ...DEFAULT_CODE_MIRROR_OPTIONS, 53 | ...this.props.options 54 | }); 55 | 56 | this._codeMirror.setOption('theme', this.props.theme) 57 | this._codeMirror.on('change', this._onChange); 58 | this._codeMirror.setValue(this.props.value || ''); 59 | } 60 | 61 | // TODO: refactor this with the new lyfecycle methods 62 | UNSAFE_componentWillMount() { 63 | if (this._codeMirror) { 64 | this._codeMirror.toTextArea(); 65 | } 66 | } 67 | 68 | // TODO: refactor this with the new lyfecycle methods 69 | UNSAFE_componentWillReceiveProps(nextProps) { 70 | if ( 71 | nextProps.value && 72 | nextProps.value !== this.props.value && 73 | this._codeMirror.getValue() !== nextProps.value 74 | ) { 75 | if (nextProps.preserveScrollPosition) { 76 | const prevScrollPosition = this._codeMirror.getScrollInfo(); 77 | 78 | this._codeMirror.setValue(nextProps.value); 79 | this._codeMirror.scrollTo( 80 | prevScrollPosition.left, 81 | prevScrollPosition.top 82 | ); 83 | } else { 84 | this._codeMirror.setValue(nextProps.value); 85 | } 86 | } else if (!nextProps.value) { 87 | this._codeMirror.setValue(''); 88 | } 89 | 90 | for (const optionName in nextProps.options) { 91 | if (nextProps.options.hasOwnProperty(optionName)) { 92 | this._updateOption(optionName, nextProps.options[optionName]); 93 | } 94 | } 95 | } 96 | 97 | focus() { 98 | this._codeMirror && this._codeMirror.focus(); 99 | } 100 | 101 | render() { 102 | return ( 103 |