some huge changes
parent
4cee59a9cf
commit
47430d0c19
|
@ -7,14 +7,21 @@
|
|||
"svgo": "svgo --pretty --indent=2 --config=svgo.yml",
|
||||
"svgo-all": "find static | grep '\\.svg$' | xargs -Iz -n 1 yarn svgo z"
|
||||
},
|
||||
"dependencies": {
|
||||
"codemirror": "5.36.0",
|
||||
"lz-string": "1.4.4",
|
||||
"react": "16.3.1",
|
||||
"react-dom": "16.3.1",
|
||||
"sw-toolbox": "3.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sandhose/prettier-animated-logo": "1.0.3",
|
||||
"babel-loader": "7.1.4",
|
||||
"babel-preset-env": "1.6.1",
|
||||
"babel-preset-react": "6.24.1",
|
||||
"docusaurus": "1.0.5",
|
||||
"js-yaml": "3.10.0",
|
||||
"svgo": "1.0.4",
|
||||
"sw-toolbox": "3.6.0",
|
||||
"webpack": "4.5.0",
|
||||
"webpack-cli": "2.0.14"
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
<link rel="stylesheet" crossorigin href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.36.0/codemirror.css">
|
||||
|
||||
<script src="/markdown.js"></script>
|
||||
<!-- <script src="/playground.js"></script> -->
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.36.0/codemirror.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.36.0/mode/javascript/javascript.js"></script>
|
||||
|
@ -41,9 +40,8 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.36.0/keymap/sublime.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.4.4/lz-string.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/preact/8.2.6/preact.min.js"></script>
|
||||
|
||||
<script src="/playground2.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.3.1/umd/react.production.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.3.1/umd/react-dom.production.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/separate-css/playground.css">
|
||||
|
||||
|
@ -77,6 +75,8 @@
|
|||
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="/playground.js"></script>
|
||||
|
||||
<!-- <div class="editors-container">
|
||||
<div id="options-details" class="options-container">
|
||||
<div class="options">
|
||||
|
@ -174,31 +174,6 @@
|
|||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- <div class="bottom-bar">
|
||||
<div class="bottom-bar-buttons">
|
||||
<button type="button" class="btn" id="button-options">
|
||||
Show options
|
||||
</button>
|
||||
<button type="button" class="btn" id="button-clear">
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="bottom-bar-buttons bottom-bar-buttons-right">
|
||||
<button type="button" class="btn" id="button-copy-link">
|
||||
Copy link
|
||||
</button>
|
||||
<button type="button" class="btn" id="button-copy-markdown">
|
||||
Copy markdown
|
||||
</button>
|
||||
<a href="https://github.com/prettier/prettier/issues/new"
|
||||
target="_blank" rel="noopener"
|
||||
class="btn" id="button-report-issue">
|
||||
Report issue
|
||||
</a>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<script src="/install-service-worker.js"></script>
|
||||
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import React from "react";
|
||||
|
||||
export default function(props) {
|
||||
return <button type="button" className="btn" {...props} />;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import React from "react";
|
||||
|
||||
import { stateToggler, shallowEqual } from "./helpers";
|
||||
import * as storage from "./storage";
|
||||
|
||||
export default class extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = Object.assign(
|
||||
{
|
||||
sidebarExpanded: false,
|
||||
astPanelVisible: false,
|
||||
docPanelVisible: false,
|
||||
toggleAstPanelVisible: () =>
|
||||
this.setState(stateToggler("astPanelVisible")),
|
||||
toggleDocPanelVisible: () =>
|
||||
this.setState(stateToggler("docPanelVisible")),
|
||||
toggleSidebarExpanded: () =>
|
||||
this.setState(stateToggler("sidebarExpanded"))
|
||||
},
|
||||
storage.get("editor_state")
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(_, prevState) {
|
||||
if (!shallowEqual(this.state, prevState)) {
|
||||
storage.set("editor_state", this.state);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children(this.state);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import React from "react";
|
||||
|
||||
export function BooleanOption({ option, value, onChange }) {
|
||||
function maybeInvert(value) {
|
||||
return option.inverted ? !value : value;
|
||||
}
|
||||
return (
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={maybeInvert(value)}
|
||||
onChange={ev => onChange(option, maybeInvert(ev.target.checked))}
|
||||
/>{" "}
|
||||
{option.cliName}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChoiceOption({ option, value, onChange }) {
|
||||
return (
|
||||
<label>
|
||||
{option.cliName}{" "}
|
||||
<select onChange={ev => onChange(option, ev.target.value)}>
|
||||
{option.choices.map(choice => (
|
||||
<option value={choice.value} selected={choice.value === value}>
|
||||
{choice.value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function NumberOption({ option, value, onChange }) {
|
||||
return (
|
||||
<label>
|
||||
{option.cliName}{" "}
|
||||
<input
|
||||
type="number"
|
||||
min={option.range.start}
|
||||
max={option.range.end}
|
||||
step={option.range.step}
|
||||
value={value}
|
||||
onChange={ev => onChange(option, parseInt(ev.target.value, 10))}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export default function(props) {
|
||||
switch (props.option.type) {
|
||||
case "boolean":
|
||||
return <BooleanOption {...props} />;
|
||||
case "int":
|
||||
return <NumberOption {...props} />;
|
||||
case "choice":
|
||||
return <ChoiceOption {...props} />;
|
||||
default:
|
||||
throw new Error("unsupported type");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
import React from "react";
|
||||
|
||||
import Button from "./Button";
|
||||
import EditorState from "./EditorState";
|
||||
import { DebugPanel, InputPanel, OutputPanel } from "./panels";
|
||||
import SidebarOptions from "./SidebarOptions";
|
||||
import WorkerApi from "./WorkerApi";
|
||||
|
||||
const ENABLED_OPTIONS = [
|
||||
"parser",
|
||||
"printWidth",
|
||||
"tabWidth",
|
||||
"useTabs",
|
||||
{ name: "semi", inverted: true },
|
||||
"singleQuote",
|
||||
{ name: "bracketSpacing", inverted: true },
|
||||
"jsxBracketSameLine",
|
||||
"arrowParens",
|
||||
"trailingComma",
|
||||
"proseWrap",
|
||||
"insertPragma",
|
||||
"requirePragma"
|
||||
].map(option => (typeof option === "string" ? { name: option } : option));
|
||||
|
||||
class Playground extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
content: "",
|
||||
loaded: false,
|
||||
options: null
|
||||
};
|
||||
this.handleOptionValueChange = this.handleOptionValueChange.bind(this);
|
||||
|
||||
this.setContent = content => this.setState({ content });
|
||||
this.clearContent = () => this.setState({ content: "" });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._worker = new WorkerApi("/worker.js");
|
||||
|
||||
this._worker
|
||||
.postMessage({ type: "meta" })
|
||||
.then(({ supportInfo, version }) => {
|
||||
const availableOptions = parsePrettierOptions(supportInfo);
|
||||
const options =
|
||||
this.state.options ||
|
||||
availableOptions.reduce((acc, option) => {
|
||||
acc[option.name] = option.default;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
this.setState({ loaded: true, availableOptions, options, version });
|
||||
});
|
||||
}
|
||||
|
||||
handleOptionValueChange(option, value) {
|
||||
this.setState(state => ({
|
||||
options: Object.assign({}, state.options, { [option.name]: value })
|
||||
}));
|
||||
}
|
||||
|
||||
componentDidUpdate(_, prevState) {
|
||||
if (
|
||||
prevState.content !== this.state.content ||
|
||||
prevState.options !== this.state.options
|
||||
) {
|
||||
this._format();
|
||||
}
|
||||
}
|
||||
|
||||
_format() {
|
||||
const { content, options } = this.state;
|
||||
this._worker
|
||||
.postMessage({
|
||||
type: "format",
|
||||
code: content,
|
||||
options
|
||||
})
|
||||
.then(({ formatted }) => this.setState({ formatted }));
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
availableOptions,
|
||||
content,
|
||||
formatted,
|
||||
loaded,
|
||||
options,
|
||||
sidebarExpanded
|
||||
} = this.state;
|
||||
|
||||
if (!loaded) return "Loading...";
|
||||
|
||||
return (
|
||||
<EditorState>
|
||||
{editorState => (
|
||||
<div className="playground-container">
|
||||
<div className="editors-container">
|
||||
<div
|
||||
className={`options-container ${
|
||||
editorState.sidebarExpanded ? "open" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="options">
|
||||
<SidebarOptions
|
||||
availableOptions={availableOptions}
|
||||
currentOptions={options}
|
||||
onOptionValueChange={this.handleOptionValueChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="editors">
|
||||
<InputPanel
|
||||
mode="jsx"
|
||||
value={content}
|
||||
onChange={this.setContent}
|
||||
/>
|
||||
{editorState.astPanelVisible ? (
|
||||
<DebugPanel value={"ast here"} />
|
||||
) : null}
|
||||
{editorState.docPanelVisible ? (
|
||||
<DebugPanel value={"doc here"} />
|
||||
) : null}
|
||||
<OutputPanel mode="jsx" value={formatted} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottom-bar">
|
||||
<div className="bottom-bar-buttons">
|
||||
<Button onClick={editorState.toggleSidebarExpanded}>
|
||||
{editorState.sidebarExpanded ? "Hide" : "Show"} options
|
||||
</Button>
|
||||
<Button onClick={this.clearContent}>Clear</Button>
|
||||
</div>
|
||||
<div className="bottom-bar-buttons bottom-bar-buttons-right">
|
||||
<Button>Copy link</Button>
|
||||
<Button>Copy markdown</Button>
|
||||
<Button>Report issue</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</EditorState>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function parsePrettierOptions(supportInfo) {
|
||||
const supportedOptions = supportInfo.options.reduce((acc, option) => {
|
||||
acc[option.name] = option;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return ENABLED_OPTIONS.reduce((optionsList, optionConfig) => {
|
||||
if (!supportedOptions[optionConfig.name]) return optionsList;
|
||||
|
||||
const option = Object.assign(
|
||||
{},
|
||||
optionConfig,
|
||||
supportedOptions[optionConfig.name]
|
||||
);
|
||||
option.cliName =
|
||||
"--" +
|
||||
(option.inverted ? "no-" : "") +
|
||||
option.name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
|
||||
optionsList.push(option);
|
||||
return optionsList;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default Playground;
|
|
@ -0,0 +1,33 @@
|
|||
import React from "react";
|
||||
|
||||
import Option from "./Option";
|
||||
|
||||
const CATEGORIES_ORDER = ["Global", "JavaScript", "Markdown", "Special"];
|
||||
|
||||
export default function({
|
||||
availableOptions,
|
||||
currentOptions,
|
||||
onOptionValueChange
|
||||
}) {
|
||||
const optionsByCategory = availableOptions.reduce((acc, option) => {
|
||||
let options;
|
||||
acc[option.category] = options = acc[option.category] || [];
|
||||
options.push(option);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return CATEGORIES_ORDER.map(category => (
|
||||
<details key={category} className="sub-options" open="true">
|
||||
<summary>{category}</summary>
|
||||
|
||||
{(optionsByCategory[category] || []).map(option => (
|
||||
<Option
|
||||
key={option.name}
|
||||
option={option}
|
||||
value={currentOptions[option.name]}
|
||||
onChange={onOptionValueChange}
|
||||
/>
|
||||
))}
|
||||
</details>
|
||||
));
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
export default function(source) {
|
||||
const worker = new Worker(source);
|
||||
let counter = 0;
|
||||
let handlers = {};
|
||||
|
||||
worker.addEventListener("message", event => {
|
||||
const { uid, message, error } = event.data;
|
||||
|
||||
if (!handlers[uid]) return;
|
||||
|
||||
const [resolve, reject] = handlers[uid];
|
||||
delete handlers[uid];
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(message);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
postMessage(message) {
|
||||
const uid = ++counter;
|
||||
return new Promise((resolve, reject) => {
|
||||
handlers[uid] = [resolve, reject];
|
||||
worker.postMessage({ uid, message });
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
export default function getCodeExample(parser) {
|
||||
switch (parser) {
|
||||
case "babylon":
|
||||
return [
|
||||
'function HelloWorld({greeting = "hello", greeted = \'"World"\', silent = false, onMouseOver,}) {',
|
||||
"",
|
||||
" if(!greeting){return null};",
|
||||
"",
|
||||
" // TODO: Don't use random in render",
|
||||
' let num = Math.floor (Math.random() * 1E+7).toString().replace(/\\.\\d+/ig, "")',
|
||||
"",
|
||||
" return <div className='HelloWorld' title={`You are visitor number ${ num }`} onMouseOver={onMouseOver}>",
|
||||
"",
|
||||
" <strong>{ greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }</strong>",
|
||||
' {greeting.endsWith(",") ? " " : <span style={{color: \'\\grey\'}}>", "</span> }',
|
||||
" <em>",
|
||||
"\t{ greeted }",
|
||||
"\t</em>",
|
||||
" { (silent)",
|
||||
' ? "."',
|
||||
' : "!"}',
|
||||
"",
|
||||
" </div>;",
|
||||
"",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "flow":
|
||||
return [
|
||||
"declare export function graphql<Props, Variables, Component: React$ComponentType<Props>>",
|
||||
" (query: GQLDocument, config?: Config<Props, QueryConfigOptions<Variables>>):",
|
||||
" (Component: Component) => React$ComponentType<$Diff<React$ElementConfig<Component>, {",
|
||||
" data: Object|void,",
|
||||
" mutate: Function|void",
|
||||
" }>>",
|
||||
"",
|
||||
'declare type FetchPolicy = "cache-first" | "cache-and-network" | "network-only" | "cache-only"'
|
||||
].join("\n");
|
||||
case "typescript":
|
||||
return [
|
||||
"interface MyInterface {",
|
||||
" foo(): string,",
|
||||
" bar: Array<number>,",
|
||||
"}",
|
||||
"",
|
||||
"export abstract class Foo implements MyInterface {",
|
||||
" foo() {",
|
||||
" // TODO: return an actual value here",
|
||||
" return 'hello'",
|
||||
" }",
|
||||
" get bar() {",
|
||||
" return [ 1,",
|
||||
"",
|
||||
" 2, 3,",
|
||||
" ]",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
"type RequestType = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'OPTIONS' | 'CONNECT' | 'DELETE' | 'TRACE'"
|
||||
].join("\n");
|
||||
case "css":
|
||||
// Excerpted from the Bootstrap source, which is licensed under the MIT license:
|
||||
// https://github.com/twbs/bootstrap/blob/v4.0.0-beta.3/LICENSE
|
||||
return [
|
||||
"@media (max-width: 480px) {",
|
||||
" .bd-examples {margin-right: -.75rem;margin-left: -.75rem",
|
||||
" }",
|
||||
" ",
|
||||
' .bd-examples>[class^="col-"] {',
|
||||
" padding-right: .75rem;",
|
||||
" padding-left: .75rem;",
|
||||
" ",
|
||||
" }",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "scss":
|
||||
// Excerpted from the Bootstrap source, which is licensed under the MIT license:
|
||||
// https://github.com/twbs/bootstrap/blob/v4.0.0-beta.3/LICENSE
|
||||
return [
|
||||
"@function color-yiq($color) {",
|
||||
" $r: red($color);$g: green($color);$b: blue($color);",
|
||||
"",
|
||||
" $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;",
|
||||
"",
|
||||
" @if ($yiq >= $yiq-contrasted-threshold) {",
|
||||
" @return $yiq-text-dark;",
|
||||
"} @else {",
|
||||
" @return $yiq-text-light;",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
"@each $color, $value in $colors {",
|
||||
" .swatch-#{$color} {",
|
||||
" color: color-yiq($value);",
|
||||
" background-color: #{$value};",
|
||||
" }",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "less":
|
||||
// Copied from http://lesscss.org/features/#detached-rulesets-feature
|
||||
return [
|
||||
"@my-ruleset: {",
|
||||
" .my-selector {",
|
||||
" @media tv {",
|
||||
" background-color: black;",
|
||||
" }",
|
||||
" }",
|
||||
" };",
|
||||
"@media (orientation:portrait) {",
|
||||
" @my-ruleset();",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "json":
|
||||
// Excerpted & adapted from Wikipedia, under the Creative Commons Attribution-ShareAlike License
|
||||
// https://en.wikipedia.org/wiki/JSON#Example
|
||||
return [
|
||||
'{"allOn": "Single", "Line": "example",',
|
||||
'"noSpace":true,',
|
||||
' "quote": {',
|
||||
" 'singleQuote': 'example',",
|
||||
' "indented": true,',
|
||||
" },",
|
||||
' "phoneNumbers": [',
|
||||
' {"type": "home",',
|
||||
' "number": "212 555-1234"},',
|
||||
' {"type": "office",',
|
||||
' "trailing": "commas by accident"},',
|
||||
" ],",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "graphql":
|
||||
return [
|
||||
"query Browse($offset: Int, $limit: Int, $categories: [String!], $search: String) {",
|
||||
" browse(limit: $limit, offset: $offset, categories: $categories, search: $search) {",
|
||||
" total,",
|
||||
" results {",
|
||||
" title",
|
||||
" price",
|
||||
" }",
|
||||
" }",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "markdown":
|
||||
return [
|
||||
"Header",
|
||||
"======",
|
||||
"",
|
||||
"_Look,_ code blocks are formatted *too!*",
|
||||
"",
|
||||
"``` js",
|
||||
"function identity(x) { return x }",
|
||||
"```",
|
||||
"",
|
||||
"Pilot|Airport|Hours",
|
||||
"--|:--:|--:",
|
||||
"John Doe|SKG|1338",
|
||||
"Jane Roe|JFK|314",
|
||||
"",
|
||||
"- - - - - - - - - - - - - - -",
|
||||
"",
|
||||
"+ List",
|
||||
" + with a [link] (/to/somewhere)",
|
||||
"+ and [another one]",
|
||||
"",
|
||||
"",
|
||||
" [another one]: http://example.com 'Example title'",
|
||||
"",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||
"Curabitur consectetur maximus risus, sed maximus tellus tincidunt et."
|
||||
].join("\n");
|
||||
case "vue":
|
||||
return [
|
||||
"<template>",
|
||||
" <p>Templates are not formatted yet ...",
|
||||
" </p>",
|
||||
"</template>",
|
||||
"",
|
||||
"<script>",
|
||||
"let Prettier = format => { your.js('though') }",
|
||||
"</script>",
|
||||
"",
|
||||
"<style>",
|
||||
".and { css: too! important }",
|
||||
"</style>"
|
||||
].join("\n");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
export function stateToggler(key) {
|
||||
return state => ({ [key]: !Boolean(state[key]) });
|
||||
}
|
||||
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
function is(x, y) {
|
||||
// SameValue algorithm
|
||||
if (x === y) {
|
||||
// Steps 1-5, 7-10
|
||||
// Steps 6.b-6.e: +0 != -0
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
} else {
|
||||
// Step 6.a: NaN == NaN
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
}
|
||||
|
||||
export function shallowEqual(objA, objB) {
|
||||
if (is(objA, objB)) return true;
|
||||
|
||||
if (
|
||||
typeof objA !== "object" ||
|
||||
objA === null ||
|
||||
typeof objB !== "object" ||
|
||||
objB === null
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const keysA = Object.keys(objA);
|
||||
const keysB = Object.keys(objB);
|
||||
|
||||
if (keysA.length !== keysB.length) return false;
|
||||
|
||||
for (let i = 0; i <= keysA.length; i++) {
|
||||
if (
|
||||
!hasOwnProperty.call(objB, keysA[i]) ||
|
||||
!is(objA[keysA[i]], objB[keysA[i]])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,693 +1,240 @@
|
|||
/* eslint-env browser */
|
||||
/* eslint no-var: off, strict: off, prefer-arrow-callback: off */
|
||||
/* global Clipboard CodeMirror formatMarkdown LZString preact */
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
var h = preact.h;
|
||||
import Playground from "./Playground";
|
||||
|
||||
var CATEGORIES = ["Global", "JavaScript", "Markdown", "Special"];
|
||||
var OPTIONS = [
|
||||
"parser",
|
||||
"printWidth",
|
||||
"tabWidth",
|
||||
"useTabs",
|
||||
{ name: "semi", inverted: true },
|
||||
"singleQuote",
|
||||
{ name: "bracketSpacing", inverted: true },
|
||||
"jsxBracketSameLine",
|
||||
"arrowParens",
|
||||
"trailingComma",
|
||||
"proseWrap",
|
||||
"insertPragma",
|
||||
"requirePragma"
|
||||
];
|
||||
ReactDOM.render(<Playground />, document.getElementById("root"));
|
||||
|
||||
// ---------- Options components -----------
|
||||
/******************************************/
|
||||
|
||||
function BooleanOption(props) {
|
||||
function maybeInvert(value) {
|
||||
return props.option.inverted ? !value : value;
|
||||
}
|
||||
return h(
|
||||
"label",
|
||||
null,
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
checked: maybeInvert(props.value),
|
||||
onChange: function(ev) {
|
||||
props.onOptionChange(props.option, maybeInvert(ev.target.checked));
|
||||
}
|
||||
}),
|
||||
" ",
|
||||
props.option.cliName
|
||||
);
|
||||
}
|
||||
|
||||
function ChoiceOption(props) {
|
||||
return h(
|
||||
"label",
|
||||
null,
|
||||
props.option.cliName,
|
||||
" ",
|
||||
h(
|
||||
"select",
|
||||
{
|
||||
onChange: function(ev) {
|
||||
props.onOptionChange(props.option, ev.target.value);
|
||||
}
|
||||
},
|
||||
props.option.choices.map(function(choice) {
|
||||
return h(
|
||||
"option",
|
||||
{ value: choice.value, selected: choice.value === props.value },
|
||||
choice.value
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function NumberOption(props) {
|
||||
return h(
|
||||
"label",
|
||||
null,
|
||||
props.option.cliName,
|
||||
" ",
|
||||
h("input", {
|
||||
type: "number",
|
||||
min: props.option.range.start,
|
||||
max: props.option.range.end,
|
||||
step: props.option.range.step,
|
||||
value: props.value,
|
||||
onChange: function(ev) {
|
||||
props.onOptionChange(props.option, parseInt(ev.target.value, 10));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function Options(props) {
|
||||
var optionsByCategory = props.availableOptions.reduce(function(acc, opt) {
|
||||
acc[opt.category] = opts = acc[opt.category] || [];
|
||||
opts.push(opt);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return h(
|
||||
"div",
|
||||
{ className: "options-container" + (props.open ? " open" : "") },
|
||||
h(
|
||||
"div",
|
||||
{ className: "options" },
|
||||
CATEGORIES.map(function(category) {
|
||||
return h(
|
||||
"details",
|
||||
{ className: "sub-options", open: "true" },
|
||||
h("summary", null, category),
|
||||
(optionsByCategory[category] || []).map(function(opt) {
|
||||
return h(getComponentByOptionType(opt), {
|
||||
option: opt,
|
||||
value: props.options[opt.name],
|
||||
onOptionChange: props.onOptionChange
|
||||
});
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// ---------- Editor components -----------
|
||||
|
||||
var Editor = createClass({
|
||||
componentDidMount: function() {
|
||||
this._codeMirror = CodeMirror.fromTextArea(this._ref, this.props.options);
|
||||
this._codeMirror.on("change", this.handleChange.bind(this));
|
||||
this._codeMirror.setValue(this.props.value || "");
|
||||
},
|
||||
handleChange: function(doc, change) {
|
||||
if (change.origin !== "setValue") {
|
||||
this.props.onChange(doc.getValue());
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (
|
||||
nextProps.value &&
|
||||
nextProps.value !== this.props.value &&
|
||||
this._codeMirror.getValue() !== nextProps.value
|
||||
) {
|
||||
this._codeMirror.setValue(nextProps.value);
|
||||
} else if (!nextProps.value) {
|
||||
this._codeMirror.setValue("");
|
||||
}
|
||||
},
|
||||
render: function(props) {
|
||||
(function() {
|
||||
function BottomBar(props) {
|
||||
return h(
|
||||
"div",
|
||||
{ className: "editor input" },
|
||||
h("textarea", {
|
||||
ref: function(ref) {
|
||||
this._ref = ref;
|
||||
}.bind(this)
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function InputEditor(props) {
|
||||
return h(Editor, {
|
||||
options: {
|
||||
lineNumbers: true,
|
||||
keyMap: "sublime",
|
||||
autoCloseBrackets: true,
|
||||
matchBrackets: true,
|
||||
showCursorWhenSelecting: true,
|
||||
tabWidth: 2,
|
||||
mode: props.mode
|
||||
},
|
||||
value: props.value,
|
||||
onChange: props.onChange
|
||||
});
|
||||
}
|
||||
|
||||
function OutputEditor(props) {
|
||||
return h(Editor, {
|
||||
options: {
|
||||
readOnly: true,
|
||||
lineNumbers: true,
|
||||
mode: props.mode
|
||||
},
|
||||
value: props.value
|
||||
});
|
||||
}
|
||||
|
||||
function Button(props) {
|
||||
return h("button", Object.assign({ type: "button", class: "btn" }, props));
|
||||
}
|
||||
|
||||
function BottomBar(props) {
|
||||
return h(
|
||||
"div",
|
||||
{ class: "bottom-bar" },
|
||||
h(
|
||||
"div",
|
||||
{ class: "bottom-bar-buttons" },
|
||||
h(
|
||||
Button,
|
||||
{ onClick: props.onShowOptionsClick },
|
||||
props.optionsOpen ? "Hide options" : "Show options"
|
||||
),
|
||||
h(Button, { onClick: props.onClearClick }, "Clear")
|
||||
),
|
||||
h(
|
||||
"div",
|
||||
{ class: "bottom-bar-buttons bottom-bar-buttons-right" },
|
||||
h(Button, null, "Copy Link"),
|
||||
h(Button, null, "Copy markdown"),
|
||||
h(
|
||||
"a",
|
||||
{
|
||||
href:
|
||||
"https://github.com/prettier/prettier/issues/new?body=" +
|
||||
encodeURIComponent(props.reportBody),
|
||||
target: "_blank",
|
||||
rel: "noopener",
|
||||
class: "btn"
|
||||
},
|
||||
"Report issue"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var App = createClass({
|
||||
state: Object.assign({ loaded: false, formatted: "" }, loadPersistedState()),
|
||||
|
||||
_format: function() {
|
||||
this._worker.postMessage(
|
||||
{ type: "format", code: this.state.content, options: this.state.options },
|
||||
function(message) {
|
||||
this.setState({ formatted: message.formatted });
|
||||
}.bind(this)
|
||||
);
|
||||
persistState(this.state);
|
||||
},
|
||||
|
||||
handleOptionChange: function(option, value) {
|
||||
this.setState(function(state) {
|
||||
return {
|
||||
options: Object.assign({}, state.options, { [option.name]: value })
|
||||
};
|
||||
}, this._format.bind(this));
|
||||
},
|
||||
handleOptionsOpen: function() {
|
||||
this.setState(
|
||||
function(state) {
|
||||
return {
|
||||
editorState: Object.assign({}, state.editorState, {
|
||||
optionsOpen: !state.editorState.optionsOpen
|
||||
})
|
||||
};
|
||||
},
|
||||
function() {
|
||||
persistState(this.state);
|
||||
}
|
||||
);
|
||||
},
|
||||
handleContentChange: function(value) {
|
||||
this.setState({ content: value }, this._format.bind(this));
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this._worker = new WorkerApi("/worker2.js");
|
||||
|
||||
this._worker.postMessage(
|
||||
{ type: "meta" },
|
||||
function(message) {
|
||||
var availableOptions = getOptions(message.supportInfo.options);
|
||||
var options =
|
||||
this.state.options ||
|
||||
availableOptions.reduce(function(options, opt) {
|
||||
options[opt.name] = opt.default;
|
||||
return options;
|
||||
}, {});
|
||||
|
||||
this.setState({
|
||||
loaded: true,
|
||||
version: message.version,
|
||||
availableOptions: availableOptions,
|
||||
options: options
|
||||
});
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this._format();
|
||||
},
|
||||
|
||||
render: function(props, state) {
|
||||
if (!state.loaded) {
|
||||
return "Loading...";
|
||||
}
|
||||
var editorState = state.editorState;
|
||||
return h(
|
||||
"div",
|
||||
{ className: "playground-container" },
|
||||
{ class: "bottom-bar" },
|
||||
h(
|
||||
"div",
|
||||
{ className: "editors-container" },
|
||||
h(Options, {
|
||||
availableOptions: state.availableOptions,
|
||||
options: state.options,
|
||||
open: editorState.optionsOpen,
|
||||
onOptionChange: this.handleOptionChange.bind(this)
|
||||
}),
|
||||
{ class: "bottom-bar-buttons" },
|
||||
h(
|
||||
Button,
|
||||
{ onClick: props.onShowOptionsClick },
|
||||
props.optionsOpen ? "Hide options" : "Show options"
|
||||
),
|
||||
h(Button, { onClick: props.onClearClick }, "Clear")
|
||||
),
|
||||
h(
|
||||
"div",
|
||||
{ class: "bottom-bar-buttons bottom-bar-buttons-right" },
|
||||
h(Button, null, "Copy Link"),
|
||||
h(Button, null, "Copy markdown"),
|
||||
h(
|
||||
"a",
|
||||
{
|
||||
href:
|
||||
"https://github.com/prettier/prettier/issues/new?body=" +
|
||||
encodeURIComponent(props.reportBody),
|
||||
target: "_blank",
|
||||
rel: "noopener",
|
||||
class: "btn"
|
||||
},
|
||||
"Report issue"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var App = createClass({
|
||||
state: Object.assign(
|
||||
{ loaded: false, formatted: "" },
|
||||
loadPersistedState()
|
||||
),
|
||||
|
||||
handleOptionChange: function(option, value) {
|
||||
this.setState(function(state) {
|
||||
return {
|
||||
options: Object.assign({}, state.options, { [option.name]: value })
|
||||
};
|
||||
}, this._format.bind(this));
|
||||
},
|
||||
handleOptionsOpen: function() {
|
||||
this.setState(
|
||||
function(state) {
|
||||
return {
|
||||
editorState: Object.assign({}, state.editorState, {
|
||||
optionsOpen: !state.editorState.optionsOpen
|
||||
})
|
||||
};
|
||||
},
|
||||
function() {
|
||||
persistState(this.state);
|
||||
}
|
||||
);
|
||||
},
|
||||
handleContentChange: function(value) {
|
||||
this.setState({ content: value }, this._format.bind(this));
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this._worker = new WorkerApi("/worker2.js");
|
||||
|
||||
this._worker.postMessage(
|
||||
{ type: "meta" },
|
||||
function(message) {
|
||||
var availableOptions = getOptions(message.supportInfo.options);
|
||||
var options =
|
||||
this.state.options ||
|
||||
availableOptions.reduce(function(options, opt) {
|
||||
options[opt.name] = opt.default;
|
||||
return options;
|
||||
}, {});
|
||||
|
||||
this.setState({
|
||||
loaded: true,
|
||||
version: message.version,
|
||||
availableOptions: availableOptions,
|
||||
options: options
|
||||
});
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this._format();
|
||||
},
|
||||
|
||||
render: function(props, state) {
|
||||
if (!state.loaded) {
|
||||
return "Loading...";
|
||||
}
|
||||
var editorState = state.editorState;
|
||||
return h(
|
||||
"div",
|
||||
{ className: "playground-container" },
|
||||
h(
|
||||
"div",
|
||||
{ className: "editors" },
|
||||
h(InputEditor, {
|
||||
mode: getCodemirrorMode(state.options),
|
||||
value: state.content,
|
||||
onChange: this.handleContentChange.bind(this)
|
||||
{ className: "editors-container" },
|
||||
h(Options, {
|
||||
availableOptions: state.availableOptions,
|
||||
options: state.options,
|
||||
open: editorState.optionsOpen,
|
||||
onOptionChange: this.handleOptionChange.bind(this)
|
||||
}),
|
||||
h(OutputEditor, {
|
||||
mode: getCodemirrorMode(state.options),
|
||||
value: state.formatted
|
||||
})
|
||||
)
|
||||
),
|
||||
h(BottomBar, {
|
||||
optionsOpen: editorState.optionsOpen,
|
||||
onShowOptionsClick: this.handleOptionsOpen.bind(this),
|
||||
onClearClick: function() {
|
||||
this.handleContentChange("");
|
||||
}.bind(this),
|
||||
reportBody: formatMarkdown(
|
||||
state.content,
|
||||
state.formatted,
|
||||
"",
|
||||
state.version,
|
||||
window.location.href,
|
||||
state.options,
|
||||
state.availableOptions.reduce(function(cliOptions, option) {
|
||||
var value = state.options[option.name];
|
||||
if (option.type === "boolean") {
|
||||
if ((value && !option.inverted) || (!value && option.inverted)) {
|
||||
cliOptions.push([option.cliName, true]);
|
||||
h(
|
||||
"div",
|
||||
{ className: "editors" },
|
||||
h(InputEditor, {
|
||||
mode: getCodemirrorMode(state.options),
|
||||
value: state.content,
|
||||
onChange: this.handleContentChange.bind(this)
|
||||
}),
|
||||
h(OutputEditor, {
|
||||
mode: getCodemirrorMode(state.options),
|
||||
value: state.formatted
|
||||
})
|
||||
)
|
||||
),
|
||||
h(BottomBar, {
|
||||
optionsOpen: editorState.optionsOpen,
|
||||
onShowOptionsClick: this.handleOptionsOpen.bind(this),
|
||||
onClearClick: function() {
|
||||
this.handleContentChange("");
|
||||
}.bind(this),
|
||||
reportBody: formatMarkdown(
|
||||
state.content,
|
||||
state.formatted,
|
||||
"",
|
||||
state.version,
|
||||
window.location.href,
|
||||
state.options,
|
||||
state.availableOptions.reduce(function(cliOptions, option) {
|
||||
var value = state.options[option.name];
|
||||
if (option.type === "boolean") {
|
||||
if (
|
||||
(value && !option.inverted) ||
|
||||
(!value && option.inverted)
|
||||
) {
|
||||
cliOptions.push([option.cliName, true]);
|
||||
}
|
||||
} else if (value !== option.default) {
|
||||
cliOptions.push([option.cliName, value]);
|
||||
}
|
||||
} else if (value !== option.default) {
|
||||
cliOptions.push([option.cliName, value]);
|
||||
}
|
||||
return cliOptions;
|
||||
}, [])
|
||||
)
|
||||
})
|
||||
);
|
||||
return cliOptions;
|
||||
}, [])
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// -------- UTILITY FUNCTIONS --------
|
||||
|
||||
function loadPersistedState() {
|
||||
var editorState = { optionsOpen: false };
|
||||
try {
|
||||
Object.assign(
|
||||
editorState,
|
||||
JSON.parse(window.localStorage.getItem("editorState"))
|
||||
);
|
||||
} catch (error) {
|
||||
// noop
|
||||
}
|
||||
var queryState = parseQuery() || {};
|
||||
return {
|
||||
editorState: editorState,
|
||||
content: typeof queryState.content === "string" ? queryState.content : "",
|
||||
options: queryState.options
|
||||
};
|
||||
}
|
||||
|
||||
function persistState(state) {
|
||||
try {
|
||||
window.localStorage.setItem(
|
||||
"editorState",
|
||||
JSON.stringify(state.editorState)
|
||||
);
|
||||
} catch (_) {
|
||||
// noop
|
||||
}
|
||||
saveQuery({ content: state.content, options: state.options });
|
||||
}
|
||||
|
||||
function getCodemirrorMode(options) {
|
||||
switch (options.parser) {
|
||||
case "css":
|
||||
case "less":
|
||||
case "scss":
|
||||
return "css";
|
||||
case "markdown":
|
||||
return "markdown";
|
||||
default:
|
||||
return "jsx";
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the URL hash as a config object
|
||||
function parseQuery() {
|
||||
var hash = document.location.hash.slice(1);
|
||||
try {
|
||||
// providing backwards support for old json encoded URIComponent
|
||||
if (hash.indexOf("%7B%22") !== -1) {
|
||||
return JSON.parse(decodeURIComponent(hash));
|
||||
}
|
||||
return JSON.parse(LZString.decompressFromEncodedURIComponent(hash));
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function saveQuery(state) {
|
||||
var hash = LZString.compressToEncodedURIComponent(JSON.stringify(state));
|
||||
if (
|
||||
typeof URL === "function" &&
|
||||
typeof history === "object" &&
|
||||
typeof history.replaceState === "function"
|
||||
) {
|
||||
var url = new URL(location);
|
||||
url.hash = hash;
|
||||
history.replaceState(null, null, url);
|
||||
} else {
|
||||
location.hash = hash;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.onload = function() {
|
||||
preact.render(h(App), document.getElementById("root"));
|
||||
};
|
||||
|
||||
// -------- Worker API -------------
|
||||
|
||||
function WorkerApi(source) {
|
||||
var worker = new Worker(source);
|
||||
var counter = 0;
|
||||
var handlers = {};
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
var uid = event.data.uid;
|
||||
var message = event.data.message;
|
||||
if (handlers[uid]) {
|
||||
var handler = handlers[uid];
|
||||
delete handlers[uid];
|
||||
|
||||
handler(message);
|
||||
}
|
||||
};
|
||||
|
||||
function postMessage(message, handler) {
|
||||
var uid = ++counter;
|
||||
handlers[uid] = handler;
|
||||
worker.postMessage({
|
||||
uid: uid,
|
||||
message: message
|
||||
});
|
||||
}
|
||||
|
||||
return { postMessage: postMessage };
|
||||
}
|
||||
|
||||
// -------- UTILITY FUNCTIONS --------
|
||||
|
||||
function loadPersistedState() {
|
||||
var editorState = { optionsOpen: false };
|
||||
try {
|
||||
Object.assign(
|
||||
editorState,
|
||||
JSON.parse(window.localStorage.getItem("editorState"))
|
||||
);
|
||||
} catch (error) {
|
||||
// noop
|
||||
}
|
||||
var queryState = parseQuery() || {};
|
||||
return {
|
||||
editorState: editorState,
|
||||
content: typeof queryState.content === "string" ? queryState.content : "",
|
||||
options: queryState.options
|
||||
};
|
||||
}
|
||||
|
||||
function persistState(state) {
|
||||
try {
|
||||
window.localStorage.setItem(
|
||||
"editorState",
|
||||
JSON.stringify(state.editorState)
|
||||
);
|
||||
} catch (_) {
|
||||
// noop
|
||||
}
|
||||
saveQuery({ content: state.content, options: state.options });
|
||||
}
|
||||
|
||||
function getComponentByOptionType(option) {
|
||||
switch (option.type) {
|
||||
case "boolean":
|
||||
return BooleanOption;
|
||||
case "int":
|
||||
return NumberOption;
|
||||
case "choice":
|
||||
return ChoiceOption;
|
||||
default:
|
||||
throw new Error("unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
function getCodemirrorMode(options) {
|
||||
switch (options.parser) {
|
||||
case "css":
|
||||
case "less":
|
||||
case "scss":
|
||||
return "css";
|
||||
case "markdown":
|
||||
return "markdown";
|
||||
default:
|
||||
return "jsx";
|
||||
}
|
||||
}
|
||||
|
||||
function getOptions(supportedOptions) {
|
||||
supportedOptions = supportedOptions.reduce(function(acc, opt) {
|
||||
acc[opt.name] = opt;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return OPTIONS.reduce(function(options, opt) {
|
||||
opt = typeof opt === "string" ? { name: opt } : opt;
|
||||
if (!supportedOptions[opt.name]) {
|
||||
return options;
|
||||
}
|
||||
|
||||
var modified = Object.assign({}, opt, supportedOptions[opt.name]);
|
||||
modified.cliName =
|
||||
"--" +
|
||||
(opt.inverted ? "no-" : "") +
|
||||
opt.name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
if (modified.type === "boolean" && opt.inverted) {
|
||||
modified.default = !modified.default;
|
||||
}
|
||||
|
||||
options.push(modified);
|
||||
return options;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// Parse the URL hash as a config object
|
||||
function parseQuery() {
|
||||
var hash = document.location.hash.slice(1);
|
||||
try {
|
||||
// providing backwards support for old json encoded URIComponent
|
||||
if (hash.indexOf("%7B%22") !== -1) {
|
||||
return JSON.parse(decodeURIComponent(hash));
|
||||
}
|
||||
return JSON.parse(LZString.decompressFromEncodedURIComponent(hash));
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function saveQuery(state) {
|
||||
var hash = LZString.compressToEncodedURIComponent(JSON.stringify(state));
|
||||
if (
|
||||
typeof URL === "function" &&
|
||||
typeof history === "object" &&
|
||||
typeof history.replaceState === "function"
|
||||
) {
|
||||
var url = new URL(location);
|
||||
url.hash = hash;
|
||||
history.replaceState(null, null, url);
|
||||
} else {
|
||||
location.hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
function getCodeExample(parser) {
|
||||
switch (parser) {
|
||||
case "babylon":
|
||||
return [
|
||||
'function HelloWorld({greeting = "hello", greeted = \'"World"\', silent = false, onMouseOver,}) {',
|
||||
"",
|
||||
" if(!greeting){return null};",
|
||||
"",
|
||||
" // TODO: Don't use random in render",
|
||||
' let num = Math.floor (Math.random() * 1E+7).toString().replace(/\\.\\d+/ig, "")',
|
||||
"",
|
||||
" return <div className='HelloWorld' title={`You are visitor number ${ num }`} onMouseOver={onMouseOver}>",
|
||||
"",
|
||||
" <strong>{ greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }</strong>",
|
||||
' {greeting.endsWith(",") ? " " : <span style={{color: \'\\grey\'}}>", "</span> }',
|
||||
" <em>",
|
||||
"\t{ greeted }",
|
||||
"\t</em>",
|
||||
" { (silent)",
|
||||
' ? "."',
|
||||
' : "!"}',
|
||||
"",
|
||||
" </div>;",
|
||||
"",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "flow":
|
||||
return [
|
||||
"declare export function graphql<Props, Variables, Component: React$ComponentType<Props>>",
|
||||
" (query: GQLDocument, config?: Config<Props, QueryConfigOptions<Variables>>):",
|
||||
" (Component: Component) => React$ComponentType<$Diff<React$ElementConfig<Component>, {",
|
||||
" data: Object|void,",
|
||||
" mutate: Function|void",
|
||||
" }>>",
|
||||
"",
|
||||
'declare type FetchPolicy = "cache-first" | "cache-and-network" | "network-only" | "cache-only"'
|
||||
].join("\n");
|
||||
case "typescript":
|
||||
return [
|
||||
"interface MyInterface {",
|
||||
" foo(): string,",
|
||||
" bar: Array<number>,",
|
||||
"}",
|
||||
"",
|
||||
"export abstract class Foo implements MyInterface {",
|
||||
" foo() {",
|
||||
" // TODO: return an actual value here",
|
||||
" return 'hello'",
|
||||
" }",
|
||||
" get bar() {",
|
||||
" return [ 1,",
|
||||
"",
|
||||
" 2, 3,",
|
||||
" ]",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
"type RequestType = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'OPTIONS' | 'CONNECT' | 'DELETE' | 'TRACE'"
|
||||
].join("\n");
|
||||
case "css":
|
||||
// Excerpted from the Bootstrap source, which is licensed under the MIT license:
|
||||
// https://github.com/twbs/bootstrap/blob/v4.0.0-beta.3/LICENSE
|
||||
return [
|
||||
"@media (max-width: 480px) {",
|
||||
" .bd-examples {margin-right: -.75rem;margin-left: -.75rem",
|
||||
" }",
|
||||
" ",
|
||||
' .bd-examples>[class^="col-"] {',
|
||||
" padding-right: .75rem;",
|
||||
" padding-left: .75rem;",
|
||||
" ",
|
||||
" }",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "scss":
|
||||
// Excerpted from the Bootstrap source, which is licensed under the MIT license:
|
||||
// https://github.com/twbs/bootstrap/blob/v4.0.0-beta.3/LICENSE
|
||||
return [
|
||||
"@function color-yiq($color) {",
|
||||
" $r: red($color);$g: green($color);$b: blue($color);",
|
||||
"",
|
||||
" $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;",
|
||||
"",
|
||||
" @if ($yiq >= $yiq-contrasted-threshold) {",
|
||||
" @return $yiq-text-dark;",
|
||||
"} @else {",
|
||||
" @return $yiq-text-light;",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
"@each $color, $value in $colors {",
|
||||
" .swatch-#{$color} {",
|
||||
" color: color-yiq($value);",
|
||||
" background-color: #{$value};",
|
||||
" }",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "less":
|
||||
// Copied from http://lesscss.org/features/#detached-rulesets-feature
|
||||
return [
|
||||
"@my-ruleset: {",
|
||||
" .my-selector {",
|
||||
" @media tv {",
|
||||
" background-color: black;",
|
||||
" }",
|
||||
" }",
|
||||
" };",
|
||||
"@media (orientation:portrait) {",
|
||||
" @my-ruleset();",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "json":
|
||||
// Excerpted & adapted from Wikipedia, under the Creative Commons Attribution-ShareAlike License
|
||||
// https://en.wikipedia.org/wiki/JSON#Example
|
||||
return [
|
||||
'{"allOn": "Single", "Line": "example",',
|
||||
'"noSpace":true,',
|
||||
' "quote": {',
|
||||
" 'singleQuote': 'example',",
|
||||
' "indented": true,',
|
||||
" },",
|
||||
' "phoneNumbers": [',
|
||||
' {"type": "home",',
|
||||
' "number": "212 555-1234"},',
|
||||
' {"type": "office",',
|
||||
' "trailing": "commas by accident"},',
|
||||
" ],",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "graphql":
|
||||
return [
|
||||
"query Browse($offset: Int, $limit: Int, $categories: [String!], $search: String) {",
|
||||
" browse(limit: $limit, offset: $offset, categories: $categories, search: $search) {",
|
||||
" total,",
|
||||
" results {",
|
||||
" title",
|
||||
" price",
|
||||
" }",
|
||||
" }",
|
||||
"}"
|
||||
].join("\n");
|
||||
case "markdown":
|
||||
return [
|
||||
"Header",
|
||||
"======",
|
||||
"",
|
||||
"_Look,_ code blocks are formatted *too!*",
|
||||
"",
|
||||
"``` js",
|
||||
"function identity(x) { return x }",
|
||||
"```",
|
||||
"",
|
||||
"Pilot|Airport|Hours",
|
||||
"--|:--:|--:",
|
||||
"John Doe|SKG|1338",
|
||||
"Jane Roe|JFK|314",
|
||||
"",
|
||||
"- - - - - - - - - - - - - - -",
|
||||
"",
|
||||
"+ List",
|
||||
" + with a [link] (/to/somewhere)",
|
||||
"+ and [another one]",
|
||||
"",
|
||||
"",
|
||||
" [another one]: http://example.com 'Example title'",
|
||||
"",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||
"Curabitur consectetur maximus risus, sed maximus tellus tincidunt et."
|
||||
].join("\n");
|
||||
case "vue":
|
||||
return [
|
||||
"<template>",
|
||||
" <p>Templates are not formatted yet ...",
|
||||
" </p>",
|
||||
"</template>",
|
||||
"",
|
||||
"<script>",
|
||||
"let Prettier = format => { your.js('though') }",
|
||||
"</script>",
|
||||
"",
|
||||
"<style>",
|
||||
".and { css: too! important }",
|
||||
"</style>"
|
||||
].join("\n");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// Preact without ES6 classes
|
||||
function createClass(obj) {
|
||||
function F() {
|
||||
preact.Component.call(this);
|
||||
}
|
||||
var p = (F.prototype = new preact.Component());
|
||||
for (var i in obj) {
|
||||
p[i] = obj[i];
|
||||
}
|
||||
return (p.constructor = F);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import CodeMirror from "codemirror";
|
||||
import React from "react";
|
||||
|
||||
class CodeMirrorPanel extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this._textareaRef = React.createRef();
|
||||
this._codeMirror = null;
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._codeMirror = CodeMirror.fromTextArea(
|
||||
this._textareaRef.current,
|
||||
this.props.options
|
||||
);
|
||||
this._codeMirror.on("change", this.handleChange);
|
||||
this._codeMirror.setValue(this.props.value || "");
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._codeMirror && this._codeMirror.toTextArea();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.value !== this._codeMirror.getValue()) {
|
||||
this._codeMirror.setValue(this.props.value);
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(doc, change) {
|
||||
if (change.origin !== "setValue") {
|
||||
this.props.onChange(doc.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="editor input">
|
||||
<textarea ref={this._textareaRef} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function InputPanel({ mode, value, onChange }) {
|
||||
return (
|
||||
<CodeMirrorPanel
|
||||
options={{
|
||||
lineNumbers: true,
|
||||
keyMap: "sublime",
|
||||
autoCloseBrackets: true,
|
||||
matchBrackets: true,
|
||||
showCursorWhenSelecting: true,
|
||||
tabWidth: 2,
|
||||
mode
|
||||
}}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function OutputPanel({ mode, value }) {
|
||||
return (
|
||||
<CodeMirrorPanel
|
||||
options={{
|
||||
readOnly: true,
|
||||
lineNumbers: true,
|
||||
mode
|
||||
}}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function DebugPanel({ value }) {
|
||||
return (
|
||||
<CodeMirrorPanel
|
||||
options={{ readOnly: true, lineNumbers: false, mode: "jsx" }}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
export function get(key) {
|
||||
try {
|
||||
return JSON.parse(window.localStorage.getItem(key));
|
||||
} catch (_) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
export function set(key, value) {
|
||||
try {
|
||||
window.localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch (_) {
|
||||
// noop
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
var parsersLoaded = {};
|
||||
|
||||
// "Polyfills" in order for all the code to run
|
||||
/* eslint-disable */
|
||||
self.global = self;
|
||||
self.util = {};
|
||||
self.path = {};
|
||||
|
@ -24,7 +25,7 @@ self.fs = { readFile: function() {} };
|
|||
os.homedir = function() {
|
||||
return "/home/prettier";
|
||||
};
|
||||
os.EOL = "\n";
|
||||
os.EOL = '\n';
|
||||
self.process = {
|
||||
argv: [],
|
||||
env: { PRETTIER_DEBUG: true },
|
||||
|
@ -57,6 +58,8 @@ self.require = function require(path) {
|
|||
return self[path];
|
||||
};
|
||||
|
||||
/* eslint-enable */
|
||||
|
||||
var prettier;
|
||||
importScripts("lib/index.js");
|
||||
if (typeof prettier === "undefined") {
|
||||
|
@ -66,36 +69,60 @@ if (typeof prettier === "undefined") {
|
|||
prettier = index; // eslint-disable-line
|
||||
}
|
||||
|
||||
self.onmessage = function(event) {
|
||||
var uid = event.data.uid;
|
||||
var message = event.data.message;
|
||||
switch (message.type) {
|
||||
case "meta":
|
||||
self.postMessage({
|
||||
uid: uid,
|
||||
message: {
|
||||
type: "meta",
|
||||
supportInfo: JSON.parse(JSON.stringify(prettier.getSupportInfo())),
|
||||
version: prettier.version
|
||||
}
|
||||
});
|
||||
break;
|
||||
self.onmessage = function(message) {
|
||||
var options = message.data.options || {};
|
||||
options.parser = options.parser || "babylon";
|
||||
|
||||
case "format":
|
||||
var options = message.options || {};
|
||||
delete options.ast;
|
||||
delete options.doc;
|
||||
delete options.output2;
|
||||
|
||||
delete options.ast;
|
||||
delete options.doc;
|
||||
delete options.output2;
|
||||
var formatted = formatCode(message.data.text, options);
|
||||
var doc;
|
||||
var ast;
|
||||
var formatted2;
|
||||
|
||||
self.postMessage({
|
||||
uid: uid,
|
||||
message: {
|
||||
formatted: formatCode(message.code, options)
|
||||
}
|
||||
});
|
||||
break;
|
||||
if (message.data.ast) {
|
||||
var actualAst;
|
||||
var errored = false;
|
||||
try {
|
||||
actualAst = prettier.__debug.parse(message.data.text, options).ast;
|
||||
ast = JSON.stringify(actualAst);
|
||||
} catch (e) {
|
||||
errored = true;
|
||||
ast = String(e);
|
||||
}
|
||||
if (!errored) {
|
||||
try {
|
||||
ast = formatCode(ast, { parser: "json" });
|
||||
} catch (e) {
|
||||
ast = JSON.stringify(actualAst, null, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message.data.doc) {
|
||||
try {
|
||||
doc = prettier.__debug.formatDoc(
|
||||
prettier.__debug.printToDoc(message.data.text, options),
|
||||
{ parser: "babylon" }
|
||||
);
|
||||
} catch (e) {
|
||||
doc = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (message.data.formatted2) {
|
||||
formatted2 = formatCode(formatted, options);
|
||||
}
|
||||
|
||||
self.postMessage({
|
||||
formatted: formatted,
|
||||
doc: doc,
|
||||
ast: ast,
|
||||
formatted2: formatted2,
|
||||
version: prettier.version
|
||||
});
|
||||
};
|
||||
|
||||
function formatCode(text, options) {
|
|
@ -4,7 +4,6 @@
|
|||
var parsersLoaded = {};
|
||||
|
||||
// "Polyfills" in order for all the code to run
|
||||
/* eslint-disable */
|
||||
self.global = self;
|
||||
self.util = {};
|
||||
self.path = {};
|
||||
|
@ -25,7 +24,7 @@ self.fs = { readFile: function() {} };
|
|||
os.homedir = function() {
|
||||
return "/home/prettier";
|
||||
};
|
||||
os.EOL = '\n';
|
||||
os.EOL = "\n";
|
||||
self.process = {
|
||||
argv: [],
|
||||
env: { PRETTIER_DEBUG: true },
|
||||
|
@ -58,8 +57,6 @@ self.require = function require(path) {
|
|||
return self[path];
|
||||
};
|
||||
|
||||
/* eslint-enable */
|
||||
|
||||
var prettier;
|
||||
importScripts("lib/index.js");
|
||||
if (typeof prettier === "undefined") {
|
||||
|
@ -69,60 +66,36 @@ if (typeof prettier === "undefined") {
|
|||
prettier = index; // eslint-disable-line
|
||||
}
|
||||
|
||||
self.onmessage = function(message) {
|
||||
var options = message.data.options || {};
|
||||
options.parser = options.parser || "babylon";
|
||||
self.onmessage = function(event) {
|
||||
var uid = event.data.uid;
|
||||
var message = event.data.message;
|
||||
switch (message.type) {
|
||||
case "meta":
|
||||
self.postMessage({
|
||||
uid: uid,
|
||||
message: {
|
||||
type: "meta",
|
||||
supportInfo: JSON.parse(JSON.stringify(prettier.getSupportInfo())),
|
||||
version: prettier.version
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
delete options.ast;
|
||||
delete options.doc;
|
||||
delete options.output2;
|
||||
case "format":
|
||||
var options = message.options || {};
|
||||
|
||||
var formatted = formatCode(message.data.text, options);
|
||||
var doc;
|
||||
var ast;
|
||||
var formatted2;
|
||||
delete options.ast;
|
||||
delete options.doc;
|
||||
delete options.output2;
|
||||
|
||||
if (message.data.ast) {
|
||||
var actualAst;
|
||||
var errored = false;
|
||||
try {
|
||||
actualAst = prettier.__debug.parse(message.data.text, options).ast;
|
||||
ast = JSON.stringify(actualAst);
|
||||
} catch (e) {
|
||||
errored = true;
|
||||
ast = String(e);
|
||||
}
|
||||
if (!errored) {
|
||||
try {
|
||||
ast = formatCode(ast, { parser: "json" });
|
||||
} catch (e) {
|
||||
ast = JSON.stringify(actualAst, null, 2);
|
||||
}
|
||||
}
|
||||
self.postMessage({
|
||||
uid: uid,
|
||||
message: {
|
||||
formatted: formatCode(message.code, options)
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.data.doc) {
|
||||
try {
|
||||
doc = prettier.__debug.formatDoc(
|
||||
prettier.__debug.printToDoc(message.data.text, options),
|
||||
{ parser: "babylon" }
|
||||
);
|
||||
} catch (e) {
|
||||
doc = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (message.data.formatted2) {
|
||||
formatted2 = formatCode(formatted, options);
|
||||
}
|
||||
|
||||
self.postMessage({
|
||||
formatted: formatted,
|
||||
doc: doc,
|
||||
ast: ast,
|
||||
formatted2: formatted2,
|
||||
version: prettier.version
|
||||
});
|
||||
};
|
||||
|
||||
function formatCode(text, options) {
|
||||
|
|
|
@ -15,9 +15,14 @@ module.exports = {
|
|||
exclude: /node_modules/,
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["env"]
|
||||
presets: ["env", "react"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
externals: {
|
||||
codemirror: "CodeMirror",
|
||||
react: "React",
|
||||
"react-dom": "ReactDOM"
|
||||
}
|
||||
};
|
||||
|
|
|
@ -884,7 +884,7 @@ babel-preset-flow@^6.23.0:
|
|||
dependencies:
|
||||
babel-plugin-transform-flow-strip-types "^6.22.0"
|
||||
|
||||
babel-preset-react@^6.24.1:
|
||||
babel-preset-react@6.24.1, babel-preset-react@^6.24.1:
|
||||
version "6.24.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380"
|
||||
dependencies:
|
||||
|
@ -1436,6 +1436,10 @@ code-point-at@^1.0.0:
|
|||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
|
||||
codemirror@5.36.0:
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.36.0.tgz#1172ad9dc298056c06e0b34e5ccd23825ca15b40"
|
||||
|
||||
collection-visit@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
|
||||
|
@ -3428,6 +3432,10 @@ lru-cache@^4.0.1, lru-cache@^4.1.1:
|
|||
pseudomap "^1.0.2"
|
||||
yallist "^2.1.2"
|
||||
|
||||
lz-string@1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
|
||||
make-dir@^1.0.0, make-dir@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
|
||||
|
@ -4285,6 +4293,15 @@ react-dom-factories@^1.0.1:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.2.tgz#eb7705c4db36fb501b3aa38ff759616aa0ff96e0"
|
||||
|
||||
react-dom@16.3.1:
|
||||
version "16.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.1.tgz#6a3c90a4fb62f915bdbcf6204422d93a7d4ca573"
|
||||
dependencies:
|
||||
fbjs "^0.8.16"
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react-dom@^15.5.4:
|
||||
version "15.6.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470"
|
||||
|
@ -4303,6 +4320,15 @@ react-dom@^16.2.0:
|
|||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react@16.3.1:
|
||||
version "16.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.3.1.tgz#4a2da433d471251c69b6033ada30e2ed1202cfd8"
|
||||
dependencies:
|
||||
fbjs "^0.8.16"
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react@^15.5.4:
|
||||
version "15.6.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
|
||||
|
|
Loading…
Reference in New Issue