├── .editorconfig ├── .eslintrc ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .gitlab-ci.yml ├── .gitpod.yml ├── .tool-versions ├── .travis.yml ├── CNAME ├── LICENSE.txt ├── README.md ├── assets ├── ast.png └── source.png ├── scripts ├── build-ci.sh ├── build-site.sh ├── check-conflicts.sh ├── definition-generator.js ├── defs │ └── jscodeshift.yaml ├── deploy.sh ├── inject-rev.sh └── publish-latest.sh ├── server ├── constants.js ├── handlers │ ├── gist │ │ ├── index.js │ │ ├── loadGist.js │ │ └── saveAnonymousGist.js │ └── parse.js ├── index.js ├── package.json └── yarn.lock └── website ├── .fontcustom-manifest.json ├── .npmrc ├── CACHE_BREAKER ├── css ├── highlight.css └── style.css ├── favicon.png ├── fontcustom ├── README.md ├── config.yml ├── fontcustom-preview.html ├── fontcustom.css ├── fontcustom_45cd59da2a1bc422647cab7f53639319.eot ├── fontcustom_45cd59da2a1bc422647cab7f53639319.svg ├── fontcustom_45cd59da2a1bc422647cab7f53639319.ttf ├── fontcustom_45cd59da2a1bc422647cab7f53639319.woff ├── fontcustom_45cd59da2a1bc422647cab7f53639319.woff2 └── input-svg │ ├── GraphQL_Logo.svg │ ├── handlebars.svg │ ├── icu.svg │ ├── java.svg │ ├── ocaml.svg │ ├── reason.svg │ ├── rust.svg │ └── scala.svg ├── index.ejs ├── package.json ├── postcss.config.js ├── src ├── app.js ├── components │ ├── ASTOutput.js │ ├── Editor.js │ ├── ErrorMessage.js │ ├── GistBanner.js │ ├── JSCodeshiftEditor.js │ ├── JSONEditor.js │ ├── LoadingIndicator.js │ ├── LocalStorage.js │ ├── PasteDropTarget.js │ ├── SettingsDrawer.js │ ├── SplitPane.js │ ├── Toolbar.js │ ├── TransformOutput.js │ ├── Transformer.js │ ├── buttons │ │ ├── CategoryButton.js │ │ ├── ForkButton.js │ │ ├── KeyMapButton.js │ │ ├── NewButton.js │ │ ├── ParserButton.js │ │ ├── PrettierButton.js │ │ ├── SaveButton.js │ │ ├── ShareButton.js │ │ ├── SnippetButton.js │ │ └── TransformButton.js │ ├── dialogs │ │ ├── SettingsDialog.js │ │ └── ShareDialog.js │ └── visualization │ │ ├── JSON.js │ │ ├── SelectedNodeContext.js │ │ ├── Tree.js │ │ ├── css │ │ └── tree.css │ │ ├── focusNodes.js │ │ ├── index.js │ │ └── tree │ │ ├── CompactArrayView.js │ │ ├── CompactObjectView.js │ │ └── Element.js ├── containers │ ├── ASTOutputContainer.js │ ├── CodeEditorContainer.js │ ├── ErrorMessageContainer.js │ ├── LoadingIndicatorContainer.js │ ├── PasteDropTargetContainer.js │ ├── SettingsDialogContainer.js │ ├── SettingsDrawerContainer.js │ ├── ShareDialogContainer.js │ ├── ToolbarContainer.js │ └── TransformerContainer.js ├── core │ ├── ParseResult.js │ └── TreeAdapter.js ├── defs │ └── jscodeshift.json ├── parsers │ ├── css │ │ ├── codeExample.txt │ │ ├── cssom.js │ │ ├── csstree.js │ │ ├── index.js │ │ ├── postcss.js │ │ ├── rework.js │ │ ├── transformers │ │ │ └── postcss │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ └── utils │ │ │ └── defaultCSSParserInterface.js │ ├── glsl │ │ ├── codeExample.txt │ │ ├── codemirror-mode │ │ │ └── glsl.js │ │ ├── glsl-parser.js │ │ └── index.js │ ├── go │ │ ├── codeExample.txt │ │ ├── go.js │ │ └── index.js │ ├── graphql │ │ ├── codeExample.txt │ │ ├── graphql-js.js │ │ └── index.js │ ├── graphviz │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── redot.js │ ├── handlebars │ │ ├── codeExample.txt │ │ ├── ember-template-recast.js │ │ ├── glimmer.js │ │ ├── handlebars.js │ │ ├── index.js │ │ ├── transformers │ │ │ ├── ember-template-recast │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── glimmer-compiler │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ └── glimmer │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ └── utils │ │ │ └── defaultHandlebarsParserInterface.js │ ├── html │ │ ├── angular-eslint.js │ │ ├── angular.js │ │ ├── codeExample.txt │ │ ├── htmlparser2.js │ │ ├── hyntax.js │ │ ├── index.js │ │ ├── parse5.js │ │ ├── posthtml.js │ │ ├── svelte.js │ │ └── transformers │ │ │ ├── posthtml │ │ │ ├── codeExample.txt │ │ │ └── index.js │ │ │ └── svelte │ │ │ ├── codeExample.txt │ │ │ └── index.js │ ├── icu │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── intl-messageformat-parser.js │ ├── index.js │ ├── java │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── java-parser.js │ ├── js │ │ ├── acorn-to-esprima.js │ │ ├── acorn.js │ │ ├── babel-eslint-parser.js │ │ ├── babel-eslint.js │ │ ├── babel-eslint8.js │ │ ├── babel-eslint9.js │ │ ├── babylon.js │ │ ├── babylon6.js │ │ ├── babylon7.js │ │ ├── codeExample.txt │ │ ├── esformatter.js │ │ ├── espree.js │ │ ├── esprima.js │ │ ├── flow.js │ │ ├── hermes.js │ │ ├── hermes │ │ │ ├── HermesWorkerClient.js │ │ │ └── hermes-worker.js │ │ ├── index.js │ │ ├── meriyah.js │ │ ├── recast.js │ │ ├── seafox.js │ │ ├── shift.js │ │ ├── swc.js │ │ ├── tenko.js │ │ ├── traceur.js │ │ ├── transformers │ │ │ ├── babel-plugin-macros │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── babel │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── babel6 │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── babel7 │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── eslint1 │ │ │ │ ├── codeExample.txt │ │ │ │ ├── index.js │ │ │ │ └── loadRulesShim.js │ │ │ ├── eslint2 │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── eslint3 │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── eslint4 │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── eslint8 │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── jscodeshift │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── prettier │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── recast │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ ├── tslint │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ │ └── typescript │ │ │ │ ├── codeExample.txt │ │ │ │ └── index.js │ │ ├── typescript-eslint-parser.js │ │ ├── typescript.js │ │ ├── uglify.js │ │ └── utils │ │ │ ├── defaultESTreeParserInterface.js │ │ │ ├── eslint4Utils.js │ │ │ └── eslintUtils.js │ ├── json │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── json-to-ast.js │ │ └── momoa.js │ ├── lua │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── luaparse.js │ ├── lucene │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── lucene.js │ ├── markdown │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── remark.js │ │ └── transformers │ │ │ └── remark │ │ │ ├── codeExample.txt │ │ │ └── index.js │ ├── mathjs │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── mathjs.js │ ├── mdx │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── mdxhast.js │ │ └── transformers │ │ │ └── mdx │ │ │ ├── codeExample.txt │ │ │ └── index.js │ ├── monkey │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── monkey.js │ ├── ocaml │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── refmt-ml.js │ ├── php │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── php-parser.js │ ├── protobuf │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── pbkit.js │ ├── pug │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── pug.js │ ├── python │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── python.js │ ├── reason │ │ ├── codeExample.txt │ │ ├── codeMirrorMode.js │ │ ├── index.js │ │ └── refmt.js │ ├── regexp │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── regexp-tree.js │ │ ├── regexpp.js │ │ ├── regjsparser.js │ │ └── transformers │ │ │ └── regexp-tree │ │ │ ├── codeExample.txt │ │ │ └── index.js │ ├── rust │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── syn.js │ ├── san │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── san-template-parser.js │ ├── scala │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── scalameta.js │ ├── solididy │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── solidity-parser-antlr.js │ │ └── solidity-parser-diligence.js │ ├── sql │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── sql-parser-cst.js │ │ └── sqlite-parser.js │ ├── svelte │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── svelte-parser.js │ ├── thrift │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── thrift-parser.js │ ├── transpilers │ │ ├── babel.js │ │ └── typescript.js │ ├── utils │ │ ├── SettingsRenderer.js │ │ ├── compileModule.js │ │ ├── defaultParserInterface.js │ │ └── protectFromLoops.js │ ├── vue │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── vue-compiler-dom.js │ │ ├── vue-eslint-parser.js │ │ └── vue-template-compiler.js │ ├── wat │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── wat-parser.js │ ├── webidl │ │ ├── codeExample.txt │ │ ├── index.js │ │ └── webidl2.js │ └── yaml │ │ ├── codeExample.txt │ │ ├── index.js │ │ ├── yaml-ast-parser.js │ │ └── yaml.js ├── shims │ └── jest-validate.js ├── storage │ ├── api.js │ ├── gist.js │ ├── index.js │ └── parse.js ├── store │ ├── actions.js │ ├── parserMiddleware.js │ ├── reducers.js │ ├── selectors.js │ ├── snippetMiddleware.js │ └── transformerMiddleware.js └── utils │ ├── classnames.js │ ├── debounce.js │ ├── pubsub.js │ └── stringify.js ├── webpack.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.js] 2 | quote_type = single 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | extends: 2 | - eslint:recommended 3 | - plugin:react/recommended 4 | - plugin:import/errors 5 | - plugin:import/warnings 6 | 7 | parserOptions: 8 | ecmaVersion: 2018 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: ["error", "always-multiline"] 21 | new-cap: "off" 22 | no-path-concat: "off" 23 | no-undef: "error" 24 | no-underscore-dangle: "off" 25 | no-unused-vars: 26 | - "warn" 27 | - varsIgnorePattern: "^_" 28 | argsIgnorePattern: "^_" 29 | no-use-before-define: "off" 30 | quotes: ["error", "single", "avoid-escape"] 31 | strict: "off" 32 | import/no-unresolved: ["error", {commonjs: true, amd: true}] 33 | import/named: "error" 34 | import/default: "error" 35 | import/namespace: "error" 36 | import/export: "error" 37 | require-in-package/require-in-package: "error" 38 | react/display-name: off 39 | 40 | settings: 41 | import/resolver: "webpack" 42 | import/ignore: 43 | - node_modules 44 | - \.json 45 | 46 | env: 47 | browser: true 48 | node: true 49 | 50 | globals: 51 | loadjs: true 52 | Promise: true 53 | Map: true 54 | Set: true 55 | WeakMap: true 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Browser (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **astexplorer settings:** 29 | - Selected parser: [e.g. acorn] 30 | - Selected transformer (if applicable): [e.g. jscodeshift] 31 | - Contents of the local storage key `explorerSettingsV1` (code can be removed if you don't want it to be public) 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | node_modules 3 | .idea 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | 2 | tasks: 3 | - init: cd website && yarn 4 | command: yarn dev 5 | - init: fish 6 | 7 | ports: 8 | - port: 8080 9 | onOpen: open-preview 10 | visibility: public 11 | 12 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs lts-gallium 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | astexplorer.net 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2021 Felix Kling and astexplorer contributors https://astexplorer.net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the “Software”), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/ast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fkling/astexplorer/8888701e97c2efebe6fb118484e67cfc0077d08c/assets/ast.png -------------------------------------------------------------------------------- /assets/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fkling/astexplorer/8888701e97c2efebe6fb118484e67cfc0077d08c/assets/source.png -------------------------------------------------------------------------------- /scripts/build-ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | rm -rf out/* 6 | ( 7 | cd website/ 8 | yarn 9 | if [ "$1" = "--dev" ]; then 10 | yarn build-dev 11 | else 12 | yarn build 13 | fi 14 | ) 15 | -------------------------------------------------------------------------------- /scripts/check-conflicts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SOURCE_DIR=$1 4 | TARGET_DIR=$2 5 | 6 | for source_file in $(ls $1); do 7 | target_path=$TARGET_DIR/$source_file 8 | source_path=$SOURCE_DIR/$source_file 9 | 10 | if ! [ -f $target_path ]; then 11 | # If a file with the same name doesn't exist in the target directory, all is 12 | # good. 13 | continue 14 | fi 15 | 16 | case $source_file in 17 | # These files are allowed to differ since the are 18 | # not cached 19 | index.html|favicon.png) 20 | continue 21 | ;; 22 | esac 23 | 24 | if ! diff --brief $target_path $source_path > /dev/null; then 25 | echo "File '$source_file' already exists but has different content." 26 | conflict=1 27 | fi 28 | done 29 | 30 | if [ -n "$conflict" ]; then 31 | echo "Bump the cache breaker." 32 | exit 1 33 | fi 34 | -------------------------------------------------------------------------------- /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 $REMOTE $BRANCH 19 | -------------------------------------------------------------------------------- /scripts/inject-rev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script replaces the special character sequence @@COMMIT@@ in a file with 4 | # a link to the commit on GitHub. The file path is passed as argument but is 5 | # likely just out/index.html 6 | 7 | rev=$(git rev-parse --short HEAD) 8 | sed -i "s%@@COMMIT@@%Build: $rev%" "$1" 9 | -------------------------------------------------------------------------------- /scripts/publish-latest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | REMOTE=${1:-"server"} 6 | LATEST_BRANCH=${2:-"website-latest"} 7 | STABLE_BRANCH=${3:-"website-stable"} 8 | 9 | TARGETPATH="../$(basename $(pwd))_$STABLE_BRANCH" 10 | WORKING_DIR=$(pwd) 11 | 12 | trap cleanup EXIT 13 | 14 | function cleanup { 15 | if [ -d $TARGETPATH ]; then 16 | echo "Cleaning up worktree" 17 | git worktree remove -f $TARGETPATH 18 | fi 19 | cd $WORKING_DIR 20 | } 21 | 22 | echo "Creating worktree for $STABLE_BRANCH..." 23 | # Initialize worktree 24 | git worktree add $TARGETPATH $STABLE_BRANCH || exit 1 25 | 26 | cd $TARGETPATH 27 | 28 | echo "Merging $LATEST_BRANCH into $STABLE_BRANCH..." 29 | git merge --squash --strategy-option=theirs $LATEST_BRANCH 30 | 31 | if ! git diff --cached --quiet; then 32 | git commit -m"Publish" 33 | $WORKING_DIR/scripts/deploy.sh $REMOTE $STABLE_BRANCH 34 | else 35 | echo "Nothing to do" 36 | fi 37 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /website/CACHE_BREAKER: -------------------------------------------------------------------------------- 1 | 27 2 | -------------------------------------------------------------------------------- /website/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fkling/astexplorer/8888701e97c2efebe6fb118484e67cfc0077d08c/website/favicon.png -------------------------------------------------------------------------------- /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/fontcustom/fontcustom.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon Font: fontcustom 3 | */ 4 | 5 | @font-face { 6 | font-family: "fontcustom"; 7 | src: url("./fontcustom_45cd59da2a1bc422647cab7f53639319.eot"); 8 | src: url("./fontcustom_45cd59da2a1bc422647cab7f53639319.eot?#iefix") format("embedded-opentype"), 9 | url("./fontcustom_45cd59da2a1bc422647cab7f53639319.woff2") format("woff2"), 10 | url("./fontcustom_45cd59da2a1bc422647cab7f53639319.woff") format("woff"), 11 | url("./fontcustom_45cd59da2a1bc422647cab7f53639319.ttf") format("truetype"), 12 | url("./fontcustom_45cd59da2a1bc422647cab7f53639319.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_45cd59da2a1bc422647cab7f53639319.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-java:before, 31 | .icon-ocaml:before, 32 | .icon-reason:before, 33 | .icon-rust:before, 34 | .icon-scala:before { 35 | display: inline-block; 36 | font-family: "fontcustom"; 37 | font-style: normal; 38 | font-weight: normal; 39 | font-variant: normal; 40 | line-height: 1; 41 | text-decoration: inherit; 42 | text-rendering: optimizeLegibility; 43 | text-transform: none; 44 | -moz-osx-font-smoothing: grayscale; 45 | -webkit-font-smoothing: antialiased; 46 | font-smoothing: antialiased; 47 | } 48 | 49 | .icon-GraphQL_Logo:before { content: "\f100"; } 50 | .icon-handlebars:before { content: "\f101"; } 51 | .icon-icu:before { content: "\f102"; } 52 | .icon-java:before { content: "\f107"; } 53 | .icon-ocaml:before { content: "\f106"; } 54 | .icon-reason:before { content: "\f105"; } 55 | .icon-rust:before { content: "\f104"; } 56 | .icon-scala:before { content: "\f103"; } 57 | -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fkling/astexplorer/8888701e97c2efebe6fb118484e67cfc0077d08c/website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.eot -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fkling/astexplorer/8888701e97c2efebe6fb118484e67cfc0077d08c/website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.ttf -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fkling/astexplorer/8888701e97c2efebe6fb118484e67cfc0077d08c/website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.woff -------------------------------------------------------------------------------- /website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fkling/astexplorer/8888701e97c2efebe6fb118484e67cfc0077d08c/website/fontcustom/fontcustom_45cd59da2a1bc422647cab7f53639319.woff2 -------------------------------------------------------------------------------- /website/fontcustom/input-svg/reason.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /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 |
14 | Built with 15 | React, 16 | Babel, 17 | Font Awesome, 18 | CodeMirror, 19 | Express, 20 | and 21 | webpack 22 | | 23 | GitHub 24 | | 25 | @@COMMIT@@ 26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /website/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer') 4 | ] 5 | } -------------------------------------------------------------------------------- /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/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 | UNSAFE_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/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 {subscribe, clear} from '../utils/pubsub.js'; 8 | import React from 'react'; 9 | 10 | export default class Editor extends React.Component { 11 | 12 | UNSAFE_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 | 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 | clear(this._subscriptions); 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 | -------------------------------------------------------------------------------- /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/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/components/SettingsDrawer.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default class SettingsDrawer extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this._expand = this._expand.bind(this); 8 | this._collapse = this._collapse.bind(this); 9 | } 10 | 11 | _expand() { 12 | this.props.onWantToExpand(); 13 | } 14 | 15 | _collapse() { 16 | this.props.onWantToCollapse(); 17 | } 18 | 19 | render() { 20 | return ( 21 | this.props.isOpen ? 22 |
23 |

Settings

24 | 25 |
26 | : 27 |
28 | ); 29 | } 30 | } 31 | 32 | SettingsDrawer.propTypes = { 33 | onWantToExpand: PropTypes.func, 34 | onWantToCollapse: PropTypes.func, 35 | isOpen: PropTypes.bool, 36 | }; 37 | -------------------------------------------------------------------------------- /website/src/components/Transformer.js: -------------------------------------------------------------------------------- 1 | import Editor from './Editor'; 2 | import JSCodeshiftEditor from './JSCodeshiftEditor'; 3 | import PropTypes from 'prop-types'; 4 | import {publish} from '../utils/pubsub.js'; 5 | import * as React from 'react'; 6 | import SplitPane from './SplitPane'; 7 | import TransformOutput from './TransformOutput'; 8 | import PrettierButton from './buttons/PrettierButton'; 9 | 10 | function resize() { 11 | 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 | keyMap: props.keyMap, 23 | }, 24 | ); 25 | 26 | const formattingEditor = (
27 | 28 | {plainEditor} 29 |
) 30 | 31 | return ( 32 | 35 | {formattingEditor} 36 | 40 | 41 | ); 42 | } 43 | 44 | Transformer.propTypes = { 45 | defaultTransformCode: PropTypes.string, 46 | transformCode: PropTypes.string, 47 | transformer: PropTypes.object, 48 | mode: PropTypes.string, 49 | keyMap: PropTypes.string, 50 | onContentChange: PropTypes.func, 51 | toggleFormatting: PropTypes.func, 52 | enableFormatting: PropTypes.bool, 53 | transformResult: PropTypes.object, 54 | }; 55 | -------------------------------------------------------------------------------- /website/src/components/buttons/ForkButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from '../../utils/classnames.js'; 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/components/buttons/KeyMapButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from '../../utils/classnames.js'; 4 | 5 | const keyMappings = ['default', 'vim', 'emacs', 'sublime'] 6 | 7 | class KeyMapButton extends React.Component { 8 | render() { 9 | return ( 10 |
14 | 25 | {
    26 | {keyMappings.map(keyMap => ( 27 |
  • this.props.onKeyMapChange(keyMap)}> 31 | 34 |
  • 35 | ))} 36 |
} 37 |
38 | ); 39 | } 40 | } 41 | 42 | KeyMapButton.propTypes = { 43 | onKeyMapChange: PropTypes.func, 44 | keyMap: PropTypes.string, 45 | } 46 | 47 | export default KeyMapButton 48 | -------------------------------------------------------------------------------- /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/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/components/buttons/PrettierButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from '../../utils/classnames.js'; 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/components/buttons/SaveButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import cx from '../../utils/classnames.js'; 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/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 | -------------------------------------------------------------------------------- /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 '../../utils/classnames.js'; 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/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/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 function JSON({parseResult}) { 8 | return ( 9 | 13 | ); 14 | } 15 | 16 | JSON.propTypes = { 17 | parseResult: PropTypes.object, 18 | }; 19 | -------------------------------------------------------------------------------- /website/src/components/visualization/SelectedNodeContext.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const SelectedNodeContext = React.createContext(); 4 | 5 | function useSelectedNode() { 6 | const context = React.useContext(SelectedNodeContext); 7 | if (!context) { 8 | throw new Error('useSelectedNode must be used within a SelectedNodeContext'); 9 | } 10 | return context; 11 | } 12 | 13 | let unselectCallback; 14 | 15 | function setSelectedNode(node, cb) { 16 | if (unselectCallback) { 17 | unselectCallback(); 18 | } 19 | if (node) { 20 | global.$node = node; 21 | unselectCallback = cb; 22 | } else { 23 | unselectCallback = null; 24 | delete global.$node; 25 | } 26 | } 27 | 28 | function SelectedNodeProvider(props) { 29 | return ; 30 | } 31 | 32 | export {SelectedNodeProvider, useSelectedNode}; 33 | -------------------------------------------------------------------------------- /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 | white-space: nowrap; 17 | } 18 | 19 | .tree-visualization ul { 20 | margin: 0; 21 | padding-left: 20px; 22 | overflow: auto; 23 | } 24 | 25 | .tree-visualization > ul { 26 | cursor: default; 27 | box-sizing: border-box; 28 | font-family: monospace; 29 | -webkit-touch-callout: none; 30 | user-select: none; 31 | flex: 1; 32 | } 33 | 34 | .tree-visualization .value-body { 35 | min-width: 300px; 36 | width: fit-content; 37 | } 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/containers/ASTOutputContainer.js: -------------------------------------------------------------------------------- 1 | import {connect} from 'react-redux'; 2 | import ASTOutput from '../components/ASTOutput'; 3 | import * as selectors from '../store/selectors'; 4 | 5 | function mapStateToProps(state) { 6 | return { 7 | parseResult: selectors.getParseResult(state), 8 | position: selectors.getCursor(state), 9 | }; 10 | } 11 | 12 | export default connect(mapStateToProps)(ASTOutput); 13 | -------------------------------------------------------------------------------- /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, getParseResult, getKeyMap} from '../store/selectors'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | keyMap: getKeyMap(state), 9 | value: getCode(state), 10 | mode: getParser(state).category.editorMode || getParser(state).category.id, 11 | error: (getParseResult(state) || {}).error, 12 | }; 13 | } 14 | 15 | function mapDispatchToProps(dispatch) { 16 | return { 17 | onContentChange: ({value, cursor}) => { 18 | dispatch(setCode({code: value, cursor})); 19 | }, 20 | onActivity: cursor => dispatch(setCursor(cursor)), 21 | }; 22 | } 23 | 24 | export default connect(mapStateToProps, mapDispatchToProps)(Editor); 25 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/containers/SettingsDrawerContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { expandSettingsDrawer, collapseSettingsDrawer } from '../store/actions'; 3 | import { showSettingsDrawer } from '../store/selectors'; 4 | import SettingsDrawer from '../components/SettingsDrawer'; 5 | 6 | function mapStateToProps(state) { 7 | return { 8 | isOpen: showSettingsDrawer(state), 9 | }; 10 | } 11 | 12 | function mapDispatchToProps(dispatch) { 13 | return { 14 | onWantToExpand: () => dispatch(expandSettingsDrawer()), 15 | onWantToCollapse: () => dispatch(collapseSettingsDrawer()), 16 | }; 17 | } 18 | 19 | export default connect( 20 | mapStateToProps, 21 | mapDispatchToProps, 22 | )(SettingsDrawer); 23 | -------------------------------------------------------------------------------- /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/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 | setKeyMap, 12 | } from '../store/actions'; 13 | import Toolbar from '../components/Toolbar'; 14 | import * as selectors from '../store/selectors'; 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 | keyMap: selectors.getKeyMap(state), 28 | showTransformer: selectors.showTransformer(state), 29 | snippet: selectors.getRevision(state), 30 | }; 31 | } 32 | 33 | function mapDispatchToProps(dispatch) { 34 | return { 35 | onParserChange: parser => { 36 | dispatch(setParser(parser)); 37 | }, 38 | onCategoryChange: category => { 39 | dispatch(selectCategory(category)); 40 | }, 41 | onParserSettingsButtonClick: () => { 42 | dispatch(openSettingsDialog()); 43 | }, 44 | onShareButtonClick: () => { 45 | dispatch(openShareDialog()); 46 | }, 47 | onTransformChange: transformer => { 48 | dispatch(transformer ? selectTransformer(transformer) : hideTransformer()); 49 | }, 50 | onKeyMapChange: keyMap => { 51 | dispatch(setKeyMap(keyMap)) 52 | }, 53 | onSave: () => dispatch(save(false)), 54 | onFork: () => dispatch(save(true)), 55 | onNew: () => { 56 | if (global.location.hash) { 57 | global.location.hash = ''; 58 | } else { 59 | dispatch(reset()); 60 | } 61 | }, 62 | }; 63 | } 64 | 65 | export default connect(mapStateToProps, mapDispatchToProps)(Toolbar); 66 | 67 | -------------------------------------------------------------------------------- /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: 15 | selectors.getParser(state).category.editorMode || 16 | selectors.getParser(state).category.id, 17 | enableFormatting: selectors.getFormattingState(state), 18 | keyMap: selectors.getKeyMap(state), 19 | transformResult: selectors.getTransformResult(state), 20 | }; 21 | } 22 | 23 | function mapDispatchToProps(dispatch) { 24 | return { 25 | onContentChange: ({value, cursor}) => { 26 | dispatch(setTransformState({code: value, cursor})); 27 | }, 28 | toggleFormatting: () => { 29 | dispatch(toggleFormatting()); 30 | }, 31 | }; 32 | } 33 | 34 | export default connect(mapStateToProps, mapDispatchToProps)(Transformer); 35 | -------------------------------------------------------------------------------- /website/src/core/ParseResult.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Describes the result of a parse process. Only exists here for documentation 3 | * purposes. 4 | */ 5 | // eslint-disable-next-line no-unused-vars 6 | const ParseResult = { 7 | /** 8 | * The generated AST 9 | */ 10 | ast: 'any', 11 | 12 | /** 13 | * An error object, if parsing resulted in an error 14 | */ 15 | error: 'Object', 16 | 17 | /** 18 | * How long it took to generate the AST 19 | */ 20 | time: 'number', 21 | 22 | treeAdapter: { 23 | /** 24 | * The type of the adapter to use, as defined in TreeAdapters.js 25 | */ 26 | type: 'string', 27 | /** 28 | * Override the default options with these values 29 | */ 30 | options: 'Object', 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /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/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 | typeProps: new Set(), 15 | 16 | loadParser(callback) { 17 | require(['cssom/lib/parse'], callback); 18 | }, 19 | 20 | parse(CSSOM, code) { 21 | return CSSOM.parse(code); 22 | }, 23 | 24 | getNodeName(node) { 25 | return node.constructor.name; 26 | }, 27 | 28 | nodeToRange(node) { 29 | let { __starts, __ends } = node; 30 | if (__ends === undefined && node.parentRule) { 31 | ({ __ends } = node.parentRule); 32 | } 33 | if (__ends !== undefined) { 34 | return [__starts, __ends]; 35 | } 36 | }, 37 | 38 | opensByDefault(node, key) { 39 | return key === 'cssRules' || key === 'style'; 40 | }, 41 | 42 | _ignoredProperties: new Set(['parentRule', 'parentStyleSheet', '_importants']), 43 | }; 44 | -------------------------------------------------------------------------------- /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, options) { 20 | return csstree.toPlainObject(csstree.parse(code, { 21 | positions: true, 22 | ...options, 23 | })); 24 | }, 25 | 26 | nodeToRange({ loc }) { 27 | if (loc && loc.start && loc.end) { 28 | return [loc.start.offset, loc.end.offset]; 29 | } 30 | }, 31 | 32 | opensByDefault(node, key) { 33 | return key === 'children'; 34 | }, 35 | 36 | getDefaultOptions() { 37 | return { 38 | context: 'stylesheet', 39 | parseValue: true, 40 | parseRulePrelude: true, 41 | parseAtrulePrelude: true, 42 | parseCustomProperty: false, 43 | }; 44 | }, 45 | 46 | _getSettingsConfiguration() { 47 | return { 48 | fields: [ 49 | ['context', [ 50 | 'stylesheet', 51 | 'atrule', 52 | 'atrulePrelude', 53 | 'mediaQueryList', 54 | 'mediaQuery', 55 | 'rule', 56 | 'selectorList', 57 | 'selector', 58 | 'block', 59 | 'declarationList', 60 | 'declaration', 61 | 'value', 62 | ]], 63 | 'parseValue', 64 | 'parseRulePrelude', 65 | 'parseAtrulePrelude', 66 | 'parseCustomProperty', 67 | ], 68 | }; 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /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/css/postcss.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultCSSParserInterface'; 2 | import pkg from 'postcss/package.json'; 3 | 4 | const ID = 'postcss'; 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(['source']), 14 | 15 | loadParser(callback) { 16 | require(['postcss/lib/parse', 'postcss-scss/lib/scss-parse', 'postcss-less/lib/', 'postcss-safe-parser'], (builtIn, scss, less, safe) => { 17 | callback({ 18 | 'built-in': builtIn, 19 | scss, 20 | less: less.parse, 21 | 'safe-parser': safe, 22 | }); 23 | }); 24 | }, 25 | 26 | parse(parsers, code, options) { 27 | return defaultParserInterface.parse.call( 28 | this, 29 | parsers[options.parser], 30 | code, 31 | ); 32 | }, 33 | 34 | nodeToRange({ source: range }) { 35 | if (!range || !range.end) return; 36 | return [ 37 | this.getOffset(range.start), 38 | this.getOffset(range.end) + 1, 39 | ]; 40 | }, 41 | 42 | opensByDefault(node, key) { 43 | return key === 'nodes'; 44 | }, 45 | 46 | _ignoredProperties: new Set(['parent', 'input']), 47 | 48 | getDefaultOptions() { 49 | return { 50 | parser: 'built-in', 51 | }; 52 | }, 53 | 54 | _getSettingsConfiguration() { 55 | return { 56 | fields: [ 57 | ['parser', ['built-in', 'scss', 'less', 'safe-parser']], 58 | ], 59 | }; 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /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/parsers/css/transformers/postcss/codeExample.txt: -------------------------------------------------------------------------------- 1 | const plugin = () => ({ 2 | postcssPlugin: 'postcss-reverse-props', 3 | Once(root) { 4 | // Transform CSS AST here 5 | root.walkRules(rule => { 6 | // Transform each rule here 7 | rule.walkDecls(decl => { 8 | // Transform each property declaration here 9 | decl.prop = decl.prop.split('').reverse().join(''); 10 | }); 11 | }); 12 | } 13 | }); 14 | 15 | plugin.postcss = true; 16 | 17 | export default plugin; 18 | -------------------------------------------------------------------------------- /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(['../../../transpilers/babel', 'postcss'], (transpile, postcss) => { 16 | callback({ transpile: transpile.default, postcss }); 17 | }); 18 | }, 19 | 20 | transform({ transpile, postcss }, transformCode, code) { 21 | transformCode = transpile( transformCode); 22 | let transform = compileModule( // eslint-disable-line no-shadow 23 | transformCode, 24 | { 25 | require(name) { 26 | switch (name) { 27 | case 'postcss': return postcss; 28 | default: throw new Error(`Cannot find module '${name}'`); 29 | } 30 | }, 31 | }, 32 | ); 33 | return postcss([ (transform.default || transform)() ]).process(code).css; 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | opensByDefault(node, key) { 61 | return key === 'children' && node.type === '(program)'; 62 | }, 63 | }; 64 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /website/src/parsers/go/codeExample.txt: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | const TIPS = ` 6 | Click on any AST node with a '+' to expand it 7 | Hovering over a node highlights the corresponding location in the source code 8 | Shift click on an AST node to expand the whole subtree 9 | `; 10 | 11 | func PrintTips() { 12 | fmt.Println(TIPS) 13 | } 14 | -------------------------------------------------------------------------------- /website/src/parsers/go/go.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface' 2 | 3 | const ID = 'go' 4 | 5 | export default { 6 | ...defaultParserInterface, 7 | 8 | id: ID, 9 | displayName: ID, 10 | version: '1.13.4', 11 | homepage: 'https://golang.org/pkg/go/', 12 | _ignoredProperties: new Set(['_type']), 13 | locationProps: new Set(['Loc']), 14 | 15 | async loadParser(callback) { 16 | require(['astexplorer-go'], async parser => { 17 | await parser.init() 18 | callback(parser) 19 | }) 20 | }, 21 | 22 | parse(parser, code) { 23 | return parser.parseFile(code) 24 | }, 25 | 26 | getNodeName(node) { 27 | return node._type 28 | }, 29 | 30 | nodeToRange(node) { 31 | if (node.Loc) { 32 | return [node.Loc.Start, node.Loc.End].map(({ Offset }) => Offset) 33 | } 34 | }, 35 | } 36 | -------------------------------------------------------------------------------- /website/src/parsers/go/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/go/go'; 2 | 3 | export const id = 'go'; 4 | export const displayName = 'Go'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'go'; 7 | -------------------------------------------------------------------------------- /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/graphql/graphql-js.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'graphql/package.json'; 3 | 4 | const ID = 'graphql-js'; 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 | typeProps: new Set(['kind']), 15 | 16 | loadParser(callback) { 17 | require(['graphql/language'], ({ parse }) => { 18 | callback({ parse }); 19 | }); 20 | }, 21 | 22 | parse({ parse }, code, options) { 23 | return parse(code, options); 24 | }, 25 | 26 | nodeToRange(node) { 27 | if (node.loc) { 28 | return [node.loc.start, node.loc.end]; 29 | } 30 | }, 31 | 32 | getNodeName(node) { 33 | return node.kind; 34 | }, 35 | 36 | opensByDefault(node, key) { 37 | return key === 'definitions'; 38 | }, 39 | 40 | getDefaultOptions() { 41 | return { 42 | noLocation: false, 43 | noSource: false, 44 | }; 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /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/graphviz/codeExample.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copy paste in a GraphViz dot file to explore the syntax tree 3 | */ 4 | 5 | digraph { 6 | rankdir=LR 7 | a [fillcolor=green] 8 | c [fillcolor=red] 9 | a -> b 10 | c -> a [dir="back"] 11 | } 12 | -------------------------------------------------------------------------------- /website/src/parsers/graphviz/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'graphviz'; 2 | export const displayName = 'Graphviz'; 3 | export const mimeTypes = ['text/vnd.graphviz']; 4 | export const fileExtension = 'gv'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/graphviz/redot.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'redot/package.json'; 3 | 4 | const ID = 'redot'; 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(['position']), 14 | 15 | loadParser(callback) { 16 | require(['redot'], callback); 17 | }, 18 | 19 | parse(redot, code) { 20 | return redot().parse(code); 21 | }, 22 | 23 | nodeToRange({ position }) { 24 | if (position) { 25 | return [position.start.offset, position.end.offset]; 26 | } 27 | }, 28 | 29 | opensByDefault(node, key) { 30 | return key === 'children'; 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/codeExample.txt: -------------------------------------------------------------------------------- 1 |
2 |

{{title}}

3 |
4 | {{body}} 5 |
6 |
7 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/ember-template-recast.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultHandlebarsParserInterface'; 2 | import pkg from 'ember-template-recast/package.json'; 3 | 4 | const ID = 'ember-template-recast'; 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(['ember-template-recast'], (recast) => callback(recast.parse)); 16 | }, 17 | 18 | opensByDefault(node, key) { 19 | return key === 'body'; 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /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/glimmerjs/glimmer-vm', 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/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/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/parsers/handlebars/transformers/ember-template-recast/codeExample.txt: -------------------------------------------------------------------------------- 1 | module.exports = function(env) { 2 | let b = env.syntax.builders; 3 | 4 | return { 5 | ElementNode(node) { 6 | node.tag = node.tag.split('').reverse().join(''); 7 | } 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/transformers/ember-template-recast/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'ember-template-recast/package.json'; 3 | 4 | const ID = 'ember-template-recast'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage || 'https://github.com/ember-template-lint/ember-template-recast', 11 | 12 | defaultParserID: 'ember-template-recast', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['../../../transpilers/babel', 'ember-template-recast'], 17 | (transpile, recast) => callback({ transpile: transpile.default, recast }), 18 | ); 19 | }, 20 | 21 | transform({ transpile, recast }, transformCode, code) { 22 | transformCode = transpile(transformCode); 23 | const transformModule = compileModule(transformCode); 24 | 25 | // allow "export default" instead of "module.exports = " 26 | const transform = transformModule.__esModule ? 27 | transformModule.default : 28 | transformModule; 29 | 30 | return recast.transform(code, transform).code; 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/transformers/glimmer-compiler/codeExample.txt: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return { 3 | name: 'ast-transform', 4 | 5 | visitor: { 6 | ElementNode(node) { 7 | node.tag = node.tag.split('').reverse().join(''); 8 | } 9 | } 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /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/glimmerjs/glimmer-vm', 11 | 12 | defaultParserID: 'glimmer', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['../../../transpilers/babel', '@glimmer/compiler'], 17 | (transpile, glimmer) => callback({ transpile: transpile.default, glimmer }), 18 | ); 19 | }, 20 | 21 | transform({ transpile, glimmer }, transformCode, code) { 22 | transformCode = transpile(transformCode); 23 | const transformModule = compileModule(transformCode); 24 | 25 | // allow "export default" instead of "module.exports = " 26 | const transform = transformModule.__esModule ? 27 | transformModule.default : 28 | transformModule; 29 | 30 | // compile template to wireformat 31 | let result = glimmer.precompile(code, { 32 | plugins: { 33 | ast: [transform], 34 | }, 35 | }); 36 | 37 | // parse wireformat into JSON 38 | let json = JSON.parse(JSON.parse(result).block); 39 | 40 | // pretty print JSON 41 | return { code: json }; 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /website/src/parsers/handlebars/transformers/glimmer/codeExample.txt: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return { 3 | name: 'ast-transform', 4 | 5 | visitor: { 6 | ElementNode(node) { 7 | node.tag = node.tag.split('').reverse().join(''); 8 | } 9 | } 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /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/glimmerjs/glimmer-vm', 11 | 12 | defaultParserID: 'glimmer', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['../../../transpilers/babel', '@glimmer/syntax'], 17 | (transpile, glimmer) => callback({ transpile: transpile.default, glimmer }), 18 | ); 19 | }, 20 | 21 | transform({ transpile, glimmer }, transformCode, code) { 22 | transformCode = transpile(transformCode); 23 | const transformModule = compileModule(transformCode); 24 | 25 | // allow "export default" instead of "module.exports = " 26 | const transform = transformModule.__esModule ? 27 | transformModule.default : 28 | transformModule; 29 | 30 | let ast = glimmer.preprocess(code, { 31 | plugins: { 32 | ast: [transform], 33 | }, 34 | }); 35 | 36 | return glimmer.print(ast); 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /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 | const serializedLoc = 'toJSON' in loc ? loc.toJSON() : loc; 24 | return [serializedLoc.start, serializedLoc.end].map(pos => this.getOffset(pos)); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /website/src/parsers/html/codeExample.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

My First Heading

6 |

My first paragraph.

7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/src/parsers/html/htmlparser2.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'htmlparser2/package.json'; 3 | 4 | const ID = 'htmlparser2'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/fb55/htmlparser2', 13 | locationProps: new Set(['startIndex', 'endIndex']), 14 | typeProps: new Set(['type', 'name']), 15 | 16 | loadParser(callback) { 17 | require(['htmlparser2/lib/Parser', 'domhandler'], (Parser, {DomHandler}) => { 18 | class Handler extends DomHandler { 19 | constructor() { 20 | super({ withStartIndices: true, withEndIndices: true }); 21 | } 22 | 23 | // It appears that htmlparser2 doesn't correctly process 24 | // ProcessingInstructions. Their "endIndex" isn't set properly. 25 | onprocessinginstruction(name, data) { 26 | this.parser.endIndex = this.parser.tokenizer._index; 27 | super.onprocessinginstruction(name, data); 28 | } 29 | 30 | } 31 | 32 | callback({ Parser, Handler }); 33 | }); 34 | }, 35 | 36 | parse({ Parser: {Parser}, Handler }, code, options) { 37 | let handler = new Handler(); 38 | new Parser(handler, options).end(code); 39 | return handler.root; 40 | }, 41 | 42 | nodeToRange(node) { 43 | if (node.type) { 44 | return [node.startIndex, node.endIndex+1]; 45 | } 46 | }, 47 | 48 | opensByDefault(node, key) { 49 | return key === 'children'; 50 | }, 51 | 52 | getNodeName(node) { 53 | let nodeName = node.type; 54 | if (nodeName && node.name) { 55 | nodeName += `(${node.name})`; 56 | } 57 | return nodeName; 58 | }, 59 | 60 | getDefaultOptions() { 61 | return { 62 | xmlMode: false, 63 | lowerCaseAttributeNames: true, 64 | lowerCaseTags: true, 65 | }; 66 | }, 67 | 68 | _ignoredProperties: new Set(['prev', 'next', 'parent', 'parentNode']), 69 | }; 70 | -------------------------------------------------------------------------------- /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/src/parsers/html/posthtml.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'posthtml-parser/package.json'; 3 | 4 | const ID = 'posthtml-parser'; 5 | const name = 'posthtml-parser'; 6 | 7 | export default { 8 | ...defaultParserInterface, 9 | 10 | id: ID, 11 | displayName: name, 12 | version: pkg.version, 13 | homepage: pkg.homepage || 'https://github.com/fb55/htmlparser2', 14 | 15 | loadParser(callback) { 16 | require(['posthtml-parser'], callback); 17 | }, 18 | 19 | parse(posthtmlParser, code, options) { 20 | return posthtmlParser(code, options); 21 | }, 22 | 23 | opensByDefault(node, key) { 24 | return key === 'content'; 25 | }, 26 | 27 | getDefaultOptions() { 28 | return { lowerCaseTags: false, lowerCaseAttributeNames: false }; 29 | }, 30 | 31 | typeProps: new Set(['tag']), 32 | }; 33 | -------------------------------------------------------------------------------- /website/src/parsers/html/svelte.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'svelte/package.json'; 3 | 4 | const ID = 'svelte'; 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(['start', 'end']), 14 | typeProps: new Set(['tag']), 15 | 16 | loadParser(callback) { 17 | require(['svelte/compiler'], callback); 18 | }, 19 | 20 | parse(parser, code, options) { 21 | return parser.compile(code, options).ast; 22 | }, 23 | 24 | nodeToRange(node) { 25 | if (node.type || node.name) { 26 | return [node.start, node.end]; 27 | } 28 | }, 29 | 30 | opensByDefault(node, key) { 31 | return key === 'children'; 32 | }, 33 | 34 | getNodeName(node) { 35 | return node.tag; 36 | }, 37 | 38 | getDefaultOptions() { 39 | return { 40 | preserveWhitespace: true, 41 | preserveComments: true, 42 | }; 43 | }, 44 | _ignoredProperties: new Set(['parent']), 45 | }; 46 | -------------------------------------------------------------------------------- /website/src/parsers/html/transformers/posthtml/codeExample.txt: -------------------------------------------------------------------------------- 1 | const reverse = str => 2 | str 3 | .split("") 4 | .reverse() 5 | .join(""); 6 | 7 | export default function(tree) { 8 | tree.match({ tag: "h1" }, node => { 9 | 10 | node.content[0] = reverse(node.content[0]); 11 | 12 | return node; 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /website/src/parsers/html/transformers/posthtml/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'posthtml/package.json'; 3 | 4 | const ID = 'posthtml'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage || 'https://github.com/posthtml/posthtml', 11 | 12 | defaultParserID: 'posthtml-parser', 13 | 14 | loadTransformer(callback) { 15 | require(['../../../transpilers/babel', 'posthtml'], (transpile, posthtml) => 16 | callback({ transpile: transpile.default, posthtml })); 17 | }, 18 | 19 | transform({ transpile, posthtml }, transformCode, code) { 20 | // transpile with babel for es6+ support 21 | transformCode = transpile(transformCode); 22 | // compile to turn from string into a module 23 | let transform = compileModule( 24 | // eslint-disable-line no-shadow 25 | transformCode, 26 | ); 27 | return posthtml() 28 | .use(transform.default || transform) 29 | .process(code, { sync: true }).html; 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /website/src/parsers/html/transformers/svelte/codeExample.txt: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return { 3 | name: 'svelte-transform', 4 | 5 | // transform function for entire markup 6 | markup: function(content) { 7 | return content; 8 | }, 9 | 10 | // transform function for script tag 11 | script:function(content, attributes) { 12 | return content.replace(/foo/g, 'baz'); 13 | }, 14 | 15 | // transform function for style tag 16 | style: function(content, attributes) { 17 | return content; 18 | } 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /website/src/parsers/html/transformers/svelte/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'svelte/package.json'; 3 | 4 | const ID = 'svelte'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage || 'https://sveltejs/svelte', 11 | 12 | defaultParserID: 'svelte', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | ['svelte/compiler'], 17 | callback, 18 | ); 19 | }, 20 | 21 | transform({ preprocess }, transformCode, code) { 22 | const transform = compileModule(transformCode); 23 | 24 | // Identity functions in case of missing transforms 25 | const _markupIdentity = (content, _filename) => content; 26 | const _scriptIdentity = (content, _attributes, _filename) => content; 27 | const _styleIdentity = (content, _attributes, _filename) => content; 28 | 29 | // Check if there is a transform 30 | // If Yes, set the appropriate transform or else use identity functions 31 | const markupTransform = transform().markup || _markupIdentity; 32 | const scriptTransform = transform().script || _scriptIdentity; 33 | const styleTransform = transform().style || _styleIdentity; 34 | 35 | const result = preprocess(code, { 36 | markup:({ content, _filename}) => { 37 | return { 38 | code: markupTransform(content), 39 | }; 40 | }, 41 | script: ({content, attributes, _filename}) => { 42 | return { 43 | code: scriptTransform(content, attributes), 44 | }; 45 | }, 46 | style: ({content, attributes, _filename}) => { 47 | return { 48 | code: styleTransform(content, attributes), 49 | }; 50 | }, 51 | }); 52 | return result; 53 | }, 54 | } 55 | -------------------------------------------------------------------------------- /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/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/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 | const TYPES = {}; 6 | 7 | export const parserSettingsConfiguration = { 8 | fields: [ 9 | 'captureLocation', 10 | 'ignoreTag', 11 | 'normalizeHashtagInPlural', 12 | 'shouldParseSkeletons', 13 | ], 14 | }; 15 | 16 | const defaultOptions = { 17 | captureLocation: true, 18 | normalizeHashtagInPlural: true, 19 | }; 20 | 21 | export default { 22 | ...defaultParserInterface, 23 | 24 | id: ID, 25 | displayName: ID, 26 | version: pkg.version, 27 | homepage: 28 | pkg.homepage || 'https://formatjs.io/docs/intl-messageformat-parser/', 29 | locationProps: new Set(['location']), 30 | 31 | loadParser(callback) { 32 | require(['intl-messageformat-parser'], (all) => { 33 | Object.keys(all.TYPE).forEach((k) => { 34 | TYPES[k] = all.TYPE[k]; 35 | }); 36 | callback(all); 37 | }); 38 | }, 39 | 40 | parse(parser, code, opts) { 41 | return parser.parse(code, opts); 42 | }, 43 | 44 | _getSettingsConfiguration() { 45 | return parserSettingsConfiguration; 46 | }, 47 | 48 | getDefaultOptions() { 49 | return defaultOptions; 50 | }, 51 | 52 | getNodeName(node) { 53 | return node.type != null && TYPES[node.type]; 54 | }, 55 | 56 | nodeToRange({ location }) { 57 | if (location && location.start && location.end) { 58 | return [location.start.offset, location.end.offset]; 59 | } 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /website/src/parsers/java/codeExample.txt: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Example { 7 | private ArrayList names; 8 | 9 | public Example() { 10 | names = new ArrayList<>(); 11 | } 12 | 13 | public void addName(String name) { 14 | names.add(name); 15 | } 16 | 17 | public List getNames() { 18 | return new ArrayList<>(names); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /website/src/parsers/java/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/clike/clike'; 2 | 3 | export const id = 'java'; 4 | export const displayName = 'Java'; 5 | export const mimeTypes = ['text/x-java-source']; 6 | export const fileExtension = 'java'; 7 | export const editorMode = 'text/x-java'; 8 | -------------------------------------------------------------------------------- /website/src/parsers/java/java-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'java-parser/package.json'; 3 | 4 | const ID = 'java-parser'; 5 | 6 | export const parserSettingsConfiguration = { 7 | fields: [], 8 | }; 9 | 10 | export default { 11 | ...defaultParserInterface, 12 | 13 | id: ID, 14 | displayName: ID, 15 | version: pkg.version, 16 | homepage: 17 | pkg.homepage || 18 | 'https://github.com/jhipster/prettier-java/tree/master/packages/java-parser', 19 | 20 | locationProps: new Set(['location']), 21 | typeProps: new Set(['name']), 22 | 23 | loadParser(callback) { 24 | require(['java-parser'], callback); 25 | }, 26 | 27 | parse(parser, code) { 28 | return parser.parse(code); 29 | }, 30 | 31 | _ignoredProperties: new Set(['tokenType']), 32 | 33 | getDefaultOptions() { 34 | return {}; 35 | }, 36 | 37 | getNodeName({ name }) { 38 | return name; 39 | }, 40 | 41 | nodeToRange({ location }) { 42 | if (!location) { 43 | return; 44 | } 45 | return [location.startOffset, location.endOffset + 1]; 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /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, code); 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 | -------------------------------------------------------------------------------- /website/src/parsers/js/babel-eslint-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from '@babel/eslint-parser/package.json'; 3 | 4 | const ID = '@babel/eslint-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', 'start', 'end', 'range']), 14 | 15 | loadParser(callback) { 16 | require(['@babel/eslint-parser'], callback); 17 | }, 18 | 19 | parse(parser, code) { 20 | const opts = { 21 | sourceType: 'module', 22 | requireConfigFile: false, 23 | babelOptions: { 24 | parserOpts: { 25 | plugins: ['jsx'], 26 | }, 27 | }, 28 | }; 29 | 30 | const ast = parser.parse(code, opts); 31 | delete ast.tokens; 32 | return ast; 33 | }, 34 | 35 | nodeToRange(node) { 36 | if (typeof node.start !== 'undefined') { 37 | return [node.start, node.end]; 38 | } 39 | }, 40 | 41 | _ignoredProperties: new Set([ 42 | '_paths', 43 | '_babelType', 44 | '__clone', 45 | ]), 46 | }; 47 | -------------------------------------------------------------------------------- /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 | showInMenu: false, 15 | 16 | loadParser(callback) { 17 | require(['babel-eslint'], callback); 18 | }, 19 | 20 | parse(parser, code) { 21 | const opts = { 22 | sourceType: 'module', 23 | }; 24 | 25 | const ast = parser.parseNoPatch(code, opts); 26 | delete ast.tokens; 27 | return ast; 28 | }, 29 | 30 | nodeToRange(node) { 31 | if (typeof node.start !== 'undefined') { 32 | return [node.start, node.end]; 33 | } 34 | }, 35 | 36 | _ignoredProperties: new Set([ 37 | '_paths', 38 | '_babelType', 39 | '__clone', 40 | ]), 41 | }; 42 | -------------------------------------------------------------------------------- /website/src/parsers/js/babel-eslint8.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'babel-eslint8/package.json'; 3 | 4 | const ID = 'babel-eslint8'; 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(['babel-eslint8'], callback); 18 | }, 19 | 20 | parse(parser, code) { 21 | const opts = { 22 | sourceType: 'module', 23 | }; 24 | 25 | const ast = parser.parseNoPatch(code, opts); 26 | delete ast.tokens; 27 | return ast; 28 | }, 29 | 30 | nodeToRange(node) { 31 | if (typeof node.start !== 'undefined') { 32 | return [node.start, node.end]; 33 | } 34 | }, 35 | 36 | _ignoredProperties: new Set([ 37 | '_paths', 38 | '_babelType', 39 | '__clone', 40 | ]), 41 | }; 42 | -------------------------------------------------------------------------------- /website/src/parsers/js/babel-eslint9.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'babel-eslint9/package.json'; 3 | 4 | const ID = 'babel-eslint9'; 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-eslint9'], 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/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 location in the source code", 13 | 14 | "Shift click on an AST node to expand the whole subtree" 15 | ]; 16 | 17 | function printTips() { 18 | tips.forEach((tip, i) => console.log(`Tip ${i}:` + tip)); 19 | } 20 | -------------------------------------------------------------------------------- /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 | if (node && typeof node === 'object') { 28 | for (let prop in node) { 29 | if (this._ignoredProperties.has(prop)) { 30 | continue; 31 | } 32 | 33 | let value = node[prop]; 34 | 35 | if (node.type !== 'Program' && prop === 'parent') { 36 | value = '[Circular]'; 37 | } 38 | 39 | yield { 40 | value, 41 | key: prop, 42 | computed: false, 43 | } 44 | } 45 | } 46 | }, 47 | 48 | _ignoredProperties: new Set([ 49 | '_paths', 50 | '_babelType', 51 | '__clone', 52 | // hide some extra properties to reduce noise 53 | 'comments', 54 | 'directives', 55 | 'extra', 56 | 'leadingComments', 57 | 'root', 58 | 'sourceType', 59 | 'tokens', 60 | 'trailingComments', 61 | ]), 62 | }; 63 | -------------------------------------------------------------------------------- /website/src/parsers/js/esprima.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'esprima/package.json'; 3 | 4 | const ID = '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(['range', 'loc']), 14 | 15 | loadParser(callback) { 16 | require(['esprima'], callback); 17 | }, 18 | 19 | parse(esprima, code, options) { 20 | return esprima.parse(code, options); 21 | }, 22 | 23 | *forEachProperty(node) { 24 | if (node && typeof node === 'object') { 25 | for (let prop in node) { 26 | if (typeof node[prop] === 'function') { 27 | continue; 28 | } 29 | yield { 30 | value: node[prop], 31 | key: prop, 32 | computed: false, 33 | }; 34 | } 35 | } 36 | }, 37 | 38 | getDefaultOptions() { 39 | return { 40 | sourceType: 'module', 41 | loc: false, 42 | range: true, 43 | tokens: false, 44 | comment: false, 45 | attachComment: false, 46 | tolerant: false, 47 | jsx: true, 48 | }; 49 | }, 50 | 51 | _getSettingsConfiguration() { 52 | return { 53 | fields: [ 54 | ['sourceType', ['script', 'module']], 55 | 'range', 56 | 'loc', 57 | 'attachComment', 58 | 'comment', 59 | 'tokens', 60 | 'tolerant', 61 | 'jsx', 62 | ], 63 | required: new Set(['range']), 64 | }; 65 | }, 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /website/src/parsers/js/flow.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'flow-parser/package.json'; 3 | 4 | const ID = 'flow'; 5 | export const defaultOptions = { 6 | enums: false, 7 | esproposal_class_instance_fields: true, 8 | esproposal_class_static_fields: true, 9 | esproposal_decorators: true, 10 | esproposal_export_star_as: true, 11 | esproposal_optional_chaining: true, 12 | esproposal_nullish_coalescing: true, 13 | tokens: false, 14 | types: true, 15 | }; 16 | export const parserSettingsConfiguration = { 17 | fields: [ 18 | 'enums', 19 | 'esproposal_class_instance_fields', 20 | 'esproposal_class_static_fields', 21 | 'esproposal_decorators', 22 | 'esproposal_export_star_as', 23 | 'esproposal_optional_chaining', 24 | 'esproposal_nullish_coalescing', 25 | 'tokens', 26 | 'types', 27 | ], 28 | }; 29 | 30 | export default { 31 | ...defaultParserInterface, 32 | 33 | id: ID, 34 | displayName: ID, 35 | version: pkg.version, 36 | homepage: pkg.homepage || 'https://flow.org/', 37 | locationProps: new Set(['range', 'loc']), 38 | 39 | loadParser(callback) { 40 | require(['flow-parser'], callback); 41 | }, 42 | 43 | parse(flowParser, code, options) { 44 | return flowParser.parse(code, options); 45 | }, 46 | 47 | getDefaultOptions() { 48 | return defaultOptions; 49 | }, 50 | 51 | _getSettingsConfiguration() { 52 | return parserSettingsConfiguration; 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /website/src/parsers/js/hermes.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | // NOTE: We load the hermes-parser package in a worker and not directly, because 3 | // it violates the typical limit on the size of a WebAssembly module that can be 4 | // compiled synchronously on the main thread. 5 | import HermesWorkerClient from './hermes/HermesWorkerClient'; 6 | import pkg from 'hermes-parser/package.json'; 7 | 8 | export const defaultOptions = { 9 | sourceType: 'unambiguous', 10 | flow: 'detect', 11 | allowReturnOutsideFunction: false, 12 | babel: false, 13 | tokens: false, 14 | }; 15 | 16 | export const parserSettingsConfiguration = { 17 | fields: [ 18 | ['sourceType', ['unambiguous', 'module', 'script']], 19 | ['flow', ['detect', 'all']], 20 | 'allowReturnOutsideFunction', 21 | 'babel', 22 | 'tokens', 23 | ], 24 | }; 25 | 26 | export default { 27 | ...defaultParserInterface, 28 | 29 | id: 'hermes', 30 | displayName: pkg.name, 31 | version: pkg.version, 32 | homepage: pkg.homepage || 'https://hermesengine.dev/', 33 | locationProps: new Set(['range', 'loc', 'start', 'end']), 34 | 35 | loadParser(callback) { 36 | callback(new HermesWorkerClient()); 37 | }, 38 | 39 | async parse(hermes, code, options) { 40 | return await hermes.parse(code, options); 41 | }, 42 | 43 | nodeToRange(node) { 44 | // For `babel: true` mode 45 | if (typeof node.start !== 'undefined') { 46 | return [node.start, node.end]; 47 | } 48 | // For `babel: false` mode 49 | return node.range; 50 | }, 51 | 52 | getDefaultOptions() { 53 | return defaultOptions; 54 | }, 55 | 56 | _getSettingsConfiguration() { 57 | return parserSettingsConfiguration; 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /website/src/parsers/js/hermes/HermesWorkerClient.js: -------------------------------------------------------------------------------- 1 | // Some ESLint rules don't understand the Webpack loader syntax. 2 | // eslint-disable-next-line require-in-package/require-in-package, import/default 3 | import HermesWorker from 'worker-loader!./hermes-worker.js'; 4 | 5 | // A Promise-based client for making requests to hermes-worker.js. 6 | export default class HermesWorkerClient { 7 | constructor() { 8 | this._nextRequestId = 0; 9 | this._requests = new Map(); 10 | this._worker = new HermesWorker(); 11 | this._worker.onmessage = this._handleMessage.bind(this); 12 | } 13 | 14 | _handleMessage(e) { 15 | const {type, action, value, requestId} = e.data; 16 | const request = this._requests.get(requestId); 17 | if (!request) { 18 | throw new Error( 19 | `Received a response for a nonexistent '${type}' request ID: ${requestId}`, 20 | ); 21 | } 22 | this._requests.delete(requestId); 23 | switch (action) { 24 | case 'resolve': 25 | request.resolve(value); 26 | break; 27 | case 'reject': 28 | // The worker sends errors as plain objects to work around the 29 | // limitations of the structured clone algorithm. 30 | request.reject(Object.assign(new Error(), value)); 31 | break; 32 | } 33 | } 34 | 35 | _request(type, args) { 36 | return new Promise((resolve, reject) => { 37 | const requestId = this._nextRequestId++; 38 | this._requests.set(requestId, {resolve, reject}); 39 | this._worker.postMessage({type, args, requestId}); 40 | }); 41 | } 42 | 43 | parse(...args) { 44 | return this._request('parse', args); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /website/src/parsers/js/hermes/hermes-worker.js: -------------------------------------------------------------------------------- 1 | /* eslint-env worker */ 2 | 3 | // A Web Worker that wraps methods from the hermes-parser package behind a 4 | // minimal request/response protocol. 5 | 6 | import * as hermesParser from 'hermes-parser'; 7 | 8 | const handlers = { 9 | parse(code, options) { 10 | return hermesParser.parse(code, options); 11 | }, 12 | }; 13 | 14 | onmessage = async function(e) { 15 | const {type, requestId, args = []} = e.data; 16 | let handler = () => { 17 | throw new Error('No handler in Hermes worker for message type: ' + type); 18 | }; 19 | if (Object.hasOwnProperty.call(handlers, type)) { 20 | handler = handlers[type]; 21 | } 22 | let value; 23 | try { 24 | value = handler(...args); 25 | } catch (e) { 26 | postMessage({ 27 | type, 28 | requestId, 29 | action: 'reject', 30 | // Errors don't survive the structured clone algorithm very well across 31 | // browsers - they're either not allowed or lose some of their properties. 32 | // Send a plain-object copy to be reconstituted by the client. 33 | value: {name: e.name, stack: e.stack, message: e.message, ...e}, 34 | }); 35 | return; 36 | } 37 | postMessage({type, requestId, action: 'resolve', value}); 38 | }; 39 | -------------------------------------------------------------------------------- /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/js/shift.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'shift-parser/package.json'; 3 | 4 | const ID = 'shift'; 5 | 6 | let lastParsedLocations; 7 | 8 | export default { 9 | ...defaultParserInterface, 10 | 11 | id: ID, 12 | displayName: ID, 13 | version: pkg.version, 14 | homepage: pkg.homepage, 15 | locationProps: new Set(['loc']), 16 | 17 | loadParser(callback) { 18 | require(['shift-parser'], callback); 19 | }, 20 | 21 | parse(shift, code, options) { 22 | const parseMethod = options.sourceType === 'module' ? 23 | 'parseModuleWithLocation' : 24 | 'parseScriptWithLocation'; 25 | const { tree, locations } = shift[parseMethod](code, options); 26 | lastParsedLocations = locations; 27 | return tree; 28 | }, 29 | 30 | nodeToRange(node) { 31 | if (lastParsedLocations && lastParsedLocations.has(node)) { 32 | let loc = lastParsedLocations.get(node); 33 | return [loc.start.offset, loc.end.offset]; 34 | } 35 | }, 36 | 37 | opensByDefault(node, key) { 38 | return ( 39 | key === 'items' || 40 | key === 'declaration' || 41 | key === 'declarators' || 42 | key === 'statements' || 43 | key === 'expression' || 44 | key === 'body' 45 | ); 46 | }, 47 | 48 | getDefaultOptions() { 49 | return { 50 | earlyErrors: false, 51 | sourceType: 'module', 52 | }; 53 | }, 54 | 55 | _getSettingsConfiguration() { 56 | return { 57 | fields: [ 58 | ['sourceType', ['script', 'module']], 59 | 'earlyErrors', 60 | ], 61 | }; 62 | }, 63 | 64 | }; 65 | -------------------------------------------------------------------------------- /website/src/parsers/js/tenko.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from 'tenko/package.json'; 3 | 4 | const ID = 'tenko'; 5 | export const defaultOptions = { 6 | goalMode: 'script', 7 | webCompat: true, 8 | strictMode: false, 9 | targetEsVersion: Infinity, 10 | ranges: true, 11 | 12 | babelCompat: false, 13 | acornCompat: false, 14 | 15 | exposeScopes: false, 16 | astUids: false, 17 | allowGlobalReturn: false, 18 | fullErrorContext: false, 19 | templateNewlineNormalization: true, 20 | }; 21 | 22 | export const parserSettingsConfiguration = { 23 | fields: [ 24 | ['goalMode', ['script', 'module']], 25 | 'allowGlobalReturn', 26 | 'strictMode', 27 | 'webCompat', 28 | ['targetEsVersion', [Infinity, 6, 7, 8, 9, 10, 11]], 29 | 'exposeScopes', 30 | 'astUids', 31 | 'fullErrorContext', 32 | 'templateNewlineNormalization', 33 | 'ranges', 34 | 'acornCompat', 35 | 'babelCompat', 36 | ], 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(['loc']), 47 | 48 | loadParser(callback) { 49 | require(['tenko'], callback); 50 | }, 51 | 52 | parse(tenko, code, options) { 53 | return tenko.Tenko(code, {...options}); 54 | }, 55 | 56 | getNodeName(node) { 57 | return node.type; 58 | }, 59 | 60 | nodeToRange(node) { 61 | if (node.loc && node.loc.range) { 62 | return [node.loc.range.start, node.loc.range.end]; 63 | } 64 | }, 65 | 66 | getDefaultOptions() { 67 | return defaultOptions; 68 | }, 69 | 70 | _getSettingsConfiguration() { 71 | return parserSettingsConfiguration; 72 | }, 73 | }; 74 | -------------------------------------------------------------------------------- /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/js/transformers/babel/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'babel5/package.json'; 3 | 4 | const ID = 'babel'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | showInMenu: false, 12 | 13 | defaultParserID: 'babylon', 14 | 15 | loadTransformer(callback) { 16 | require( 17 | ['../../../transpilers/babel', 'babel5'], 18 | (transpile, babel) => callback({ transpile: transpile.default, babel: babel }), 19 | ); 20 | }, 21 | 22 | transform({ transpile, babel }, transformCode, code) { 23 | transformCode = transpile(transformCode); 24 | let transform = compileModule( // eslint-disable-line no-shadow 25 | transformCode, 26 | ); 27 | 28 | return babel.transform(code, { 29 | whitelist: [], 30 | plugins: [transform.default || transform], 31 | sourceMaps: true, 32 | }); 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /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/babel6/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'babel6/package.json'; 3 | 4 | const ID = 'babelv6'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | showInMenu: false, 12 | 13 | defaultParserID: 'babylon6', 14 | 15 | loadTransformer(callback) { 16 | require([ 17 | '../../../transpilers/babel', 18 | 'babel6', 19 | 'recast', 20 | ], (transpile, babel, recast) => callback({ transpile: transpile.default, babel, recast })); 21 | }, 22 | 23 | transform({ transpile, babel, recast }, transformCode, code) { 24 | transformCode = transpile(transformCode); 25 | let transform = compileModule( // eslint-disable-line no-shadow 26 | transformCode, 27 | ); 28 | 29 | return babel.transform(code, { 30 | parserOpts: { 31 | parser: recast.parse, 32 | plugins: [ 33 | 'asyncGenerators', 34 | 'classConstructorCall', 35 | 'classProperties', 36 | 'decorators', 37 | 'doExpressions', 38 | 'exportExtensions', 39 | 'flow', 40 | 'functionSent', 41 | 'functionBind', 42 | 'jsx', 43 | 'objectRestSpread', 44 | 'dynamicImport', 45 | ], 46 | }, 47 | generatorOpts: { 48 | generator: recast.print, 49 | }, 50 | plugins: [(transform.default || transform)(babel)], 51 | sourceMaps: true, 52 | }); 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /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/src/parsers/js/transformers/babel7/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'babel7/package.json'; 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 | '../../../transpilers/babel', 17 | 'babel7', 18 | 'recast', 19 | ], (transpile, babel, recast) => callback({ transpile: transpile.default, babel, recast })); 20 | }, 21 | 22 | transform({ transpile, babel, recast }, transformCode, code) { 23 | transformCode = transpile(transformCode); 24 | let transform = compileModule( // eslint-disable-line no-shadow 25 | transformCode, 26 | ); 27 | 28 | return babel.transformAsync(code, { 29 | parserOpts: { 30 | parser: recast.parse, 31 | plugins: [ 32 | 'asyncGenerators', 33 | 'bigInt', 34 | 'classPrivateMethods', 35 | 'classPrivateProperties', 36 | 'classProperties', 37 | ['decorators', {decoratorsBeforeExport: false}], 38 | 'doExpressions', 39 | 'dynamicImport', 40 | 'exportDefaultFrom', 41 | 'exportNamespaceFrom', 42 | 'flow', 43 | 'flowComments', 44 | 'functionBind', 45 | 'functionSent', 46 | 'importMeta', 47 | 'jsx', 48 | 'logicalAssignment', 49 | 'nullishCoalescingOperator', 50 | 'numericSeparator', 51 | 'objectRestSpread', 52 | 'optionalCatchBinding', 53 | 'optionalChaining', 54 | ['pipelineOperator', {proposal: 'minimal'}], 55 | 'throwExpressions', 56 | ], 57 | }, 58 | retainLines: false, 59 | generatorOpts: { 60 | generator: recast.print, 61 | }, 62 | plugins: [(transform.default || transform)(babel)], 63 | sourceMaps: true, 64 | }); 65 | }, 66 | }; 67 | -------------------------------------------------------------------------------- /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/eslint1/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint1/package.json'; 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 | showInMenu: false, 12 | 13 | defaultParserID: 'acorn-to-esprima', 14 | 15 | loadTransformer(callback) { 16 | require( 17 | [ 18 | // Explicitly require just the stuff we care about to avoid loading 19 | // RuleTester and CLIEngine, which are unnecessary and bloat out the 20 | // package size. 21 | 'eslint1/lib/eslint', 22 | 'eslint1/lib/util/source-code', 23 | 'eslint1/lib/rules', 24 | '../../utils/eslintUtils', 25 | ], 26 | (eslint, sourceCode, rules, utils) => callback({eslint, sourceCode, rules, utils}), 27 | ); 28 | }, 29 | 30 | transform({ eslint, sourceCode, rules, utils }, transformCode, code) { 31 | utils.defineRule(rules, transformCode); 32 | return utils.runRule(code, eslint, sourceCode); 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/eslint2/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint2/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 | showInMenu: false, 12 | 13 | defaultParserID: 'babel-eslint', 14 | 15 | loadTransformer(callback) { 16 | require( 17 | [ 18 | // Explicitly require just the stuff we care about to avoid loading 19 | // RuleTester and CLIEngine, which are unnecessary and bloat out the 20 | // package size. 21 | 'eslint2/lib/eslint', 22 | 'eslint2/lib/util/source-code', 23 | 'eslint2/lib/rules', 24 | '../../utils/eslintUtils', 25 | ], 26 | (eslint, sourceCode, rules, utils) => callback({eslint, sourceCode, rules, utils}), 27 | ); 28 | }, 29 | 30 | transform({ eslint, rules, sourceCode, utils }, transformCode, code) { 31 | utils.defineRule(rules, transformCode); 32 | return utils.runRule(code, eslint, sourceCode); 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /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/eslint3/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint3/package.json'; 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 | showInMenu: false, 12 | 13 | defaultParserID: 'babel-eslint', 14 | 15 | loadTransformer(callback) { 16 | require( 17 | [ 18 | // Explicitly require just the stuff we care about to avoid loading 19 | // RuleTester and CLIEngine, which are unnecessary and bloat out the 20 | // package size. 21 | 'eslint3/lib/eslint', 22 | 'eslint3/lib/util/source-code', 23 | 'eslint3/lib/rules', 24 | '../../utils/eslintUtils', 25 | ], 26 | (eslint, sourceCode, rules, utils) => callback({eslint, sourceCode, rules, utils}), 27 | ); 28 | }, 29 | 30 | transform({ eslint, rules, sourceCode, utils }, transformCode, code) { 31 | utils.defineRule(rules, transformCode); 32 | return utils.runRule(code, eslint, sourceCode); 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint4/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function(context) { 2 | return { 3 | TemplateLiteral(node) { 4 | context.report({ 5 | node, 6 | message: 'Do not use template literals', 7 | 8 | fix(fixer) { 9 | if (node.expressions.length) { 10 | // Can't auto-fix template literal with expressions 11 | return; 12 | } 13 | 14 | return [ 15 | fixer.replaceTextRange([node.start, node.start + 1], '"'), 16 | fixer.replaceTextRange([node.end - 1, node.end], '"'), 17 | ]; 18 | }, 19 | }); 20 | } 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint4/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint4/package.json'; 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 | [ 17 | 'eslint4/lib/linter', 18 | 'eslint4/lib/util/source-code', 19 | '../../utils/eslint4Utils', 20 | ], 21 | (Linter, sourceCode, utils) => callback({eslint: new Linter(), sourceCode, utils}), 22 | ); 23 | }, 24 | 25 | transform({ eslint, sourceCode, utils }, transformCode, code) { 26 | utils.defineRule(eslint, transformCode); 27 | return utils.runRule(code, eslint, sourceCode); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint8/codeExample.txt: -------------------------------------------------------------------------------- 1 | export const meta = { 2 | type: 'problem', 3 | hasSuggestions: true, 4 | fixable: true, 5 | }; 6 | 7 | export function create(context) { 8 | return { 9 | TemplateLiteral(node) { 10 | context.report({ 11 | node, 12 | message: 'Do not use template literals', 13 | 14 | fix(fixer) { 15 | if (node.expressions.length) { 16 | // Can't auto-fix template literal with expressions 17 | return; 18 | } 19 | 20 | return [ 21 | fixer.replaceTextRange([node.start, node.start + 1], '"'), 22 | fixer.replaceTextRange([node.end - 1, node.end], '"'), 23 | ]; 24 | }, 25 | }); 26 | } 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/eslint8/index.js: -------------------------------------------------------------------------------- 1 | import pkg from 'eslint8/package.json'; 2 | 3 | const ID = 'eslint-v8'; 4 | const name = 'ESLint v8'; 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 | 'eslint8/lib/linter', 17 | 'eslint8/lib/source-code', 18 | '../../utils/eslint4Utils', 19 | ], (Linter, sourceCode, utils) => 20 | callback({ eslint: new Linter.Linter(), sourceCode, utils })); 21 | }, 22 | 23 | transform({ eslint, sourceCode, utils }, transformCode, code) { 24 | utils.defineRule(eslint, transformCode); 25 | return utils.runRule(code, eslint, sourceCode); 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/jscodeshift/codeExample.txt: -------------------------------------------------------------------------------- 1 | // jscodeshift can take a parser, like "babel", "babylon", "flow", "ts", or "tsx" 2 | // Read more: https://github.com/facebook/jscodeshift#parser 3 | export const parser = '{{parser}}' 4 | 5 | // Press ctrl+space for code completion 6 | export default function transformer(file, api) { 7 | const j = api.jscodeshift; 8 | 9 | return j(file.source) 10 | .find(j.Identifier) 11 | .forEach(path => { 12 | j(path).replaceWith( 13 | j.identifier(path.node.name.split('').reverse().join('')) 14 | ); 15 | }) 16 | .toSource(); 17 | } 18 | -------------------------------------------------------------------------------- /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": "babel" 12 | } 13 | -------------------------------------------------------------------------------- /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( 17 | ['../../../transpilers/babel', 'prettier/standalone', 'prettier/parser-babel'], 18 | (transpile, prettier, babel) => callback({ transpile: transpile.default, prettier, babel }), 19 | ); 20 | }, 21 | 22 | transform({ transpile, prettier, babel }, transformCode, code) { 23 | transformCode = transpile(transformCode); 24 | const options = compileModule(transformCode); 25 | return prettier.format( 26 | code, 27 | Object.assign({plugins: [babel]}, options.default || options), 28 | ); 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/recast/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default function transformer(code, { recast, parsers }) { 2 | const ast = recast.parse(code, { parser: parsers.esprima }); 3 | 4 | recast.visit(ast, { 5 | visitIdentifier(path) { 6 | path.node.name = path.node.name.split("").reverse().join(""); 7 | return false; 8 | } 9 | }); 10 | 11 | return recast.print(ast).code; 12 | } 13 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/recast/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'recast/package.json'; 3 | 4 | const ID = 'recast'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage || 'https://github.com/benjamn/recast', 11 | 12 | defaultParserID: 'recast', 13 | 14 | loadTransformer(callback) { 15 | require( 16 | [ 17 | '../../../transpilers/babel', 18 | 'recast', 19 | 'recast/parsers/acorn', 20 | 'recast/parsers/babel', 21 | 'recast/parsers/babylon', 22 | 'recast/parsers/esprima', 23 | 'recast/parsers/flow', 24 | 'recast/parsers/typescript', 25 | ], 26 | (transpile, recast, acorn, babel, babylon, esprima, flow, typescript) => { 27 | callback({ 28 | transpile: transpile.default, 29 | recast, 30 | parsers: { 31 | acorn, 32 | babel, 33 | babylon, 34 | esprima, 35 | flow, 36 | typescript, 37 | }, 38 | }); 39 | }, 40 | ); 41 | }, 42 | 43 | transform( 44 | { transpile, recast, parsers }, 45 | transformCode, 46 | code, 47 | ) { 48 | transformCode = transpile(transformCode); 49 | const transformModule = compileModule( // eslint-disable-line no-shadow 50 | transformCode, 51 | ); 52 | const transform = transformModule.__esModule ? 53 | transformModule.default : 54 | transformModule; 55 | 56 | const result = transform( 57 | code, 58 | { 59 | recast, 60 | parsers, 61 | }, 62 | ); 63 | if (typeof result !== 'string') { 64 | throw new Error( 65 | 'Transformers must either return undefined, null or a string, not ' + 66 | `"${typeof result}".`, 67 | ); 68 | } 69 | return result; 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/tslint/codeExample.txt: -------------------------------------------------------------------------------- 1 | export class Rule extends Lint.Rules.AbstractRule { 2 | public static FAILURE_STRING = "Do not use template literals"; 3 | 4 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 5 | return this.applyWithWalker(new NoTemplateExpressionWalker(sourceFile, this.getOptions())); 6 | } 7 | } 8 | 9 | // The walker takes care of all the work. 10 | class NoTemplateExpressionWalker extends Lint.RuleWalker { 11 | public visitTemplateExpression(node: ts.TemplateExpression) { 12 | // create a failure at the current position 13 | this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); 14 | 15 | // call the base version of this visitor to actually parse this node 16 | super.visitTemplateExpression(node); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/tslint/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'tslint/package.json'; 3 | 4 | const ID = 'tslint'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: pkg.homepage, 11 | 12 | defaultParserID: 'typescript', 13 | 14 | loadTransformer(callback) { 15 | require([ 16 | '../../../transpilers/typescript', 17 | 'tslint/lib/index', 18 | 'typescript', 19 | ], 20 | ( 21 | transpile, 22 | tslint, 23 | typescript, 24 | ) => callback({transpile: transpile.default, tslint, typescript})); 25 | }, 26 | 27 | transform({ transpile, tslint, typescript }, transformCode, code) { 28 | transformCode = transpile(transformCode); 29 | let transform = compileModule( // eslint-disable-line no-shadow 30 | transformCode, 31 | { 32 | Lint: tslint, 33 | ts: typescript, 34 | }, 35 | ); 36 | 37 | let linter = new tslint.Linter({}); 38 | let rule = new transform.Rule({}); 39 | let sourceFile = linter.getSourceFile('astExplorer.ts', code); 40 | let ruleFailures = linter.applyRule(rule, sourceFile); 41 | 42 | return formatResults(ruleFailures); 43 | }, 44 | }; 45 | 46 | function formatResults(results) { 47 | return results.length === 0 48 | ? 'Lint rule not fired.' 49 | : results.map(formatResult).join('').trim(); 50 | } 51 | 52 | function formatResult(result) { 53 | let { line, character } = result.startPosition.lineAndCharacter; 54 | let rawLine = result.rawLines.split('\n')[line]; 55 | let pointer = '-'.repeat(character) + '^'; 56 | return ` 57 | // ${result.failure} (at ${line+1}:${character+1}) 58 | ${rawLine} 59 | // ${pointer} 60 | `; 61 | } 62 | -------------------------------------------------------------------------------- /website/src/parsers/js/transformers/typescript/codeExample.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * The transformer signature is based on https://github.com/cevek/ttypescript#program 3 | */ 4 | export default function (program) { 5 | const checker = program.getTypeChecker(); 6 | return (context) => { 7 | return (sourceFile) => { 8 | const visitor = (node) => { 9 | // This branch evaluates '2 + 2' like expressions and replaces the node with the result (in this case '4') 10 | if (ts.isBinaryExpression(node)) { 11 | if (ts.isNumericLiteral(node.left) && ts.isNumericLiteral(node.right)) { 12 | // We could parse `node.text` as a number, or we can use the typechecker to get type info for nodes 13 | const lhs = checker.getTypeAtLocation(node.left); 14 | const rhs = checker.getTypeAtLocation(node.right); 15 | 16 | switch (node.operatorToken.kind) { 17 | case ts.SyntaxKind.PlusToken: 18 | return context.factory.createNumericLiteral(lhs.value + rhs.value); 19 | } 20 | } 21 | } 22 | // 23 | if (ts.isIdentifier(node) && node.text === 'printTips' || node.text === 'tips') { 24 | return context.factory.createIdentifier(node.text.split('').reverse().join('')); 25 | } 26 | return ts.visitEachChild(node, visitor, context); 27 | }; 28 | return ts.visitNode(sourceFile, visitor); 29 | }; 30 | }; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /website/src/parsers/js/typescript-eslint-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from './utils/defaultESTreeParserInterface'; 2 | import pkg from '@typescript-eslint/parser/package.json'; 3 | 4 | const ID = '@typescript-eslint/parser'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://typescript-eslint.io/', 13 | locationProps: new Set(['loc', 'start', 'end', 'range']), 14 | 15 | loadParser(callback) { 16 | require(['@typescript-eslint/parser'], callback); 17 | }, 18 | 19 | parse(parser, code, options) { 20 | return parser.parse(code, options); 21 | }, 22 | 23 | getDefaultOptions() { 24 | return { 25 | range: true, 26 | loc: false, 27 | tokens: false, 28 | comment: false, 29 | useJSXTextNode: false, 30 | ecmaVersion: 6, 31 | sourceType: 'module', 32 | 33 | ecmaFeatures: { 34 | jsx: true, 35 | }, 36 | }; 37 | }, 38 | 39 | _getSettingsConfiguration(defaultOptions) { 40 | return { 41 | fields: [ 42 | ['ecmaVersion', [3, 5, 6, 7, 8, 9], value => Number(value)], 43 | ['sourceType', ['script', 'module']], 44 | 'range', 45 | 'loc', 46 | 'tokens', 47 | 'comment', 48 | 'useJSXTextNode', 49 | { 50 | key: 'ecmaFeatures', 51 | title: 'ecmaFeatures', 52 | fields: Object.keys(defaultOptions.ecmaFeatures), 53 | settings: 54 | settings => settings.ecmaFeatures || {...defaultOptions.ecmaFeatures}, 55 | }, 56 | ], 57 | required: new Set(['range']), 58 | }; 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /website/src/parsers/js/uglify.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from '../../../node_modules/uglify-es/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 | typeProps: new Set(['TYPE']), 16 | 17 | loadParser(callback) { 18 | require([ 19 | 'raw-loader?esModule=false!uglify-es/lib/utils.js', 20 | 'raw-loader?esModule=false!uglify-es/lib/ast.js', 21 | 'raw-loader?esModule=false!uglify-es/lib/parse.js', 22 | ], (...contents) => { 23 | contents.push('exports.parse = parse;'); 24 | callback(compileModule(contents.join('\n\n'))); 25 | }); 26 | }, 27 | 28 | parse(UglifyJS, code) { 29 | return UglifyJS.parse(code); 30 | }, 31 | 32 | getNodeName(node) { 33 | let type = node.TYPE; 34 | if (type === 'Token') { 35 | type += `(${node.type})`; 36 | } 37 | return type; 38 | }, 39 | 40 | nodeToRange(node) { 41 | let start, end; 42 | switch (node.TYPE) { 43 | case 'Token': 44 | start = end = node; 45 | break; 46 | case undefined: 47 | return null; 48 | default: 49 | ({ start, end } = node); 50 | break; 51 | } 52 | if (start && end) { 53 | return [start.pos, end.endpos]; 54 | } 55 | return null; 56 | }, 57 | 58 | opensByDefault(node, key) { 59 | return ( 60 | key === 'body' || 61 | key === 'elements' || // array literals 62 | key === 'definitions' || // variable declaration 63 | key === 'properties' 64 | ); 65 | }, 66 | 67 | _ignoredProperties: new Set(['_walk', 'CTOR']), 68 | }; 69 | -------------------------------------------------------------------------------- /website/src/parsers/js/utils/defaultESTreeParserInterface.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../../utils/defaultParserInterface'; 2 | 3 | export default { 4 | ...defaultParserInterface, 5 | 6 | opensByDefault(node, key) { 7 | return ( 8 | Boolean(node) && node.type === 'Program' || 9 | key === 'body' || 10 | key === 'elements' || // array literals 11 | key === 'declarations' || // variable declaration 12 | key === 'expression' // expression statements 13 | ); 14 | }, 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /website/src/parsers/js/utils/eslintUtils.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../utils/compileModule'; 2 | import transpile from '../../transpilers/babel'; 3 | import {parseNoPatch} from 'babel-eslint'; 4 | 5 | export function formatResults(results) { 6 | return results.length === 0 7 | ? 'Lint rule not fired.' 8 | : results.map(formatResult).join('').trim(); 9 | } 10 | 11 | export function formatResult(result) { 12 | var pointer = '-'.repeat(result.column - 1) + '^'; 13 | return ` 14 | // ${result.message} (at ${result.line}:${result.column}) 15 | ${result.source} 16 | // ${pointer} 17 | `; 18 | } 19 | 20 | export function defineRule(eslintRules, code) { 21 | // Compile the transform code and install it as an ESLint rule. The rule 22 | // name doesn't really matter here, so we'll just use a hard-coded name. 23 | code = transpile(code); 24 | const rule = compileModule(code); 25 | eslintRules.define('astExplorerRule', rule.default || rule); 26 | } 27 | 28 | export function runRule(code, eslint, sourceCode) { 29 | // Run the ESLint rule on the AST of the provided code. 30 | // Reference: http://eslint.org/docs/developer-guide/nodejs-api 31 | const ast = parseNoPatch(code, { 32 | sourceType: 'module', 33 | }); 34 | const results = eslint.verify(new sourceCode(code, ast), { 35 | env: {es6: true}, 36 | parserOptions: { 37 | ecmaVersion: 8, 38 | sourceType: 'module', 39 | ecmaFeatures: {experimentalObjectRestSpread: true}, 40 | }, 41 | rules: { 42 | astExplorerRule: 2, 43 | }, 44 | }); 45 | return formatResults(results); 46 | } 47 | -------------------------------------------------------------------------------- /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/json/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/javascript/javascript'; 2 | 3 | export const id = 'json'; 4 | export const displayName = 'JSON'; 5 | export const mimeTypes = ['application/json']; 6 | export const fileExtension = 'json'; 7 | export const editorMode = {name: 'javascript', json: true}; 8 | -------------------------------------------------------------------------------- /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 | nodeToRange({loc}) { 24 | if (loc) { 25 | return [ 26 | loc.start.offset, 27 | loc.end.offset, 28 | ]; 29 | } 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /website/src/parsers/json/momoa.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from '@humanwhocodes/momoa/package.json'; 3 | 4 | const ID = 'momoa'; 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(['@humanwhocodes/momoa'], callback); 17 | }, 18 | 19 | parse(momoa, code, options) { 20 | return momoa.parse(code, options); 21 | }, 22 | 23 | nodeToRange({loc}) { 24 | if (loc) { 25 | return [ 26 | loc.start.offset, 27 | loc.end.offset, 28 | ]; 29 | } 30 | }, 31 | 32 | getDefaultOptions() { 33 | return { 34 | comments: true, 35 | tokens: true, 36 | ranges: true, 37 | }; 38 | }, 39 | 40 | } 41 | -------------------------------------------------------------------------------- /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/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/src/parsers/lua/luaparse.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | import pkg from 'luaparse/package.json'; 4 | 5 | const ID = 'luaparse'; 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(['range', 'loc']), 15 | 16 | loadParser(callback) { 17 | require(['luaparse'], callback); 18 | }, 19 | 20 | parse(luaparse, code, options={}) { 21 | return luaparse.parse(code, options); 22 | }, 23 | 24 | getDefaultOptions() { 25 | return { 26 | ranges: true, 27 | locations: false, 28 | comments: true, 29 | scope: false, 30 | luaVersion: '5.1', 31 | }; 32 | }, 33 | 34 | _getSettingsConfiguration() { 35 | return { 36 | fields: [ 37 | 'ranges', 38 | 'locations', 39 | 'comments', 40 | 'scope', 41 | ['luaVersion', ['5.1', '5.2', '5.3']], 42 | ], 43 | required: new Set(['ranges']), 44 | }; 45 | 46 | }, 47 | 48 | renderSettings(parserSettings, onChange) { 49 | return ( 50 |
51 |

52 | 55 | Option descriptions 56 | 57 |

58 | {defaultParserInterface.renderSettings.call( 59 | this, 60 | parserSettings, 61 | onChange, 62 | )} 63 |
64 | ); 65 | }, 66 | }; 67 | -------------------------------------------------------------------------------- /website/src/parsers/lucene/codeExample.txt: -------------------------------------------------------------------------------- 1 | name:frank OR job:engineer AND food:/marshmal+ows/ 2 | -------------------------------------------------------------------------------- /website/src/parsers/lucene/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'lucene'; 2 | export const displayName = 'Lucene'; 3 | export const mimeTypes = ['']; 4 | export const fileExtension = 'lucene'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/lucene/lucene.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'lucene/package.json'; 3 | 4 | const ID = 'lucene'; 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(['fieldLocation', 'termLocation', 'location']), 14 | 15 | loadParser(callback) { 16 | require(['lucene'], callback); 17 | }, 18 | 19 | parse({parse}, code) { 20 | return parse(code); 21 | }, 22 | 23 | nodeToRange(node) { 24 | let start = []; 25 | let end = []; 26 | 27 | if (node.location) { 28 | start.push(node.location.start.offset); 29 | end.push(node.location.end.offset); 30 | } 31 | if (node.fieldLocation) { 32 | start.push(node.fieldLocation.start.offset); 33 | end.push(node.fieldLocation.end.offset); 34 | } 35 | if (node.termLocation) { 36 | start.push(node.termLocation.start.offset); 37 | end.push(node.termLocation.end.offset); 38 | } 39 | 40 | if (start.length === 0 || end.length === 0) { 41 | return; 42 | } 43 | 44 | return [start.reduce((a, b) => Math.min(a, b)), end.reduce((a, b) => Math.max(a, b))]; 45 | }, 46 | 47 | getDefaultOptions() { 48 | return {}; 49 | }, 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /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/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/markdown/transformers/remark/codeExample.txt: -------------------------------------------------------------------------------- 1 | // available utilities are: "unist-util-is", "unist-util-visit", and "unist-util-visit-parents" 2 | const visit = require("unist-util-visit"); 3 | 4 | module.exports = function attacher(options) { 5 | return function transformer(tree, vfile) { 6 | // add a level to headings, for example `# heading` to `## heading` 7 | visit(tree, "heading", (node) => { 8 | node.depth += 1 9 | }); 10 | return tree; 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /website/src/parsers/markdown/transformers/remark/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from 'remark/package.json'; 3 | 4 | const ID = 'remark'; 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 | 'remark', 17 | 'unist-util-is', 18 | 'unist-util-visit', 19 | 'unist-util-visit-parents', 20 | ], ({ remark }, { is }, { visit }, { visitParents }) => { 21 | callback({ 22 | remark, 23 | 'unist-util-is': is, 24 | 'unist-util-visit': visit, 25 | 'unist-util-visit-parents': visitParents, 26 | }); 27 | }); 28 | }, 29 | 30 | transform({ remark, ...availableModules }, transformCode, code) { 31 | function sandboxRequire(name) { 32 | if (!Object.getOwnPropertyNames(availableModules).includes(name)) 33 | throw new Error(`Cannot find module '${name}'`); 34 | return availableModules[name]; 35 | } 36 | 37 | const transform = compileModule(transformCode, { require: sandboxRequire }); 38 | return remark().use(transform).processSync(code).value; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /website/src/parsers/mathjs/codeExample.txt: -------------------------------------------------------------------------------- 1 | (sin(2x) + e^x) / 2 2 | -------------------------------------------------------------------------------- /website/src/parsers/mathjs/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'mathjs'; 2 | export const displayName = 'Math.js'; 3 | export const mimeTypes = ['text/mathjs']; 4 | export const fileExtension = 'mathjs'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/mathjs/mathjs.js: -------------------------------------------------------------------------------- 1 | import pkg from 'mathjs/package.json'; 2 | 3 | import defaultParserInterface from '../utils/defaultParserInterface' 4 | 5 | const ID = 'mathjs' 6 | 7 | export default { 8 | ...defaultParserInterface, 9 | 10 | id: ID, 11 | displayName: ID, 12 | version: pkg.version, 13 | homepage: 'https://mathjs.org/', 14 | locationProps: new Set(['span']), 15 | 16 | defaultParserID: 'mathjs', 17 | 18 | async loadParser(callback) { 19 | require(['mathjs'], callback); 20 | }, 21 | 22 | parse(parser, code) { 23 | try { 24 | return parser.parse(code) 25 | } catch (message) { 26 | // AST Explorer expects the thrown error to be an object, not a string. 27 | throw new SyntaxError(message); 28 | } 29 | }, 30 | 31 | getNodeName(node) { 32 | return node.type 33 | }, 34 | 35 | // TODO once this feature is added to mathjs 36 | // nodeToRange(node) { 37 | // }, 38 | 39 | opensByDefault(node) { 40 | return node.type === 'BlockNode' 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /website/src/parsers/mdx/codeExample.txt: -------------------------------------------------------------------------------- 1 | import MyComp from './components/MyComp' 2 | 3 | export const meta = { 4 | title: 'Page Title', 5 | description: 'This is a page description', 6 | } 7 | 8 | # Hello 9 | 10 | 11 | Component children 12 | 13 | 14 | Some *emphasis*, **importance**, and `code`. 15 | 16 | --- 17 | 18 | ```javascript 19 | console.log('!'); 20 | ``` 21 | 22 | * foo 23 | * bar 24 | * baz 25 | -------------------------------------------------------------------------------- /website/src/parsers/mdx/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'mdx'; 2 | export const displayName = 'MDX'; 3 | export const mimeTypes = ['text/mdx']; 4 | export const fileExtension = 'mdx'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/mdx/mdxhast.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from '@mdx-js/mdx/package.json'; 3 | 4 | const ID = 'mdxhast'; 5 | 6 | function removeNewlines(node) { 7 | if (node.children != null) { 8 | node.children = node.children.filter(node => node.value !== '\n'); 9 | node.children.forEach(removeNewlines); 10 | } 11 | } 12 | 13 | export default { 14 | ...defaultParserInterface, 15 | 16 | id: ID, 17 | displayName: ID, 18 | version: pkg.version, 19 | homepage: 'https://mdxjs.com', 20 | locationProps: new Set(['position']), 21 | 22 | loadParser(callback) { 23 | require(['@mdx-js/mdx', '@mdx-js/mdx/mdx-ast-to-mdx-hast'], (mdx, mdxAstToMdxHast) => callback({mdx, mdxAstToMdxHast})); 24 | }, 25 | 26 | parse({mdx, mdxAstToMdxHast}, code) { 27 | let result; 28 | mdx.sync(code, { 29 | hastPlugins: [ 30 | mdxAstToMdxHast, 31 | () => removeNewlines, 32 | () => tree => { 33 | result = tree; 34 | }, 35 | ], 36 | }); 37 | 38 | return result; 39 | }, 40 | 41 | nodeToRange({ position }) { 42 | if (position) { 43 | return [position.start.offset, position.end.offset]; 44 | } 45 | }, 46 | 47 | opensByDefault(node, key) { 48 | return key === 'children'; 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /website/src/parsers/mdx/transformers/mdx/codeExample.txt: -------------------------------------------------------------------------------- 1 | export default { 2 | mdPlugins: [], 3 | hastPlugins: [], 4 | } 5 | -------------------------------------------------------------------------------- /website/src/parsers/mdx/transformers/mdx/index.js: -------------------------------------------------------------------------------- 1 | import compileModule from '../../../utils/compileModule'; 2 | import pkg from '@mdx-js/mdx/package.json'; 3 | 4 | const ID = 'mdx'; 5 | 6 | export default { 7 | id: ID, 8 | displayName: ID, 9 | version: pkg.version, 10 | homepage: 'https://mdxjs.com', 11 | 12 | defaultParserID: 'mdxhast', 13 | 14 | loadTransformer(callback) { 15 | require([ 16 | '../../../transpilers/babel', 17 | '@mdx-js/mdx', 18 | 'prettier/standalone', 19 | 'prettier/parser-babel', 20 | ], (transpile, mdx, prettier, babel) => { 21 | callback({ transpile: transpile.default, mdx, prettier, babel }); 22 | }); 23 | }, 24 | 25 | transform({ transpile, mdx, prettier, babylon }, transformCode, code) { 26 | transformCode = transpile(transformCode); 27 | const transform = compileModule(transformCode); 28 | const jsxCode = mdx.sync(code, { 29 | ...(transform.default || transform), 30 | }); 31 | try { 32 | return prettier.format(jsxCode, { 33 | parser: 'babylon', 34 | plugins: [babylon], 35 | }); 36 | } catch (err) { 37 | return ` 38 | ${err.message} 39 | 40 | ------------ 41 | Full output: 42 | ------------ 43 | 44 | ${jsxCode.trim()} 45 | `.trim(); 46 | } 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /website/src/parsers/monkey/codeExample.txt: -------------------------------------------------------------------------------- 1 | let version = 1 + (50 / 2) - (8 * 3); 2 | 3 | let name = "The Monkey programming language"; 4 | 5 | let isMonkeyFastNow = true; 6 | 7 | let people = [{"name": "Anna", "age": 24}, {"name": "Bob", "age": 99}]; 8 | 9 | let getName = fn(person) { person["name"]; }; 10 | getName(people[0]); 11 | getName(people[1]); 12 | 13 | puts(len(people)) 14 | 15 | let fibonacci = fn(x) { 16 | if (x == 0) { 17 | 0 18 | } else { 19 | if (x == 1) { 20 | return 1; 21 | } else { 22 | fibonacci(x - 1) + fibonacci(x - 2); 23 | } 24 | } 25 | }; 26 | 27 | 28 | let newAdder = fn(a, b) { 29 | fn(c) { a + b + c }; 30 | }; 31 | 32 | let adder = newAdder(1, 2); 33 | 34 | adder(8); 35 | -------------------------------------------------------------------------------- /website/src/parsers/monkey/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'monkey'; 2 | export const displayName = 'Monkey'; 3 | export const mimeTypes = []; 4 | export const fileExtension = 'monkey'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/monkey/monkey.js: -------------------------------------------------------------------------------- 1 | import pkg from '@gengjiawen/monkey-wasm/package.json'; 2 | 3 | import defaultParserInterface from '../utils/defaultParserInterface' 4 | 5 | const ID = 'monkey' 6 | 7 | export default { 8 | ...defaultParserInterface, 9 | 10 | id: ID, 11 | displayName: ID, 12 | version: pkg.version, 13 | homepage: 'https://monkeylang.org/', 14 | locationProps: new Set(['span']), 15 | 16 | async loadParser(callback) { 17 | require(['@gengjiawen/monkey-wasm/monkey_wasm.js'], callback); 18 | }, 19 | 20 | parse(parser, code) { 21 | try { 22 | return JSON.parse(parser.parse(code)); 23 | } catch (message) { 24 | // AST Explorer expects the thrown error to be an object, not a string. 25 | throw new SyntaxError(message); 26 | } 27 | }, 28 | 29 | getNodeName(node) { 30 | return node.type 31 | }, 32 | 33 | nodeToRange(node) { 34 | if (node && node.span && typeof node.span.start === 'number') { 35 | return [node.span.start, node.span.end]; 36 | } 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /website/src/parsers/ocaml/codeExample.txt: -------------------------------------------------------------------------------- 1 | let tips = [ 2 | "Click on any AST node with a '+' to expand it"; 3 | 4 | "Hovering over a node highlights the 5 | corresponding location in the source code"; 6 | 7 | "Shift click on an AST node to expand the whole subtree" 8 | ] 9 | 10 | let printTips () = 11 | tips |> (List.iteri (fun i tip -> Printf.printf "Tip %d: %s\n" i tip)) 12 | -------------------------------------------------------------------------------- /website/src/parsers/ocaml/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/mllike/mllike'; 2 | 3 | export const id = 'ocaml'; 4 | export const editorMode = 'text/x-ocaml'; 5 | export const displayName = 'OCaml'; 6 | export const mimeTypes = ['text/x-ocaml']; 7 | export const fileExtension = 'ml'; 8 | -------------------------------------------------------------------------------- /website/src/parsers/ocaml/refmt-ml.js: -------------------------------------------------------------------------------- 1 | import config from '../reason/refmt'; 2 | 3 | const ID = 'refmt-ml'; 4 | 5 | export default { 6 | ...config, 7 | id: ID, 8 | parse: function(parser, code) { 9 | return parser.parseOcaml(code); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /website/src/parsers/php/codeExample.txt: -------------------------------------------------------------------------------- 1 | $tip) { 15 | echo "Tip $i: " . $tip; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /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/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 | typeProps: new Set(['kind']), 24 | 25 | loadParser(callback) { 26 | require(['php-parser'], callback); 27 | }, 28 | 29 | parse(Engine, code) { 30 | const parser = new Engine(defaultOptions); 31 | return parser.parseCode(code, ''); 32 | }, 33 | 34 | getNodeName(node) { 35 | return node.kind; 36 | }, 37 | 38 | nodeToRange(node) { 39 | if (node.loc && node.loc.start && node.loc.end) { 40 | return [node.loc.start.offset, node.loc.end.offset]; 41 | } 42 | }, 43 | 44 | opensByDefault(node, key) { 45 | return key === 'body' || key === 'what' || key === 'items'; 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /website/src/parsers/protobuf/codeExample.txt: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tutorial; 3 | 4 | import "google/protobuf/timestamp.proto"; 5 | 6 | option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb"; 7 | 8 | message Person { 9 | string name = 1; 10 | int32 id = 2; // Unique ID number for this person. 11 | string email = 3; 12 | 13 | enum PhoneType { 14 | MOBILE = 0; 15 | HOME = 1; 16 | WORK = 2; 17 | } 18 | 19 | message PhoneNumber { 20 | string number = 1; 21 | PhoneType type = 2; 22 | } 23 | 24 | repeated PhoneNumber phones = 4; 25 | 26 | google.protobuf.Timestamp last_updated = 5; 27 | } 28 | 29 | // Our address book file is just one of these. 30 | message AddressBook { 31 | repeated Person people = 1; 32 | } 33 | -------------------------------------------------------------------------------- /website/src/parsers/protobuf/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/protobuf/protobuf'; 2 | 3 | export const id = 'protobuf'; 4 | export const displayName = 'Protocol Buffers'; 5 | export const mimeTypes = ['text/x-protobuf']; 6 | export const fileExtension = 'proto'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/protobuf/pbkit.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'pbkit/package.json'; 3 | 4 | const ID = 'pbkit'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | id: ID, 9 | displayName: ID, 10 | version: pkg.version, 11 | homepage: 'https://github.com/riiid/pbkit', 12 | locationProps: new Set(['start', 'end']), 13 | typeProps: new Set(['type']), 14 | 15 | loadParser(callback) { 16 | require(['pbkit/core/parser/proto'], callback); 17 | }, 18 | 19 | parse(parser, code) { 20 | return parser.parse(code).ast; 21 | }, 22 | 23 | nodeToRange(node) { 24 | const { start, end } = node; 25 | return [start, end]; 26 | }, 27 | 28 | opensByDefault(node, key) { 29 | if (key === 'statements') { 30 | return true; 31 | } 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /website/src/parsers/pug/codeExample.txt: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | title Pug 5 | script(type='text/javascript'). 6 | const foo = true; 7 | let bar = function() {}; 8 | if (foo) { 9 | bar(1 + 5) 10 | } 11 | body 12 | h1 Pug - node template engine 13 | #container.col 14 | p You are amazing 15 | p 16 | | Pug is a terse and simple 17 | | templating language with a 18 | | strong focus on performance 19 | | and powerful features. 20 | -------------------------------------------------------------------------------- /website/src/parsers/pug/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/pug/pug'; 2 | 3 | export const id = 'pug'; 4 | export const displayName = 'Pug'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'pug'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/pug/pug.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'pug-parser/package.json'; 3 | 4 | const ID = 'pug'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/pugjs/pug', 13 | typeProps: new Set(['type', 'name']), 14 | locationProps: new Set(['line', 'column']), 15 | 16 | loadParser(callback) { 17 | require(['pug-lexer', 'pug-parser'], (lex, parse) => { 18 | callback({ lex, parse }); 19 | }); 20 | }, 21 | 22 | parse({ lex, parse }, code) { 23 | return parse(lex(code, {}), { src: code }); 24 | }, 25 | 26 | opensByDefault(node, key) { 27 | switch (key) { 28 | case 'block': 29 | case 'nodes': 30 | return true; 31 | } 32 | }, 33 | 34 | getNodeName(node) { 35 | let { type } = node; 36 | /* eslint-disable no-fallthrough */ 37 | switch (type) { 38 | case 'Block': return ''; 39 | case 'Doctype': return `Doctype(${node.val})`; 40 | case 'Comment': if (node.buffer) return 'Comment(buffer)'; 41 | case 'NamedBlock': return `Block:${node.mode}(${node.name})`; 42 | case 'Code': if (node.val === 'break') return 'Code(break)'; 43 | case 'When': if (node.expr === 'default') return 'When(default)'; 44 | case 'Include': 45 | case 'RawInclude': 46 | case 'Extends': 47 | case 'Each': 48 | case 'While': 49 | case 'Conditional': 50 | case 'Case': 51 | case 'AttributeBlock': 52 | case 'Text': return type; 53 | default: type = 'Attribute'; 54 | case 'Filter': 55 | case 'Mixin': if (node.call) type = 'Mixin:call'; 56 | case 'Tag': return `${type}(${node.name})`; 57 | } 58 | /* eslint-enable no-fallthrough */ 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /website/src/parsers/python/codeExample.txt: -------------------------------------------------------------------------------- 1 | print("Hello world") 2 | 3 | num1 = 1.5 4 | num2 = 6.3 5 | 6 | # Add two numbers 7 | sum = num1 + num2 8 | 9 | # Display the sum 10 | print('The sum of {0} and {1} is {2}'.format(num1, num2, sum)) 11 | 12 | # Note: change this value for a different result 13 | num = 8 14 | 15 | # To take the input from the user 16 | #num = float(input('Enter a number: ')) 17 | 18 | num_sqrt = num ** 0.5 19 | print('The square root of %0.3f is %0.3f'%(num ,num_sqrt)) 20 | 21 | -------------------------------------------------------------------------------- /website/src/parsers/python/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/python/python'; 2 | 3 | export const id = 'python'; 4 | export const displayName = 'Python'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'py'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/python/python.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'filbert/package.json'; 3 | 4 | const ID = 'python'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/differentmatt/filbert', 13 | locationProps: new Set(['range', 'loc', 'start', 'end']), 14 | 15 | loadParser(callback) { 16 | require(['filbert'], (parser) => { 17 | callback({ parser }); 18 | }); 19 | }, 20 | 21 | parse({ parser }, code) { 22 | return parser.parse(code, { 23 | locations: true, 24 | ranges: true, 25 | }); 26 | }, 27 | 28 | opensByDefault(node, key) { 29 | switch (key) { 30 | case 'block': 31 | case 'nodes': 32 | return true; 33 | } 34 | }, 35 | 36 | nodeToRange(node) { 37 | const { range } = node; 38 | if (typeof range === 'object') { 39 | return range; 40 | } 41 | }, 42 | 43 | }; 44 | -------------------------------------------------------------------------------- /website/src/parsers/reason/codeExample.txt: -------------------------------------------------------------------------------- 1 | let tips = [ 2 | "Click on any AST node with a '+' to expand it", 3 | 4 | "Hovering over a node highlights the 5 | corresponding location in the source code", 6 | 7 | "Shift click on an AST node to expand the whole subtree", 8 | ]; 9 | 10 | let printTips = () => 11 | tips |> List.iteri((i, tip) => Printf.printf("Tip %d: %s\n", i, tip)); 12 | -------------------------------------------------------------------------------- /website/src/parsers/reason/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'reason'; 2 | export const displayName = 'Reason'; 3 | export const mimeTypes = []; 4 | export const fileExtension = 're'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/reason/refmt.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import esyPkg from 'astexplorer-refmt/esy.json'; 3 | import CodeMirror from 'codemirror'; 4 | import addCodeMirrorMode from './codeMirrorMode'; 5 | 6 | addCodeMirrorMode(CodeMirror); 7 | 8 | const ID = 'refmt'; 9 | const locKeys = [ 10 | 'loc', 11 | 'pcd_loc', 12 | 'pcf_loc', 13 | 'pci_loc', 14 | 'pcl_loc', 15 | 'pctf_loc', 16 | 'pcty_loc', 17 | 'pexp_loc', 18 | 'pext_loc', 19 | 'pincl_loc', 20 | 'pld_loc', 21 | 'pmb_loc', 22 | 'pmd_loc', 23 | 'pmod_loc', 24 | 'pmtd_loc', 25 | 'pmty_loc', 26 | 'popen_loc', 27 | 'ppat_loc', 28 | 'psig_loc', 29 | 'pstr_loc', 30 | 'ptyp_loc', 31 | 'ptype_loc', 32 | 'pval_loc', 33 | 'pvb_loc', 34 | ]; 35 | const parserVersion = esyPkg.dependencies['@esy-ocaml/reason']; 36 | 37 | export default { 38 | ...defaultParserInterface, 39 | 40 | id: ID, 41 | displayName: ID, 42 | version: parserVersion, 43 | homepage: `https://www.npmjs.com/package/@esy-ocaml/reason/v/${parserVersion}`, 44 | locationProps: new Set(locKeys), 45 | 46 | loadParser(callback) { 47 | require(['astexplorer-refmt'], callback); 48 | }, 49 | 50 | parse(parser, code) { 51 | return parser.parseReason(code); 52 | }, 53 | 54 | getNodeName(node) { 55 | return node.type; 56 | }, 57 | 58 | nodeToRange(node) { 59 | const locKey = locKeys.find(key => Object.prototype.hasOwnProperty.call(node, key)); 60 | if (locKey) { 61 | const range = [ 62 | node[locKey].loc_start.pos_cnum, 63 | node[locKey].loc_end.pos_cnum, 64 | ]; 65 | return range; 66 | } 67 | }, 68 | }; 69 | -------------------------------------------------------------------------------- /website/src/parsers/regexp/codeExample.txt: -------------------------------------------------------------------------------- 1 | /[a-z]/i -------------------------------------------------------------------------------- /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/src/parsers/regexp/regexp-tree.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'regexp-tree/package.json'; 3 | 4 | const ID = 'regexp-tree'; 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(['regexp-tree'], (regexpTree) => { 17 | callback(regexpTree); 18 | }); 19 | }, 20 | 21 | parse(regexpTree, code, options={}) { 22 | regexpTree 23 | .parser 24 | .setOptions(options); 25 | 26 | return regexpTree.parse(code); 27 | }, 28 | 29 | nodeToRange(node) { 30 | if (node.loc != null) { 31 | return [node.loc.start, node.loc.end]; 32 | } 33 | }, 34 | 35 | opensByDefault(node, key) { 36 | return ( 37 | node.type === 'RegExp' || 38 | key === 'body' || 39 | key === 'expressions' 40 | ); 41 | }, 42 | 43 | getDefaultOptions() { 44 | return { 45 | captureLocations: true, 46 | }; 47 | }, 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /website/src/parsers/regexp/regexpp.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'regexpp/package.json'; 3 | 4 | const ID = 'regexpp'; 5 | 6 | /** @type {import("regexpp").RegExpParser.Options} */ 7 | export const defaultOptions = { 8 | strict: false, 9 | ecmaVersion: 2020, 10 | }; 11 | 12 | export default { 13 | ...defaultParserInterface, 14 | 15 | id: ID, 16 | displayName: ID, 17 | version: pkg.version, 18 | homepage: pkg.homepage, 19 | locationProps: new Set(['end', 'start']), 20 | 21 | loadParser(callback) { 22 | require(['regexpp'], callback); 23 | }, 24 | 25 | parse(regexpp, code, options) { 26 | if (Object.keys(options).length === 0) { 27 | options = this.getDefaultOptions(); 28 | } 29 | return regexpp.parseRegExpLiteral(code, options); 30 | }, 31 | 32 | nodeToRange(node) { 33 | if (typeof node.start === 'number' && typeof node.end === 'number') { 34 | return [node.start, node.end]; 35 | } 36 | }, 37 | 38 | opensByDefault(node, key) { 39 | return ( 40 | key === 'pattern' || 41 | key === 'elements' || 42 | key === 'element' || 43 | key === 'alternatives' 44 | ); 45 | }, 46 | 47 | getDefaultOptions() { 48 | return defaultOptions; 49 | }, 50 | 51 | _ignoredProperties: new Set(['parent', 'references', 'resolved']), 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /website/src/parsers/regexp/regjsparser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'regjsparser/package.json'; 3 | 4 | const ID = 'regjsparser'; 5 | 6 | export const defaultOptions = { 7 | unicodePropertyEscape: true, 8 | namedGroups: true, 9 | lookbehind: true, 10 | }; 11 | 12 | export default { 13 | ...defaultParserInterface, 14 | 15 | id: ID, 16 | displayName: ID, 17 | version: pkg.version, 18 | homepage: pkg.homepage, 19 | locationProps: new Set(['range']), 20 | 21 | loadParser(callback) { 22 | require(['regjsparser'], callback); 23 | }, 24 | 25 | parse(regjsparser, code, options) { 26 | if (Object.keys(options).length === 0) { 27 | options = this.getDefaultOptions(); 28 | } 29 | var firstSlash = code.indexOf('/'); 30 | var lastSlash = code.lastIndexOf('/'); 31 | if (firstSlash !== 0 || lastSlash < 1) { 32 | throw new Error('Please wrap the regex pattern by slash `/`, i.e. /foo/'); 33 | } 34 | var flags = code.slice(lastSlash + 1); 35 | var pattern = code.slice(firstSlash + 1, lastSlash); 36 | return regjsparser.parse(pattern, flags, options); 37 | }, 38 | 39 | nodeToRange(node) { 40 | if (node.range != null) { 41 | return [node.range[0] + 1, node.range[1] + 1]; 42 | } 43 | }, 44 | 45 | opensByDefault(node, key) { 46 | return ( 47 | key === 'body' 48 | ); 49 | }, 50 | 51 | getDefaultOptions() { 52 | return defaultOptions; 53 | }, 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /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/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 | '../../../transpilers/babel', 17 | 'regexp-tree', 18 | ], (transpile, regexpTree) => callback({ transpile: transpile.default, regexpTree })); 19 | }, 20 | 21 | transform({ transpile, regexpTree }, transformCode, code) { 22 | transformCode = transpile(transformCode); 23 | let handler = compileModule( // eslint-disable-line no-shadow 24 | transformCode, 25 | ); 26 | 27 | return regexpTree.transform(code, handler).toString(); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /website/src/parsers/rust/codeExample.txt: -------------------------------------------------------------------------------- 1 | const TIPS: &[&str] = &[ 2 | "Click on any AST node with a '+' to expand it", 3 | 4 | "Hovering over a node highlights the \ 5 | corresponding location in the source code", 6 | 7 | "Shift click on an AST node to expand the whole subtree", 8 | ]; 9 | 10 | pub fn print_tips() { 11 | for (i, tip) in TIPS.iter().enumerate() { 12 | println!("Tip {}: {}.", i, tip); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /website/src/parsers/rust/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/rust/rust'; 2 | 3 | export const id = 'rust'; 4 | export const displayName = 'Rust'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'rs'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/rust/syn.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'astexplorer-syn/package.json'; 3 | 4 | const ID = 'syn'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: `https://docs.rs/syn/${pkg.version}/syn/`, 13 | _ignoredProperties: new Set(['_type']), 14 | locationProps: new Set(['span']), 15 | 16 | loadParser(callback) { 17 | require(['astexplorer-syn'], callback); 18 | }, 19 | 20 | parse(parser, code) { 21 | this.lineOffsets = []; 22 | let index = 0; 23 | do { 24 | this.lineOffsets.push(index); 25 | } while ((index = code.indexOf('\n', index) + 1)); // eslint-disable-line no-cond-assign 26 | return parser.parseFile(code); 27 | }, 28 | 29 | getNodeName(node) { 30 | return node._type; 31 | }, 32 | 33 | nodeToRange(node) { 34 | if (node.span) { 35 | return [node.span.start, node.span.end].map( 36 | ({ line, column }) => this.lineOffsets[line - 1] + column, 37 | ); 38 | } 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /website/src/parsers/san/codeExample.txt: -------------------------------------------------------------------------------- 1 |
2 |

{{title}}

3 |
4 | 5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /website/src/parsers/san/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/htmlmixed/htmlmixed'; 2 | 3 | export const id = 'san'; 4 | export const displayName = 'San'; 5 | export const mimeTypes = ['text/html']; 6 | export const fileExtension = 'san.html'; 7 | export const editorMode = 'htmlmixed'; 8 | -------------------------------------------------------------------------------- /website/src/parsers/san/san-template-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'san/package.json'; 3 | 4 | const ID = 'san'; 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([]), 14 | typeProps: new Set(['tag']), 15 | 16 | loadParser(callback) { 17 | require(['san'], callback); 18 | }, 19 | 20 | parse(parser, code, options) { 21 | return parser.parseTemplate(code, options).children[0]; 22 | }, 23 | 24 | opensByDefault(node, key) { 25 | return key === 'children'; 26 | }, 27 | 28 | getNodeName(node) { 29 | return node.tagName; 30 | }, 31 | 32 | getDefaultOptions() { 33 | return {}; 34 | }, 35 | _ignoredProperties: new Set([]), 36 | }; 37 | -------------------------------------------------------------------------------- /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/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/src/parsers/scala/scalameta.js: -------------------------------------------------------------------------------- 1 | import pkg from 'scalameta-parsers/package.json'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | 4 | const ID = 'scalameta'; 5 | 6 | const dialects = { 7 | 'Scala 2.10': 'Scala210', 8 | 'Scala 2.11': 'Scala211', 9 | 'Scala 2.12': 'Scala212', 10 | 'Scala 2.13': 'Scala213', 11 | 'Scala 3': 'Scala3', 12 | 'Sbt 0.13.6': 'Sbt0136', 13 | 'Sbt 0.13.7': 'Sbt0137', 14 | 'Sbt 1': 'Sbt 1', 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/scalameta/scalameta', 24 | locationProps: new Set(['pos']), 25 | 26 | loadParser(callback) { 27 | require(['scalameta-parsers'], callback); 28 | }, 29 | 30 | parse(scalametaParser, code, options) { 31 | const parsed = scalametaParser.parseSource(code, options); 32 | const { error, lineNumber, columnNumber } = parsed; 33 | if (error) { 34 | const e = new SyntaxError(parsed.error); 35 | e.lineNumber = lineNumber + 1; 36 | e.columnNumber = columnNumber + 1; 37 | throw e; 38 | } 39 | return parsed; 40 | }, 41 | 42 | nodeToRange(node) { 43 | if (node.pos) { 44 | return [node.pos.start, node.pos.end]; 45 | } 46 | }, 47 | 48 | opensByDefault(node, key) { 49 | return ( 50 | node.type === 'Program' || 51 | key === 'body' || 52 | key === 'self' || 53 | key === 'stats' 54 | ); 55 | }, 56 | 57 | getDefaultOptions() { 58 | return { 59 | dialect: 'Scala213', 60 | }; 61 | }, 62 | 63 | _getSettingsConfiguration() { 64 | return { 65 | fields: [['dialect', dialects]], 66 | required: new Set('dialect'), 67 | }; 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /website/src/parsers/solididy/codeExample.txt: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | contract SimpleStore { 3 | function set(uint _value) public { 4 | value = _value; 5 | } 6 | 7 | function get() public constant returns (uint) { 8 | return value; 9 | } 10 | 11 | uint value; 12 | } -------------------------------------------------------------------------------- /website/src/parsers/solididy/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/javascript/javascript'; 2 | 3 | export const id = 'solididy'; 4 | export const displayName = 'Solidity'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'sol'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/solididy/solidity-parser-antlr.js: -------------------------------------------------------------------------------- 1 | import pkg from 'solidity-parser-antlr/package.json'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | 4 | const ID = 'solidity-parser-antlr'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/federicobond/solidity-parser-antlr', 13 | 14 | loadParser(callback) { 15 | require(['solidity-parser-antlr'], callback); 16 | }, 17 | 18 | parse(parser, code, options) { 19 | return parser.parse(code, options); 20 | }, 21 | 22 | opensByDefault(node, key) { 23 | return node.type === 'SourceUnit' || 24 | node.type === 'ContractDefinition' || 25 | key === 'children' || 26 | key === 'subNodes' || 27 | key === 'body' 28 | }, 29 | 30 | getDefaultOptions() { 31 | return { 32 | range: true, 33 | loc: false, 34 | tolerant: false, 35 | }; 36 | }, 37 | 38 | _getSettingsConfiguration() { 39 | return { 40 | fields: [ 41 | 'range', 42 | 'loc', 43 | 'tolerant', 44 | ], 45 | }; 46 | }, 47 | 48 | }; 49 | 50 | -------------------------------------------------------------------------------- /website/src/parsers/solididy/solidity-parser-diligence.js: -------------------------------------------------------------------------------- 1 | import pkg from 'solidity-parser-diligence/package.json'; 2 | import defaultParserInterface from '../utils/defaultParserInterface'; 3 | 4 | const ID = 'solidity-parser-diligence'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/consensys/solidity-parser-antlr', 13 | 14 | loadParser(callback) { 15 | require(['solidity-parser-diligence'], callback); 16 | }, 17 | 18 | parse(parser, code, options) { 19 | return parser.parse(code, options); 20 | }, 21 | 22 | opensByDefault(node, key) { 23 | return node.type === 'SourceUnit' || 24 | node.type === 'ContractDefinition' || 25 | key === 'children' || 26 | key === 'subNodes' || 27 | key === 'body' 28 | }, 29 | 30 | getDefaultOptions() { 31 | return { 32 | range: true, 33 | loc: false, 34 | tolerant: false, 35 | }; 36 | }, 37 | 38 | _getSettingsConfiguration() { 39 | return { 40 | fields: [ 41 | 'range', 42 | 'loc', 43 | 'tolerant', 44 | ], 45 | }; 46 | }, 47 | 48 | }; 49 | 50 | -------------------------------------------------------------------------------- /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/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/src/parsers/sql/sql-parser-cst.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'sql-parser-cst/package.json'; 3 | 4 | const ID = 'sql-parser-cst'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/nene/sql-parser-cst', 13 | locationProps: new Set(['range']), 14 | 15 | loadParser(callback) { 16 | require(['sql-parser-cst'], callback); 17 | }, 18 | 19 | parse(parser, code, options) { 20 | return parser.parse(code, options); 21 | }, 22 | 23 | getNodeName(node) { 24 | return node.type; 25 | }, 26 | 27 | nodeToRange(node) { 28 | return node.range; 29 | }, 30 | 31 | getDefaultOptions() { 32 | return { 33 | dialect: 'sqlite', 34 | preserveComments: true, 35 | includeRange: true, 36 | }; 37 | }, 38 | 39 | _getSettingsConfiguration() { 40 | return { 41 | fields: [ 42 | ['dialect', ['sqlite', 'mysql']], 43 | 'preserveComments', 44 | 'preserveNewlines', 45 | 'preserveSpaces', 46 | 'includeRange', 47 | ], 48 | }; 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /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 | opensByDefault(node, key) { 23 | return key === 'statement'; 24 | }, 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /website/src/parsers/svelte/codeExample.txt: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Hello {name}!

6 | -------------------------------------------------------------------------------- /website/src/parsers/svelte/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/htmlmixed/htmlmixed'; 2 | 3 | export const id = 'svelte'; 4 | export const displayName = 'Svelte'; 5 | export const mimeTypes = ['text/html']; 6 | export const fileExtension = 'svelte'; 7 | export const editorMode = 'htmlmixed'; 8 | -------------------------------------------------------------------------------- /website/src/parsers/svelte/svelte-parser.js: -------------------------------------------------------------------------------- 1 | export { default } from '../html/svelte'; 2 | -------------------------------------------------------------------------------- /website/src/parsers/thrift/codeExample.txt: -------------------------------------------------------------------------------- 1 | namespace js test 2 | 3 | const string test = 'test' 4 | 5 | struct MyStruct { 6 | 1: optional string test 7 | } 8 | 9 | service MyService { 10 | void ping() 11 | } 12 | -------------------------------------------------------------------------------- /website/src/parsers/thrift/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'thrift-idl'; 2 | export const displayName = 'Thrift IDL'; 3 | export const mimeTypes = ['text/x-thrift-idl']; 4 | export const fileExtension = 'thrift'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/thrift/thrift-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from '@creditkarma/thrift-parser/package.json'; 3 | 4 | const ID = 'ck-thrift-parser'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: 'https://github.com/creditkarma/thrift-parser', 13 | locationProps: new Set(['location']), 14 | 15 | loadParser(callback) { 16 | require(['@creditkarma/thrift-parser'], callback); 17 | }, 18 | 19 | parse({parse}, code) { 20 | return parse(code); 21 | }, 22 | 23 | getNodeName(node) { 24 | return node.type; 25 | }, 26 | 27 | nodeToRange({ loc }) { 28 | if (loc !== null && loc !== undefined) { 29 | return [loc.start.index, loc.end.index]; 30 | } 31 | }, 32 | 33 | opensByDefault(node, key) { 34 | return node === 'ThriftDocument' || key === 'body'; 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /website/src/parsers/transpilers/babel.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 | import protect from '../utils/protectFromLoops'; 6 | 7 | const options = { 8 | presets: [es2015, stage0], 9 | plugins: [flowStripTypes], 10 | ast: false, 11 | babelrc: false, 12 | highlightCode: false, 13 | }; 14 | 15 | export default function transpile(code) { 16 | let es5Code = babel.transform(code, options).code; 17 | es5Code = protect(es5Code); 18 | return es5Code; 19 | } 20 | -------------------------------------------------------------------------------- /website/src/parsers/transpilers/typescript.js: -------------------------------------------------------------------------------- 1 | import ts from 'typescript'; 2 | import protect from '../utils/protectFromLoops'; 3 | 4 | let compilerOptions = { module: ts.ModuleKind.System }; 5 | 6 | export default function transpile(code) { 7 | let es5Code = ts.transpileModule(code, compilerOptions).outputText; 8 | es5Code = protect(es5Code); 9 | return es5Code; 10 | } 11 | -------------------------------------------------------------------------------- /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/utils/protectFromLoops.js: -------------------------------------------------------------------------------- 1 | import halts, {loopProtect} from 'halting-problem'; 2 | 3 | export default function protect(jsCode) { 4 | // assert that there are no obvious infinite loops 5 | halts(jsCode); 6 | // guard against non-obvious loops with a timeout of 5 seconds 7 | let start = Date.now(); 8 | jsCode = loopProtect( 9 | jsCode, 10 | [ 11 | // this function gets called in all possible loops 12 | // it gets passed the line number as its only argument 13 | '(function (line) {', 14 | 'if (Date.now() > ' + (start + 5000) + ') {', 15 | ' throw new Error("Infinite loop detected on line " + line);', 16 | '}', 17 | '})', 18 | ].join(''), 19 | ); 20 | 21 | return jsCode; 22 | } 23 | -------------------------------------------------------------------------------- /website/src/parsers/vue/codeExample.txt: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /website/src/parsers/vue/index.js: -------------------------------------------------------------------------------- 1 | import 'codemirror/mode/vue/vue'; 2 | 3 | export const id = 'vue'; 4 | export const displayName = 'Vue'; 5 | export const mimeTypes = []; 6 | export const fileExtension = 'vue'; 7 | -------------------------------------------------------------------------------- /website/src/parsers/vue/vue-compiler-dom.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from '@vue/compiler-dom/package.json'; 3 | 4 | const ID = '@vue/compiler-dom'; 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(['start', 'end']), 14 | typeProps: new Set(['tag']), 15 | 16 | loadParser(callback) { 17 | require(['@vue/compiler-dom'], callback); 18 | }, 19 | 20 | parse(parser, code, options) { 21 | return parser.parse(code, options); 22 | }, 23 | 24 | nodeToRange(node) { 25 | if (node.type || node.name) { 26 | return [node.loc.start.offset, node.loc.end.offset]; 27 | } 28 | }, 29 | 30 | opensByDefault(node, key) { 31 | return key === 'children'; 32 | }, 33 | 34 | getNodeName(node) { 35 | return node.tag; 36 | }, 37 | 38 | getDefaultOptions() { 39 | return {}; 40 | }, 41 | 42 | _ignoredProperties: new Set([ 43 | 'components', 44 | 'directives', 45 | 'codegenNode', 46 | 'helpers', 47 | 'hoists', 48 | 'imports', 49 | 'cached', 50 | 'temps', 51 | ]), 52 | }; 53 | -------------------------------------------------------------------------------- /website/src/parsers/vue/vue-eslint-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'vue-eslint-parser/package.json'; 3 | 4 | const ID = 'vue-eslint-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(['start', 'end']), 14 | typeProps: new Set(['tag']), 15 | 16 | loadParser(callback) { 17 | require(['vue-eslint-parser'], callback); 18 | }, 19 | 20 | parse(parser, code, options) { 21 | if (Object.keys(options).length === 0) { 22 | options = this.getDefaultOptions(); 23 | } 24 | return parser.parse(code, options); 25 | }, 26 | 27 | nodeToRange(node) { 28 | if (node.type || node.name) { 29 | return node.range; 30 | } 31 | }, 32 | 33 | opensByDefault(node, key) { 34 | return key === 'children'; 35 | }, 36 | 37 | getNodeName(node) { 38 | return node.tag; 39 | }, 40 | 41 | getDefaultOptions() { 42 | return { 43 | ecmaVersion: 10, 44 | sourceType: 'module', 45 | vueFeatures: { 46 | filter: true, 47 | interpolationAsNonHTML: false, 48 | }, 49 | }; 50 | }, 51 | 52 | _getSettingsConfiguration() { 53 | const defaultOptions = this.getDefaultOptions(); 54 | 55 | return { 56 | fields: [ 57 | ['ecmaVersion', [3, 5, 6, 7, 8, 9, 10, 11], value => Number(value)], 58 | ['sourceType', ['script', 'module']], 59 | { 60 | key: 'vueFeatures', 61 | title: 'vueFeatures', 62 | fields: Object.keys(defaultOptions.vueFeatures), 63 | settings: 64 | settings => settings.vueFeatures || {...defaultOptions.vueFeatures}, 65 | }, 66 | ], 67 | }; 68 | }, 69 | 70 | _ignoredProperties: new Set(['parent', 'tokens']), 71 | }; 72 | -------------------------------------------------------------------------------- /website/src/parsers/vue/vue-template-compiler.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'vue-template-compiler/package.json'; 3 | 4 | const ID = 'vue-template-compiler'; 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(['start', 'end']), 14 | typeProps: new Set(['tag']), 15 | 16 | loadParser(callback) { 17 | require(['vue-template-compiler/browser'], callback); 18 | }, 19 | 20 | parse(parser, code, options) { 21 | return parser.compile(code, options).ast; 22 | }, 23 | 24 | nodeToRange(node) { 25 | if (node.type || node.name) { 26 | return [node.start, node.end]; 27 | } 28 | }, 29 | 30 | opensByDefault(node, key) { 31 | return key === 'children'; 32 | }, 33 | 34 | getNodeName(node) { 35 | return node.tag; 36 | }, 37 | 38 | getDefaultOptions() { 39 | return { 40 | outputSourceRange: true, 41 | whitespace: 'preserve', 42 | }; 43 | }, 44 | _ignoredProperties: new Set(['parent']), 45 | }; 46 | -------------------------------------------------------------------------------- /website/src/parsers/wat/codeExample.txt: -------------------------------------------------------------------------------- 1 | ;; This is WebAssembly Text Format (WAT). 2 | ;; Paste or drop some WAT here and explore 3 | 4 | (module 5 | 6 | ;; this is simple function that adds a couple of parameters 7 | (func (param $a i32) (param $b i32) 8 | (get_local $a) 9 | (get_local $b) 10 | (i32.add) 11 | ) 12 | 13 | ;; this statement exports the function to the host environment 14 | (export "add" (func $add)) 15 | ) -------------------------------------------------------------------------------- /website/src/parsers/wat/index.js: -------------------------------------------------------------------------------- 1 | export const id = 'wat'; 2 | export const displayName = 'WAT'; 3 | export const mimeTypes = ['application/wasm']; 4 | export const fileExtension = 'wat'; 5 | -------------------------------------------------------------------------------- /website/src/parsers/wat/wat-parser.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from '@webassemblyjs/wast-parser/package.json'; 3 | 4 | const ID = 'wat-parser'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: 'https://webassembly.js.org/', 13 | 14 | locationProps: new Set(['loc']), 15 | 16 | getOffset({ line, column }) { 17 | return this.lineOffsets[line - 1] + column; 18 | }, 19 | 20 | nodeToRange({ loc }) { 21 | if (!loc) return; 22 | return [loc.start, loc.end].map(pos => this.getOffset(pos)); 23 | }, 24 | 25 | loadParser(callback) { 26 | require(['@webassemblyjs/wast-parser'], function(parser) { 27 | callback(parser); 28 | }); 29 | }, 30 | 31 | parse({ parse }, code) { 32 | this.lineOffsets = []; 33 | let index = 0; 34 | do { 35 | this.lineOffsets.push(index); 36 | } while (index = code.indexOf('\n', index) + 1); // eslint-disable-line no-cond-assign 37 | return parse(code); 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /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/webidl/webidl2.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'webidl2/package.json'; 3 | 4 | const ID = 'webidl2'; 5 | 6 | export default { 7 | ...defaultParserInterface, 8 | 9 | id: ID, 10 | displayName: ID, 11 | version: pkg.version, 12 | homepage: pkg.homepage || 'https://github.com/w3c/webidl2.js', 13 | typeProps: new Set(['name', 'type', 'idlType', 'escapedName']), 14 | 15 | getNodeName(node) { 16 | if (node.name) { 17 | return node.name + (node.optional ? '?' : ''); 18 | } else if (node.type) { 19 | return node.type; 20 | } else if (node.idlType) { 21 | return node.idlType.idlType || node.idlType; 22 | } 23 | }, 24 | 25 | loadParser(callback) { 26 | require(['webidl2'], callback); 27 | }, 28 | 29 | parse({ parse }, code, options) { 30 | return parse(code, options); 31 | }, 32 | 33 | opensByDefault(node, key) { 34 | return key === 'members'; 35 | }, 36 | 37 | getDefaultOptions() { 38 | return { 39 | concrete: false, 40 | }; 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /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/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']), 16 | locationProps: new Set(['startPosition', 'endPosition']), 17 | typeProps: new Set(['kind']), 18 | 19 | nodeToRange(node) { 20 | if (typeof node.startPosition === 'number') { 21 | return [node.startPosition, node.endPosition]; 22 | } 23 | }, 24 | 25 | getNodeName(node) { 26 | return Kind[node.kind]; 27 | }, 28 | 29 | loadParser(callback) { 30 | require(['yaml-ast-parser'], function(yamlAstParser) { 31 | Kind = yamlAstParser.Kind; 32 | callback(yamlAstParser); 33 | }); 34 | }, 35 | 36 | parse({ load }, code) { 37 | return load(code); 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /website/src/parsers/yaml/yaml.js: -------------------------------------------------------------------------------- 1 | import defaultParserInterface from '../utils/defaultParserInterface'; 2 | import pkg from 'yaml/package.json'; 3 | 4 | const ID = 'yaml'; 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(['position']), 14 | 15 | loadParser(callback) { 16 | require(['yaml'], callback); 17 | }, 18 | 19 | nodeToRange(node) { 20 | if (node.range) { 21 | return node.range; 22 | } 23 | if (node.type === 'PAIR' && (node.key || node.value)) { 24 | if (node.key && node.value) { 25 | return [node.key.range[0], node.value.range[1]]; 26 | } else if (node.key) { 27 | return node.key.range; 28 | } else { 29 | return node.value.range; 30 | } 31 | } 32 | }, 33 | 34 | parse({ parseAllDocuments }, code, options) { 35 | return parseAllDocuments(code, options); 36 | }, 37 | 38 | getDefaultOptions() { 39 | return { 40 | keepBlobsInJSON: true, 41 | keepCstNodes: false, 42 | keepNodeTypes: true, 43 | merge: false, 44 | mapAsMap: false, 45 | simpleKeys: false, 46 | maxAliasCount: 100, 47 | prettyErrors: true, 48 | }; 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /website/src/shims/jest-validate.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | validate(){}, 3 | }; 4 | -------------------------------------------------------------------------------- /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/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/src/utils/classnames.js: -------------------------------------------------------------------------------- 1 | export default function cx(...configs) { 2 | return configs.map( 3 | config => typeof config === 'string' ? 4 | config : 5 | Object.keys(config).filter(k => config[k]).join(' '), 6 | ).join(' '); 7 | } 8 | -------------------------------------------------------------------------------- /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/utils/pubsub.js: -------------------------------------------------------------------------------- 1 | const subscribers = {}; 2 | 3 | export function subscribe(topic, handler) { 4 | let handlers = subscribers[topic]; 5 | if (!handlers) { 6 | handlers = subscribers[topic] = []; 7 | } 8 | if (handlers.indexOf(handler) === -1) { 9 | handlers.push(handler); 10 | } 11 | 12 | return () => handlers.splice(handlers.indexOf(handler), 1); 13 | } 14 | 15 | export function publish(topic, data) { 16 | if (subscribers[topic]) { 17 | setTimeout(function callSubscribers() { 18 | if (subscribers[topic]) { 19 | const handlers = subscribers[topic]; 20 | for (var i = 0; i < handlers.length; i++) { 21 | handlers[i](data); 22 | } 23 | } 24 | }, 0); 25 | } 26 | } 27 | 28 | export function clear(unsubscribers) { 29 | unsubscribers.forEach(call); 30 | } 31 | 32 | function call(f) { 33 | return f(); 34 | } 35 | -------------------------------------------------------------------------------- /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 | case 'bigint': 14 | return Number.isNaN(value) ? 'NaN' : String(value); 15 | default: 16 | return JSON.stringify(value); 17 | } 18 | } 19 | --------------------------------------------------------------------------------