├── CNAME ├── .gitignore ├── website ├── src │ ├── parsers │ │ ├── regexp │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ ├── transformers │ │ │ │ └── regexp-tree │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ └── regexp-tree.js │ │ ├── scala │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ └── scalameta.js │ │ ├── json │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ └── json-to-ast.js │ │ ├── handlebars │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ ├── transformers │ │ │ │ ├── glimmer │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ └── glimmer-compiler │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ ├── handlebars.js │ │ │ ├── glimmer.js │ │ │ └── utils │ │ │ │ └── defaultHandlebarsParserInterface.js │ │ ├── icu │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ └── intl-messageformat-parser.js │ │ ├── html │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ ├── parse5.js │ │ │ └── htmlparser2.js │ │ ├── sql │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ └── sqlite-parser.js │ │ ├── lua │ │ │ ├── index.js │ │ │ ├── codeExample.txt │ │ │ └── luaparse.js │ │ ├── css │ │ │ ├── index.js │ │ │ ├── codeExample.txt │ │ │ ├── utils │ │ │ │ └── defaultCSSParserInterface.js │ │ │ ├── transformers │ │ │ │ └── postcss │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ ├── rework.js │ │ │ ├── csstree.js │ │ │ ├── cssom.js │ │ │ └── postcss.js │ │ ├── markdown │ │ │ ├── codeExample.txt │ │ │ ├── index.js │ │ │ └── remark.js │ │ ├── php │ │ │ ├── index.js │ │ │ ├── codeExample.txt │ │ │ └── php-parser.js │ │ ├── js │ │ │ ├── transformers │ │ │ │ ├── eslint1 │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ ├── loadRulesShim.js │ │ │ │ │ └── index.js │ │ │ │ ├── eslint2 │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ ├── eslint3 │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ ├── eslint4 │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ ├── babel │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ ├── prettier │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ ├── babel6 │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ ├── babel7 │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ │ └── jscodeshift │ │ │ │ │ ├── codeExample.txt │ │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ ├── utils │ │ │ │ ├── defaultESTreeParserInterface.js │ │ │ │ ├── eslint4Utils.js │ │ │ │ └── eslintUtils.js │ │ │ ├── codeExample.txt │ │ │ ├── babel-eslint.js │ │ │ ├── esformatter.js │ │ │ ├── flow.js │ │ │ ├── acorn-to-esprima.js │ │ │ ├── typescript-eslint-parser.js │ │ │ ├── esprima.js │ │ │ ├── uglify.js │ │ │ ├── shift.js │ │ │ ├── espree.js │ │ │ ├── acorn.js │ │ │ ├── babylon6.js │ │ │ ├── babylon7.js │ │ │ ├── babylon.js │ │ │ └── recast.js │ │ ├── yaml │ │ │ ├── index.js │ │ │ └── yaml-ast-parser.js │ │ ├── graphql │ │ │ ├── index.js │ │ │ ├── codeExample.txt │ │ │ └── graphql-js.js │ │ ├── webidl │ │ │ ├── index.js │ │ │ ├── webidl2.js │ │ │ └── codeExample.txt │ │ ├── glsl │ │ │ ├── index.js │ │ │ ├── codeExample.txt │ │ │ └── glsl-parser.js │ │ ├── utils │ │ │ ├── compileModule.js │ │ │ ├── transformJSCode.js │ │ │ ├── defaultParserInterface.js │ │ │ └── SettingsRenderer.js │ │ └── index.js │ ├── shims │ │ └── jest-validate.js │ ├── components │ │ ├── visualization │ │ │ ├── index.js │ │ │ ├── JSON.js │ │ │ ├── css │ │ │ │ └── tree.css │ │ │ ├── tree │ │ │ │ ├── CompactObjectView.js │ │ │ │ ├── CompactArrayView.js │ │ │ │ └── RecursiveTreeElement.js │ │ │ └── Tree.js │ │ ├── LoadingIndicator.js │ │ ├── buttons │ │ │ ├── NewButton.js │ │ │ ├── ShareButton.js │ │ │ ├── SaveButton.js │ │ │ ├── PrettierButton.js │ │ │ ├── ForkButton.js │ │ │ ├── ParserButton.js │ │ │ ├── SnippetButton.js │ │ │ ├── CategoryButton.js │ │ │ └── TransformButton.js │ │ ├── LocalStorage.js │ │ ├── ErrorMessage.js │ │ ├── dialogs │ │ │ ├── ShareDialog.js │ │ │ └── SettingsDialog.js │ │ ├── getFocusPath.js │ │ ├── Transformer.js │ │ ├── GistBanner.js │ │ ├── JSONEditor.js │ │ ├── Toolbar.js │ │ └── JSCodeshiftEditor.js │ ├── storage │ │ ├── api.js │ │ └── index.js │ ├── utils │ │ ├── logger.js │ │ ├── debounce.js │ │ ├── stringify.js │ │ └── url.js │ ├── containers │ │ ├── LoadingIndicatorContainer.js │ │ ├── PasteDropTargetContainer.js │ │ ├── ErrorMessageContainer.js │ │ ├── ShareDialogContainer.js │ │ ├── ASTOutputContainer.js │ │ ├── CodeEditorContainer.js │ │ ├── SettingsDialogContainer.js │ │ ├── TransformerContainer.js │ │ └── ToolbarContainer.js │ └── store │ │ ├── selectors.js │ │ └── actions.js ├── packages │ ├── babel5 │ │ ├── index.js │ │ ├── babel5-package.js │ │ └── package.json │ ├── babel6 │ │ ├── index.js │ │ ├── babel6-package.js │ │ └── package.json │ ├── babel7 │ │ ├── index.js │ │ ├── babel7-package.js │ │ └── package.json │ ├── babylon5 │ │ ├── index.js │ │ ├── babylon-package.js │ │ └── package.json │ ├── babylon6 │ │ ├── index.js │ │ ├── babylon-package.js │ │ └── package.json │ ├── babylon7 │ │ ├── index.js │ │ ├── babylon-package.js │ │ └── package.json │ ├── eslint1 │ │ ├── package.js │ │ ├── package.json │ │ └── index.js │ ├── eslint3 │ │ ├── eslint3-package.js │ │ ├── package.json │ │ └── index.js │ ├── eslint4 │ │ ├── eslint4-package.js │ │ ├── package.json │ │ └── index.js │ └── eslint2 │ │ ├── package.json │ │ └── index.js ├── favicon.png ├── postcss.config.js ├── fontcustom │ ├── fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.eot │ ├── fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.ttf │ ├── fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff │ ├── fontcustom_fa81bdf903b1ca9429bee04d81a635dd.eot │ ├── fontcustom_fa81bdf903b1ca9429bee04d81a635dd.ttf │ ├── fontcustom_fa81bdf903b1ca9429bee04d81a635dd.woff │ ├── fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff2 │ ├── README.md │ ├── input-svg │ │ ├── scala.svg │ │ ├── GraphQL_Logo.svg │ │ ├── icu.svg │ │ └── handlebars.svg │ ├── fontcustom.css │ └── config.yml ├── index.ejs └── .fontcustom-manifest.json ├── assets ├── ast.png └── source.png ├── .gitmodules ├── app.json ├── server ├── constants.js ├── package.json ├── handlers │ ├── gist │ │ ├── loadGist.js │ │ ├── index.js │ │ └── saveAnonymousGist.js │ └── parse.js └── index.js ├── scripts ├── deploy.sh ├── build-site.sh └── definition-generator.js ├── .travis.yml └── .eslintrc /CNAME: -------------------------------------------------------------------------------- 1 | astexplorer.net 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | node_modules 3 | .idea 4 | -------------------------------------------------------------------------------- /website/src/parsers/regexp/codeExample.txt: -------------------------------------------------------------------------------- 1 | /[a-z]/i -------------------------------------------------------------------------------- /website/packages/babel5/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babel-core'); 2 | -------------------------------------------------------------------------------- /website/packages/babel6/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babel-core'); 2 | -------------------------------------------------------------------------------- /website/packages/babel7/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babel-core'); 2 | -------------------------------------------------------------------------------- /website/packages/babylon5/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babylon'); 2 | -------------------------------------------------------------------------------- /website/packages/babylon6/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babylon'); 2 | -------------------------------------------------------------------------------- /website/packages/babylon7/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babylon'); 2 | -------------------------------------------------------------------------------- /website/packages/eslint1/package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('eslint/package.json'); 2 | -------------------------------------------------------------------------------- /website/src/shims/jest-validate.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | validate(){}, 3 | }; 4 | -------------------------------------------------------------------------------- /assets/ast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/assets/ast.png -------------------------------------------------------------------------------- /assets/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/assets/source.png -------------------------------------------------------------------------------- /website/packages/babel5/babel5-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babel-core/package.json'); 2 | -------------------------------------------------------------------------------- /website/packages/babel6/babel6-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babel-core/package.json'); 2 | -------------------------------------------------------------------------------- /website/packages/babel7/babel7-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babel-core/package.json'); 2 | -------------------------------------------------------------------------------- /website/packages/babylon5/babylon-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babylon/package.json'); 2 | -------------------------------------------------------------------------------- /website/packages/babylon6/babylon-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babylon/package.json'); 2 | -------------------------------------------------------------------------------- /website/packages/babylon7/babylon-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('babylon/package.json'); 2 | -------------------------------------------------------------------------------- /website/packages/eslint3/eslint3-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('eslint/package.json'); 2 | -------------------------------------------------------------------------------- /website/packages/eslint4/eslint4-package.js: -------------------------------------------------------------------------------- 1 | module.exports = require('eslint/package.json'); 2 | -------------------------------------------------------------------------------- /website/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/favicon.png -------------------------------------------------------------------------------- /website/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer') 4 | ] 5 | } -------------------------------------------------------------------------------- /website/src/parsers/scala/codeExample.txt: -------------------------------------------------------------------------------- 1 | object Main { 2 | def main(args: Array[String]): Unit = { 3 | println("Hello, World!") 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /website/src/parsers/json/codeExample.txt: -------------------------------------------------------------------------------- 1 | { 2 | "key1": [true, false, null], 3 | "key2": { 4 | "key3": [1, 2, "3", 1e10, 1e-3] 5 | } 6 | } -------------------------------------------------------------------------------- /website/src/parsers/handlebars/codeExample.txt: -------------------------------------------------------------------------------- 1 |
2 |

{{title}}

3 |
4 | {{body}} 5 |
6 |
7 | -------------------------------------------------------------------------------- /website/src/components/visualization/index.js: -------------------------------------------------------------------------------- 1 | import JSON from './JSON'; 2 | import Tree from './Tree'; 3 | 4 | export default [ 5 | Tree, 6 | JSON, 7 | ]; 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "packages/uglify2-harmony"] 2 | path = website/packages/uglify2-harmony 3 | url = https://github.com/mishoo/UglifyJS2.git 4 | branch = harmony 5 | -------------------------------------------------------------------------------- /website/src/parsers/icu/codeExample.txt: -------------------------------------------------------------------------------- 1 | On {takenDate, date, short} {name} took {numPhotos, plural, 2 | =0 {no photos.} 3 | =1 {one photo.} 4 | other {# photos.} 5 | } -------------------------------------------------------------------------------- /website/src/parsers/regexp/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'regexp'; 2 | export const displayName = 'RegExp'; 3 | export const mimeTypes = ['text/regexp']; 4 | export const fileExtension = 'regexp'; -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.eot -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.ttf -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_fa81bdf903b1ca9429bee04d81a635dd.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/fontcustom/fontcustom_fa81bdf903b1ca9429bee04d81a635dd.eot -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_fa81bdf903b1ca9429bee04d81a635dd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/fontcustom/fontcustom_fa81bdf903b1ca9429bee04d81a635dd.ttf -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_fa81bdf903b1ca9429bee04d81a635dd.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/fontcustom/fontcustom_fa81bdf903b1ca9429bee04d81a635dd.woff -------------------------------------------------------------------------------- /website/src/parsers/html/codeExample.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

My First Heading

6 |

My first paragraph.

7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephfrazier/astexplorer/master/website/fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff2 -------------------------------------------------------------------------------- /website/src/parsers/sql/codeExample.txt: -------------------------------------------------------------------------------- 1 | -- 2 | -- This is an example query 3 | -- 4 | SELECT 5 | foo, bar as baz 6 | FROM 7 | mytable 8 | WHERE 9 | foo LIKE '%neat%' 10 | ORDER BY 11 | foo DESC 12 | -------------------------------------------------------------------------------- /website/src/parsers/lua/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/lua/lua'; 2 | 3 | export const id = 'lua'; 4 | export const displayName = 'Lua'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'lua'; 7 | -------------------------------------------------------------------------------- /website/packages/babylon5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babylon5", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babylon": "^5.8.38" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/packages/babylon6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babylon6", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babylon": "^6.1.21" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/src/parsers/css/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/css/css'; 2 | 3 | export const id = 'css'; 4 | export const displayName = 'CSS'; 5 | export const mimeTypes = ['text/css']; 6 | export const fileExtension = 'css'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/markdown/codeExample.txt: -------------------------------------------------------------------------------- 1 | # Hello 2 | 3 | Some *emphasis*, **importance**, and `code`. 4 | 5 | --- 6 | 7 | ```javascript 8 | console.log('!'); 9 | ``` 10 | 11 | * foo 12 | * bar 13 | * baz 14 | -------------------------------------------------------------------------------- /website/src/parsers/sql/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/sql/sql'; 2 | 3 | export const id = 'sql'; 4 | export const displayName = 'SQL'; 5 | export const mimeTypes = ['text/x-sql']; 6 | export const fileExtension = 'sql'; 7 | -------------------------------------------------------------------------------- /website/packages/babylon7/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babylon7", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babylon": "7.0.0-beta.12" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/src/parsers/icu/index.js: -------------------------------------------------------------------------------- 1 | // import 'codemirror/mode/html/html'; 2 | 3 | export const id = 'icu'; 4 | export const displayName = 'ICU'; 5 | export const mimeTypes = ['text/plain']; 6 | export const fileExtension = 'txt'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/php/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/php/php'; 2 | 3 | export const id = 'php'; 4 | export const displayName = 'PHP'; 5 | export const mimeTypes = ['application/php']; 6 | export const fileExtension = 'php'; 7 | -------------------------------------------------------------------------------- /website/src/storage/api.js: -------------------------------------------------------------------------------- 1 | import 'isomorphic-fetch'; 2 | 3 | const API_HOST = process.env.API_HOST || ''; 4 | 5 | export default function api(path, options) { 6 | return fetch(`${API_HOST}/api/v1${path}`, options); 7 | } 8 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint1/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function(context) { 2 | return { 3 | TemplateLiteral(node) { 4 | context.report(node, 'Do not use template literals'); 5 | } 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint2/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function(context) { 2 | return { 3 | TemplateLiteral(node) { 4 | context.report(node, 'Do not use template literals'); 5 | } 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint3/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function(context) { 2 | return { 3 | TemplateLiteral(node) { 4 | context.report(node, 'Do not use template literals'); 5 | } 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint4/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function(context) { 2 | return { 3 | TemplateLiteral(node) { 4 | context.report(node, 'Do not use template literals'); 5 | } 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /website/src/parsers/scala/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/clike/clike'; 2 | 3 | export const id = 'text/x-scala'; 4 | export const displayName = 'Scala'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'scala'; 7 | -------------------------------------------------------------------------------- /website/packages/babel5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel5", 3 | "version": "1.0.0", 4 | "description": "Wrapper for Babel v5", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babel-core": "^5.8.38" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/packages/babel6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel6", 3 | "version": "1.0.0", 4 | "description": "Wrapper for Babel6", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babel-core": "^6.2.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/packages/eslint1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint1", 3 | "version": "1.0.0", 4 | "description": "Wrapper for ESLint 1", 5 | "main": "index.js", 6 | "dependencies": { 7 | "eslint": "^1.10.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/packages/eslint2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint2", 3 | "version": "1.0.0", 4 | "description": "Wrapper for ESLint 2", 5 | "main": "index.js", 6 | "dependencies": { 7 | "eslint": "^2.5.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/packages/eslint3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint3", 3 | "version": "1.0.0", 4 | "description": "Wrapper for ESLint 3", 5 | "main": "index.js", 6 | "dependencies": { 7 | "eslint": "^3.16.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/packages/eslint4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint4", 3 | "version": "1.0.0", 4 | "description": "Wrapper for ESLint 4", 5 | "main": "index.js", 6 | "dependencies": { 7 | "eslint": "^4.0.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/src/parsers/yaml/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/yaml/yaml'; 2 | 3 | export const id = 'yaml'; 4 | export const displayName = 'YAML'; 5 | export const mimeTypes = ['application/yaml']; 6 | export const fileExtension = 'yaml'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/html/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/htmlmixed/htmlmixed'; 2 | 3 | export const id = 'htmlmixed'; 4 | export const displayName = 'HTML'; 5 | export const mimeTypes = ['text/html']; 6 | export const fileExtension = 'html'; 7 | -------------------------------------------------------------------------------- /website/packages/babel7/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel7", 3 | "version": "1.0.0", 4 | "description": "Wrapper for Babel v7", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babel-core": "7.0.0-alpha.12" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/src/parsers/graphql/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror-graphql/mode'; 2 | 3 | export const id = 'graphql'; 4 | export const displayName = 'GraphQL'; 5 | export const mimeTypes = ['application/graphql']; 6 | export const fileExtension = 'graphql'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/json/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/javascript/javascript'; 2 | 3 | export const id = 'json'; 4 | export const displayName = 'JSON'; 5 | export const mimeTypes = ['text/javascript']; 6 | export const fileExtension = 'json'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/webidl/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/webidl/webidl'; 2 | 3 | export const id = 'webidl'; 4 | export const displayName = 'Web IDL'; 5 | export const mimeTypes = ['text/x-webidl']; 6 | export const fileExtension = 'webidl'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/js/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/javascript/javascript'; 2 | 3 | export const id = 'javascript'; 4 | export const displayName = 'JavaScript'; 5 | export const mimeTypes = ['text/javascript']; 6 | export const fileExtension = 'js'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/markdown/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/markdown/markdown'; 2 | 3 | export const id = 'markdown'; 4 | export const displayName = 'Markdown'; 5 | export const mimeTypes = ['text/markdown']; 6 | export const fileExtension = 'md'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/regexp/transformers/regexp-tree/codeExample.txt: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Char({node}) { 3 | // Replace 'a' chars with 'b'. 4 | if (node.kind === 'simple' && node.value === 'a') { 5 | node.value = 'b'; 6 | } 7 | } 8 | }; -------------------------------------------------------------------------------- /website/src/parsers/glsl/index.js: -------------------------------------------------------------------------------- 1 | import './codemirror-mode/glsl'; 2 | 3 | export const id = 'glsl'; 4 | export const displayName = 'GLSL'; 5 | export const mimeTypes = ['x-shader/x-vertex', 'x-shader/x-fragment']; 6 | export const fileExtension = 'glsl'; 7 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astexplorer", 3 | "scripts": { 4 | }, 5 | "env": { 6 | }, 7 | "formation": { 8 | }, 9 | "addons": [ 10 | 11 | ], 12 | "buildpacks": [ 13 | { 14 | "url": "heroku/nodejs" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/handlebars/handlebars'; 2 | 3 | export const id = 'handlebars'; 4 | export const displayName = 'Handlebars'; 5 | export const mimeTypes = ['text/x-handlebars-template']; 6 | export const fileExtension = 'handlebars'; 7 | -------------------------------------------------------------------------------- /website/src/utils/logger.js: -------------------------------------------------------------------------------- 1 | export function logEvent(category, action, label) { 2 | global.ga('send', 'event', category, action, label); 3 | } 4 | 5 | export function logError(message, fatal) { 6 | global.ga('send', 'exception', {exDescription: message, exFatal: fatal}); 7 | } 8 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/transformers/glimmer/codeExample.txt: -------------------------------------------------------------------------------- 1 | module.exports = class { 2 | transform(ast) { 3 | this.syntax.traverse(ast, { 4 | ElementNode(node) { 5 | node.tag = node.tag.split('').reverse().join(''); 6 | } 7 | }); 8 | 9 | return ast; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/babel/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function ({Plugin, types: t}) { 2 | return new Plugin('ast-transform', { 3 | visitor: { 4 | Identifier(node) { 5 | return t.identifier(node.name.split('').reverse().join('')); 6 | } 7 | } 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/transformers/glimmer-compiler/codeExample.txt: -------------------------------------------------------------------------------- 1 | module.exports = class { 2 | transform(ast) { 3 | this.syntax.traverse(ast, { 4 | ElementNode(node) { 5 | node.tag = node.tag.split('').reverse().join(''); 6 | } 7 | }); 8 | 9 | return ast; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/prettier/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": false, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | 11 | "parser": "babylon" 12 | } 13 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint1/loadRulesShim.js: -------------------------------------------------------------------------------- 1 | module.exports = function loadRules(/*rulesDir*/) { 2 | // By default, ESLint tries to load all available rules by looking for every 3 | // file in its "rules" directory. Since we don't care about any of the bundled 4 | // rules, just completely ignore them. 5 | return []; 6 | } 7 | -------------------------------------------------------------------------------- /server/constants.js: -------------------------------------------------------------------------------- 1 | if (!process.env.AUTH_TOKEN) { 2 | console.error( 3 | 'AUTH_TOKEN is not set! That will result in all gists being anonymous, ' + 4 | 'which is probably not what you want.' 5 | ); 6 | process.exit(1); 7 | } 8 | 9 | module.exports = { 10 | AUTH_TOKEN: process.env.AUTH_TOKEN, 11 | SETTINGS_FORMAT: 2, 12 | }; 13 | -------------------------------------------------------------------------------- /website/packages/eslint1/index.js: -------------------------------------------------------------------------------- 1 | // Explicitly require just the stuff we care about to avoid loading 2 | // RuleTester and CLIEngine, which are unnecessary and bloat out the 3 | // package size. 4 | module.exports = { 5 | eslint: require('eslint/lib/eslint'), 6 | sourceCode: require('eslint/lib/util/source-code'), 7 | rules: require('eslint/lib/rules'), 8 | }; 9 | -------------------------------------------------------------------------------- /website/packages/eslint2/index.js: -------------------------------------------------------------------------------- 1 | // Explicitly require just the stuff we care about to avoid loading 2 | // RuleTester and CLIEngine, which are unnecessary and bloat out the 3 | // package size. 4 | module.exports = { 5 | eslint: require('eslint/lib/eslint'), 6 | sourceCode: require('eslint/lib/util/source-code'), 7 | rules: require('eslint/lib/rules'), 8 | }; 9 | -------------------------------------------------------------------------------- /website/packages/eslint3/index.js: -------------------------------------------------------------------------------- 1 | // Explicitly require just the stuff we care about to avoid loading 2 | // RuleTester and CLIEngine, which are unnecessary and bloat out the 3 | // package size. 4 | module.exports = { 5 | eslint: require('eslint/lib/eslint'), 6 | sourceCode: require('eslint/lib/util/source-code'), 7 | rules: require('eslint/lib/rules'), 8 | }; 9 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/babel6/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function (babel) { 2 | const { types: t } = babel; 3 | 4 | return { 5 | name: "ast-transform", // not required 6 | visitor: { 7 | Identifier(path) { 8 | path.node.name = path.node.name.split('').reverse().join(''); 9 | } 10 | } 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/babel7/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function (babel) { 2 | const { types: t } = babel; 3 | 4 | return { 5 | name: "ast-transform", // not required 6 | visitor: { 7 | Identifier(path) { 8 | path.node.name = path.node.name.split('').reverse().join(''); 9 | } 10 | } 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /website/packages/eslint4/index.js: -------------------------------------------------------------------------------- 1 | // Explicitly require just the stuff we care about to avoid loading 2 | // RuleTester and CLIEngine, which are unnecessary and bloat out the 3 | // package size. 4 | const Linter = require('eslint/lib/linter'); 5 | 6 | module.exports = { 7 | eslint: new Linter(), 8 | sourceCode: require('eslint/lib/util/source-code') 9 | }; 10 | -------------------------------------------------------------------------------- /website/src/parsers/css/codeExample.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Paste or drop some CSS here and explore 3 | * the syntax tree created by chosen parser. 4 | * Enjoy! 5 | */ 6 | 7 | @media screen and (min-width: 480px) { 8 | body { 9 | background-color: lightgreen; 10 | } 11 | } 12 | 13 | #main { 14 | border: 1px solid black; 15 | } 16 | 17 | ul li { 18 | padding: 5px; 19 | } 20 | -------------------------------------------------------------------------------- /website/src/containers/LoadingIndicatorContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import LoadingIndicator from '../components/LoadingIndicator'; 3 | import {isLoadingSnippet} from '../store/selectors'; 4 | 5 | function mapStateToProps(state) { 6 | return { 7 | visible: isLoadingSnippet(state), 8 | }; 9 | } 10 | 11 | export default connect(mapStateToProps)(LoadingIndicator); 12 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astexplorer-server", 3 | "version": "2.0.0", 4 | "main": "index.js", 5 | "private": true, 6 | "author": "Felix Kling", 7 | "dependencies": { 8 | "body-parser": "^1.15.2", 9 | "express": "^4.14.0", 10 | "github-api": "https://github.com/fkling/github.git" 11 | }, 12 | "scripts": { 13 | "start": "STATIC=../out node index.js" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /website/src/utils/debounce.js: -------------------------------------------------------------------------------- 1 | export default function debounce(f, timeout=100) { 2 | let timer; 3 | let lastArgs; 4 | let lastThis; 5 | 6 | return function(...args) { 7 | lastThis = this; 8 | lastArgs = args; 9 | if (timer) { 10 | return; 11 | } 12 | timer = setTimeout(() => { 13 | timer = null; 14 | f.apply(lastThis, lastArgs); 15 | }, timeout); 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/jscodeshift/codeExample.txt: -------------------------------------------------------------------------------- 1 | // Press ctrl+space for code completion 2 | export default function transformer(file, api) { 3 | const j = api.jscodeshift; 4 | 5 | return j(file.source) 6 | .find(j.Identifier) 7 | .forEach(path => { 8 | j(path).replaceWith( 9 | j.identifier(path.node.name.split('').reverse().join('')) 10 | ); 11 | }) 12 | .toSource(); 13 | } 14 | -------------------------------------------------------------------------------- /website/src/parsers/utils/compileModule.js: -------------------------------------------------------------------------------- 1 | export default function compileModule(code, globals = {}) { 2 | let exports = {}; 3 | let module = { exports }; 4 | let globalNames = Object.keys(globals); 5 | let keys = ['module', 'exports', ...globalNames]; 6 | let values = [module, exports, ...globalNames.map(key => globals[key])]; 7 | new Function(keys.join(), code).apply(exports, values); 8 | return module.exports; 9 | } 10 | -------------------------------------------------------------------------------- /website/src/parsers/php/codeExample.txt: -------------------------------------------------------------------------------- 1 | $tip) { 15 | echo "Tip $i: " . $tip; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | REMOTE=${1:-"server"} 6 | BRANCH=${2:-"master"} 7 | 8 | if ! git diff --quiet && git diff --cached --quiet; then 9 | echo >&2 "You have uncommitted changes, you probably don't want to update the server." 10 | exit 1 11 | fi 12 | 13 | for i in {5..1}; do 14 | printf "\rPushing '$BRANCH' to '$REMOTE' in $i ..." && sleep 1; 15 | done 16 | 17 | echo "\nPushing..." 18 | git push -f $REMOTE $BRANCH 19 | -------------------------------------------------------------------------------- /website/src/components/LoadingIndicator.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default function LoadingIndicator(props) { 5 | return props.visible ? 6 |
8 |
9 | 10 |
11 |
: 12 | null; 13 | } 14 | 15 | LoadingIndicator.propTypes = { 16 | visible: PropTypes.bool, 17 | }; 18 | -------------------------------------------------------------------------------- /website/src/parsers/graphql/codeExample.txt: -------------------------------------------------------------------------------- 1 | # Paste or drop some GraphQL queries or schema 2 | # definitions here and explore the syntax tree 3 | # created by the GraphQL parser. 4 | 5 | query GetUser($userId: ID!) { 6 | user(id: $userId) { 7 | id, 8 | name, 9 | isViewerFriend, 10 | profilePicture(size: 50) { 11 | ...PictureFragment 12 | } 13 | } 14 | } 15 | 16 | fragment PictureFragment on Picture { 17 | uri, 18 | width, 19 | height 20 | } 21 | -------------------------------------------------------------------------------- /website/src/parsers/js/utils/defaultESTreeParserInterface.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../../utils/defaultParserInterface'; 2 | export default { 3 | ...defaultParserInterface, 4 | 5 | opensByDefault(node, key) { 6 | return ( 7 | node.type === 'Program' || 8 | key === 'body' || 9 | key === 'elements' || // array literals 10 | key === 'declarations' || // variable declaration 11 | key === 'expression' 12 | ); 13 | }, 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - node 5 | cache: 6 | yarn: true 7 | directories: 8 | - website/node_modules 9 | before_install: 10 | - curl -o- -L https://yarnpkg.com/install.sh | bash 11 | - export PATH=$HOME/.yarn/bin:$PATH 12 | install: 13 | - cd website 14 | - yarn install --production=false 15 | before_script: 16 | - yarn lint 17 | script: 18 | - yarn watch -- --no-watch 19 | env: 20 | - NODE_ENV=development 21 | - NODE_ENV=production 22 | -------------------------------------------------------------------------------- /server/handlers/gist/loadGist.js: -------------------------------------------------------------------------------- 1 | const {AUTH_TOKEN} = require('../../constants'); 2 | const GitHub = require('github-api'); 3 | 4 | module.exports = function loadGist(req, res, next) { 5 | const gh = new GitHub({token: AUTH_TOKEN}); 6 | const gist = gh.getGist(req.params.snippetid); 7 | const latest = req.params.revisionid === 'latest'; 8 | (latest ? 9 | gist.read() : 10 | gist.getRevision(req.params.revisionid) 11 | ) 12 | .then(response => res.json(response.data)) 13 | .catch(next); 14 | }; 15 | -------------------------------------------------------------------------------- /website/src/containers/PasteDropTargetContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import PasteDropTarget from '../components/PasteDropTarget'; 3 | import {setError, dropText} from '../store/actions'; 4 | 5 | function mapDispatchToProps(dispatch) { 6 | return { 7 | onText: (type, event, code, categoryId) => { 8 | dispatch(dropText(code, categoryId)); 9 | }, 10 | onError: error => dispatch(setError(error)), 11 | }; 12 | } 13 | 14 | export default connect(null, mapDispatchToProps)(PasteDropTarget); 15 | -------------------------------------------------------------------------------- /website/src/parsers/utils/transformJSCode.js: -------------------------------------------------------------------------------- 1 | import * as babel from 'babel-core'; 2 | import es2015 from 'babel-preset-es2015'; 3 | import stage0 from 'babel-preset-stage-0'; 4 | import flowStripTypes from 'babel-plugin-transform-flow-strip-types'; 5 | 6 | const options = { 7 | presets: [es2015, stage0], 8 | plugins: [flowStripTypes], 9 | ast: false, 10 | babelrc: false, 11 | highlightCode: false, 12 | }; 13 | 14 | export default function transform(code) { 15 | return babel.transform(code, options).code; 16 | } 17 | -------------------------------------------------------------------------------- /website/src/components/buttons/NewButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default function SaveButton({saving, forking, onNew}) { 5 | return ( 6 | 12 | ); 13 | } 14 | 15 | SaveButton.propTypes = { 16 | saving: PropTypes.bool, 17 | forking: PropTypes.bool, 18 | onNew: PropTypes.func, 19 | }; 20 | 21 | -------------------------------------------------------------------------------- /website/src/components/buttons/ShareButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default function ShareButton({onShareButtonClick, snippet}) { 5 | return ( 6 | 12 | ); 13 | } 14 | 15 | ShareButton.propTypes = { 16 | onShareButtonClick: PropTypes.func.isRequired, 17 | snippet: PropTypes.object, 18 | }; 19 | -------------------------------------------------------------------------------- /server/handlers/gist/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const loadGist = require('./loadGist'); 3 | const saveAnonymousGist = require('./saveAnonymousGist'); 4 | 5 | module.exports = express.Router() 6 | // Load snippet 7 | .get('/:snippetid/:revisionid', loadGist) 8 | // Create new "anonymous" snippet 9 | .post('/', saveAnonymousGist.create) 10 | // Update "anonymous" snippet 11 | .patch('/:snippetid', saveAnonymousGist.update) 12 | // Fork "anonymous" snippet 13 | .post('/:snippetid/:revisionid', saveAnonymousGist.fork); 14 | -------------------------------------------------------------------------------- /website/src/containers/ErrorMessageContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import ErrorMessage from '../components/ErrorMessage'; 3 | import {clearError} from '../store/actions'; 4 | import {getError} from '../store/selectors'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | error: getError(state), 9 | }; 10 | } 11 | 12 | 13 | function mapDispatchToProps(dispatch) { 14 | return { 15 | onWantToClose: () => dispatch(clearError()), 16 | }; 17 | } 18 | 19 | export default connect(mapStateToProps, mapDispatchToProps)(ErrorMessage); 20 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/handlebars.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultHandlebarsParserInterface'; 2 | import pkg from 'handlebars/package.json'; 3 | 4 | const ID = 'handlebars'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage, 13 | 14 | loadParser(callback) { 15 | require(['handlebars'], (handlebars) => callback(handlebars.parse)); 16 | }, 17 | 18 | opensByDefault(node, key) { 19 | return key === 'body'; 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /website/src/utils/stringify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a JS value to a sensible string representation. 3 | */ 4 | export default function stringify(value) { 5 | switch (typeof value) { 6 | case 'function': 7 | return value.toString().match(/function[^(]*\([^)]*\)/)[0]; 8 | case 'object': 9 | return value ? JSON.stringify(value, stringify) : 'null'; 10 | case 'undefined': 11 | return 'undefined'; 12 | case 'number': 13 | return global.isNaN(value) ? 'NaN' : value; 14 | default: 15 | return JSON.stringify(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /website/src/parsers/css/utils/defaultCSSParserInterface.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../../utils/defaultParserInterface'; 2 | 3 | export default { 4 | ...defaultParserInterface, 5 | 6 | getOffset({ line, column }) { 7 | return this.lineOffsets[line - 1] + column - 1; 8 | }, 9 | 10 | parse(parseCSS, code) { 11 | this.lineOffsets = []; 12 | let index = 0; 13 | do { 14 | this.lineOffsets.push(index); 15 | } while (index = code.indexOf('\n', index) + 1); // eslint-disable-line no-cond-assign 16 | return parseCSS(code); 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /website/src/components/visualization/JSON.js: -------------------------------------------------------------------------------- 1 | import JSONEditor from '../JSONEditor'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | 5 | import stringify from 'json-stringify-safe'; 6 | 7 | export default class JSON extends React.Component { 8 | render() { 9 | return ( 10 | 14 | ); 15 | } 16 | } 17 | 18 | JSON.propTypes = { 19 | ast: PropTypes.oneOfType([ 20 | PropTypes.array, 21 | PropTypes.object, 22 | ]), 23 | }; 24 | -------------------------------------------------------------------------------- /website/src/parsers/css/transformers/postcss/codeExample.txt: -------------------------------------------------------------------------------- 1 | import * as postcss from 'postcss'; 2 | 3 | export default postcss.plugin('postcss-reverse-props', (options = {}) => { 4 | // Work with options here 5 | return root => { 6 | // Transform CSS AST here 7 | root.walkRules(rule => { 8 | // Transform each rule here 9 | rule.walkDecls(decl => { 10 | // Transform each property declaration here 11 | decl.prop = decl.prop.split('').reverse().join(''); 12 | }); 13 | }); 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /website/src/parsers/js/codeExample.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Paste or drop some JavaScript here and explore 3 | * the syntax tree created by chosen parser. 4 | * You can use all the cool new features from ES6 5 | * and even more. Enjoy! 6 | */ 7 | 8 | let tips = [ 9 | "Click on any AST node with a '+' to expand it", 10 | 11 | "Hovering over a node highlights the \ 12 | corresponding part in the source code", 13 | 14 | "Shift click on an AST node expands the whole substree" 15 | ]; 16 | 17 | function printTips() { 18 | tips.forEach((tip, i) => console.log(`Tip ${i}:` + tip)); 19 | } 20 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/glimmer.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultHandlebarsParserInterface'; 2 | import pkg from '@glimmer/syntax/package.json'; 3 | 4 | const ID = 'glimmer'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/tildeio/glimmer', 13 | 14 | loadParser(callback) { 15 | require(['@glimmer/syntax'], (glimmer) => callback(glimmer.preprocess)); 16 | }, 17 | 18 | opensByDefault(node, key) { 19 | return key === 'body'; 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /website/src/containers/ShareDialogContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import {closeShareDialog} from '../store/actions'; 3 | import {showShareDialog, getRevision} from '../store/selectors'; 4 | import ShareDialog from '../components/dialogs/ShareDialog'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | visible: showShareDialog(state), 9 | snippet: getRevision(state), 10 | }; 11 | } 12 | 13 | function mapDispatchToProps(dispatch) { 14 | return { 15 | onWantToClose: () => dispatch(closeShareDialog()), 16 | }; 17 | } 18 | 19 | export default connect(mapStateToProps, mapDispatchToProps)(ShareDialog); 20 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/prettier/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'prettier/package.json'; 3 | 4 | const ID = 'prettier'; 5 | const name = 'prettier'; 6 | 7 | export default { 8 | id: ID, 9 | displayName: name, 10 | version: pkg.version, 11 | homepage: pkg.homepage, 12 | 13 | defaultParserID: 'babylon7', 14 | 15 | loadTransformer(callback) { 16 | require(['prettier'], callback); 17 | }, 18 | 19 | transform(prettier, transformCode, code) { 20 | const options = compileModule(transformCode); 21 | return prettier.format(code, options.default || options); 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /website/src/parsers/utils/defaultParserInterface.js: -------------------------------------------------------------------------------- 1 | export default { 2 | showInMenu: true, 3 | _ignoredProperties: new Set(), 4 | locationProps: new Set(), 5 | 6 | opensByDefault(/*node, key*/) { 7 | return false; 8 | }, 9 | 10 | nodeToRange(node) { 11 | return node.range; 12 | }, 13 | 14 | getNodeName(node) { 15 | return node.type; 16 | }, 17 | 18 | *forEachProperty(node) { 19 | for (let prop in node) { 20 | if (this._ignoredProperties.has(prop)) { 21 | continue; 22 | } 23 | yield { 24 | value: node[prop], 25 | key: prop, 26 | computed: false, 27 | } 28 | } 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /website/src/components/visualization/css/tree.css: -------------------------------------------------------------------------------- 1 | .tree-visualization { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .tree-visualization > .toolbar { 7 | padding: 5px; 8 | flex-shrink: 0; 9 | } 10 | 11 | .tree-visualization > .toolbar > label { 12 | cursor: pointer; 13 | margin-right: 5px; 14 | -webkit-touch-callout: none; 15 | user-select: none; 16 | } 17 | 18 | .tree-visualization ul { 19 | margin: 0; 20 | padding-left: 20px; 21 | overflow-y: auto; 22 | } 23 | 24 | .tree-visualization > ul { 25 | cursor: default; 26 | box-sizing: border-box; 27 | font-family: monospace; 28 | -webkit-touch-callout: none; 29 | user-select: none; 30 | } 31 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint4/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint4/eslint4-package'; 2 | 3 | const ID = 'eslint-v4'; 4 | const name = 'ESLint v4'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: name, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'babel-eslint', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['eslint4', '../../utils/eslint4Utils'], 17 | (eslint, utils) => callback({...eslint, utils}) 18 | ); 19 | }, 20 | 21 | transform({ eslint, sourceCode, utils }, transformCode, code) { 22 | utils.defineRule(eslint, transformCode); 23 | return utils.runRule(code, eslint, sourceCode); 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint1/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint1/package.js'; 2 | 3 | const ID = 'eslint-v1'; 4 | const name = 'ESLint v1' 5 | 6 | export default { 7 | id: ID, 8 | displayName: name, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'acorn-to-esprima', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['eslint1', '../../utils/eslintUtils'], 17 | (eslint, utils) => callback({...eslint, utils}) 18 | ); 19 | }, 20 | 21 | transform({ eslint, sourceCode, rules, utils }, transformCode, code) { 22 | utils.defineRule(rules, transformCode); 23 | return utils.runRule(code, eslint, sourceCode); 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint3/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint3/eslint3-package'; 2 | 3 | const ID = 'eslint-v3'; 4 | const name = 'ESLint v3' 5 | 6 | export default { 7 | id: ID, 8 | displayName: name, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'babel-eslint', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['eslint3', '../../utils/eslintUtils'], 17 | (eslint, utils) => callback({...eslint, utils}) 18 | ); 19 | }, 20 | 21 | transform({ eslint, rules, sourceCode, utils }, transformCode, code) { 22 | utils.defineRule(rules, transformCode); 23 | return utils.runRule(code, eslint, sourceCode); 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /website/src/parsers/regexp/transformers/regexp-tree/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'regexp-tree/package.json'; 3 | 4 | const ID = 'regexp-tree'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: ID, 13 | 14 | loadTransformer(callback) { 15 | require([ 16 | 'regexp-tree', 17 | ], callback); 18 | }, 19 | 20 | transform(regexpTree, transformCode, code) { 21 | let handler = compileModule( // eslint-disable-line no-shadow 22 | transformCode 23 | ); 24 | 25 | return regexpTree.transform(code, handler).toString(); 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /website/src/containers/ASTOutputContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import ASTOutput from '../components/ASTOutput'; 3 | import {setParseError} from '../store/actions'; 4 | import * as selectors from '../store/selectors'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | code: selectors.getCode(state), 9 | parser: selectors.getParser(state), 10 | parserSettings: selectors.getParserSettings(state), 11 | cursor: selectors.getCursor(state), 12 | }; 13 | } 14 | 15 | function mapDispatchToProps(dispatch) { 16 | return { 17 | onParseError: error => dispatch(setParseError(error)), 18 | }; 19 | } 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(ASTOutput); 22 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint2/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint2/node_modules/eslint/package.json'; 2 | 3 | const ID = 'eslint-v2'; 4 | const name = 'ESLint v2' 5 | 6 | export default { 7 | id: ID, 8 | displayName: name, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'babel-eslint', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['eslint2', '../../utils/eslintUtils'], 17 | (eslint, utils) => callback({...eslint, utils}) 18 | ); 19 | }, 20 | 21 | transform({ eslint, rules, sourceCode, utils }, transformCode, code) { 22 | utils.defineRule(rules, transformCode); 23 | return utils.runRule(code, eslint, sourceCode); 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /website/src/parsers/sql/sqlite-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'sqlite-parser/package.json'; 3 | 4 | const ID = 'sqlite-parser'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/codeschool/sqlite-parser', 13 | 14 | loadParser(callback) { 15 | require(['sqlite-parser'], callback); 16 | }, 17 | 18 | parse(sqliteParser, code) { 19 | return sqliteParser(code); 20 | }, 21 | 22 | getNodeName(node) { 23 | return node.type; 24 | }, 25 | 26 | opensByDefault(node, key) { 27 | return key === 'statement'; 28 | }, 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /website/src/containers/CodeEditorContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import {setCode, setCursor} from '../store/actions'; 3 | import Editor from '../components/Editor'; 4 | import {getCode, getParser, getParseError} from '../store/selectors'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | value: getCode(state), 9 | mode: getParser(state).category.id, 10 | error: getParseError(state), 11 | }; 12 | } 13 | 14 | function mapDispatchToProps(dispatch) { 15 | return { 16 | onContentChange: ({value, cursor}) => { 17 | dispatch(setCode({code: value, cursor})); 18 | }, 19 | onActivity: cursor => dispatch(setCursor(cursor)), 20 | }; 21 | } 22 | 23 | export default connect(mapStateToProps, mapDispatchToProps)(Editor); 24 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/babel/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'babel5/babel5-package'; 3 | 4 | const ID = 'babel'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'babylon', 13 | 14 | loadTransformer(callback) { 15 | require(['babel5'], callback); 16 | }, 17 | 18 | transform(babel, transformCode, code) { 19 | let transform = compileModule( // eslint-disable-line no-shadow 20 | transformCode 21 | ); 22 | 23 | return babel.transform(code, { 24 | whitelist: [], 25 | plugins: [transform.default || transform], 26 | sourceMaps: true, 27 | }); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/utils/defaultHandlebarsParserInterface.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../../utils/defaultParserInterface'; 2 | 3 | export default { 4 | ...defaultParserInterface, 5 | 6 | locationProps: new Set(['loc']), 7 | 8 | parse(parseHandlebars, code) { 9 | this.lineOffsets = []; 10 | let index = 0; 11 | do { 12 | this.lineOffsets.push(index); 13 | } while (index = code.indexOf('\n', index) + 1); // eslint-disable-line no-cond-assign 14 | return parseHandlebars(code); 15 | }, 16 | 17 | getOffset({ line, column }) { 18 | return this.lineOffsets[line - 1] + column; 19 | }, 20 | 21 | nodeToRange({ loc }) { 22 | if (!loc) return; 23 | return [loc.start, loc.end].map(pos => this.getOffset(pos)); 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /website/src/components/LocalStorage.js: -------------------------------------------------------------------------------- 1 | const storage = global.localStorage; 2 | const key = 'explorerSettingsV1'; 3 | const noop = () => {}; 4 | 5 | export const writeState = storage ? 6 | state => { 7 | try { 8 | storage.setItem(key, JSON.stringify(state)); 9 | } catch(e) { 10 | // eslint-disable-next-line no-console 11 | console.warn('Unable to write to local storage.'); 12 | } 13 | } : 14 | noop; 15 | 16 | export const readState = storage ? 17 | () => { 18 | try { 19 | const state = storage.getItem(key); 20 | if (state) { 21 | return JSON.parse(state); 22 | } 23 | } catch(e) { 24 | // eslint-disable-next-line no-console 25 | console.warn('Unable to read from local storage.'); 26 | } 27 | } : 28 | noop; 29 | -------------------------------------------------------------------------------- /website/src/parsers/json/json-to-ast.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'json-to-ast/package.json'; 3 | 4 | const ID = 'jsonToAst'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage, 13 | locationProps: new Set(['loc']), 14 | 15 | loadParser(callback) { 16 | require(['json-to-ast'], callback); 17 | }, 18 | 19 | parse(jsonToAst, code) { 20 | return jsonToAst(code); 21 | }, 22 | 23 | getNodeName(node) { 24 | return node.type; 25 | }, 26 | 27 | nodeToRange({loc}) { 28 | if (loc) { 29 | return [ 30 | loc.start.offset, 31 | loc.end.offset, 32 | ]; 33 | } 34 | }, 35 | } 36 | -------------------------------------------------------------------------------- /website/src/components/buttons/SaveButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from 'classnames'; 4 | 5 | export default function SaveButton({canSave, saving, forking, onSave}) { 6 | return ( 7 | 23 | ); 24 | } 25 | 26 | SaveButton.propTypes = { 27 | canSave: PropTypes.bool, 28 | saving: PropTypes.bool, 29 | forking: PropTypes.bool, 30 | onSave: PropTypes.func, 31 | }; 32 | -------------------------------------------------------------------------------- /website/src/containers/SettingsDialogContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import {closeSettingsDialog, setParserSettings} from '../store/actions'; 3 | import {showSettingsDialog, getParser, getParserSettings} from '../store/selectors'; 4 | import SettingsDialog from '../components/dialogs/SettingsDialog'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | visible: showSettingsDialog(state), 9 | parser: getParser(state), 10 | parserSettings: getParserSettings(state), 11 | }; 12 | } 13 | 14 | function mapDispatchToProps(dispatch) { 15 | return { 16 | onSave: (parser, newSettings) => dispatch(setParserSettings(newSettings)), 17 | onWantToClose: () => dispatch(closeSettingsDialog()), 18 | }; 19 | } 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(SettingsDialog); 22 | -------------------------------------------------------------------------------- /website/src/components/buttons/PrettierButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from 'classnames'; 4 | 5 | export default function PrettierButton(props) { 6 | return (); 20 | } 21 | 22 | PrettierButton.propTypes = { 23 | toggleFormatting: PropTypes.func, 24 | enableFormatting: PropTypes.bool, 25 | } 26 | -------------------------------------------------------------------------------- /website/src/parsers/css/rework.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultCSSParserInterface'; 2 | import pkg from 'css/package.json'; 3 | 4 | const ID = 'rework'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/reworkcss/rework', 13 | locationProps: new Set(['position']), 14 | 15 | loadParser(callback) { 16 | require(['css/lib/parse'], callback); 17 | }, 18 | 19 | nodeToRange({ position: range }) { 20 | if (!range) return; 21 | return [range.start, range.end].map(pos => this.getOffset(pos)); 22 | }, 23 | 24 | opensByDefault(node, key) { 25 | return key === 'rules'; 26 | }, 27 | 28 | _ignoredProperties: new Set(['parsingErrors', 'source', 'content']), 29 | }; 30 | -------------------------------------------------------------------------------- /website/src/components/visualization/tree/CompactObjectView.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default function CompactObjectView({keys, onClick}) { 5 | if (keys.length === 0) { 6 | return {'{ }'}; 7 | } else { 8 | if (keys.length > 5) { 9 | keys = keys.slice(0, 5).concat([`... +${keys.length - 5}`]); 10 | } 11 | return ( 12 | 13 | {' {'} 14 | 15 | {keys.join(', ')} 16 | 17 | {'}'} 18 | 19 | ); 20 | } 21 | } 22 | 23 | CompactObjectView.propTypes = { 24 | keys: PropTypes.arrayOf(PropTypes.string).isRequired, 25 | onClick: PropTypes.func, 26 | }; 27 | -------------------------------------------------------------------------------- /website/src/parsers/lua/codeExample.txt: -------------------------------------------------------------------------------- 1 | --[[ 2 | Paste or drop some Lua here and explore 3 | the syntax tree created by chosen parser. 4 | Enjoy! 5 | --]] 6 | 7 | function allwords () 8 | local line = io.read() -- current line 9 | local pos = 1 -- current position in the line 10 | return function () -- iterator function 11 | while line do -- repeat while there are lines 12 | local s, e = string.find(line, "%w+", pos) 13 | if s then -- found a word? 14 | pos = e + 1 -- update next position 15 | return string.sub(line, s, e) -- return the word 16 | else 17 | line = io.read() -- word not found; try next line 18 | pos = 1 -- restart from first position 19 | end 20 | end 21 | return nil -- no more lines: end of traversal 22 | end 23 | end -------------------------------------------------------------------------------- /website/src/components/ErrorMessage.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default class ErrorMessage extends React.Component { 5 | render() { 6 | return this.props.error ? 7 |
8 |
9 |

10 | 11 | {' '} 12 | Error 13 |

14 |
{this.props.error.message}
15 |
16 | 21 |
22 |
23 |
: 24 | null; 25 | } 26 | } 27 | 28 | ErrorMessage.propTypes = { 29 | error: PropTypes.object, 30 | onWantToClose: PropTypes.func, 31 | }; 32 | -------------------------------------------------------------------------------- /website/src/parsers/glsl/codeExample.txt: -------------------------------------------------------------------------------- 1 | // Game Of Life ( https://gl-react-cookbook.surge.sh/gol ) 2 | precision highp float; 3 | varying vec2 uv; 4 | uniform float size; 5 | uniform sampler2D t; // the previous world state 6 | void main() { 7 | float prev = step(0.5, texture2D(t, uv).r); 8 | float c = 1.0 / size; 9 | float sum = 10 | step(0.5, texture2D(t, uv + vec2(-1.0, -1.0)*c).r) + 11 | step(0.5, texture2D(t, uv + vec2(-1.0, 0.0)*c).r) + 12 | step(0.5, texture2D(t, uv + vec2(-1.0, 1.0)*c).r) + 13 | step(0.5, texture2D(t, uv + vec2( 0.0, 1.0)*c).r) + 14 | step(0.5, texture2D(t, uv + vec2( 1.0, 1.0)*c).r) + 15 | step(0.5, texture2D(t, uv + vec2( 1.0, 0.0)*c).r) + 16 | step(0.5, texture2D(t, uv + vec2( 1.0, -1.0)*c).r) + 17 | step(0.5, texture2D(t, uv + vec2( 0.0, -1.0)*c).r); 18 | float next = prev==1.0 && sum >= 2.0 && sum <= 3.0 || sum == 3.0 ? 1.0 : 0.0; 19 | gl_FragColor = vec4(vec3(next), 1.0); 20 | } 21 | -------------------------------------------------------------------------------- /scripts/build-site.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | BRANCH=${1:-"gh-pages"} 6 | TARGETPATH="../$(basename $(pwd))_gh-pages" 7 | 8 | if ! git diff --quiet && git diff --cached --quiet; then 9 | echo >&2 "Cannot build, your index contains uncommitted changes." 10 | exit 1 11 | fi 12 | 13 | # Initialize worktree 14 | rm -rf $TARGETPATH 15 | git worktree prune 16 | git worktree add $TARGETPATH $BRANCH 17 | 18 | echo "Building..." 19 | rm -rf out/* 20 | pushd website/ 21 | yarn build 22 | popd 23 | echo "Copying artifacts..." 24 | cp -R out/ "$TARGETPATH/" 25 | cp README.md "$TARGETPATH/README.md" 26 | cp CNAME "$TARGETPATH/CNAME" 27 | 28 | # Commit changes 29 | pushd $TARGETPATH 30 | echo "Committing..." 31 | git add -A 32 | if git diff --quiet && git diff --cached --quiet; then 33 | echo "No changes, nothing to commit..." 34 | exit 0 35 | fi 36 | git commit -m"Update site" 37 | popd 38 | 39 | rm -rf $TARGETPATH 40 | git worktree prune 41 | -------------------------------------------------------------------------------- /website/src/components/buttons/ForkButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from 'classnames'; 4 | 5 | export default class ForkButton extends React.Component { 6 | render() { 7 | const { canFork, saving, forking, onFork } = this.props; 8 | return ( 9 | 25 | ); 26 | } 27 | } 28 | 29 | ForkButton.propTypes = { 30 | canFork: PropTypes.bool, 31 | saving: PropTypes.bool, 32 | forking: PropTypes.bool, 33 | onFork: PropTypes.func, 34 | }; 35 | -------------------------------------------------------------------------------- /website/src/parsers/css/csstree.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'css-tree/package.json'; 3 | 4 | const ID = 'csstree'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/csstree/csstree', 13 | locationProps: new Set(['loc']), 14 | 15 | loadParser(callback) { 16 | require(['css-tree'], callback); 17 | }, 18 | 19 | parse(csstree, code) { 20 | return csstree.toPlainObject(csstree.parse(code, { 21 | positions: true, 22 | })); 23 | }, 24 | 25 | getNodeName(node) { 26 | return node.type; 27 | }, 28 | 29 | nodeToRange({ loc }) { 30 | if (loc && loc.start && loc.end) { 31 | return [loc.start.offset, loc.end.offset]; 32 | } 33 | }, 34 | 35 | opensByDefault(node, key) { 36 | return key === 'children'; 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /website/fontcustom/README.md: -------------------------------------------------------------------------------- 1 | # Custom Fonts 2 | 3 | There's not a great way to customize font-awesome at the moment 4 | (see: https://github.com/FortAwesome/Font-Awesome/wiki/Customize-Font-Awesome). 5 | 6 | So we are using [fontcustom](https://github.com/FontCustom/fontcustom) to add 7 | custom logos / fonts to the app. 8 | 9 | ## Build Instructions 10 | 11 | - install fontcustom: https://github.com/FontCustom/fontcustom#installation 12 | - add the svgs you want to convert to the `./fontcustom/input-svg/` directory 13 | - in the root of the astexplorer project, run: 14 | ```bash 15 | yarn run fontcustom 16 | ``` 17 | - you can then confirm things worked by running 18 | `open ./fontcustom/fontcustom-preview.html` 19 | - now you can reference your icons in a very similar manner to font-awesome, 20 | for example: 21 | - font-awesome: `` 22 | - fontcustom: `` 23 | (if you added `./fontcustom/input-svg/myfoo.svg`) 24 | -------------------------------------------------------------------------------- /website/src/utils/url.js: -------------------------------------------------------------------------------- 1 | import queryString from 'query-string'; 2 | 3 | const DATA_PATTERN = /^#(?:\/([^\/?]+)(?:\/(\d*))?\/?)?(?:\?(.+))?/; 4 | 5 | 6 | export function getDataFromURI() { 7 | const match = global.location.hash.match(DATA_PATTERN); 8 | if (match) { 9 | let params = { 10 | id: match[1], 11 | rev: Number(match[2]) || 0, 12 | }; 13 | if (match[3]) { 14 | Object.assign(params, queryString.parse(match[3])); 15 | } 16 | return params; 17 | } 18 | return {}; 19 | } 20 | 21 | export function updateURI(data) { 22 | const currentParams = getDataFromURI(); 23 | const {id, rev, ...params} = Object.assign(currentParams, data); 24 | 25 | let hash = ''; 26 | if (id) { 27 | hash += '/' + id; 28 | if (rev) { 29 | hash += '/' + rev; 30 | } 31 | } 32 | if (Object.keys(params).length > 0) { 33 | hash += '?' + queryString.stringify(params); 34 | } 35 | 36 | global.location.hash = hash; 37 | } 38 | -------------------------------------------------------------------------------- /website/src/parsers/css/transformers/postcss/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'postcss/package.json'; 3 | 4 | const ID = 'postcss'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'postcss', 13 | 14 | loadTransformer(callback) { 15 | require(['postcss'], postcss => { 16 | callback({ postcss }); 17 | }); 18 | }, 19 | 20 | transform({ postcss }, transformCode, code) { 21 | let transform = compileModule( // eslint-disable-line no-shadow 22 | transformCode, 23 | { 24 | require(name) { 25 | switch (name) { 26 | case 'postcss': return postcss; 27 | default: throw new Error(`Cannot find module '${name}'`); 28 | } 29 | }, 30 | } 31 | ); 32 | return postcss([ (transform.default || transform)() ]).process(code).css; 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/transformers/glimmer/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from '@glimmer/syntax/package.json'; 3 | 4 | const ID = 'glimmer'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage || 'https://github.com/tildeio/glimmer', 11 | 12 | defaultParserID: 'glimmer', 13 | 14 | loadTransformer(callback) { 15 | require(['@glimmer/syntax'], callback); 16 | }, 17 | 18 | transform(glimmer, transformCode, code) { 19 | const transformModule = compileModule(transformCode); 20 | 21 | // allow "export default" instead of "module.exports = " 22 | const transform = transformModule.__esModule ? 23 | transformModule.default : 24 | transformModule; 25 | 26 | let ast = glimmer.preprocess(code, { 27 | plugins: { 28 | ast: [transform], 29 | }, 30 | }); 31 | 32 | return glimmer.print(ast); 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /website/src/parsers/js/babel-eslint.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'babel-eslint/package.json'; 3 | 4 | const ID = 'babel-eslint'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage, 13 | locationProps: new Set(['loc', 'start', 'end', 'range']), 14 | 15 | loadParser(callback) { 16 | require(['babel-eslint'], callback); 17 | }, 18 | 19 | parse(parser, code) { 20 | const opts = { 21 | sourceType: 'module', 22 | }; 23 | 24 | const ast = parser.parseNoPatch(code, opts); 25 | delete ast.tokens; 26 | return ast; 27 | }, 28 | 29 | nodeToRange(node) { 30 | if (typeof node.start !== 'undefined') { 31 | return [node.start, node.end]; 32 | } 33 | }, 34 | 35 | _ignoredProperties: new Set([ 36 | '_paths', 37 | '_babelType', 38 | '__clone', 39 | ]), 40 | }; 41 | -------------------------------------------------------------------------------- /website/src/parsers/icu/intl-messageformat-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'intl-messageformat-parser/package.json'; 3 | 4 | const ID = 'intl-messageformat-parser'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/yahoo/intl-messageformat-parser', 13 | locationProps: new Set(['location']), 14 | 15 | loadParser(callback) { 16 | require(['intl-messageformat-parser'], callback); 17 | }, 18 | 19 | parse(parser, code) { 20 | return parser.parse(code); 21 | }, 22 | 23 | getNodeName(node) { 24 | return node.type; 25 | }, 26 | 27 | nodeToRange({ location }) { 28 | if (location && location.start && location.end) { 29 | return [location.start.offset, location.end.offset]; 30 | } 31 | }, 32 | 33 | opensByDefault(node, key) { 34 | return key === 'elements' || key === 'options'; 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /website/src/parsers/yaml/yaml-ast-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'yaml-ast-parser/package.json'; 3 | 4 | const ID = 'yaml-ast-parser'; 5 | let Kind = null; 6 | 7 | export default { 8 | ...defaultParserInterface, 9 | 10 | id: ID, 11 | displayName: ID, 12 | version: pkg.version, 13 | homepage: pkg.homepage || 'https://www.npmjs.com/package/yaml-ast-parser', 14 | 15 | _ignoredProperties: new Set(['parent', 'errors', 'kind']), 16 | locationProps: new Set(['startPosition', 'endPosition']), 17 | 18 | nodeToRange(node) { 19 | if (typeof node.startPosition === 'number') { 20 | return [node.startPosition, node.endPosition]; 21 | } 22 | }, 23 | 24 | getNodeName(node) { 25 | return Kind[node.kind]; 26 | }, 27 | 28 | loadParser(callback) { 29 | require(['yaml-ast-parser'], function(yamlAstParser) { 30 | Kind = yamlAstParser.Kind; 31 | callback(yamlAstParser); 32 | }); 33 | }, 34 | 35 | parse({ load }, code) { 36 | return load(code); 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /website/src/parsers/php/php-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'php-parser/package.json'; 3 | 4 | const ID = 'php-parser'; 5 | 6 | const defaultOptions = { 7 | parser: { 8 | extractDoc: true, 9 | }, 10 | ast: { 11 | withPositions: true, 12 | }, 13 | }; 14 | 15 | export default { 16 | ...defaultParserInterface, 17 | 18 | id: ID, 19 | displayName: ID, 20 | version: pkg.version, 21 | homepage: pkg.homepage, 22 | locationProps: new Set(['loc']), 23 | 24 | loadParser(callback) { 25 | require(['php-parser'], callback); 26 | }, 27 | 28 | parse(Engine, code, options) { 29 | const parser = new Engine({...defaultOptions, ...options}); 30 | return parser.parseCode(code, ''); 31 | }, 32 | 33 | getNodeName(node) { 34 | return node.kind; 35 | }, 36 | 37 | nodeToRange(node) { 38 | if (node.loc && node.loc.start && node.loc.end) { 39 | return [node.loc.start.offset, node.loc.end.offset]; 40 | } 41 | }, 42 | 43 | opensByDefault(node, key) { 44 | return key === 'body' || key === 'what' || key === 'items'; 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /website/src/components/visualization/tree/CompactArrayView.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default class CompactArrayView extends React.Component { 5 | shouldComponentUpdate(nextProps) { 6 | return nextProps.array.length !== this.props.array.length; 7 | } 8 | 9 | render() { 10 | let {array} = this.props; 11 | let count = array.length; 12 | 13 | if (count === 0) { 14 | return {'[ ]'}; 15 | } 16 | else { 17 | return ( 18 | 19 | {'['} 20 | 21 | {count + ' element' + (count > 1 ? 's' : '')} 22 | 23 | {']'} 24 | 25 | ); 26 | } 27 | } 28 | } 29 | 30 | CompactArrayView.propTypes = { 31 | /** 32 | * The array of elements to represent. 33 | */ 34 | array: PropTypes.oneOfType([ 35 | PropTypes.array, 36 | PropTypes.shape({ length: PropTypes.number }), 37 | ]).isRequired, 38 | onClick: PropTypes.func, 39 | }; 40 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | extends: 2 | - eslint:recommended 3 | - plugin:react/recommended 4 | - plugin:import/errors 5 | - plugin:import/warnings 6 | 7 | parserOptions: 8 | ecmaVersion: 6 9 | sourceType: module 10 | ecmaFeatures: 11 | jsx: true 12 | experimentalObjectRestSpread: true 13 | 14 | plugins: 15 | - react 16 | - import 17 | - require-in-package 18 | 19 | rules: 20 | comma-dangle: [2, "always-multiline"] 21 | new-cap: 0 22 | no-path-concat: 0 23 | no-undef: 2 24 | no-underscore-dangle: 0 25 | no-unused-vars: 26 | - 1 27 | - varsIgnorePattern: '^_' 28 | no-use-before-define: 0 29 | quotes: [2, "single", "avoid-escape"] 30 | strict: 0 31 | import/no-unresolved: [2, {commonjs: true, amd: true}] 32 | import/named: 2 33 | import/default: 2 34 | import/namespace: 2 35 | import/export: 2 36 | require-in-package/require-in-package: 2 37 | 38 | settings: 39 | import/resolver: "webpack" 40 | import/ignore: 41 | - node_modules 42 | - \.json 43 | 44 | env: 45 | browser: true 46 | node: true 47 | 48 | globals: 49 | loadjs: true 50 | Promise: true 51 | Map: true 52 | Set: true 53 | WeakMap: true 54 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/transformers/glimmer-compiler/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from '@glimmer/compiler/package.json'; 3 | 4 | const ID = 'glimmer-compiler'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage || 'https://github.com/tildeio/glimmer', 11 | 12 | defaultParserID: 'glimmer', 13 | 14 | loadTransformer(callback) { 15 | require(['@glimmer/compiler'], callback); 16 | }, 17 | 18 | transform(glimmer, transformCode, code) { 19 | const transformModule = compileModule(transformCode); 20 | 21 | // allow "export default" instead of "module.exports = " 22 | const transform = transformModule.__esModule ? 23 | transformModule.default : 24 | transformModule; 25 | 26 | // compile template to wireformat 27 | let result = glimmer.precompile(code, { 28 | plugins: { 29 | ast: [transform], 30 | }, 31 | }); 32 | 33 | // parse wireformat into JSON 34 | let json = JSON.parse(JSON.parse(result).block); 35 | 36 | // pretty print JSON 37 | return { code: json }; 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /website/src/parsers/css/cssom.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'cssom/package.json'; 3 | 4 | const ID = 'cssom'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/NV/CSSOM', 13 | locationProps: new Set(['__starts', '__ends']), 14 | 15 | loadParser(callback) { 16 | require(['cssom/lib/parse'], callback); 17 | }, 18 | 19 | parse(CSSOM, code) { 20 | return CSSOM.parse(code); 21 | }, 22 | 23 | getNodeName(node) { 24 | return node.constructor.name; 25 | }, 26 | 27 | nodeToRange(node) { 28 | let { __starts, __ends } = node; 29 | if (__ends === undefined && node.parentRule) { 30 | ({ __ends } = node.parentRule); 31 | } 32 | if (__ends !== undefined) { 33 | return [__starts, __ends]; 34 | } 35 | }, 36 | 37 | opensByDefault(node, key) { 38 | return key === 'cssRules' || key === 'style'; 39 | }, 40 | 41 | _ignoredProperties: new Set(['parentRule', 'parentStyleSheet', '_importants']), 42 | }; 43 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const express = require('express'); 3 | const path = require('path'); 4 | 5 | const app = express(); 6 | app.use(bodyParser.json()); 7 | 8 | app.use('/api/v1/gist', require('./handlers/gist')); 9 | 10 | if (process.env.SNIPPET_FILE && process.env.REVISION_FILE) { 11 | console.log('Serving Parse snippets enabled.'); 12 | app.use('/api/v1/parse', require('./handlers/parse')); 13 | } 14 | 15 | // `next` is needed here to mark this as an error handler 16 | // eslint-disable-next-line no-unused-vars 17 | app.use((err, req, res, next) => { 18 | console.error((new Date()).toLocaleString(), err); 19 | if (err.response) { 20 | res.status(err.response.status).send(err.response.statusText); 21 | return; 22 | } 23 | // eslint-disable-next-line no-console 24 | res.status(500).send('Something went wrong'); 25 | }); 26 | 27 | if (process.env.STATIC) { 28 | app.use(express.static(path.join(__dirname, process.env.STATIC))); 29 | } 30 | 31 | const PORT = process.env.PORT || 8080; 32 | app.listen( 33 | PORT, 34 | 'localhost', 35 | () => { 36 | console.log(`Server listening on port ${PORT}!`); 37 | } 38 | ); 39 | -------------------------------------------------------------------------------- /website/src/components/dialogs/ShareDialog.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default class ShareDialog extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this._outerClick = this._outerClick.bind(this); 8 | } 9 | 10 | _outerClick(event) { 11 | if (event.target === document.getElementById('ShareDialog')) { 12 | this.props.onWantToClose(); 13 | } 14 | } 15 | 16 | render() { 17 | if (this.props.visible) { 18 | return ( 19 |
20 |
21 |
22 | {this.props.snippet.getShareInfo()} 23 |
24 |
25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | return null; 32 | } 33 | } 34 | 35 | ShareDialog.propTypes = { 36 | onWantToClose: PropTypes.func.isRequired, 37 | visible: PropTypes.bool.isRequired, 38 | snippet: PropTypes.object, 39 | }; 40 | -------------------------------------------------------------------------------- /website/src/containers/TransformerContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import Transformer from '../components/Transformer'; 3 | import {setTransformState, toggleFormatting} from '../store/actions'; 4 | import * as selectors from '../store/selectors'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | transformer: selectors.getTransformer(state), 9 | // Either the transform example or the transform code from the current 10 | // revision. This is what we compare against to determine whether something 11 | // changed and we can save. 12 | defaultTransformCode: selectors.getInitialTransformCode(state), 13 | transformCode: selectors.getTransformCode(state), 14 | mode: selectors.getParser(state).category.id, 15 | code: selectors.getCode(state), 16 | enableFormatting: selectors.getFormattingState(state), 17 | }; 18 | } 19 | 20 | function mapDispatchToProps(dispatch) { 21 | return { 22 | onContentChange: ({value, cursor}) => { 23 | dispatch(setTransformState({code: value, cursor})); 24 | }, 25 | toggleFormatting: () => { 26 | dispatch(toggleFormatting()); 27 | }, 28 | }; 29 | } 30 | 31 | export default connect(mapStateToProps, mapDispatchToProps)(Transformer); 32 | -------------------------------------------------------------------------------- /website/src/components/getFocusPath.js: -------------------------------------------------------------------------------- 1 | function isInRange(range, pos) { 2 | return pos >= range[0] && pos <= range[1]; 3 | } 4 | 5 | export function nodeToRange(parser, node) { 6 | let range = parser.nodeToRange(node); 7 | if (range) { 8 | return range; 9 | } 10 | if (node.length > 0) { 11 | // check first and last child 12 | let rangeFirst = node[0] && parser.nodeToRange(node[0]); 13 | let rangeLast = node[node.length - 1] && 14 | parser.nodeToRange(node[node.length - 1]); 15 | if (rangeFirst && rangeLast) { 16 | return [rangeFirst[0], rangeLast[1]]; 17 | } 18 | } 19 | } 20 | 21 | export default function getFocusPath(node, pos, parser, seen = new Set()) { 22 | seen.add(node); 23 | 24 | let path = []; 25 | let range = nodeToRange(parser, node); 26 | if (range) { 27 | if (isInRange(range, pos)) { 28 | path.push(node); 29 | } else { 30 | return []; 31 | } 32 | } 33 | for (let {value} of parser.forEachProperty(node)) { 34 | if (value && typeof value === 'object' && !seen.has(value)) { 35 | let childPath = getFocusPath(value, pos, parser, seen); 36 | if (childPath.length > 0) { 37 | path = path.concat(childPath); 38 | break; 39 | } 40 | } 41 | } 42 | return path; 43 | } 44 | -------------------------------------------------------------------------------- /website/src/storage/index.js: -------------------------------------------------------------------------------- 1 | export default class StorageHandler { 2 | constructor(backends) { 3 | this._backends = backends; 4 | } 5 | 6 | _first() { 7 | return this._backends[0]; 8 | } 9 | 10 | _owns(revision) { 11 | for (const backend of this._backends) { 12 | if (backend.owns(revision)) { 13 | return backend; 14 | } 15 | } 16 | return null; 17 | } 18 | 19 | updateHash(revision) { 20 | global.location.hash = revision.getPath(); 21 | } 22 | 23 | fetchFromURL() { 24 | if (/^#?\/?$/.test(global.location.hash)) { 25 | return Promise.resolve(null); 26 | } 27 | for (const backend of this._backends) { 28 | if (backend.matchesURL()) { 29 | return backend.fetchFromURL(); 30 | } 31 | } 32 | return Promise.reject(new Error('Unknown URL format.')); 33 | } 34 | 35 | /** 36 | * Create a new snippet. 37 | */ 38 | create(data) { 39 | return this._first().create(data); 40 | } 41 | 42 | /** 43 | * Update an existing snippet. 44 | */ 45 | update(revision, data) { 46 | return this._first().update(revision, data); 47 | } 48 | 49 | /** 50 | * Fork existing snippet. 51 | */ 52 | fork(revision, data) { 53 | return this._first().fork(revision, data); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /website/fontcustom/input-svg/scala.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 6 | 7 | -------------------------------------------------------------------------------- /website/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AST explorer 5 | 6 | 7 | <%= htmlWebpackPlugin.files.webpackManifest %> 8 | 9 | 10 |
11 |
12 |
13 | Built with 14 | React, 15 | Babel, 16 | Font Awesome, 17 | CodeMirror, 18 | Express, 19 | and 20 | WebPack 21 | | 22 | GitHub 23 |
24 |
25 | 26 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/babel6/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'babel6/babel6-package'; 3 | 4 | const ID = 'babelv6'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'babylon6', 13 | 14 | loadTransformer(callback) { 15 | require([ 16 | 'babel6', 17 | 'recast', 18 | ], (babel, recast) => callback({ babel, recast })); 19 | }, 20 | 21 | transform({ babel, recast }, transformCode, code) { 22 | let transform = compileModule( // eslint-disable-line no-shadow 23 | transformCode 24 | ); 25 | 26 | return babel.transform(code, { 27 | parserOpts: { 28 | parser: recast.parse, 29 | plugins: [ 30 | 'asyncGenerators', 31 | 'classConstructorCall', 32 | 'classProperties', 33 | 'decorators', 34 | 'doExpressions', 35 | 'exportExtensions', 36 | 'flow', 37 | 'functionSent', 38 | 'functionBind', 39 | 'jsx', 40 | 'objectRestSpread', 41 | 'dynamicImport', 42 | ], 43 | }, 44 | generatorOpts: { 45 | generator: recast.print, 46 | }, 47 | plugins: [(transform.default || transform)(babel)], 48 | sourceMaps: true, 49 | }); 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /website/src/parsers/js/esformatter.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'esformatter-parser/package.json'; 3 | 4 | const ID = 'esformatter-parser'; 5 | const name = 'esformatter'; 6 | 7 | export default { 8 | ...defaultParserInterface, 9 | 10 | id: ID, 11 | displayName: name, 12 | version: pkg.version, 13 | homepage: pkg.homepage, 14 | locationProps: new Set(['loc', 'start', 'end', 'range']), 15 | 16 | loadParser(callback) { 17 | require(['esformatter-parser'], (parser) => { 18 | callback(parser); 19 | }); 20 | }, 21 | 22 | parse(parser, code) { 23 | return parser.parse(code); 24 | }, 25 | 26 | *forEachProperty(node) { 27 | for (let prop in node) { 28 | if (this._ignoredProperties.has(prop)) { 29 | continue; 30 | } 31 | 32 | let value = node[prop]; 33 | 34 | if (node.type !== 'Program' && prop === 'parent') { 35 | value = '[Circular]'; 36 | } 37 | 38 | yield { 39 | value, 40 | key: prop, 41 | computed: false, 42 | } 43 | } 44 | }, 45 | 46 | _ignoredProperties: new Set([ 47 | '_paths', 48 | '_babelType', 49 | '__clone', 50 | // hide some extra properties to reduce noise 51 | 'comments', 52 | 'directives', 53 | 'extra', 54 | 'leadingComments', 55 | 'root', 56 | 'sourceType', 57 | 'tokens', 58 | 'trailingComments', 59 | ]), 60 | }; 61 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/babel7/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'babel7/babel7-package'; 3 | 4 | const ID = 'babelv7'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'babylon7', 13 | 14 | loadTransformer(callback) { 15 | require([ 16 | 'babel7', 17 | 'recast', 18 | ], (babel, recast) => callback({ babel, recast })); 19 | }, 20 | 21 | transform({ babel, recast }, transformCode, code) { 22 | let transform = compileModule( // eslint-disable-line no-shadow 23 | transformCode 24 | ); 25 | 26 | return babel.transform(code, { 27 | parserOpts: { 28 | parser: recast.parse, 29 | plugins: [ 30 | 'asyncGenerators', 31 | 'classPrivateProperties', 32 | 'classProperties', 33 | 'decorators', 34 | 'doExpressions', 35 | 'exportExtensions', 36 | 'flow', 37 | 'functionSent', 38 | 'functionBind', 39 | 'jsx', 40 | 'objectRestSpread', 41 | 'dynamicImport', 42 | 'numericSeparator', 43 | 'importMeta', 44 | ], 45 | }, 46 | generatorOpts: { 47 | generator: recast.print, 48 | }, 49 | plugins: [(transform.default || transform)(babel)], 50 | sourceMaps: true, 51 | }); 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /website/src/parsers/graphql/graphql-js.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'graphql/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'graphql-js'; 7 | const defaultOptions = { 8 | noLocation: false, 9 | noSource: false, 10 | }; 11 | 12 | const parserSettingsConfiguration = { 13 | fields: Object.keys(defaultOptions), 14 | }; 15 | 16 | export default { 17 | ...defaultParserInterface, 18 | 19 | id: ID, 20 | displayName: ID, 21 | version: pkg.version, 22 | homepage: pkg.homepage, 23 | locationProps: new Set(['loc']), 24 | 25 | loadParser(callback) { 26 | require(['graphql/language'], ({ parse }) => { 27 | callback({ parse }); 28 | }); 29 | }, 30 | 31 | parse({ parse }, code, options) { 32 | return parse(code, {...defaultOptions, ...options}); 33 | }, 34 | 35 | nodeToRange(node) { 36 | if (node.loc) { 37 | return [node.loc.start, node.loc.end]; 38 | } 39 | }, 40 | 41 | getNodeName(node) { 42 | return node.kind; 43 | }, 44 | 45 | opensByDefault(node, key) { 46 | return key === 'definitions'; 47 | }, 48 | 49 | renderSettings(parserSettings, onChange) { 50 | return ( 51 | 56 | ); 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /website/src/parsers/js/utils/eslint4Utils.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../utils/compileModule'; 2 | import {parseNoPatch} from 'babel-eslint'; 3 | 4 | export function formatResults(results) { 5 | return results.length === 0 6 | ? 'Lint rule not fired.' 7 | : results.map(formatResult).join('').trim(); 8 | } 9 | 10 | export function formatResult(result) { 11 | var pointer = '-'.repeat(result.column - 1) + '^'; 12 | return ` 13 | // ${result.message} (at ${result.line}:${result.column}) 14 | ${result.source} 15 | // ${pointer} 16 | `; 17 | } 18 | 19 | export function defineRule(eslint, code) { 20 | // Compile the transform code and install it as an ESLint rule. The rule 21 | // name doesn't really matter here, so we'll just use a hard-coded name. 22 | const rule = compileModule(code); 23 | eslint.defineRule('astExplorerRule', rule.default || rule); 24 | } 25 | 26 | export function runRule(code, eslint, sourceCode) { 27 | // Run the ESLint rule on the AST of the provided code. 28 | // Reference: http://eslint.org/docs/developer-guide/nodejs-api 29 | const ast = parseNoPatch(code, { 30 | sourceType: 'module', 31 | }); 32 | const results = eslint.verify(new sourceCode(code, ast), { 33 | env: {es6: true}, 34 | parserOptions: { 35 | ecmaVersion: 8, 36 | sourceType: 'module', 37 | ecmaFeatures: {experimentalObjectRestSpread: true}, 38 | }, 39 | rules: { 40 | astExplorerRule: 2, 41 | }, 42 | }); 43 | return formatResults(results); 44 | } 45 | -------------------------------------------------------------------------------- /website/src/parsers/js/utils/eslintUtils.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../utils/compileModule'; 2 | import {parseNoPatch} from 'babel-eslint'; 3 | 4 | export function formatResults(results) { 5 | return results.length === 0 6 | ? 'Lint rule not fired.' 7 | : results.map(formatResult).join('').trim(); 8 | } 9 | 10 | export function formatResult(result) { 11 | var pointer = '-'.repeat(result.column - 1) + '^'; 12 | return ` 13 | // ${result.message} (at ${result.line}:${result.column}) 14 | ${result.source} 15 | // ${pointer} 16 | `; 17 | } 18 | 19 | export function defineRule(eslintRules, code) { 20 | // Compile the transform code and install it as an ESLint rule. The rule 21 | // name doesn't really matter here, so we'll just use a hard-coded name. 22 | const rule = compileModule(code); 23 | eslintRules.define('astExplorerRule', rule.default || rule); 24 | } 25 | 26 | export function runRule(code, eslint, sourceCode) { 27 | // Run the ESLint rule on the AST of the provided code. 28 | // Reference: http://eslint.org/docs/developer-guide/nodejs-api 29 | const ast = parseNoPatch(code, { 30 | sourceType: 'module', 31 | }); 32 | const results = eslint.verify(new sourceCode(code, ast), { 33 | env: {es6: true}, 34 | parserOptions: { 35 | ecmaVersion: 8, 36 | sourceType: 'module', 37 | ecmaFeatures: {experimentalObjectRestSpread: true}, 38 | }, 39 | rules: { 40 | astExplorerRule: 2, 41 | }, 42 | }); 43 | return formatResults(results); 44 | } 45 | -------------------------------------------------------------------------------- /website/src/parsers/webidl/webidl2.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'webidl2/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'webidl2'; 7 | const defaultOptions = { 8 | allowNestedTypedefs: false, 9 | }; 10 | 11 | const parserSettingsConfiguration = { 12 | fields: Object.keys(defaultOptions), 13 | }; 14 | 15 | export default { 16 | ...defaultParserInterface, 17 | 18 | id: ID, 19 | displayName: ID, 20 | version: pkg.version, 21 | homepage: pkg.homepage || 'https://github.com/w3c/webidl2.js', 22 | 23 | getNodeName(node) { 24 | if (node.name) { 25 | return node.name + (node.optional ? '?' : ''); 26 | } else if (node.type) { 27 | return node.type; 28 | } else if (node.idlType) { 29 | return node.idlType.idlType || node.idlType; 30 | } 31 | }, 32 | 33 | loadParser(callback) { 34 | require(['webidl2'], callback); 35 | }, 36 | 37 | parse({ parse }, code, options) { 38 | return parse(code, {...defaultOptions, ...options}); 39 | }, 40 | 41 | opensByDefault(node, key) { 42 | return key === 'members'; 43 | }, 44 | 45 | renderSettings(parserSettings, onChange) { 46 | return ( 47 | 52 | ); 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /website/src/parsers/js/flow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; // eslint-disable-line no-unused-vars 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'flow-parser/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'flow'; 7 | export const defaultOptions = { 8 | esproposal_class_instance_fields: true, 9 | esproposal_class_static_fields: true, 10 | esproposal_decorators: true, 11 | esproposal_export_star_as: true, 12 | types: true, 13 | }; 14 | export const parserSettingsConfiguration = { 15 | fields: [ 16 | 'esproposal_class_instance_fields', 17 | 'esproposal_class_static_fields', 18 | 'esproposal_decorators', 19 | 'esproposal_export_star_as', 20 | 'types', 21 | ], 22 | }; 23 | 24 | export default { 25 | ...defaultParserInterface, 26 | 27 | id: ID, 28 | displayName: ID, 29 | version: pkg.version, 30 | homepage: pkg.homepage || 'https://flowtype.org/', 31 | locationProps: new Set(['range', 'loc']), 32 | 33 | loadParser(callback) { 34 | require(['flow-parser'], callback); 35 | }, 36 | 37 | parse(flowParser, code, options) { 38 | return flowParser.parse(code, {...defaultOptions, ...options}); 39 | }, 40 | 41 | renderSettings(parserSettings, onChange) { 42 | return ( 43 | 48 | ); 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /website/src/parsers/js/acorn-to-esprima.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'acorn-to-esprima/package.json'; 3 | 4 | const ID = 'acorn-to-esprima'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage, 13 | locationProps: new Set(['loc', 'start', 'end', 'range']), 14 | showInMenu: false, 15 | 16 | loadParser(callback) { 17 | require(['acorn-to-esprima', 'babel5'], (acornToEsprima, {acorn: {tokTypes}, traverse, parse}) => { 18 | callback({ 19 | ...acornToEsprima, 20 | tokTypes, 21 | traverse, 22 | parse, 23 | }); 24 | }); 25 | }, 26 | 27 | parse(parser, code) { 28 | const opts = { 29 | locations: true, 30 | ranges: true, 31 | }; 32 | 33 | const comments = opts.onComment = []; 34 | const tokens = opts.onToken = []; 35 | 36 | let ast = parser.parse(code, opts); 37 | 38 | ast.tokens = parser.toTokens(tokens, parser.tokTypes); 39 | parser.convertComments(comments); 40 | ast.comments = comments; 41 | parser.attachComments(ast, comments, ast.tokens); 42 | parser.toAST(ast, parser.traverse); 43 | 44 | return ast; 45 | }, 46 | 47 | nodeToRange(node) { 48 | if (typeof node.start !== 'undefined') { 49 | return [node.start, node.end]; 50 | } 51 | }, 52 | 53 | _ignoredProperties: new Set([ 54 | '_paths', 55 | '_babelType', 56 | '__clone', 57 | ]), 58 | }; 59 | -------------------------------------------------------------------------------- /server/handlers/parse.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const snippets = prepareData(require(process.env.SNIPPET_FILE)); 4 | const snippetRevisions = prepareData(require(process.env.REVISION_FILE)); 5 | 6 | function notFound(req, res) { 7 | console.error(`Not found: ${req.path}`); 8 | res.sendStatus(404); 9 | } 10 | 11 | module.exports = express.Router() 12 | // Load snippet 13 | .get('/:snippetid/:revisionid', load); 14 | 15 | function prepareData(data) { 16 | // Array of objects -> Map index by object ID 17 | const m = new Map(); 18 | data.forEach(obj => m.set(obj._id, obj)); 19 | return m; 20 | } 21 | 22 | function load(req, res, next) { 23 | const snippet = snippets.get(req.params.snippetid); 24 | let revisionID = req.params.revisionid; 25 | 26 | if (!snippet) { 27 | return notFound(req, res); 28 | } 29 | 30 | if (revisionID === 'latest') { 31 | revisionID = snippet.revisions.length - 1; 32 | } 33 | 34 | if (+revisionID != revisionID || revisionID >= snippet.revisions.length) { 35 | return notFound(req, res); 36 | } 37 | 38 | const revision = snippetRevisions.get(snippet.revisions[revisionID].objectId); 39 | 40 | if (!revision) { 41 | return notFound(req, res); 42 | } 43 | 44 | const copy = Object.assign( 45 | { 46 | revisionID: +revisionID, 47 | snippetID: snippet._id, 48 | }, 49 | revision 50 | ); 51 | delete copy._id; 52 | 53 | // The data will never change but we don't want to keep it in caches 54 | // unnecessarily 55 | // res.append('Cache-Control', 'max-age=86400, public'); // 24h 56 | res.json(copy); 57 | } 58 | -------------------------------------------------------------------------------- /website/src/parsers/lua/luaparse.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'luaparse/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'luaparse'; 7 | 8 | const defaultOptions = { 9 | ranges: true, 10 | locations: false, 11 | comments: true, 12 | scope: false, 13 | luaVersion: '5.1', 14 | }; 15 | 16 | const settingsConfiguration = { 17 | fields: [ 18 | 'ranges', 19 | 'locations', 20 | 'comments', 21 | 'scope', 22 | ['luaVersion', ['5.1', '5.2', '5.3']], 23 | ], 24 | required: new Set(['ranges']), 25 | }; 26 | 27 | export default { 28 | ...defaultParserInterface, 29 | 30 | id: ID, 31 | displayName: ID, 32 | version: `${pkg.version}`, 33 | homepage: pkg.homepage, 34 | locationProps: new Set(['range', 'loc']), 35 | 36 | loadParser(callback) { 37 | require(['luaparse'], callback); 38 | }, 39 | 40 | parse(luaparse, code, options={}) { 41 | return luaparse.parse(code, {...defaultOptions, ...options}); 42 | }, 43 | 44 | renderSettings(parserSettings, onChange) { 45 | return ( 46 |
47 |

48 | 51 | Option descriptions 52 | 53 |

54 | 59 |
60 | ); 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /website/src/components/buttons/ParserButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import {getParserByID} from '../../parsers'; 4 | 5 | export default class ParserButton extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this._onClick = this._onClick.bind(this); 9 | } 10 | 11 | _onClick({currentTarget}) { 12 | let parserID = currentTarget.getAttribute('data-id'); 13 | this.props.onParserChange(getParserByID(parserID)); 14 | } 15 | 16 | render() { 17 | const parsers = this.props.category.parsers.filter(p => p.showInMenu); 18 | return ( 19 |
20 | 21 | 22 |  {this.props.parser.displayName} 23 | 24 |
    25 | {parsers.map(parser => ( 26 |
  • 27 | 30 |
  • 31 | ))} 32 |
33 | 41 |
42 | ); 43 | } 44 | } 45 | 46 | ParserButton.propTypes = { 47 | onParserChange: PropTypes.func, 48 | onParserSettingsButtonClick: PropTypes.func, 49 | parser: PropTypes.object, 50 | category: PropTypes.object, 51 | }; 52 | -------------------------------------------------------------------------------- /website/src/parsers/regexp/regexp-tree.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'regexp-tree/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'regexp-tree'; 7 | 8 | const defaultOptions = { 9 | captureLocations: true, 10 | }; 11 | 12 | const settingsConfiguration = { 13 | fields: Object.keys(defaultOptions), 14 | }; 15 | 16 | export default { 17 | ...defaultParserInterface, 18 | 19 | id: ID, 20 | displayName: ID, 21 | version: pkg.version, 22 | homepage: pkg.homepage, 23 | locationProps: new Set(['loc']), 24 | 25 | loadParser(callback) { 26 | require(['regexp-tree'], (regexpTree) => { 27 | callback(regexpTree); 28 | }); 29 | }, 30 | 31 | parse(regexpTree, code, options={}) { 32 | options = Object.assign({}, defaultOptions, options); 33 | 34 | regexpTree 35 | .parser 36 | .setOptions(options); 37 | 38 | return regexpTree.parse(code); 39 | }, 40 | 41 | nodeToRange(node) { 42 | if (node.loc != null) { 43 | return [node.loc.start, node.loc.end]; 44 | } 45 | }, 46 | 47 | getNodeName(node) { 48 | return node.type; 49 | }, 50 | 51 | opensByDefault(node, key) { 52 | return ( 53 | node.type === 'RegExp' || 54 | key === 'body' || 55 | key === 'expressions' 56 | ); 57 | }, 58 | 59 | renderSettings(parserSettings, onChange) { 60 | return ( 61 | 66 | ); 67 | }, 68 | }; 69 | -------------------------------------------------------------------------------- /website/fontcustom/fontcustom.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon Font: fontcustom 3 | */ 4 | 5 | @font-face { 6 | font-family: "fontcustom"; 7 | src: url("./fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.eot"); 8 | src: url("./fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.eot?#iefix") format("embedded-opentype"), 9 | url("./fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff2") format("woff2"), 10 | url("./fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff") format("woff"), 11 | url("./fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.ttf") format("truetype"), 12 | url("./fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.svg#fontcustom") format("svg"); 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | @media screen and (-webkit-min-device-pixel-ratio:0) { 18 | @font-face { 19 | font-family: "fontcustom"; 20 | src: url("./fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.svg#fontcustom") format("svg"); 21 | } 22 | } 23 | 24 | [data-icon]:before { content: attr(data-icon); } 25 | 26 | [data-icon]:before, 27 | .icon-GraphQL_Logo:before, 28 | .icon-handlebars:before, 29 | .icon-icu:before, 30 | .icon-scala:before { 31 | display: inline-block; 32 | font-family: "fontcustom"; 33 | font-style: normal; 34 | font-weight: normal; 35 | font-variant: normal; 36 | line-height: 1; 37 | text-decoration: inherit; 38 | text-rendering: optimizeLegibility; 39 | text-transform: none; 40 | -moz-osx-font-smoothing: grayscale; 41 | -webkit-font-smoothing: antialiased; 42 | font-smoothing: antialiased; 43 | } 44 | 45 | .icon-GraphQL_Logo:before { content: "\f100"; } 46 | .icon-handlebars:before { content: "\f101"; } 47 | .icon-icu:before { content: "\f102"; } 48 | .icon-scala:before { content: "\f103"; } 49 | -------------------------------------------------------------------------------- /website/src/components/Transformer.js: -------------------------------------------------------------------------------- 1 | import Editor from './Editor'; 2 | import JSCodeshiftEditor from './JSCodeshiftEditor'; 3 | import PropTypes from 'prop-types'; 4 | import PubSub from 'pubsub-js'; 5 | import React from 'react'; 6 | import SplitPane from './SplitPane'; 7 | import TransformOutput from './TransformOutput'; 8 | import PrettierButton from './buttons/PrettierButton'; 9 | 10 | function resize() { 11 | PubSub.publish('PANEL_RESIZE'); 12 | } 13 | 14 | export default function Transformer(props) { 15 | const plainEditor = React.createElement( 16 | props.transformer.id === 'jscodeshift' ? JSCodeshiftEditor : Editor, 17 | { 18 | highlight: false, 19 | value: props.transformCode, 20 | onContentChange: props.onContentChange, 21 | enableFormatting: props.enableFormatting, 22 | } 23 | ); 24 | 25 | const formattingEditor = (
26 | 27 | {plainEditor} 28 |
) 29 | 30 | return ( 31 | 34 | {formattingEditor} 35 | 41 | 42 | ); 43 | } 44 | 45 | Transformer.propTypes = { 46 | defaultTransformCode: PropTypes.string, 47 | transformCode: PropTypes.string, 48 | transformer: PropTypes.object, 49 | code: PropTypes.string, 50 | mode: PropTypes.string, 51 | onContentChange: PropTypes.func, 52 | toggleFormatting: PropTypes.func, 53 | enableFormatting: PropTypes.bool, 54 | }; 55 | -------------------------------------------------------------------------------- /website/src/parsers/js/typescript-eslint-parser.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'typescript-eslint-parser/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'typescript-eslint-parser'; 7 | 8 | const defaultOptions = { 9 | range: true, 10 | loc: false, 11 | tokens: false, 12 | comment: false, 13 | tolerant: false, 14 | useJSXTextNode: false, 15 | 16 | ecmaFeatures: { 17 | jsx: true, 18 | }, 19 | }; 20 | 21 | const parserSettingsConfiguration = { 22 | fields: [ 23 | 'range', 24 | 'loc', 25 | 'tokens', 26 | 'comment', 27 | 'tolerant', 28 | 'useJSXTextNode', 29 | { 30 | key: 'ecmaFeatures', 31 | title: 'ecmaFeatures', 32 | fields: Object.keys(defaultOptions.ecmaFeatures), 33 | settings: 34 | settings => settings.ecmaFeatures || {...defaultOptions.ecmaFeatures}, 35 | }, 36 | ], 37 | required: new Set(['range']), 38 | }; 39 | 40 | export default { 41 | ...defaultParserInterface, 42 | 43 | id: ID, 44 | displayName: ID, 45 | version: pkg.version, 46 | homepage: pkg.homepage, 47 | locationProps: new Set(['loc', 'start', 'end', 'range']), 48 | 49 | loadParser(callback) { 50 | require(['typescript-eslint-parser'], callback); 51 | }, 52 | 53 | parse(parser, code, options) { 54 | return parser.parse(code, {...defaultOptions, ...options} ); 55 | }, 56 | 57 | renderSettings(parserSettings, onChange) { 58 | return ( 59 | 64 | ); 65 | }, 66 | }; 67 | -------------------------------------------------------------------------------- /website/src/parsers/js/esprima.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; // eslint-disable-line no-unused-vars 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'esprima/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'esprima'; 7 | const defaultOptions = { 8 | sourceType: 'module', 9 | loc: false, 10 | range: true, 11 | tokens: false, 12 | comment: false, 13 | attachComment: false, 14 | tolerant: false, 15 | jsx: true, 16 | }; 17 | 18 | const parserSettingsConfiguration = { 19 | fields: [ 20 | ['sourceType', ['script', 'module']], 21 | 'range', 22 | 'loc', 23 | 'attachComment', 24 | 'comment', 25 | 'tokens', 26 | 'tolerant', 27 | 'jsx', 28 | ], 29 | required: new Set(['range']), 30 | }; 31 | 32 | export default { 33 | ...defaultParserInterface, 34 | 35 | id: ID, 36 | displayName: ID, 37 | version: pkg.version, 38 | homepage: pkg.homepage, 39 | locationProps: new Set(['range', 'loc']), 40 | 41 | loadParser(callback) { 42 | require(['esprima'], callback); 43 | }, 44 | 45 | parse(esprima, code, options) { 46 | return esprima.parse(code, {...defaultOptions, ...options}); 47 | }, 48 | 49 | *forEachProperty(node) { 50 | for (let prop in node) { 51 | if (typeof node[prop] === 'function') { 52 | continue; 53 | } 54 | yield { 55 | value: node[prop], 56 | key: prop, 57 | computed: false, 58 | }; 59 | } 60 | }, 61 | 62 | renderSettings(parserSettings, onChange) { 63 | return ( 64 | 69 | ); 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /website/src/parsers/markdown/remark.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'remark/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'remark'; 7 | const defaultOptions = { 8 | gfm: true, 9 | yaml: true, 10 | commonmark: false, 11 | footnotes: false, 12 | }; 13 | 14 | const parserSettingsConfiguration = { 15 | fields: Object.keys(defaultOptions), 16 | }; 17 | 18 | export default { 19 | ...defaultParserInterface, 20 | 21 | id: ID, 22 | displayName: ID, 23 | version: pkg.version, 24 | homepage: pkg.homepage, 25 | locationProps: new Set(['position']), 26 | 27 | loadParser(callback) { 28 | require(['remark'], callback); 29 | }, 30 | 31 | parse(remark, code, options) { 32 | return remark() 33 | .data('settings', {...defaultOptions, ...options}) 34 | .parse(code); 35 | }, 36 | 37 | nodeToRange({position}) { 38 | if (position) { 39 | return [position.start.offset, position.end.offset]; 40 | } 41 | }, 42 | 43 | getNodeName(node) { 44 | return node.type; 45 | }, 46 | 47 | opensByDefault(node, key) { 48 | return key === 'children'; 49 | }, 50 | 51 | renderSettings(parserSettings, onChange) { 52 | return ( 53 |
54 |

55 | 58 | Option descriptions 59 | 60 |

61 | 66 |
67 | ); 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /website/src/components/buttons/SnippetButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import ForkButton from './ForkButton'; 4 | import NewButton from './NewButton'; 5 | import SaveButton from './SaveButton'; 6 | import ShareButton from './ShareButton'; 7 | import cx from 'classnames'; 8 | 9 | export default function SnippetButton(props) { 10 | const canForkAndNotSave = props.canFork && !props.canSave; 11 | const savingOrForking = props.saving || props.forking; 12 | 13 | return ( 14 |
15 | 16 | 17 |  Snippet 18 | 19 |
    20 |
  • 21 |
  • 22 |
  • 23 |
  • 24 |
25 | 44 |
45 | ); 46 | } 47 | 48 | SnippetButton.propTypes = { 49 | canFork: PropTypes.bool, 50 | canSave: PropTypes.bool, 51 | forking: PropTypes.bool, 52 | onFork: PropTypes.func, 53 | onSave: PropTypes.func, 54 | saving: PropTypes.bool, 55 | }; 56 | -------------------------------------------------------------------------------- /website/src/parsers/js/uglify.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from '../../../packages/uglify2-harmony/package.json'; 3 | import compileModule from '../utils/compileModule'; 4 | 5 | const ID = 'uglify-js'; 6 | 7 | export default { 8 | ...defaultParserInterface, 9 | 10 | id: ID, 11 | displayName: ID, 12 | version: pkg.version, 13 | homepage: pkg.homepage, 14 | locationProps: new Set(['start', 'end']), 15 | 16 | loadParser(callback) { 17 | require([ 18 | 'raw-loader!../../../packages/uglify2-harmony/lib/utils.js', 19 | 'raw-loader!../../../packages/uglify2-harmony/lib/ast.js', 20 | 'raw-loader!../../../packages/uglify2-harmony/lib/parse.js', 21 | ], (...contents) => { 22 | contents.push('exports.parse = parse;'); 23 | callback(compileModule(contents.join('\n\n'))); 24 | }); 25 | }, 26 | 27 | parse(UglifyJS, code) { 28 | return UglifyJS.parse(code); 29 | }, 30 | 31 | getNodeName(node) { 32 | let type = node.TYPE; 33 | if (type === 'Token') { 34 | type += `(${node.type})`; 35 | } 36 | return type; 37 | }, 38 | 39 | nodeToRange(node) { 40 | let start, end; 41 | switch (node.TYPE) { 42 | case 'Token': 43 | start = end = node; 44 | break; 45 | case undefined: 46 | return; 47 | default: 48 | ({ start, end } = node); 49 | break; 50 | } 51 | return [start.pos, end.endpos]; 52 | }, 53 | 54 | opensByDefault(node, key) { 55 | return ( 56 | key === 'body' || 57 | key === 'elements' || // array literals 58 | key === 'definitions' || // variable declaration 59 | key === 'properties' 60 | ); 61 | }, 62 | 63 | _ignoredProperties: new Set(['_walk', 'CTOR']), 64 | }; 65 | -------------------------------------------------------------------------------- /website/src/parsers/js/shift.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; // eslint-disable-line no-unused-vars 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'shift-parser/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'shift'; 7 | const defaultOptions = { 8 | loc: true, 9 | earlyErrors: false, 10 | sourceType: 'module', 11 | }; 12 | 13 | const parserSettingsConfiguration = { 14 | fields: [ 15 | ['sourceType', ['script', 'module']], 16 | 'loc', 17 | 'earlyErrors', 18 | ], 19 | required: new Set(['loc']), 20 | }; 21 | 22 | export default { 23 | ...defaultParserInterface, 24 | 25 | id: ID, 26 | displayName: ID, 27 | version: pkg.version, 28 | homepage: pkg.homepage, 29 | locationProps: new Set(['loc']), 30 | 31 | loadParser(callback) { 32 | require(['shift-parser'], callback); 33 | }, 34 | 35 | parse(shift, code, options) { 36 | options = {...defaultOptions, ...options}; 37 | const parseMethod = options.sourceType === 'module' ? 38 | 'parseModule' : 39 | 'parseScript'; 40 | return shift[parseMethod](code, options); 41 | }, 42 | 43 | nodeToRange({ loc }) { 44 | if (loc) { 45 | return [loc.start.offset, loc.end.offset]; 46 | } 47 | }, 48 | 49 | renderSettings(parserSettings, onChange) { 50 | return ( 51 | 56 | ); 57 | }, 58 | 59 | opensByDefault(node, key) { 60 | return ( 61 | key === 'items' || 62 | key === 'declaration' || 63 | key === 'declarators' || 64 | key === 'statements' || 65 | key === 'expression' || 66 | key === 'body' 67 | ); 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /website/src/parsers/css/postcss.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from './utils/defaultCSSParserInterface'; 3 | import pkg from 'postcss/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'postcss'; 7 | const defaultOptions = { 8 | parser: 'built-in', 9 | }; 10 | 11 | const parserSettingsConfiguration = { 12 | fields: [ 13 | ['parser', ['built-in', 'scss', 'less', 'safe-parser']], 14 | ], 15 | }; 16 | 17 | export default { 18 | ...defaultParserInterface, 19 | 20 | id: ID, 21 | displayName: ID, 22 | version: pkg.version, 23 | homepage: pkg.homepage, 24 | locationProps: new Set(['source']), 25 | 26 | loadParser(callback) { 27 | require(['postcss/lib/parse', 'postcss-scss/lib/scss-parse', 'postcss-less/dist/less-parse', 'postcss-safe-parser'], (builtIn, scss, less, safe) => { 28 | callback({ 29 | 'built-in': builtIn, 30 | scss, 31 | less, 32 | 'safe-parser': safe, 33 | }); 34 | }); 35 | }, 36 | 37 | parse(parsers, code, options) { 38 | return defaultParserInterface.parse.call( 39 | this, 40 | parsers[options.parser || defaultOptions.parser], 41 | code 42 | ); 43 | }, 44 | 45 | nodeToRange({ source: range }) { 46 | if (!range || !range.end) return; 47 | return [ 48 | this.getOffset(range.start), 49 | this.getOffset(range.end) + 1, 50 | ]; 51 | }, 52 | 53 | opensByDefault(node, key) { 54 | return key === 'nodes'; 55 | }, 56 | 57 | _ignoredProperties: new Set(['parent', 'input']), 58 | 59 | renderSettings(parserSettings, onChange) { 60 | return ( 61 | 66 | ); 67 | }, 68 | }; 69 | -------------------------------------------------------------------------------- /website/src/components/GistBanner.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Data storage is moved from Parse to Gists. It won't be possible anymore to 3 | * save new revisions of existing Parse snippets. We let the visitor know. 4 | */ 5 | 6 | import PropTypes from 'prop-types'; 7 | import React from 'react'; 8 | import {connect} from 'react-redux'; 9 | import {getRevision} from '../store/selectors'; 10 | 11 | const buttonStyle = { 12 | backgroundColor: 'transparent', 13 | border: 'none', 14 | cursor: 'pointer', 15 | float: 'left', 16 | fontSize: 14, 17 | margin: 0, 18 | padding: 0, 19 | paddingRight: 10, 20 | }; 21 | 22 | class GistBanner extends React.Component { 23 | constructor(props) { 24 | super(props); 25 | this.state = { 26 | visible: true, 27 | }; 28 | this._hide = this._hide.bind(this); 29 | } 30 | 31 | componentWillReceiveProps(newProps) { 32 | const newRevision = newProps.revision; 33 | const oldRevision = this.props.revision; 34 | if (newRevision && 35 | (!oldRevision || newRevision.getSnippetID() !== oldRevision.getSnippetID())) { 36 | this.setState({visible: true}); 37 | } 38 | } 39 | 40 | _hide() { 41 | this.setState({visible: false}); 42 | } 43 | 44 | render() { 45 | if (!this.state.visible) { 46 | return null; 47 | } 48 | 49 | if (!this.props.revision || this.props.revision.canSave()) { 50 | return null; 51 | } 52 | 53 | return ( 54 |
55 | This snippet is read-only. You can still save changes 56 | by forking it. 57 | 60 |
61 | ); 62 | } 63 | } 64 | 65 | GistBanner.propTypes = { 66 | revision: PropTypes.object, 67 | } 68 | 69 | export default connect(state => ({revision: getRevision(state)}))(GistBanner); 70 | -------------------------------------------------------------------------------- /website/.fontcustom-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "checksum": { 3 | "previous": "ded1f9e27e44b12eb0dbab11fa9eb21be9c2fc4cd000b173521a3da8e5da3360", 4 | "current": "ded1f9e27e44b12eb0dbab11fa9eb21be9c2fc4cd000b173521a3da8e5da3360" 5 | }, 6 | "fonts": [ 7 | "fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.ttf", 8 | "fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.svg", 9 | "fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff", 10 | "fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.eot", 11 | "fontcustom/fontcustom_ded1f9e27e44b12eb0dbab11fa9eb21b.woff2" 12 | ], 13 | "glyphs": { 14 | "GraphQL_Logo": { 15 | "codepoint": 61696, 16 | "source": "./fontcustom/input-svg/GraphQL_Logo.svg" 17 | }, 18 | "handlebars": { 19 | "codepoint": 61697, 20 | "source": "./fontcustom/input-svg/handlebars.svg" 21 | }, 22 | "icu": { 23 | "codepoint": 61698, 24 | "source": "./fontcustom/input-svg/icu.svg" 25 | }, 26 | "scala": { 27 | "codepoint": 61699, 28 | "source": "./fontcustom/input-svg/scala.svg" 29 | } 30 | }, 31 | "options": { 32 | "autowidth": false, 33 | "config": "./fontcustom/config.yml", 34 | "copyright": "", 35 | "css3": false, 36 | "css_selector": ".icon-{{glyph}}", 37 | "debug": false, 38 | "font_ascent": 448, 39 | "font_descent": 64, 40 | "font_design_size": 16, 41 | "font_em": 512, 42 | "font_name": "fontcustom", 43 | "force": false, 44 | "input": { 45 | "templates": "./fontcustom/input-svg/", 46 | "vectors": "./fontcustom/input-svg/" 47 | }, 48 | "no_hash": false, 49 | "output": { 50 | "css": "fontcustom", 51 | "fonts": "fontcustom", 52 | "preview": "fontcustom" 53 | }, 54 | "preprocessor_path": null, 55 | "quiet": false, 56 | "templates": [ 57 | "css", 58 | "preview" 59 | ] 60 | }, 61 | "templates": [ 62 | "fontcustom/fontcustom.css", 63 | "fontcustom/fontcustom-preview.html" 64 | ] 65 | } -------------------------------------------------------------------------------- /website/src/components/buttons/CategoryButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from 'classnames'; 4 | import {getCategoryByID, categories} from '../../parsers'; 5 | 6 | const categoryIcon = { 7 | javascript: 'fa-jsfiddle', 8 | css: 'fa-css3', 9 | graphql: 'icon-GraphQL_Logo', 10 | handlebars: 'icon-handlebars', 11 | htmlmixed: 'fa-html5', 12 | icu: 'icon-icu', 13 | sql: 'fa-database', 14 | 'text/x-scala': 'icon-scala', 15 | webidl: 'fa-th-list', 16 | yaml: 'fa-yc', 17 | }; 18 | 19 | export default class CategoryButton extends React.Component { 20 | constructor(props) { 21 | super(props); 22 | this._onClick = this._onClick.bind(this); 23 | } 24 | 25 | _onClick({currentTarget}) { 26 | let categoryID = currentTarget.getAttribute('data-id'); 27 | this.props.onCategoryChange(getCategoryByID(categoryID)); 28 | } 29 | 30 | render() { 31 | return ( 32 |
33 | 34 | 41 |  {this.props.category.displayName} 42 | 43 |
    44 | {categories.map(category => ( 45 |
  • 46 | 55 |
  • 56 | ))} 57 |
58 |
59 | ); 60 | } 61 | } 62 | 63 | CategoryButton.propTypes = { 64 | onCategoryChange: PropTypes.func.isRequired, 65 | category: PropTypes.object.isRequired, 66 | }; 67 | -------------------------------------------------------------------------------- /website/src/components/JSONEditor.js: -------------------------------------------------------------------------------- 1 | import CodeMirror from 'codemirror'; 2 | import 'codemirror/mode/javascript/javascript'; 3 | import 'codemirror/addon/fold/foldgutter'; 4 | import 'codemirror/addon/fold/foldcode'; 5 | import 'codemirror/addon/fold/brace-fold'; 6 | import PropTypes from 'prop-types'; 7 | import PubSub from 'pubsub-js'; 8 | import React from 'react'; 9 | 10 | export default class Editor extends React.Component { 11 | 12 | componentWillReceiveProps(nextProps) { 13 | if (nextProps.value !== this.codeMirror.getValue()) { 14 | // preserve scroll position 15 | let info = this.codeMirror.getScrollInfo(); 16 | this.codeMirror.setValue(nextProps.value); 17 | this.codeMirror.scrollTo(info.left, info.top); 18 | } 19 | } 20 | 21 | shouldComponentUpdate() { 22 | return false; 23 | } 24 | 25 | componentDidMount() { 26 | this._subscriptions = []; 27 | this.codeMirror = CodeMirror( // eslint-disable-line new-cap 28 | this.container, 29 | { 30 | value: this.props.value, 31 | mode: {name: 'javascript', json: true}, 32 | readOnly: true, 33 | lineNumbers: true, 34 | foldGutter: true, 35 | gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], 36 | } 37 | ); 38 | 39 | this._subscriptions.push( 40 | PubSub.subscribe('PANEL_RESIZE', () => { 41 | if (this.codeMirror) { 42 | this.codeMirror.refresh(); 43 | } 44 | }) 45 | ); 46 | } 47 | 48 | componentWillUnmount() { 49 | this._unbindHandlers(); 50 | let container = this.container; 51 | container.removeChild(container.children[0]); 52 | this.codeMirror = null; 53 | } 54 | 55 | _unbindHandlers() { 56 | this._subscriptions.forEach(PubSub.unsubscribe); 57 | } 58 | 59 | render() { 60 | return ( 61 |
this.container = c}/> 62 | ); 63 | } 64 | } 65 | 66 | Editor.propTypes = { 67 | value: PropTypes.string, 68 | className: PropTypes.string, 69 | }; 70 | -------------------------------------------------------------------------------- /server/handlers/gist/saveAnonymousGist.js: -------------------------------------------------------------------------------- 1 | const {AUTH_TOKEN, SETTINGS_FORMAT} = require('../../constants'); 2 | const GitHub = require('github-api'); 3 | 4 | const gh = new GitHub({token: AUTH_TOKEN}); 5 | 6 | /** 7 | * Expects an array of the form [[filename, content], [filename, content], ...] 8 | */ 9 | function makeFiles(files) { 10 | return files.reduce( 11 | (obj, [filename, content]) => (obj[filename] = (content ? {content} : content), obj), 12 | {} 13 | ); 14 | } 15 | 16 | function getDataFromBody(body, additionalData={}) { 17 | const files = [ 18 | [ 19 | 'astexplorer.json', 20 | JSON.stringify( 21 | Object.assign( 22 | { 23 | v: SETTINGS_FORMAT, 24 | parserID: body.parserID, 25 | toolID: body.toolID, 26 | settings: body.settings, 27 | versions: body.versions, 28 | }, 29 | additionalData 30 | ), 31 | null, 32 | 2 33 | ), 34 | ], 35 | [body.filename, body.code], 36 | ]; 37 | 38 | // null value indicates deletion 39 | if (body.transform || body.transform === null) { 40 | files.push(['transform.js', body.transform]); 41 | } 42 | 43 | return { 44 | files: makeFiles(files), 45 | description: body.description, 46 | public: Boolean(body.public), 47 | }; 48 | } 49 | 50 | exports.create = (req, res, next) => { 51 | gh.getGist() 52 | .create(getDataFromBody(req.body)) 53 | .then(response => res.json(response.data)) 54 | .catch(next); 55 | }; 56 | 57 | exports.update = (req, res, next) => { 58 | gh.getGist(req.params.snippetid) 59 | .update(getDataFromBody(req.body)) 60 | .then(response => res.json(response.data)) 61 | .catch(next); 62 | }; 63 | 64 | exports.fork = (req, res, next) => { 65 | // We cannot really "fork" an "anonymous" snippet because a user (astexplorer) 66 | // cannot fork it's own gist. 67 | const data = getDataFromBody(req.body); 68 | 69 | gh.getGist() 70 | .create(data) 71 | .then(response => res.json(response.data)) 72 | .catch(next); 73 | }; 74 | -------------------------------------------------------------------------------- /website/src/parsers/glsl/glsl-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'glsl-parser/package.json'; 3 | 4 | const ID = 'glsl-parser'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage, 13 | locationProps: new Set(['loc']), 14 | _ignoredProperties: new Set([ 15 | 'loc', // we ignore the loc itself because it's actually a locally enhanced (not in the actual parser data) 16 | 'parent', // it's pointless to display the parent node in the tree browser 17 | 'stage', // same 18 | ]), 19 | 20 | loadParser(callback) { 21 | require(['glsl-tokenizer/string', 'glsl-parser/direct'], ( 22 | tokenize, 23 | parse 24 | ) => { 25 | callback({ tokenize, parse }); 26 | }); 27 | }, 28 | 29 | parse({ tokenize, parse }, code) { 30 | const tokens = tokenize(code); 31 | const ast = parse(tokens); 32 | // the parser does not yet provide the "end" so this is a workaround https://github.com/stackgl/glsl-parser/issues/17 33 | function decoratePosition(node, end) { 34 | node.loc = { 35 | start: node.token.position || 0, 36 | end, 37 | }; 38 | node.children.forEach((child, i) => { 39 | const nextSibling = node.children[i + 1]; 40 | decoratePosition( 41 | child, 42 | nextSibling && nextSibling.token && 'position' in nextSibling.token 43 | ? nextSibling.token.position - 44 | (nextSibling.token.preceding || []) 45 | .reduce((s, n) => s + (n.data || '').length, 0) 46 | : end 47 | ); 48 | }); 49 | } 50 | decoratePosition(ast, code.length); 51 | return ast; 52 | }, 53 | 54 | nodeToRange({ loc }) { 55 | if (loc) { 56 | return [loc.start, loc.end]; 57 | } 58 | }, 59 | 60 | getNodeName(node) { 61 | return node.type; 62 | }, 63 | 64 | opensByDefault(node, key) { 65 | return key === 'children' && node.type === '(program)'; 66 | }, 67 | }; 68 | -------------------------------------------------------------------------------- /website/src/containers/ToolbarContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import { 3 | save, 4 | selectCategory, 5 | openSettingsDialog, 6 | openShareDialog, 7 | selectTransformer, 8 | hideTransformer, 9 | setParser, 10 | reset, 11 | } from '../store/actions'; 12 | import Toolbar from '../components/Toolbar'; 13 | import * as selectors from '../store/selectors'; 14 | import {logEvent} from '../utils/logger'; 15 | 16 | function mapStateToProps(state) { 17 | const parser = selectors.getParser(state); 18 | 19 | return { 20 | forking: selectors.isForking(state), 21 | saving: selectors.isSaving(state), 22 | canSave: selectors.canSave(state), 23 | canFork: selectors.canFork(state), 24 | category: parser.category, 25 | parser, 26 | transformer: selectors.getTransformer(state), 27 | showTransformer: selectors.showTransformer(state), 28 | snippet: selectors.getRevision(state), 29 | }; 30 | } 31 | 32 | function mapDispatchToProps(dispatch) { 33 | return { 34 | onParserChange: parser => { 35 | dispatch(setParser(parser)); 36 | logEvent('parser', 'select', parser.id); 37 | }, 38 | onCategoryChange: category => { 39 | dispatch(selectCategory(category)); 40 | logEvent('category', 'select', category.id); 41 | }, 42 | onParserSettingsButtonClick: () => { 43 | dispatch(openSettingsDialog()); 44 | logEvent('parser', 'open_settings'); 45 | }, 46 | onShareButtonClick: () => { 47 | dispatch(openShareDialog()); 48 | logEvent('ui', 'open_share'); 49 | }, 50 | onTransformChange: transformer => { 51 | dispatch(transformer ? selectTransformer(transformer) : hideTransformer()); 52 | if (transformer) { 53 | logEvent('tool', 'select', transformer.id); 54 | } 55 | }, 56 | onSave: () => dispatch(save(false)), 57 | onFork: () => dispatch(save(true)), 58 | onNew: () => { 59 | if (global.location.hash) { 60 | global.location.hash = ''; 61 | } else { 62 | dispatch(reset()); 63 | } 64 | }, 65 | }; 66 | } 67 | 68 | export default connect(mapStateToProps, mapDispatchToProps)(Toolbar); 69 | 70 | -------------------------------------------------------------------------------- /website/src/parsers/html/parse5.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'parse5/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'parse5'; 7 | const defaultOptions = { 8 | treeAdapter: 'default', 9 | }; 10 | 11 | const parserSettingsConfiguration = { 12 | fields : [ 13 | ['treeAdapter', ['default', 'htmlparser2']], 14 | ], 15 | }; 16 | 17 | export default { 18 | ...defaultParserInterface, 19 | 20 | id: ID, 21 | displayName: ID, 22 | version: pkg.version, 23 | homepage: pkg.homepage, 24 | locationProps: new Set(['__location']), 25 | 26 | loadParser(callback) { 27 | require([ 28 | 'parse5/lib/parser', 29 | 'parse5/lib/tree_adapters/default', 30 | 'parse5/lib/tree_adapters/htmlparser2', 31 | ], (Parser, defaultAdapter, htmlparser2Adapter) => { 32 | callback({ 33 | Parser, 34 | TreeAdapters: { 35 | default: defaultAdapter, 36 | htmlparser2: htmlparser2Adapter, 37 | }, 38 | }); 39 | }); 40 | }, 41 | 42 | parse({ Parser, TreeAdapters }, code, options) { 43 | this.options = {...defaultOptions, ...options}; 44 | return new Parser({ 45 | treeAdapter: TreeAdapters[this.options.treeAdapter], 46 | locationInfo: true, 47 | }).parse(code); 48 | }, 49 | 50 | getNodeName(node) { 51 | if (this.options.treeAdapter === 'htmlparser2') { 52 | return node.type + (node.name && node.type !== 'root' ? `(${node.name})` : ''); 53 | } else { 54 | return node.nodeName; 55 | } 56 | }, 57 | 58 | nodeToRange({ __location: loc }) { 59 | if (loc) { 60 | return [loc.startOffset, loc.endOffset]; 61 | } 62 | }, 63 | 64 | opensByDefault(node, key) { 65 | return key === 'children' || key === 'childNodes'; 66 | }, 67 | 68 | renderSettings(parserSettings, onChange) { 69 | return ( 70 | 75 | ); 76 | }, 77 | 78 | _ignoredProperties: new Set(['parentNode', 'prev', 'next', 'parent', 'firstChild', 'lastChild']), 79 | }; 80 | -------------------------------------------------------------------------------- /website/src/parsers/js/espree.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'espree/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'espree'; 7 | const defaultOptions = { 8 | range: true, 9 | loc: false, 10 | comment: false, 11 | attachComment: false, 12 | tokens: false, 13 | tolerant: false, 14 | ecmaVersion: 6, 15 | sourceType: 'module', 16 | 17 | ecmaFeatures: { 18 | jsx: true, 19 | globalReturn: true, 20 | experimentalObjectRestSpread: true, 21 | }, 22 | }; 23 | const parserSettingsConfiguration = { 24 | fields: [ 25 | ['ecmaVersion', [3, 5, 6, 7], value => Number(value)], 26 | ['sourceType', ['script', 'module']], 27 | 'range', 28 | 'loc', 29 | 'comment', 30 | 'attachComment', 31 | 'tokens', 32 | 'tolerant', 33 | { 34 | key: 'ecmaFeatures', 35 | title: 'ecmaFeatures', 36 | fields: Object.keys(defaultOptions.ecmaFeatures), 37 | settings: 38 | settings => settings.ecmaFeatures || {...defaultOptions.ecmaFeatures}, 39 | }, 40 | ], 41 | }; 42 | 43 | export default { 44 | ...defaultParserInterface, 45 | 46 | id: ID, 47 | displayName: ID, 48 | version: pkg.version, 49 | homepage: pkg.homepage, 50 | locationProps: new Set(['range', 'loc', 'start', 'end']), 51 | 52 | loadParser(callback) { 53 | require(['espree'], callback); 54 | }, 55 | 56 | parse(espree, code, options) { 57 | return espree.parse(code, {...defaultOptions, ...options}); 58 | }, 59 | 60 | nodeToRange(node) { 61 | if (typeof node.start === 'number') { 62 | return [node.start, node.end]; 63 | } 64 | }, 65 | 66 | renderSettings(parserSettings, onChange) { 67 | return ( 68 |
69 |

70 | 73 | Option descriptions 74 | 75 |

76 | 81 |
82 | ); 83 | }, 84 | }; 85 | -------------------------------------------------------------------------------- /website/src/components/dialogs/SettingsDialog.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default class SettingsDialog extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this._outerClick = this._outerClick.bind(this); 8 | this._onChange = this._onChange.bind(this); 9 | this._reset = this._reset.bind(this); 10 | this._saveAndClose = this._saveAndClose.bind(this); 11 | this.state = { 12 | parserSettings: this.props.parserSettings, 13 | }; 14 | } 15 | 16 | componentWillReceiveProps(nextProps) { 17 | this.setState({parserSettings: nextProps.parserSettings}); 18 | } 19 | 20 | _outerClick(event) { 21 | if (event.target === document.getElementById('SettingsDialog')) { 22 | this._saveAndClose(); 23 | } 24 | } 25 | 26 | _onChange(newSettings) { 27 | this.setState({parserSettings: newSettings}); 28 | } 29 | 30 | _saveAndClose() { 31 | this.props.onSave(this.props.parser, this.state.parserSettings); 32 | this.props.onWantToClose(); 33 | } 34 | 35 | _reset() { 36 | this.setState({parserSettings: {}}); 37 | } 38 | 39 | render() { 40 | if (this.props.visible && this.props.parser.renderSettings) { 41 | return ( 42 |
43 |
44 |
45 |

{this.props.parser.displayName} Settings

46 |
47 |
48 | {this.props.parser.renderSettings( 49 | this.state.parserSettings, 50 | this._onChange 51 | )} 52 |
53 |
54 | 57 | 58 |
59 |
60 |
61 | ); 62 | } 63 | return null; 64 | } 65 | } 66 | 67 | SettingsDialog.propTypes = { 68 | onSave: PropTypes.func, 69 | onWantToClose: PropTypes.func, 70 | visible: PropTypes.bool, 71 | parser: PropTypes.object.isRequired, 72 | parserSettings: PropTypes.object, 73 | }; 74 | -------------------------------------------------------------------------------- /website/src/components/Toolbar.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import CategoryButton from './buttons/CategoryButton'; 4 | import ParserButton from './buttons/ParserButton'; 5 | import SnippetButton from './buttons/SnippetButton'; 6 | import TransformButton from './buttons/TransformButton'; 7 | 8 | export default function Toolbar(props) { 9 | let {parser, transformer, showTransformer} = props; 10 | let parserInfo = parser.id; 11 | let transformerInfo = ''; 12 | if (parser) { 13 | if (parser.version) { 14 | parserInfo += '-' + parser.version; 15 | } 16 | if (parser.homepage) { 17 | parserInfo = 18 | {parserInfo}; 19 | } 20 | } 21 | if (showTransformer) { 22 | transformerInfo = transformer.displayName; 23 | if (transformer.version) { 24 | transformerInfo += '-' + transformer.version; 25 | } 26 | if (transformer.homepage) { 27 | transformerInfo = 28 | {transformerInfo}; 29 | } 30 | transformerInfo = Transformer: {transformerInfo}; 31 | } 32 | 33 | return ( 34 |
35 |

AST Explorer

36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 |
48 | Parser: {parserInfo}
49 | {transformerInfo} 50 |
51 |
52 | ); 53 | } 54 | 55 | Toolbar.propTypes = { 56 | saving: PropTypes.bool, 57 | forking: PropTypes.bool, 58 | onSave: PropTypes.func, 59 | onFork: PropTypes.func, 60 | onParserChange: PropTypes.func, 61 | onParserSettingsButtonClick: PropTypes.func, 62 | onShareButtonClick: PropTypes.func, 63 | onTransformChange: PropTypes.func, 64 | parser: PropTypes.object, 65 | transformer: PropTypes.object, 66 | showTransformer: PropTypes.bool, 67 | canSave: PropTypes.bool, 68 | canFork: PropTypes.bool, 69 | }; 70 | -------------------------------------------------------------------------------- /website/src/components/buttons/TransformButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from 'classnames'; 4 | import {getTransformerByID} from '../../parsers'; 5 | 6 | export default class TransformButton extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this._onClick = this._onClick.bind(this); 10 | this._onToggle = this._onToggle.bind(this); 11 | } 12 | 13 | _onClick({target}) { 14 | let transformID; 15 | if (target.nodeName.toLowerCase() === 'li') { 16 | transformID = target.children[0].value; 17 | } else { 18 | transformID = target.value; 19 | } 20 | this.props.onTransformChange(getTransformerByID(transformID)); 21 | } 22 | 23 | _onToggle() { 24 | if (this.props.transformer) { 25 | this.props.onTransformChange(null); 26 | } 27 | } 28 | 29 | render() { 30 | return ( 31 |
36 | 51 | {!!this.props.category.transformers.length &&
    52 | {this.props.category.transformers.map(transformer => ( 53 |
  • 60 | 63 |
  • 64 | ))} 65 |
} 66 |
67 | ); 68 | } 69 | } 70 | 71 | TransformButton.propTypes = { 72 | category: PropTypes.object, 73 | transformer: PropTypes.object, 74 | showTransformer: PropTypes.bool, 75 | onTransformChange: PropTypes.func, 76 | }; 77 | -------------------------------------------------------------------------------- /website/src/parsers/index.js: -------------------------------------------------------------------------------- 1 | const localRequire = require.context('./', true, /^\.\/(?!utils)[^/]+\/(transformers\/([^/]+)\/)?(codeExample\.txt|[^/]+?\.js)$/); 2 | 3 | const files = 4 | localRequire.keys() 5 | .map(name => name.split('/').slice(1)); 6 | 7 | const categoryByID = {}; 8 | const parserByID = {}; 9 | const transformerByID = {}; 10 | 11 | const restrictedParserNames = new Set([ 12 | 'index.js', 13 | 'codeExample.txt', 14 | 'transformers', 15 | 'utils', 16 | ]); 17 | 18 | export const categories = 19 | files 20 | .filter(name => name[1] === 'index.js') 21 | .map(([catName]) => { 22 | let category = localRequire(`./${catName}/index.js`); 23 | 24 | categoryByID[category.id] = category; 25 | 26 | category.codeExample = localRequire(`./${catName}/codeExample.txt`); 27 | 28 | let catFiles = 29 | files 30 | .filter(([curCatName]) => curCatName === catName) 31 | .map(name => name.slice(1)); 32 | 33 | category.parsers = 34 | catFiles 35 | .filter(([parserName]) => !restrictedParserNames.has(parserName)) 36 | .map(([parserName]) => { 37 | let parser = localRequire(`./${catName}/${parserName}`); 38 | parser = parser.__esModule ? parser.default : parser; 39 | parserByID[parser.id] = parser; 40 | parser.category = category; 41 | return parser; 42 | }); 43 | 44 | category.transformers = 45 | catFiles 46 | .filter(([dirName, , fileName]) => dirName === 'transformers' && fileName === 'index.js') 47 | .map(([, transformerName]) => { 48 | let transformerDir = `./${catName}/transformers/${transformerName}`; 49 | let transformer = localRequire(`${transformerDir}/index.js`); 50 | transformer = transformer.__esModule ? transformer.default : transformer; 51 | transformerByID[transformer.id] = transformer; 52 | transformer.defaultTransform = localRequire(`${transformerDir}/codeExample.txt`); 53 | return transformer; 54 | }); 55 | 56 | return category; 57 | }); 58 | 59 | export function getDefaultCategory() { 60 | return categoryByID.javascript; 61 | } 62 | 63 | export function getDefaultParser(category = getDefaultCategory()) { 64 | return category.parsers[0]; 65 | } 66 | 67 | export function getCategoryByID(id) { 68 | return categoryByID[id]; 69 | } 70 | 71 | export function getParserByID(id) { 72 | return parserByID[id]; 73 | } 74 | 75 | export function getTransformerByID(id) { 76 | return transformerByID[id]; 77 | } 78 | -------------------------------------------------------------------------------- /website/src/parsers/scala/scalameta.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pkg from 'scalameta-parsers/package.json'; 3 | import defaultParserInterface from '../utils/defaultParserInterface'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'scalameta'; 7 | 8 | const dialects = { 9 | 'Scala 2.10': 'Scala210', 10 | 'Scala 2.11': 'Scala211', 11 | 'Scala 2.12': 'Scala212', 12 | 'Sbt 0.13.6': 'Sbt0136', 13 | 'Sbt 0.13.7': 'Sbt0137', 14 | 'Dotty': 'Dotty', 15 | 'Typelevel 2.11': 'Typelevel211', 16 | 'Typelevel 2.12': 'Typelevel212', 17 | 'Paradise 2.11': 'Paradise211', 18 | 'Paradise 2.12': 'Paradise212', 19 | 'Paradise Typelevel 2.11': 'ParadiseTypelevel211', 20 | 'Paradise Typelevel 2.12': 'ParadiseTypelevel212', 21 | } 22 | 23 | const defaultOptions = { 24 | dialect: 'Scala 2.11', 25 | } 26 | 27 | const settingsConfiguration = { 28 | fields: [ 29 | ['dialect', Object.keys(dialects)], 30 | ], 31 | required: new Set('dialect'), 32 | } 33 | 34 | export default { 35 | ...defaultParserInterface, 36 | 37 | id: ID, 38 | displayName: ID, 39 | version: pkg.version, 40 | homepage: pkg.homepage || 'https://github.com/scalameta/scalameta', 41 | locationProps: new Set(['pos']), 42 | 43 | loadParser(callback) { 44 | require(['scalameta-parsers'], callback); 45 | }, 46 | 47 | parse(scalametaParser, code, options) { 48 | const parsed = scalametaParser.parseSource(code, { 49 | ...defaultOptions, 50 | ...options, 51 | dialect: dialects[defaultOptions.dialect || options.dialect], 52 | }); 53 | const { error, lineNumber, columnNumber } = parsed; 54 | if (error) { 55 | const e = new SyntaxError(parsed.error); 56 | e.lineNumber = lineNumber + 1; 57 | e.columnNumber = columnNumber + 1; 58 | throw e; 59 | } 60 | return parsed; 61 | }, 62 | 63 | nodeToRange(node) { 64 | if (node.pos) { 65 | return [node.pos.start, node.pos.end]; 66 | } 67 | }, 68 | 69 | getNodeName(node) { 70 | return node.type; 71 | }, 72 | 73 | opensByDefault(node, key) { 74 | return node.type === 'Program' 75 | || key === 'body' 76 | || key === 'self' 77 | || key === 'stats'; 78 | }, 79 | 80 | renderSettings(parserSettings, onChange) { 81 | return ( 82 | 87 | ) 88 | }, 89 | 90 | }; 91 | 92 | -------------------------------------------------------------------------------- /website/src/components/visualization/Tree.js: -------------------------------------------------------------------------------- 1 | import Element from './tree/Element'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | import PubSub from 'pubsub-js'; 5 | import {logEvent} from '../../utils/logger'; 6 | 7 | import './css/tree.css' 8 | 9 | export default class Tree extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | 13 | this.state = {autofocus: true, hideFunctions: true}; 14 | } 15 | 16 | _setOption(name, event) { 17 | this.setState({[name]: event.target.checked}); 18 | logEvent( 19 | 'tree_view_settings', 20 | event.target.checked ? 'enabled' : 'disabled', 21 | name 22 | ); 23 | } 24 | 25 | render() { 26 | return ( 27 |
28 |
29 | 37 | 45 | 53 | 61 |
62 |
    {PubSub.publish('CLEAR_HIGHLIGHT');}}> 63 | 70 |
71 |
72 | ); 73 | } 74 | } 75 | 76 | Tree.propTypes = { 77 | focusPath: PropTypes.array, 78 | ast: PropTypes.oneOfType([ 79 | PropTypes.array, 80 | PropTypes.object, 81 | ]), 82 | parser: PropTypes.object, 83 | }; 84 | -------------------------------------------------------------------------------- /website/src/components/visualization/tree/RecursiveTreeElement.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | function shouldAutoFocus({value, settings, focusPath}) { 5 | return !!settings.autofocus && focusPath.indexOf(value) > -1; 6 | } 7 | 8 | 9 | /** 10 | * This is a higher order component the prevents infinite recursion when opening 11 | * the element tree. 12 | */ 13 | export default function RecursiveTreeElement(Element) { 14 | const openValues = new WeakMap(); 15 | 16 | function addValue(value) { 17 | if (openValues.has(value)) { 18 | openValues.set(value, openValues.get(value) + 1); 19 | } else { 20 | openValues.set(value, 1); 21 | } 22 | } 23 | 24 | function removeValue(value) { 25 | let n = openValues.get(value) - 1; 26 | if (n === 0) { 27 | openValues.delete(value); 28 | } else { 29 | openValues.set(value, n); 30 | } 31 | } 32 | 33 | class RecursiveElement extends React.Component { 34 | constructor(props) { 35 | super(props); 36 | let {deepOpen} = props; 37 | let open = shouldAutoFocus(props); 38 | if (props.value && typeof props.value === 'object') { 39 | if (openValues.has(props.value)) { 40 | deepOpen = false; 41 | open = false; 42 | } 43 | addValue(props.value); 44 | } 45 | this.state = {deepOpen, open}; 46 | } 47 | 48 | componentWillUnmount() { 49 | const {value} = this.props; 50 | if (value && typeof value === 'object') { 51 | removeValue(value); 52 | } 53 | } 54 | 55 | componentWillReceiveProps(props) { 56 | let {deepOpen} = props; 57 | let open = shouldAutoFocus(props); 58 | if (!this.props.value !== props.value) { 59 | if (this.props.value && typeof this.props.value === 'object') { 60 | removeValue(this.props.value); 61 | } 62 | if (props.value && typeof props.value === 'object') { 63 | if (openValues.has(props.value)) { 64 | deepOpen = false; 65 | open = false; 66 | } 67 | addValue(props.value); 68 | } 69 | } 70 | this.setState({deepOpen, open}); 71 | } 72 | 73 | render() { 74 | const {props} = this; 75 | return ( 76 | 81 | ); 82 | } 83 | } 84 | 85 | RecursiveElement.propTypes = { 86 | deepOpen: PropTypes.bool, 87 | value: PropTypes.any, 88 | }; 89 | 90 | return RecursiveElement; 91 | 92 | } 93 | -------------------------------------------------------------------------------- /website/src/parsers/js/acorn.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'acorn/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'acorn'; 7 | const defaultOptions = { 8 | ecmaVersion: 7, 9 | sourceType: 'module', 10 | allowReserved: false, 11 | allowReturnOutsideFunction: false, 12 | allowImportExportEverywhere: false, 13 | allowHashBang: false, 14 | locations: false, 15 | loose: false, 16 | ranges: false, 17 | preserveParens: false, 18 | 'plugins.jsx': true, 19 | }; 20 | 21 | const settingsConfiguration = { 22 | fields: [ 23 | ['ecmaVersion', [3, 5, 6, 7, 8], x => Number(x)], 24 | ['sourceType', ['script', 'module']], 25 | 'allowReserved', 26 | 'allowReturnOutsideFunction', 27 | 'allowImportExportEverywhere', 28 | 'allowHashBang', 29 | 'locations', 30 | 'loose', 31 | 'ranges', 32 | 'preserveParens', 33 | 'plugins.jsx', 34 | ], 35 | }; 36 | 37 | export default { 38 | ...defaultParserInterface, 39 | 40 | id: ID, 41 | displayName: ID, 42 | version: `${pkg.version}`, 43 | homepage: pkg.homepage, 44 | locationProps: new Set(['range', 'loc', 'start', 'end']), 45 | 46 | loadParser(callback) { 47 | require(['acorn', 'acorn/dist/acorn_loose', 'acorn-jsx/inject'], (acorn, acornLoose, jsxInject) => { 48 | acorn = jsxInject(acorn); 49 | callback({ 50 | acorn, 51 | acornLoose, 52 | }); 53 | }); 54 | }, 55 | 56 | parse(parsers, code, options={}) { 57 | options = Object.assign({}, defaultOptions, options); 58 | const parser = options.loose ? 59 | parsers.acornLoose.parse_dammit : 60 | parsers.acorn.parse; 61 | 62 | // put deep option into correspondent place 63 | return parser(code, { 64 | ...options, 65 | plugins: options['plugins.jsx'] && !options.loose ? { jsx: true } : {}, 66 | }); 67 | }, 68 | 69 | nodeToRange(node) { 70 | if (typeof node.start === 'number') { 71 | return [node.start, node.end]; 72 | } 73 | }, 74 | 75 | renderSettings(parserSettings, onChange) { 76 | return ( 77 |
78 |

79 | 82 | Option descriptions 83 | 84 |

85 | 90 |
91 | ); 92 | }, 93 | }; 94 | -------------------------------------------------------------------------------- /website/src/parsers/html/htmlparser2.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'htmlparser2/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'htmlparser2'; 7 | const defaultOptions = { 8 | xmlMode: false, 9 | lowerCaseAttributeNames: true, 10 | lowerCaseTags: true, 11 | }; 12 | 13 | const parserSettingsConfiguration = { 14 | fields: Object.keys(defaultOptions), 15 | }; 16 | 17 | export default { 18 | ...defaultParserInterface, 19 | 20 | id: ID, 21 | displayName: ID, 22 | version: pkg.version, 23 | homepage: pkg.homepage || 'https://github.com/fb55/htmlparser2', 24 | locationProps: new Set(['startIndex']), 25 | 26 | loadParser(callback) { 27 | require(['htmlparser2/lib/Parser', 'domhandler'], (Parser, DomHandler) => { 28 | class Handler extends DomHandler { 29 | constructor() { 30 | super({ withStartIndices: true }); 31 | } 32 | 33 | _setEnd(elem) { 34 | elem.endIndex = this._parser.endIndex + 1; 35 | } 36 | 37 | onprocessinginstruction(name, data) { 38 | this._parser.endIndex = this._parser._tokenizer._index; 39 | super.onprocessinginstruction(name, data); 40 | } 41 | 42 | _addDomElement(elem) { 43 | super._addDomElement(elem); 44 | this._setEnd(elem); 45 | } 46 | } 47 | 48 | Handler.prototype.onclosetag = 49 | Handler.prototype.oncommentend = 50 | Handler.prototype.oncdataend = 51 | function onElemEnd() { 52 | this._setEnd(this._tagStack.pop()); 53 | }; 54 | 55 | callback({ Parser, Handler }); 56 | }); 57 | }, 58 | 59 | parse({ Parser, Handler }, code, options) { 60 | let handler = new Handler(); 61 | new Parser(handler, {...defaultOptions, ...options}).end(code); 62 | return handler.dom; 63 | }, 64 | 65 | nodeToRange(node) { 66 | if (node.type) { 67 | return [node.startIndex, node.endIndex]; 68 | } 69 | }, 70 | 71 | opensByDefault(node, key) { 72 | return key === 'children'; 73 | }, 74 | 75 | getNodeName(node) { 76 | let nodeName = node.type; 77 | if (nodeName && node.name) { 78 | nodeName += `(${node.name})`; 79 | } 80 | return nodeName; 81 | }, 82 | 83 | renderSettings(parserSettings, onChange) { 84 | return ( 85 | 90 | ); 91 | }, 92 | 93 | _ignoredProperties: new Set(['prev', 'next', 'parent', 'endIndex']), 94 | }; 95 | -------------------------------------------------------------------------------- /website/src/parsers/js/babylon6.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'babylon6/babylon-package'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const availablePlugins = [ 7 | 'asyncGenerators', 8 | 'classConstructorCall', 9 | 'classProperties', 10 | 'decorators', 11 | 'doExpressions', 12 | 'estree', 13 | 'exportExtensions', 14 | 'flow', 15 | 'functionSent', 16 | 'functionBind', 17 | 'jsx', 18 | 'objectRestSpread', 19 | 'dynamicImport', 20 | ]; 21 | 22 | const ID = 'babylon6'; 23 | export const defaultOptions = { 24 | sourceType: 'module', 25 | allowImportExportEverywhere: false, 26 | allowReturnOutsideFunction: false, 27 | plugins: [ 28 | 'asyncGenerators', 29 | 'classConstructorCall', 30 | 'classProperties', 31 | 'decorators', 32 | 'doExpressions', 33 | 'exportExtensions', 34 | 'flow', 35 | 'functionSent', 36 | 'functionBind', 37 | 'jsx', 38 | 'objectRestSpread', 39 | 'dynamicImport', 40 | ], 41 | }; 42 | 43 | export const parserSettingsConfiguration = { 44 | fields: [ 45 | ['sourceType', ['module', 'script']], 46 | 'allowReturnOutsideFunction', 47 | 'allowImportExportEverywhere', 48 | { 49 | key: 'plugins', 50 | title: 'Plugins', 51 | fields: availablePlugins, 52 | settings: settings => settings.plugins || defaultOptions.plugins, 53 | values: plugins => availablePlugins.reduce( 54 | (obj, name) => ((obj[name] = plugins.indexOf(name) > -1), obj), 55 | {} 56 | ), 57 | }, 58 | ], 59 | }; 60 | 61 | export default { 62 | ...defaultParserInterface, 63 | 64 | id: ID, 65 | displayName: ID, 66 | version: pkg.version, 67 | homepage: pkg.homepage, 68 | locationProps: new Set(['loc', 'start', 'end']), 69 | 70 | loadParser(callback) { 71 | require(['babylon6'], callback); 72 | }, 73 | 74 | parse(babylon, code, options) { 75 | return babylon.parse(code, {...defaultOptions, ...options}); 76 | }, 77 | 78 | getNodeName(node) { 79 | switch (typeof node.type) { 80 | case 'string': 81 | return node.type; 82 | case 'object': 83 | return `Token (${node.type.label})`; 84 | } 85 | }, 86 | 87 | nodeToRange(node) { 88 | if (typeof node.start !== 'undefined') { 89 | return [node.start, node.end]; 90 | } 91 | }, 92 | 93 | renderSettings(parserSettings, onChange) { 94 | return ( 95 | 100 | ); 101 | }, 102 | }; 103 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/jscodeshift/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'jscodeshift/package.json'; 3 | 4 | const ID = 'jscodeshift'; 5 | 6 | const sessionMethods = new Set(); 7 | 8 | export default { 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/facebook/jscodeshift', 13 | 14 | defaultParserID: 'recast', 15 | 16 | loadTransformer(callback) { 17 | require(['jscodeshift'], jscodeshift => { 18 | const { registerMethods } = jscodeshift; 19 | 20 | let origMethods; 21 | 22 | jscodeshift.registerMethods({ 23 | hasOwnProperty(name) { 24 | // compare only against current-session & very original methods 25 | if (!origMethods) { 26 | origMethods = new Set(Object.getOwnPropertyNames(this)); 27 | } 28 | return origMethods.has(name) || sessionMethods.has(name); 29 | }, 30 | }); 31 | 32 | // patch in order to collect user-defined method names 33 | jscodeshift.registerMethods = function (methods) { 34 | registerMethods.apply(this, arguments); 35 | for (let name in methods) { 36 | sessionMethods.add(name); 37 | } 38 | }; 39 | 40 | callback({jscodeshift}); 41 | } 42 | ); 43 | }, 44 | 45 | transform( 46 | {jscodeshift}, 47 | transformCode, 48 | code 49 | ) { 50 | sessionMethods.clear(); 51 | const transformModule = compileModule( // eslint-disable-line no-shadow 52 | transformCode 53 | ); 54 | const transform = transformModule.__esModule ? 55 | transformModule.default : 56 | transformModule; 57 | 58 | const counter = Object.create(null); 59 | let statsWasCalled = false; 60 | 61 | const result = transform( 62 | { 63 | path: 'Live.js', 64 | source: code, 65 | }, 66 | { 67 | jscodeshift: transformModule.parser ? 68 | jscodeshift.withParser(transformModule.parser) : 69 | jscodeshift, 70 | stats: (value, quantity=1) => { 71 | statsWasCalled = true; 72 | counter[value] = (counter[value] ? counter[value] : 0) + quantity; 73 | }, 74 | }, 75 | {} 76 | ); 77 | if (statsWasCalled) { 78 | console.log(JSON.stringify(counter, null, 4)); // eslint-disable-line no-console 79 | } 80 | if (result == null) { 81 | // If null is returned, the jscodeshift runner won't touch the original 82 | // code, so we just return that. 83 | return code; 84 | } else if (typeof result !== 'string') { 85 | throw new Error( 86 | 'Transformers must either return undefined, null or a string, not ' + 87 | `"${typeof result}".` 88 | ); 89 | } 90 | return result; 91 | }, 92 | }; 93 | -------------------------------------------------------------------------------- /website/src/parsers/js/babylon7.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'babylon7/babylon-package'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const availablePlugins = [ 7 | 'asyncGenerators', 8 | 'classProperties', 9 | 'classPrivateProperties', 10 | 'decorators', 11 | 'doExpressions', 12 | 'estree', 13 | 'exportExtensions', 14 | 'flow', 15 | 'functionSent', 16 | 'functionBind', 17 | 'jsx', 18 | 'objectRestSpread', 19 | 'dynamicImport', 20 | 'numericSeparator', 21 | 'importMeta', 22 | ]; 23 | 24 | const ID = 'babylon7'; 25 | export const defaultOptions = { 26 | sourceType: 'module', 27 | allowImportExportEverywhere: false, 28 | allowReturnOutsideFunction: false, 29 | ranges: false, 30 | plugins: [ 31 | 'asyncGenerators', 32 | 'classProperties', 33 | 'decorators', 34 | 'doExpressions', 35 | 'exportExtensions', 36 | 'flow', 37 | 'functionSent', 38 | 'functionBind', 39 | 'jsx', 40 | 'objectRestSpread', 41 | 'dynamicImport', 42 | ], 43 | }; 44 | 45 | export const parserSettingsConfiguration = { 46 | fields: [ 47 | ['sourceType', ['module', 'script']], 48 | 'allowReturnOutsideFunction', 49 | 'allowImportExportEverywhere', 50 | 'ranges', 51 | { 52 | key: 'plugins', 53 | title: 'Plugins', 54 | fields: availablePlugins, 55 | settings: settings => settings.plugins || defaultOptions.plugins, 56 | values: plugins => availablePlugins.reduce( 57 | (obj, name) => ((obj[name] = plugins.indexOf(name) > -1), obj), 58 | {} 59 | ), 60 | }, 61 | ], 62 | }; 63 | 64 | export default { 65 | ...defaultParserInterface, 66 | 67 | id: ID, 68 | displayName: ID, 69 | version: pkg.version, 70 | homepage: pkg.homepage, 71 | locationProps: new Set(['range', 'loc', 'start', 'end']), 72 | 73 | loadParser(callback) { 74 | require(['babylon7'], callback); 75 | }, 76 | 77 | parse(babylon, code, options) { 78 | return babylon.parse(code, {...defaultOptions, ...options}); 79 | }, 80 | 81 | getNodeName(node) { 82 | switch (typeof node.type) { 83 | case 'string': 84 | return node.type; 85 | case 'object': 86 | return `Token (${node.type.label})`; 87 | } 88 | }, 89 | 90 | nodeToRange(node) { 91 | if (typeof node.start !== 'undefined') { 92 | return [node.start, node.end]; 93 | } 94 | }, 95 | 96 | renderSettings(parserSettings, onChange) { 97 | return ( 98 | 103 | ); 104 | }, 105 | }; 106 | -------------------------------------------------------------------------------- /website/fontcustom/input-svg/GraphQL_Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/src/components/JSCodeshiftEditor.js: -------------------------------------------------------------------------------- 1 | import CodeMirror from 'codemirror'; 2 | import PropTypes from 'prop-types'; 3 | import Editor from './Editor'; 4 | 5 | import 'codemirror/addon/hint/show-hint.css'; 6 | import 'codemirror/addon/tern/tern.css'; 7 | 8 | let server; 9 | 10 | export default class JSCodeshiftEditor extends Editor { 11 | constructor(props) { 12 | super(props); 13 | loadTern(); 14 | } 15 | 16 | componentDidMount() { 17 | super.componentDidMount(); 18 | 19 | this.codeMirror.setOption('extraKeys', { 20 | 'Ctrl-Space': cm => server && server.complete(cm), 21 | 'Ctrl-I': cm => server && server.showType(cm), 22 | 'Ctrl-O': cm => server && server.showDocs(cm), 23 | }); 24 | 25 | this._bindCMHandler('cursorActivity', cm => { 26 | server && server.updateArgHints(cm); 27 | }); 28 | } 29 | } 30 | 31 | function loadTern() { 32 | require( 33 | [ 34 | 'codemirror/addon/hint/show-hint', 35 | 'codemirror/addon/tern/tern', 36 | 'acorn', 37 | ], 38 | (_1, _2, acorn) => { 39 | global.acorn = acorn; 40 | require( 41 | [ 42 | 'tern', 43 | 'tern/plugin/doc_comment', 44 | 'tern/lib/infer', 45 | '../defs/jscodeshift.json', 46 | 'tern/defs/ecmascript.json', 47 | ], 48 | (tern, _, infer, jscs_def, ecmascript) => { 49 | global.tern = tern; 50 | tern.registerPlugin('transformer', server => { 51 | server.on('afterLoad', file => { 52 | const fnVal = file.scope.props.transformer; 53 | if (fnVal) { 54 | const fnType = fnVal.getFunctionType(); 55 | const cx = infer.cx(); 56 | fnType.propagate(new infer.IsCallee( 57 | infer.cx().topScope, 58 | [ 59 | cx.definitions.jscodeshift.file, 60 | cx.definitions.jscodeshift.apiObject, 61 | ], 62 | null, 63 | infer.ANull 64 | )); 65 | } 66 | }); 67 | }); 68 | 69 | server = new CodeMirror.TernServer({ 70 | defs: [jscs_def, ecmascript], 71 | plugins: { 72 | transformer: {strong: true}, 73 | }, 74 | }); 75 | } 76 | ); 77 | } 78 | ); 79 | } 80 | 81 | JSCodeshiftEditor.propTypes = { 82 | value: PropTypes.string, 83 | highlight: PropTypes.bool, 84 | lineNumbers: PropTypes.bool, 85 | readOnly: PropTypes.bool, 86 | onContentChange: PropTypes.func, 87 | onActivity: PropTypes.func, 88 | posFromIndex: PropTypes.func, 89 | error: PropTypes.object, 90 | mode: PropTypes.string, 91 | }; 92 | 93 | JSCodeshiftEditor.defaultProps = Object.assign( 94 | {}, 95 | Editor.defaultProps, 96 | { 97 | highlight: false, 98 | } 99 | ); 100 | -------------------------------------------------------------------------------- /website/src/parsers/webidl/codeExample.txt: -------------------------------------------------------------------------------- 1 | [ 2 | Constructor(ArrayBuffer buffer, 3 | optional unsigned long byteOffset, 4 | optional unsigned long byteLength) 5 | ] 6 | interface DataView { 7 | // Gets the value of the given type at the specified byte offset 8 | // from the start of the view. There is no alignment constraint; 9 | // multi-byte values may be fetched from any offset. 10 | // 11 | // For multi-byte values, the optional littleEndian argument 12 | // indicates whether a big-endian or little-endian value should be 13 | // read. If false or undefined, a big-endian value is read. 14 | // 15 | // These methods raise an exception if they would read 16 | // beyond the end of the view. 17 | byte getInt8(unsigned long byteOffset); 18 | octet getUint8(unsigned long byteOffset); 19 | short getInt16(unsigned long byteOffset, 20 | optional boolean littleEndian); 21 | unsigned short getUint16(unsigned long byteOffset, 22 | optional boolean littleEndian); 23 | long getInt32(unsigned long byteOffset, 24 | optional boolean littleEndian); 25 | unsigned long getUint32(unsigned long byteOffset, 26 | optional boolean littleEndian); 27 | float getFloat32(unsigned long byteOffset, 28 | optional boolean littleEndian); 29 | double getFloat64(unsigned long byteOffset, 30 | optional boolean littleEndian); 31 | 32 | // Stores a value of the given type at the specified byte offset 33 | // from the start of the view. There is no alignment constraint; 34 | // multi-byte values may be stored at any offset. 35 | // 36 | // For multi-byte values, the optional littleEndian argument 37 | // indicates whether the value should be stored in big-endian or 38 | // little-endian byte order. If false or undefined, the value is 39 | // stored in big-endian byte order. 40 | // 41 | // These methods raise an exception if they would write 42 | // beyond the end of the view. 43 | void setInt8(unsigned long byteOffset, 44 | byte value); 45 | void setUint8(unsigned long byteOffset, 46 | octet value); 47 | void setInt16(unsigned long byteOffset, 48 | short value, 49 | optional boolean littleEndian); 50 | void setUint16(unsigned long byteOffset, 51 | unsigned short value, 52 | optional boolean littleEndian); 53 | void setInt32(unsigned long byteOffset, 54 | long value, 55 | optional boolean littleEndian); 56 | void setUint32(unsigned long byteOffset, 57 | unsigned long value, 58 | optional boolean littleEndian); 59 | void setFloat32(unsigned long byteOffset, 60 | float value, 61 | optional boolean littleEndian); 62 | void setFloat64(unsigned long byteOffset, 63 | double value, 64 | optional boolean littleEndian); 65 | }; 66 | DataView implements ArrayBufferView; 67 | -------------------------------------------------------------------------------- /website/src/parsers/js/babylon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'babylon5/babylon-package'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | const ID = 'babylon'; 7 | const defaultOptions = { 8 | sourceType: 'module', 9 | allowReserved: false, 10 | allowReturnOutsideFunction: false, 11 | strictMode: false, 12 | 13 | features: { 14 | 'es7.asyncFunctions': true, 15 | 'es7.classProperties': true, 16 | 'es7.comprehensions': true, 17 | 'es7.decorators': true, 18 | 'es7.exportExtensions': true, 19 | 'es7.functionBind': true, 20 | 'es7.objectRestSpread': true, 21 | 'es7.trailingFunctionCommas': true, 22 | }, 23 | 24 | plugins: { jsx: true, flow: true }, 25 | }; 26 | 27 | const parserSettingsConfiguration = { 28 | fields: [ 29 | ['sourceType', ['module', 'script']], 30 | 'allowReserved', 31 | 'allowReturnOutsideFunction', 32 | 'strictMode', 33 | { 34 | key: 'features', 35 | title: 'Features', 36 | fields: Object.keys(defaultOptions.features), 37 | settings: settings => settings.features || {...defaultOptions.features}, 38 | }, 39 | { 40 | key: 'plugins', 41 | title: 'Plugins', 42 | fields: Object.keys(defaultOptions.plugins), 43 | settings: settings => settings.plugins || {...defaultOptions.plugins}, 44 | values: plugins => Object.keys(defaultOptions.plugins).reduce( 45 | (obj, name) => ((obj[name] = name in plugins), obj), 46 | {} 47 | ), 48 | update: (plugins, name, value) => { 49 | if (value) { 50 | return {...plugins, [name]: true}; 51 | } 52 | plugins = {...plugins}; 53 | delete plugins[name]; 54 | return plugins; 55 | }, 56 | }, 57 | ], 58 | }; 59 | 60 | export default { 61 | ...defaultParserInterface, 62 | 63 | id: ID, 64 | displayName: ID, 65 | version: pkg.version, 66 | homepage: pkg.homepage, 67 | locationProps: new Set(['loc', 'start', 'end']), 68 | 69 | loadParser(callback) { 70 | require(['babylon5'], callback); 71 | }, 72 | 73 | parse(babylon, code, parserSettings) { 74 | return babylon.parse( 75 | code, 76 | {...defaultOptions, ...parserSettings} 77 | ); 78 | }, 79 | 80 | getNodeName(node) { 81 | switch (typeof node.type) { 82 | case 'string': 83 | return node.type; 84 | case 'object': 85 | return `Token (${node.type.label})`; 86 | } 87 | }, 88 | 89 | nodeToRange(node) { 90 | if (typeof node.start !== 'undefined') { 91 | return [node.start, node.end]; 92 | } 93 | }, 94 | 95 | _ignoredProperties: new Set([ 96 | '__clone', 97 | ]), 98 | 99 | renderSettings(parserSettings, onChange) { 100 | return ( 101 |
102 | 107 |
108 | ); 109 | }, 110 | }; 111 | -------------------------------------------------------------------------------- /website/fontcustom/config.yml: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # Font Custom Configuration 3 | # This file should live in the directory where you run `fontcustom compile`. 4 | # For more info, visit . 5 | # ============================================================================= 6 | 7 | 8 | # ----------------------------------------------------------------------------- 9 | # Project Info 10 | # ----------------------------------------------------------------------------- 11 | 12 | # The font's name. Also determines the file names of generated templates. 13 | #font_name: icons 14 | 15 | # Format of CSS selectors. {{glyph}} is substituted for the glyph name. 16 | #css_selector: .i-{{glyph}} 17 | 18 | # Generate fonts without asset-busting hashes. 19 | #no_hash: true 20 | 21 | # Encode WOFF fonts into the generated CSS. 22 | #base64: true 23 | 24 | # Forces compilation, even if inputs have not changed 25 | #force: true 26 | 27 | # Display (possibly useful) debugging messages. 28 | #debug: true 29 | 30 | # Hide status messages. 31 | #quiet: true 32 | 33 | 34 | # ----------------------------------------------------------------------------- 35 | # Input / Output Locations 36 | # You can save generated fonts, CSS, and other files to different locations 37 | # here. Font Custom can also read input vectors and templates from different 38 | # places. 39 | # 40 | # NOTE: 41 | # - Be sure to preserve the whitespace in these YAML hashes. 42 | # - INPUT[:vectors] and OUTPUT[:fonts] are required. Everything else is 43 | # optional. 44 | # - Specify output locations for custom templates by including their file 45 | # names as the key. 46 | # ----------------------------------------------------------------------------- 47 | 48 | #input: 49 | # vectors: my/vectors 50 | # templates: my/templates 51 | 52 | #output: 53 | # fonts: app/assets/fonts 54 | # css: app/assets/stylesheets 55 | # preview: app/views/styleguide 56 | # my-custom-template.yml: path/to/template/output 57 | 58 | 59 | # ----------------------------------------------------------------------------- 60 | # Templates 61 | # A YAML array of templates and files to generate alongside fonts. Custom 62 | # templates should be saved in the INPUT[:templates] directory and referenced 63 | # by their base file name. 64 | # 65 | # For Rails and Compass templates, set `preprocessor_path` as the relative 66 | # path from OUTPUT[:css] to OUTPUT[:fonts]. By default, these are the same 67 | # directory. 68 | # 69 | # Included in Font Custom: preview, css, scss, scss-rails 70 | # Default: css, preview 71 | # ----------------------------------------------------------------------------- 72 | 73 | #templates: 74 | #- scss-rails 75 | #- preview 76 | #- my-custom-template.yml 77 | 78 | #preprocessor_path: ../fonts/ 79 | 80 | 81 | # ----------------------------------------------------------------------------- 82 | # Font Settings (defaults shown) 83 | # ----------------------------------------------------------------------------- 84 | 85 | # Size (in pica points) for which your font is designed. 86 | #font_design_size: 16 87 | 88 | # The em size. Setting this will scale the entire font to the given size. 89 | #font_em: 512 90 | 91 | # The font's ascent and descent. Used to calculate the baseline. 92 | #font_ascent: 448 93 | #font_descent: 64 94 | 95 | # Horizontally fit glyphs to their individual vector widths. 96 | #autowidth: false 97 | -------------------------------------------------------------------------------- /website/src/store/selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import isEqual from 'lodash.isequal'; 3 | import {getParserByID, getTransformerByID} from '../parsers'; 4 | 5 | // UI related 6 | 7 | export function getFormattingState(state) { 8 | return state.enableFormatting; 9 | } 10 | 11 | export function getCursor(state) { 12 | return state.cursor; 13 | } 14 | 15 | export function getError(state) { 16 | return state.error; 17 | } 18 | 19 | export function isLoadingSnippet(state) { 20 | return state.loadingSnippet; 21 | } 22 | 23 | export function showSettingsDialog(state) { 24 | return state.showSettingsDialog; 25 | } 26 | 27 | export function showShareDialog(state) { 28 | return state.showShareDialog; 29 | } 30 | 31 | export function isForking(state) { 32 | return state.forking; 33 | } 34 | 35 | export function isSaving(state) { 36 | return state.saving; 37 | } 38 | 39 | // Parser related 40 | 41 | export function getParser(state) { 42 | return getParserByID(state.workbench.parser); 43 | } 44 | 45 | export function getParserSettings(state) { 46 | return state.workbench.parserSettings; 47 | } 48 | 49 | export function getParseError(state) { 50 | return state.workbench.parseError; 51 | } 52 | 53 | // Code related 54 | export function getRevision(state) { 55 | return state.activeRevision; 56 | } 57 | 58 | export function getCode(state) { 59 | return state.workbench.code; 60 | } 61 | 62 | export function getInitialCode(state) { 63 | return state.workbench.initialCode; 64 | } 65 | 66 | const isCodeDirty = createSelector( 67 | [getCode, getInitialCode], 68 | (code, initialCode) => code !== initialCode 69 | ); 70 | 71 | // Transform related 72 | 73 | export function getTransformCode(state) { 74 | return state.workbench.transform.code; 75 | } 76 | 77 | export function getInitialTransformCode(state) { 78 | return state.workbench.transform.initialCode; 79 | } 80 | 81 | export function getTransformer(state) { 82 | return getTransformerByID(state.workbench.transform.transformer); 83 | } 84 | 85 | export function showTransformer(state) { 86 | return state.showTransformPanel; 87 | } 88 | 89 | const isTransformDirty = createSelector( 90 | [getTransformCode, getInitialTransformCode], 91 | (code, initialCode) => code !== initialCode 92 | ); 93 | 94 | export const canFork = createSelector( 95 | [getRevision], 96 | (revision) => !!revision 97 | ); 98 | 99 | const canSaveCode = createSelector( 100 | [getRevision, isCodeDirty], 101 | (revision, dirty) => ( 102 | !revision || // can always save if there is no revision 103 | dirty 104 | ) 105 | ); 106 | 107 | export const canSaveTransform = createSelector( 108 | [showTransformer, isTransformDirty], 109 | (showTransformer, dirty) => showTransformer && dirty 110 | ); 111 | 112 | const didParserSettingsChange = createSelector( 113 | [getParserSettings, getRevision, getParser], 114 | (parserSettings, revision, parser) => { 115 | const savedParserSettings = revision && revision.getParserSettings(); 116 | return ( 117 | !!revision && 118 | ( 119 | parser.id !== revision.getParserID() || 120 | !!savedParserSettings && !isEqual(parserSettings, savedParserSettings) 121 | ) 122 | ) 123 | 124 | } 125 | ); 126 | 127 | export const canSave = createSelector( 128 | [getRevision, canSaveCode, canSaveTransform, didParserSettingsChange], 129 | (revision, canSaveCode, canSaveTransform, didParserSettingsChange) => ( 130 | (canSaveCode || canSaveTransform || didParserSettingsChange) && 131 | (!revision || revision.canSave()) 132 | ) 133 | ); 134 | -------------------------------------------------------------------------------- /website/fontcustom/input-svg/icu.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /website/fontcustom/input-svg/handlebars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /website/src/parsers/js/recast.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; // eslint-disable-line no-unused-vars 2 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 3 | import pkg from 'recast/package.json'; 4 | import SettingsRenderer from '../utils/SettingsRenderer'; 5 | 6 | import flowParser, * as flowSettings from './flow'; 7 | import babylonParser, * as babylonSettings from './babylon6'; 8 | 9 | const ID = 'recast'; 10 | const defaultOptions = { 11 | tolerant: false, 12 | range: true, 13 | parser: 'esprima', 14 | flow: flowSettings.defaultOptions, 15 | babylon: babylonSettings.defaultOptions, 16 | }; 17 | 18 | const parserSettingsConfiguration = { 19 | fields: [ 20 | ['parser', ['esprima', 'babel5', 'babylon6', 'flow']], 21 | 'range', 22 | 'tolerant', 23 | { 24 | key: 'flow', 25 | title: 'Flow Settings', 26 | fields: flowSettings.parserSettingsConfiguration.fields, 27 | settings: settings => settings.flow || defaultOptions.flow, 28 | }, 29 | { 30 | key: 'babylon', 31 | title: 'Babylon 6 Settings', 32 | fields: babylonSettings.parserSettingsConfiguration.fields, 33 | settings: settings => settings.babylon || defaultOptions.babylon, 34 | }, 35 | ], 36 | required: new Set(['range']), 37 | }; 38 | 39 | export default { 40 | ...defaultParserInterface, 41 | 42 | id: ID, 43 | displayName: ID, 44 | version: pkg.version, 45 | homepage: pkg.homepage, 46 | locationProps: new Set(['range', 'loc', 'start', 'end']), 47 | 48 | loadParser(callback) { 49 | require( 50 | ['recast', 'babel5', 'babylon6', 'flow-parser'], 51 | (recast, babelCore, babylon6, flow) => { 52 | callback({ 53 | recast, 54 | parsers: { 55 | 'babel5': babelCore, 56 | babylon6, 57 | flow, 58 | }, 59 | }); 60 | } 61 | ); 62 | }, 63 | 64 | parse({ recast, parsers }, code, options) { 65 | options = {...defaultOptions, ...options}; 66 | const flowOptions = options.flow; 67 | const babylonOptions = options.babylon; 68 | delete options.flow; 69 | delete options.babylon; 70 | 71 | switch (options.parser) { 72 | case 'esprima': 73 | delete options.parser; // default parser 74 | break; 75 | case 'flow': 76 | options.parser = { 77 | parse(code) { 78 | return flowParser.parse(parsers.flow, code, flowOptions); 79 | }, 80 | }; 81 | break; 82 | case 'babylon6': 83 | options.parser = { 84 | parse(code) { 85 | return babylonParser.parse(parsers.babylon6, code, babylonOptions); 86 | }, 87 | }; 88 | break; 89 | default: 90 | options.parser = parsers[options.parser]; 91 | } 92 | return recast.parse(code, options); 93 | }, 94 | 95 | _ignoredProperties: new Set(['__clone']), 96 | 97 | *forEachProperty(node) { 98 | for (let prop in node) { 99 | if ( 100 | this._ignoredProperties.has(prop) || typeof node[prop] === 'function' 101 | ) { 102 | continue; 103 | } 104 | yield { 105 | value: node[prop], 106 | key: prop, 107 | computed: false, 108 | }; 109 | } 110 | }, 111 | 112 | nodeToRange(node) { 113 | if (typeof node.start === 'number') { 114 | return [node.start, node.end]; 115 | } 116 | return node.range; 117 | }, 118 | 119 | renderSettings(parserSettings, onChange) { 120 | return ( 121 | 126 | ); 127 | }, 128 | }; 129 | -------------------------------------------------------------------------------- /scripts/definition-generator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const astTypes = require('ast-types'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const t = require('ast-types/lib/types'); 7 | 8 | const types = {}; 9 | const builders = {}; 10 | const typeCheckers = {}; 11 | 12 | function sanitizeTypeName(typeName, fieldName, type) { 13 | if (type === 'boolean') { 14 | return 'bool'; 15 | } 16 | 17 | switch(fieldName) { 18 | case 'operator': 19 | case 'kind': 20 | return 'string'; 21 | case 'value': 22 | if (typeName === 'TemplateElement') { 23 | return 'TemplateElementValue'; 24 | } 25 | default: 26 | if (/number [><=]+ \d+/.test(type)) { 27 | return 'number'; 28 | } 29 | return type; 30 | } 31 | } 32 | 33 | function typeNameToString(name) { 34 | return typeof name === 'function' ? name().replace(/\s+/g, '') : name; 35 | } 36 | 37 | for (const typeName in astTypes.namedTypes) { 38 | const typeDef = astTypes.Type.def(typeName); 39 | const definition = { 40 | '!proto': 'ASTNode', 41 | }; 42 | const builder = {}; 43 | 44 | // Type check function 45 | typeCheckers[typeName] = 'TypeDefinition'; 46 | 47 | // Type definition 48 | typeDef.fieldNames.forEach(fieldName => { 49 | definition[fieldName] = sanitizeTypeName( 50 | typeNameToString(typeName, fieldName, typeDef.allFields[fieldName].type.name) 51 | ); 52 | }); 53 | 54 | // Builder 55 | if (t.getBuilderName(typeName) in astTypes.builders) { 56 | let additionalDocs = ''; 57 | 58 | const args = typeDef.buildParams.map(name => { 59 | let type = typeNameToString(typeDef.allFields[name].type.name); 60 | switch(name) { 61 | case 'operator': 62 | case 'kind': 63 | additionalDocs += `${name} (string) one of: ${type}\n`; 64 | type = 'string'; 65 | break; 66 | case 'value': 67 | if (typeName === 'TemplateElement') { 68 | additionalDocs += `${name} (object) has form {cooked: string, raw: string}`; 69 | type = 'TemplateElementValue'; 70 | } 71 | default: 72 | let match; 73 | if ((match = type.match(/number ([><=]+ \d+)/))) { 74 | additionalDocs += '${name} (number) must be ' + match[1]; 75 | type = 'number'; 76 | break; 77 | } 78 | } 79 | return `${name}: ${type}`; 80 | }); 81 | builders[t.getBuilderName(typeName)] = Object.assign(builder, { 82 | '!type': `fn(${args.join(', ')}) -> ${typeName}`, 83 | '!doc': `Builds an AST node of type '${typeName}'.`, 84 | }); 85 | if (typeDef.supertypeList.length > 1) { 86 | builder['!doc'] += '\nSuper types: ' + typeDef.supertypeList.slice(1).join(', '); 87 | } 88 | if (additionalDocs) { 89 | builder['!doc'] += '\n\n ' + additionalDocs; 90 | } 91 | } 92 | 93 | types[typeName] = definition; 94 | } 95 | 96 | const extraTypes = { 97 | TemplateElementValue: { 98 | cooked: 'string', 99 | raw: 'string', 100 | }, 101 | TypeDefinition: { 102 | name: 'string', 103 | check: 'fn(node: Node, deep: ?) -> bool', 104 | }, 105 | }; 106 | 107 | var ternDefinition = require('js-yaml').safeLoad( 108 | fs.readFileSync(path.join(__dirname, './defs/jscodeshift.yaml'), 'utf-8') 109 | ); 110 | Object.assign(ternDefinition['!define'], types, extraTypes); 111 | Object.assign( 112 | ternDefinition['!define'].apiObject.jscodeshift, 113 | typeCheckers, 114 | builders 115 | ); 116 | 117 | const target = path.resolve(__dirname, '../src/defs/jscodeshift.json'); 118 | fs.writeFileSync( 119 | target, 120 | JSON.stringify(ternDefinition, null, 2) 121 | ); 122 | process.stdout.write(`Written to "${path.relative(process.cwd(), target)}"...`); 123 | -------------------------------------------------------------------------------- /website/src/store/actions.js: -------------------------------------------------------------------------------- 1 | export const SET_ERROR = 'SET_ERROR'; 2 | export const CLEAR_ERROR = 'CLEAR_ERROR'; 3 | export const LOAD_SNIPPET = 'LOAD_SNIPPET'; 4 | export const START_LOADING_SNIPPET = 'START_LOADING_SNIPPET'; 5 | export const DONE_LOADING_SNIPPET = 'DONE_LOADING_SNIPPET'; 6 | export const CLEAR_SNIPPET = 'CLEAR_SNIPPET'; 7 | export const SELECT_CATEGORY = 'CHANGE_CATEGORY'; 8 | export const SELECT_TRANSFORMER = 'SELECT_TRANSFORMER'; 9 | export const HIDE_TRANSFORMER = 'HIDE_TRANSFORMER'; 10 | export const SET_TRANSFORM = 'SET_TRANSFORM'; 11 | export const SET_PARSER = 'SET_PARSER'; 12 | export const SET_PARSER_SETTINGS = 'SET_PARSER_SETTINGS'; 13 | export const SET_PARSE_ERROR = 'SET_PARSE_ERROR'; 14 | export const SET_SNIPPET = 'SET_SNIPPET'; 15 | export const OPEN_SETTINGS_DIALOG = 'OPEN_SETTINGS_DIALOG'; 16 | export const CLOSE_SETTINGS_DIALOG = 'CLOSE_SETTINGS_DIALOG'; 17 | export const OPEN_SHARE_DIALOG = 'OPEN_SHARE_DIALOG'; 18 | export const CLOSE_SHARE_DIALOG = 'CLOSE_SHARE_DIALOG'; 19 | export const SET_CODE = 'SET_CODE'; 20 | export const SET_CURSOR = 'SET_CURSOR'; 21 | export const DROP_TEXT = 'DROP_TEXT'; 22 | export const SAVE = 'SAVE'; 23 | export const START_SAVE = 'START_SAVE'; 24 | export const END_SAVE = 'END_SAVE'; 25 | export const RESET = 'RESET'; 26 | export const TOGGLE_FORMATTING = 'TOGGLE_FORMATTING'; 27 | 28 | export function setParser(parser) { 29 | return {type: SET_PARSER, parser}; 30 | } 31 | 32 | export function setParserSettings(settings) { 33 | return {type: SET_PARSER_SETTINGS, settings}; 34 | } 35 | 36 | export function save(fork=false) { 37 | return {type: SAVE, fork}; 38 | } 39 | 40 | export function startSave(fork) { 41 | return {type: START_SAVE, fork}; 42 | } 43 | 44 | export function endSave(fork) { 45 | return {type: END_SAVE, fork}; 46 | } 47 | 48 | export function setSnippet(revision) { 49 | return {type: SET_SNIPPET, revision}; 50 | } 51 | 52 | export function setParseError(error) { 53 | return {type: SET_PARSE_ERROR, error}; 54 | } 55 | 56 | export function selectCategory(category) { 57 | return {type: SELECT_CATEGORY, category}; 58 | } 59 | 60 | export function clearSnippet() { 61 | return {type: CLEAR_SNIPPET}; 62 | } 63 | 64 | export function startLoadingSnippet() { 65 | return {type: START_LOADING_SNIPPET}; 66 | } 67 | 68 | export function doneLoadingSnippet() { 69 | return {type: DONE_LOADING_SNIPPET}; 70 | } 71 | 72 | export function loadSnippet() { 73 | return {type: LOAD_SNIPPET}; 74 | } 75 | 76 | export function openSettingsDialog() { 77 | return {type: OPEN_SETTINGS_DIALOG}; 78 | } 79 | 80 | export function closeSettingsDialog() { 81 | return {type: CLOSE_SETTINGS_DIALOG}; 82 | } 83 | 84 | export function openShareDialog() { 85 | return {type: OPEN_SHARE_DIALOG}; 86 | } 87 | 88 | export function closeShareDialog() { 89 | return {type: CLOSE_SHARE_DIALOG}; 90 | } 91 | 92 | export function setError(error) { 93 | return {type: SET_ERROR, error}; 94 | } 95 | 96 | export function clearError() { 97 | return {type: CLEAR_ERROR}; 98 | } 99 | 100 | export function selectTransformer(transformer) { 101 | return {type: SELECT_TRANSFORMER, transformer}; 102 | } 103 | 104 | export function hideTransformer() { 105 | return {type: HIDE_TRANSFORMER}; 106 | } 107 | 108 | export function setTransformState(state) { 109 | return {type: SET_TRANSFORM, ...state}; 110 | } 111 | 112 | export function setCode(state) { 113 | return {type: SET_CODE, ...state}; 114 | } 115 | 116 | export function setCursor(cursor) { 117 | return {type: SET_CURSOR, cursor}; 118 | } 119 | 120 | export function dropText(text, categoryId) { 121 | return {type: DROP_TEXT, text, categoryId}; 122 | } 123 | 124 | export function reset() { 125 | return {type: RESET}; 126 | } 127 | 128 | export function toggleFormatting() { 129 | return {type: TOGGLE_FORMATTING}; 130 | } 131 | -------------------------------------------------------------------------------- /website/src/parsers/utils/SettingsRenderer.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | const identity = v => v; 5 | 6 | function valuesFromArray(settings) { 7 | return settings.reduce( 8 | (obj, name) => ( 9 | (obj[name] = settings.indexOf(name) > -1), 10 | obj 11 | ), 12 | {} 13 | ); 14 | } 15 | 16 | function getValuesFromSettings(settings) { 17 | if (Array.isArray(settings)) { 18 | return valuesFromArray(settings); 19 | } 20 | return settings; 21 | } 22 | 23 | function defaultUpdater(settings, name, value) { 24 | return {...settings, [name]: value}; 25 | } 26 | 27 | function arrayUpdater(settings, name, value) { 28 | settings = new Set(settings); 29 | if (value) { 30 | settings.add(name); 31 | } else { 32 | settings.delete(name); 33 | } 34 | return Array.from(settings); 35 | } 36 | 37 | function getUpdateStrategy(settings) { 38 | if (Array.isArray(settings)) { 39 | return arrayUpdater; 40 | } 41 | return defaultUpdater; 42 | } 43 | 44 | export default function SettingsRenderer(props) { 45 | const {settingsConfiguration, parserSettings, onChange} = props; 46 | const { 47 | title, 48 | fields, 49 | required = new Set(), 50 | update=getUpdateStrategy(parserSettings), 51 | } = settingsConfiguration; 52 | const values = 53 | (settingsConfiguration.values || getValuesFromSettings)(parserSettings); 54 | 55 | return ( 56 |
57 | {title ?

{title}

: null} 58 |
    59 | {fields.map(setting => { 60 | if (typeof setting === 'string') { 61 | return ( 62 |
  • 63 | 77 |
  • 78 | ); 79 | } else if(Array.isArray(setting)) { 80 | const [fieldName, options, converter=identity] = setting; 81 | return ( 82 |
  • 83 | 97 |
  • 98 | ); 99 | } else if (setting && typeof setting === 'object') { 100 | return ( 101 | onChange( 107 | {...parserSettings, [setting.key]: settings} 108 | ) 109 | } 110 | /> 111 | ); 112 | } 113 | })} 114 |
115 |
116 | ); 117 | } 118 | 119 | SettingsRenderer.propTypes = { 120 | settingsConfiguration: PropTypes.object.isRequired, 121 | parserSettings: PropTypes.oneOfType([ 122 | PropTypes.object, 123 | PropTypes.array, 124 | ]).isRequired, 125 | onChange: PropTypes.func.isRequired, 126 | }; 127 | --------------------------------------------------------------------------------