-
Notifications
You must be signed in to change notification settings - Fork 11
Prettier enhancements: handle more file types & improve loading states #184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { html } from '../utils/rplus.js'; | ||
|
||
// Taken from https://material.io/resources/icons/?search=check&icon=check_circle_outline&style=baseline | ||
// Modified with a `fill="white"` | ||
|
||
export default html` | ||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
<path d="M0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" /> | ||
<path | ||
fill="white" | ||
d="M16.59 7.58L10 14.17l-3.59-3.58L5 12l5 5 8-8zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" | ||
/> | ||
</svg> | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { html } from '../utils/rplus.js'; | ||
|
||
// Taken from https://material.io/resources/icons/?search=error&icon=error_outline&style=baseline | ||
// Modified with a `fill="white"` | ||
|
||
export default html` | ||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
<path d="M0 0h24v24H0V0z" fill="none" /> | ||
<path | ||
fill="white" | ||
d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" | ||
/> | ||
</svg> | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { html } from '../utils/rplus.js'; | ||
|
||
/** | ||
* `loader` from https://feathericons.com/ | ||
* animateTransform adapted from https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animateTransform | ||
* | ||
* NOTE: This will not actually animate in its current usage as it gets blocked by the synchronous prettier format function | ||
* May be able to use Web Worker... | ||
*/ | ||
|
||
export default html` | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="24" | ||
height="24" | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
stroke="currentColor" | ||
stroke-width="2" | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
class="feather feather-loader" | ||
> | ||
<line x1="12" y1="2" x2="12" y2="6"></line> | ||
<line x1="12" y1="18" x2="12" y2="22"></line> | ||
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line> | ||
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line> | ||
<line x1="2" y1="12" x2="6" y2="12"></line> | ||
<line x1="18" y1="12" x2="22" y2="12"></line> | ||
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line> | ||
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line> | ||
<animateTransform | ||
attributeName="transform" | ||
attributeType="XML" | ||
type="rotate" | ||
from="0" | ||
to="360" | ||
dur="2.5s" | ||
repeatCount="indefinite" | ||
/> | ||
</svg> | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/* global prettier, prettierPlugins */ | ||
import PrettierIcon from '../components/PrettierIcon.js'; | ||
import CheckMarkIcon from '../components/PrettierCheckMarkIcon.js'; | ||
import LoadingIcon from '../components/PrettierLoadingIcon.js'; | ||
import ErrorIcon from '../components/PrettierErrorIcon.js'; | ||
|
||
/** | ||
* UTILS | ||
*/ | ||
|
||
const getFinalExtension = ext => | ||
ext ? ext.slice(ext.lastIndexOf('.') + 1) : ''; // Also works when no `.` present in string | ||
|
||
/** | ||
* STATE SETUP | ||
*/ | ||
|
||
const prettierButtonMessages = { | ||
before: 'Format Code', | ||
during: 'Formatting...', | ||
after: 'Formatted', | ||
error: 'Formatting Failed', | ||
}; | ||
|
||
const beforePrettierState = { | ||
message: prettierButtonMessages.before, | ||
disabled: false, // redundant? | ||
icon: PrettierIcon, | ||
}; | ||
|
||
const duringPrettierState = { | ||
message: prettierButtonMessages.during, | ||
disabled: true, | ||
icon: LoadingIcon, | ||
}; | ||
|
||
const afterPrettierState = { | ||
message: prettierButtonMessages.after, | ||
disabled: true, | ||
icon: CheckMarkIcon, | ||
}; | ||
|
||
const errorPrettierState = { | ||
message: prettierButtonMessages.error, | ||
disabled: true, | ||
icon: ErrorIcon, | ||
}; | ||
|
||
const cannotPrettierState = { | ||
hidden: true, | ||
icon: null, | ||
}; | ||
|
||
const pickInitialPrettierState = requestFile => { | ||
if (!prettierParserMap[getFinalExtension(requestFile)]) { | ||
return cannotPrettierState; | ||
} | ||
return beforePrettierState; | ||
}; | ||
|
||
/** | ||
* HANDLING OF VARIOUS FILE TYPES | ||
* `parserScriptUrl`s from https://unpkg.com/browse/prettier@1.13.0/ | ||
* `parserName`s from https://prettier.io/docs/en/options.html#parser | ||
*/ | ||
|
||
const prettierParserMap = { | ||
css: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-postcss.js', | ||
parserName: 'css', | ||
}, | ||
js: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-babylon.js', | ||
parserName: 'babylon', | ||
}, | ||
json: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-babylon.js', | ||
parserName: 'json', | ||
}, | ||
// TODO: uncomment markdown entry after addressing issue no. 181 | ||
// https://github.com/FormidableLabs/runpkg/issues/181 | ||
// md: { | ||
// parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-markdown.js', | ||
// parserName: 'markdown', | ||
// }, | ||
less: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-postcss.js', | ||
parserName: 'css', | ||
}, | ||
map: { | ||
// e.g. `vuetify@2.2.15/lib/components/VDatePicker/VDatePickerDateTable.js.map` | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-babylon.js', | ||
parserName: 'json', | ||
}, | ||
mjs: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-babylon.js', | ||
parserName: 'babylon', | ||
}, | ||
sass: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-postcss.js', | ||
parserName: 'css', | ||
}, | ||
scss: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-postcss.js', | ||
parserName: 'css', | ||
}, | ||
ts: { | ||
parserScriptUrl: 'https://unpkg.com/prettier@1.13.0/parser-typescript.js', | ||
parserName: 'typescript', | ||
}, | ||
}; | ||
|
||
/** | ||
* HANDLING PRETTIER FORMAT BUTTON PRESS | ||
*/ | ||
|
||
const loadPrettierParserScriptForExtension = ({ | ||
fileData, | ||
setPrettierButtonState, | ||
dispatch, | ||
request, | ||
}) => { | ||
const trueExt = getFinalExtension(fileData.extension); | ||
const parserLangConfig = prettierParserMap[trueExt]; | ||
|
||
if (!parserLangConfig) { | ||
// This *should* never happen thanks to `pickInitialPrettierState` | ||
console.error( | ||
'File extension not supported. Prettier button should be hidden/disabled' | ||
); | ||
setPrettierButtonState(cannotPrettierState); | ||
return; | ||
} | ||
|
||
const scriptUrl = parserLangConfig.parserScriptUrl; | ||
|
||
const isScriptAlreadyPresent = [ | ||
...document.querySelectorAll('head script'), | ||
].some(script => script.src === scriptUrl); | ||
|
||
const tryToPrettify = () => { | ||
try { | ||
const code = prettier.format(fileData.code, { | ||
parser: parserLangConfig.parserName, | ||
plugins: prettierPlugins, | ||
}); | ||
dispatch({ | ||
type: 'setCache', | ||
payload: { | ||
['https://unpkg.com/' + request.path]: { | ||
...fileData, | ||
code, | ||
}, | ||
}, | ||
}); | ||
setPrettierButtonState(afterPrettierState); | ||
} catch (e) { | ||
console.error(e); | ||
setPrettierButtonState(errorPrettierState); | ||
Comment on lines
+156
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💭 put the button state setting in the UI component? better separation of concerns? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💭 right after the call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm 50/50 about this! On one hand it could be nice to keep the Open to going the route you suggest though 🤔. Might see what others think |
||
} | ||
}; | ||
|
||
if (isScriptAlreadyPresent) { | ||
tryToPrettify(); | ||
return; | ||
} | ||
|
||
// If we have have a parser for this extension and the script isn't already imported... | ||
const parserScript = document.createElement('script'); | ||
parserScript.src = scriptUrl; | ||
parserScript.onload = () => { | ||
tryToPrettify(); | ||
}; | ||
|
||
document.head.appendChild(parserScript); | ||
}; | ||
|
||
export { | ||
prettierButtonMessages, | ||
duringPrettierState, | ||
errorPrettierState, | ||
loadPrettierParserScriptForExtension, | ||
pickInitialPrettierState, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🆒 way to handle the UI states
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤗 Yeah I prefer the neatness of having these separated out clearly! Probably influenced by TS enums and Redux