diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 10b7e8dd..cb3793fe 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,15 +1,28 @@ - + -```js +**Prettier 1.6.1** +[Playground link](https://prettier.io/playground/#.....) +```sh +# Options (if any): +--single-quote +``` + +**Input:** +```jsx +// code snippet +``` + +**Output:** +```jsx // code snippet ``` **Expected behavior:** - -**Actual behavior:** \ No newline at end of file diff --git a/scripts/build/build.js b/scripts/build/build.js index 106f3921..4a321a57 100755 --- a/scripts/build/build.js +++ b/scripts/build/build.js @@ -4,6 +4,7 @@ const path = require("path"); const pkg = require("../../package.json"); +const formatMarkdown = require("../../website/static/markdown"); const shell = require("shelljs"); const rootDir = path.join(__dirname, "..", ".."); @@ -99,15 +100,22 @@ shell.sed( ); shell.echo("Update ISSUE_TEMPLATE.md"); -shell.sed( - "-i", - /(Prettier Version.+?)\d+\.\d+\.\d+/, - `$1${pkg.version}`, - ".github/ISSUE_TEMPLATE.md" +const issueTemplate = shell.cat(".github/ISSUE_TEMPLATE.md").stdout; +const newIssueTemplate = issueTemplate.replace( + /-->[^]*$/, + "-->\n\n" + + formatMarkdown( + "// code snippet", + "// code snippet", + "", + pkg.version, + "https://prettier.io/playground/#.....", + { parser: "babylon" }, + [["# Options (if any):", true], ["--single-quote", true]], + true + ) ); - -shell.echo("Create prettier-version.js"); -pipe(`prettierVersion = "${pkg.version}";\n`).to(`${docs}/prettier-version.js`); +pipe(newIssueTemplate).to(".github/ISSUE_TEMPLATE.md"); shell.echo("Copy sw-toolbox.js to docs"); shell.cp("node_modules/sw-toolbox/sw-toolbox.js", `${docs}/sw-toolbox.js`); diff --git a/website/pages/playground/index.html b/website/pages/playground/index.html index 936baa6b..72f52b53 100644 --- a/website/pages/playground/index.html +++ b/website/pages/playground/index.html @@ -1,5 +1,5 @@ - + @@ -11,8 +11,8 @@ - - + + @@ -21,13 +21,15 @@ - - + + + + @@ -35,93 +37,79 @@ +
- - -

Prettier

+
+ +

Prettier

@@ -231,59 +315,73 @@
-
+
-
- +
+
-
- +
+
-
- +
+
-
- +
+ +
+
+
-
+ -
-
+
+
+ +
+ +
Options
- - + +
- - - - - + + + + +
- - + + +
-
- + + diff --git a/website/static/lib/prettier-version.js b/website/static/lib/prettier-version.js deleted file mode 100644 index 4924a180..00000000 --- a/website/static/lib/prettier-version.js +++ /dev/null @@ -1 +0,0 @@ -prettierVersion = "1.6.1"; diff --git a/website/static/markdown.js b/website/static/markdown.js new file mode 100644 index 00000000..4eedfb17 --- /dev/null +++ b/website/static/markdown.js @@ -0,0 +1,87 @@ +/* eslint-env browser */ +/* eslint no-var: off, strict: off, prefer-arrow-callback: off */ + +// NOTE: This file must work both in the playground, and in the build scripts to +// generate the issue template. + +(function() { + function formatMarkdown( + input, + output, + output2, + version, + url, + options, + cliOptions, + full + ) { + var syntax = getMarkdownSyntax(options); + var optionsString = formatCLIOptions(cliOptions); + var isIdempotent = output2 === "" || output === output2; + + return [ + "**Prettier " + version + "**", + "[Playground link](" + url + ")", + optionsString === "" ? null : codeBlock(optionsString, "sh"), + "", + "**Input:**", + codeBlock(input, syntax), + "", + "**Output:**", + codeBlock(output, syntax) + ] + .concat( + isIdempotent + ? [] + : ["", "**Second Output:**", codeBlock(output2, syntax)] + ) + .concat(full ? ["", "**Expected behavior:**", ""] : []) + .filter(function(part) { + return part != null; + }) + .join("\n"); + } + + function getMarkdownSyntax(options) { + switch (options.parser) { + case "babylon": + case "flow": + return "jsx"; + case "typescript": + return "tsx"; + case "postcss": + return "scss"; + default: + return options.parser; + } + } + + function formatCLIOptions(cliOptions) { + return cliOptions + .map(function(option) { + var name = option[0]; + var value = option[1]; + return value === true ? name : name + " " + value; + }) + .join("\n"); + } + + function codeBlock(content, syntax) { + var backtickSequences = content.match(/`+/g) || []; + var longestBacktickSequenceLength = Math.max.apply( + null, + backtickSequences.map(function(backticks) { + return backticks.length; + }) + ); + var fenceLength = Math.max(3, longestBacktickSequenceLength + 1); + var fence = Array(fenceLength + 1).join("`"); + return [fence + (syntax || ""), content, fence].join("\n"); + } + + if (typeof module !== "undefined" && module.exports) { + module.exports = formatMarkdown; + } else { + window.formatMarkdown = formatMarkdown; + } +})(); diff --git a/website/static/playground.js b/website/static/playground.js index 33ee0162..9f53dd22 100644 --- a/website/static/playground.js +++ b/website/static/playground.js @@ -1,6 +1,30 @@ /* eslint-env browser */ /* eslint no-var: off, strict: off, prefer-arrow-callback: off */ -/* global CodeMirror prettierVersion */ +/* global Clipboard CodeMirror formatMarkdown */ + +var prettierVersion = "?"; +var inputEditor; +var docEditor; +var astEditor; +var outputEditor; +var output2Editor; + +var OPTIONS = [ + "printWidth", + "tabWidth", + "singleQuote", + "trailingComma", + "bracketSpacing", + "jsxBracketSameLine", + "parser", + "semi", + "useTabs", + "doc", + "ast", + "output2" +]; + +var IDEMPOTENT_MESSAGE = "✓ Second format is unchanged."; var state = (function loadState(hash) { try { @@ -8,147 +32,278 @@ var state = (function loadState(hash) { } catch (error) { return { options: undefined, - content: - 'hello ( "world"\n);\n\n' + - '[ "lorem", "ipsum", \'dolor\', sit("amet"), consectetur[ \'adipiscing\' ] + "elit" ].reduce(\n (first, second) => first + second,\n "")\n\n' + - "const Foo = ({ bar, baz, things }) => {\n" + - ' return
\n' + - "
{things.map(thing => reallyLongPleaseDontPutOnOneLine(thing) ?

{ok}

: )\n" + - " }
}" + content: [ + '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
", + "", + " { greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }", + ' {greeting.endsWith(",") ? " " : ", " }', + " ", + "\t{ greeted }", + "\t", + " { (silent)", + ' ? "."', + ' : "!"}', + "", + "
;", + "", + "}" + ].join("\n") }; } })(decodeURIComponent(location.hash.slice(1))); var worker = new Worker("/worker.js"); + +worker.onmessage = function(message) { + if (prettierVersion === "?") { + prettierVersion = message.data.version; + document.getElementById("version").textContent = prettierVersion; + } + if (outputEditor && docEditor && astEditor) { + outputEditor.setValue(message.data.formatted); + docEditor.setValue(message.data.doc || ""); + astEditor.setValue(message.data.ast || ""); + output2Editor.setValue( + message.data.formatted === "" + ? "" + : message.data.formatted2 === message.data.formatted + ? IDEMPOTENT_MESSAGE + : message.data.formatted2 || "" + ); + document.getElementById("button-report-issue").search = + "body=" + encodeURIComponent(createMarkdown(true)); + } +}; + // Warm up the worker (load the current parser while CodeMirror loads) worker.postMessage({ text: "", options: state.options }); window.onload = function() { - var OPTIONS = [ - "printWidth", - "tabWidth", - "singleQuote", - "trailingComma", - "bracketSpacing", - "jsxBracketSameLine", - "parser", - "semi", - "useTabs", - "doc", - "ast" - ]; state.options && setOptions(state.options); - function setOptions(options) { - OPTIONS.forEach(function(option) { - var elem = document.getElementById(option); - if (elem.tagName === "SELECT") { - elem.value = options[option]; - } else if (elem.type === "number") { - elem.value = options[option]; - } else { - var isInverted = elem.hasAttribute("data-inverted"); - elem.checked = isInverted ? !options[option] : options[option]; - } - }); - } - - function getOptions() { - var options = {}; - OPTIONS.forEach(function(option) { - var elem = document.getElementById(option); - if (elem.tagName === "SELECT") { - options[option] = elem.value; - } else if (elem.type === "number") { - options[option] = Number(elem.value); - } else { - var isInverted = elem.hasAttribute("data-inverted"); - options[option] = isInverted ? !elem.checked : elem.checked; - } - }); - return options; - } - - function replaceHash(hash) { - 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 formatAsync() { - var options = getOptions(); - - outputEditor.setOption("rulers", [ - { column: options.printWidth, color: "#444444" } - ]); - document.querySelector(".ast").style.display = options.ast - ? "flex" - : "none"; - document.querySelector(".doc").style.display = options.doc - ? "flex" - : "none"; - - var value = encodeURIComponent( - JSON.stringify( - Object.assign({ content: inputEditor.getValue(), options: options }) - ) - ); - replaceHash(value); - worker.postMessage({ - text: inputEditor.getValue(), - options: options, - ast: options.ast, - doc: options.doc - }); - } - var editorOptions = { lineNumbers: true, keyMap: "sublime", autoCloseBrackets: true, matchBrackets: true, showCursorWhenSelecting: true, - theme: "neat", - tabWidth: 2 + tabWidth: 2, + mode: "jsx" }; - var inputEditor = CodeMirror.fromTextArea( + inputEditor = CodeMirror.fromTextArea( document.getElementById("input-editor"), editorOptions ); inputEditor.on("change", formatAsync); - var docEditor = CodeMirror.fromTextArea( - document.getElementById("doc-editor"), - { readOnly: true, lineNumbers: false, theme: "neat" } - ); - var astEditor = CodeMirror.fromTextArea( - document.getElementById("ast-editor"), - { readOnly: true, lineNumbers: false, theme: "neat" } - ); - var outputEditor = CodeMirror.fromTextArea( - document.getElementById("output-editor"), - { readOnly: true, lineNumbers: true, theme: "neat" } - ); - - Array.from(document.querySelectorAll("textarea")).forEach(function(element) { - element.classList.remove("loading"); + docEditor = CodeMirror.fromTextArea(document.getElementById("doc-editor"), { + readOnly: true, + lineNumbers: false, + mode: "jsx" }); + astEditor = CodeMirror.fromTextArea(document.getElementById("ast-editor"), { + readOnly: true, + lineNumbers: false, + mode: "jsx" + }); + outputEditor = CodeMirror.fromTextArea( + document.getElementById("output-editor"), + { + readOnly: true, + lineNumbers: true, + mode: "jsx" + } + ); + output2Editor = CodeMirror.fromTextArea( + document.getElementById("output2-editor"), + { + readOnly: true, + lineNumbers: true, + mode: "jsx" + } + ); - worker.onmessage = function(message) { - outputEditor.setValue(message.data.formatted); - docEditor.setValue(message.data.doc || ""); - astEditor.setValue(message.data.ast || ""); - }; + var editors = document.querySelectorAll(".editor"); + for (var i = 0; i < editors.length; i++) { + editors[i].classList.remove("loading"); + } inputEditor.setValue(state.content); document.querySelector(".options-container").onchange = formatAsync; - document.querySelector(".version").innerText = prettierVersion; + + document.getElementById("button-clear").onclick = function() { + inputEditor.setValue(""); + }; + + var clipboard = new Clipboard("#button-copy-link, #button-copy-markdown", { + text: function(trigger) { + switch (trigger.id) { + case "button-copy-link": + return window.location.href; + case "button-copy-markdown": + return createMarkdown(); + default: + return ""; + } + } + }); + clipboard.on("success", function(e) { + showTooltip(e.trigger, "Copied!"); + }); + clipboard.on("error", function(e) { + showTooltip(e.trigger, "Press ctrl+c to copy"); + }); }; + +function setOptions(options) { + OPTIONS.forEach(function(option) { + var elem = document.getElementById(option); + if (elem.tagName === "SELECT") { + elem.value = options[option]; + } else if (elem.type === "number") { + elem.value = options[option]; + } else { + var isInverted = elem.hasAttribute("data-inverted"); + elem.checked = isInverted ? !options[option] : options[option]; + } + }); +} + +function getOptions() { + var options = {}; + OPTIONS.forEach(function(option) { + var elem = document.getElementById(option); + if (elem.tagName === "SELECT") { + options[option] = elem.value; + } else if (elem.type === "number") { + options[option] = Number(elem.value); + } else { + var isInverted = elem.hasAttribute("data-inverted"); + options[option] = isInverted ? !elem.checked : elem.checked; + } + }); + return options; +} + +function getCLIOptions() { + return OPTIONS.sort() + .map(function(option) { + var elem = document.getElementById(option); + var match = elem.parentNode.textContent.match(/--\S+/); + if (!match) { + return null; + } + var name = match[0]; + if (elem.tagName === "SELECT") { + if (elem.value === elem.options[0].value) { + return null; + } + return [name, elem.value]; + } else if (elem.type === "number") { + if (elem.value === elem.getAttribute("value")) { + return null; + } + return [name, elem.value]; + } else if (elem.type === "checkbox") { + if (!elem.checked) { + return null; + } + return [name, true]; + } + return null; + }) + .filter(Boolean); +} + +function replaceHash(hash) { + 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 getCodemirrorMode(options) { + switch (options.parser) { + case "postcss": + return "css"; + default: + return "jsx"; + } +} + +function formatAsync() { + var options = getOptions(); + + var mode = getCodemirrorMode(options); + inputEditor.setOption("mode", mode); + outputEditor.setOption("mode", mode); + output2Editor.setOption("mode", mode); + + [outputEditor, output2Editor].forEach(function(editor) { + editor.setOption("rulers", [ + { column: options.printWidth, color: "#444444" } + ]); + }); + document.querySelector(".ast").style.display = options.ast ? "" : "none"; + document.querySelector(".doc").style.display = options.doc ? "" : "none"; + document.querySelector(".output2").style.display = options.output2 + ? "" + : "none"; + + var value = encodeURIComponent( + JSON.stringify( + Object.assign({ content: inputEditor.getValue(), options: options }) + ) + ); + replaceHash(value); + worker.postMessage({ + text: inputEditor.getValue(), + options: options, + ast: options.ast, + doc: options.doc, + formatted2: options.output2 + }); +} + +function createMarkdown(full) { + var input = inputEditor.getValue(); + var output = outputEditor.getValue(); + var output2 = output2Editor.getValue(); + var options = getOptions(); + var cliOptions = getCLIOptions(); + var markdown = formatMarkdown( + input, + output, + output2 === IDEMPOTENT_MESSAGE ? "" : output2, + prettierVersion, + window.location.href, + options, + cliOptions, + full + ); + return markdown; +} + +function showTooltip(elem, text) { + var tooltip = document.createElement("span"); + tooltip.className = "tooltip"; + tooltip.textContent = text; + elem.appendChild(tooltip); + window.setTimeout(function() { + elem.removeChild(tooltip); + }, 2000); +} diff --git a/website/static/service-worker.js b/website/static/service-worker.js index b6ee9ec5..6dcc9bcc 100644 --- a/website/static/service-worker.js +++ b/website/static/service-worker.js @@ -7,22 +7,24 @@ importScripts("lib/sw-toolbox.js"); toolbox.precache([ // Scripts - "lib/prettier-version.js", "lib/index.js", "lib/parser-babylon.js", "lib/parser-typescript.js", "lib/parser-postcss.js", "lib/parser-flow.js", "lib/parser-graphql.js", + "markdown.js", "playground.js", "lib/sw-toolbox.js", "lib/sw-toolbox-companion.js", // CodeMirror "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/codemirror.css", - "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/theme/neat.css", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/codemirror.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/mode/javascript/javascript.js", + "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/mode/xml/xml.js", + "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/mode/jsx/jsx.js", + "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/mode/css/css.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/addon/display/rulers.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/addon/search/searchcursor.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/addon/edit/matchbrackets.js", @@ -30,6 +32,7 @@ toolbox.precache([ "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/addon/comment/comment.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/addon/wrap/hardwrap.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/keymap/sublime.js", + "https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js", // Images "/prettier.png" diff --git a/website/static/worker.js b/website/static/worker.js index 36d764a7..ac67e8ba 100644 --- a/website/static/worker.js +++ b/website/static/worker.js @@ -27,20 +27,29 @@ self.onmessage = function(message) { delete options.ast; delete options.doc; + delete options.output2; var formatted = formatCode(message.data.text, options); var doc; var ast; + var formatted2; if (message.data.ast) { + var actualAst; + var errored = false; try { - ast = JSON.stringify( - prettier.__debug.parse(message.data.text, options), - null, - 2 - ); + actualAst = prettier.__debug.parse(message.data.text, options); + ast = JSON.stringify(actualAst); } catch (e) { - ast = e.toString(); + errored = true; + ast = String(e); + } + if (!errored) { + try { + ast = formatCode(ast, { parser: "json" }); + } catch (e) { + ast = JSON.stringify(actualAst, null, 2); + } } } @@ -52,11 +61,21 @@ self.onmessage = function(message) { { parser: "babylon" } ); } catch (e) { - doc = e.toString(); + doc = String(e); } } - self.postMessage({ formatted: formatted, doc: doc, ast: ast }); + 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) { @@ -70,7 +89,7 @@ function formatCode(text, options) { lazyLoadParser(e.parser); return formatCode(text, options); } - return e.toString(); + return String(e); } }