Skip to content

Commit

Permalink
feat: implement js playground (#243)
Browse files Browse the repository at this point in the history
* chore: implement state

* refactor

* implement js playground
  • Loading branch information
yeonjuan authored Dec 4, 2024
1 parent 2220b70 commit 8a9492a
Show file tree
Hide file tree
Showing 11 changed files with 528 additions and 242 deletions.
16 changes: 12 additions & 4 deletions packages/website/src/components/playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@
text-decoration: underline dashed red;
}
</style>
<h1 class="text-2xl">Playground</h1>
<div class="flex flex-col gap-2 mt-3 px-2 w-full ">
<div class="flex-1 min-h-[300px] border-[1px] border-slate rounded md:min-w-[400px]">
<h1 class="sr-only">Playground</h1>
<div class="flex flex-col mt-3 px-2 w-full ">
<div class="flex" id="language-tabs">
<div class="py-2 px-4 rounded-t cursor-pointer" data-language="html">
<h2>HTML</h2>
</div>
<div class="p-2 py-2 px-4 rounded-t cursor-pointer" data-language="javascript">
<h2>JavaScript </h2>
</div>
</div>
<div class="flex-1 min-h-[300px] border-[1px] border-slate rounded md:min-w-[400px] mb-2">
<textarea id="code-editor"></textarea>
</div>
<div class="flex flex-col md:flex-row flex-1 h-[400px] gap-2">
Expand Down Expand Up @@ -42,7 +50,7 @@ <h1 class="text-2xl">Playground</h1>
</div>
</div>
</div>
<script type="module" src="~/src/scripts/playground/main.js"></script>
<script type="module" src="~/src/scripts/playground/app.js"></script>
<script>
const $tabs = document.getElementById("tabs");
$tabs.addEventListener("click", (e) => {
Expand Down
103 changes: 103 additions & 0 deletions packages/website/src/scripts/playground/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @typedef {import("codemirror").Editor} Editor
*/
import { getInitial } from "./helpers";
import { Model } from "./model";
import { View } from "./view";

const INITIAL_HTML = /* html */ `<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
<li> foo </li>
</div>
</body>
</html>
`;

const INITAIL_CONFIG = JSON.stringify(
{ rules: { "@html-eslint/indent": "error" } },
null,
2
);

class App {
constructor() {
this.view = new View();
this.model = new Model();
this.model.on("lint", () => this.handleLint());
this.model.on("changeLanguage", () => this.handleLanguageChange());
this.view.codeEditor.on("change", (editor) =>
this.handleCodeChange(editor)
);
this.view.configEditor.on("change", (editor) => {
this.handleConfigChange(editor);
});
this.view.$languageTabs.addEventListener("click", (event) => {
const $tab = event.target.closest("[data-language]");
this.model.setLanguage($tab.dataset.language);
});
}

start() {
const decoded = decodeURIComponent(
escape(window.atob(window.location.hash.substring(1)))
);
this.model.load(decoded);

this.view.codeEditor.setValue(this.model.getCode());
this.view.configEditor.setValue(
JSON.stringify({ rules: this.model.rules }, null, 2)
);
this.handleCodeChange(this.view.codeEditor);
this.handleConfigChange(this.view.configEditor);
this.handleLanguageChange();
}

/**
* @param {Editor} editor
*/
handleConfigChange(editor) {
try {
const rules = JSON.parse(editor.getValue()).rules;
this.model.setRules(rules);
this.model.lint();
} catch (e) {
this.view.renderErrors([
{
line: 0,
column: 0,
message: e.message,
fatal: true,
},
]);
}
}

/**
* @param {Editor} editor
*/
handleCodeChange(editor) {
this.model.setCode(editor.getValue());
this.model.lint();
}

handleLint() {
this.view.renderErrors(this.model.messages);
this.view.renderFixed(this.model.fixed);
const hash = this.model.toHash();
window.location.hash = hash;
}

handleLanguageChange() {
this.view.renderLanguageTabs(this.model.language.value);
this.view.codeEditor.setOption("mode", this.model.language.mime());
this.view.codeEditor.setValue(this.model.getCode());
this.model.lint();
}
}

const app = new App();
app.start();
41 changes: 0 additions & 41 deletions packages/website/src/scripts/playground/constants.js

This file was deleted.

24 changes: 0 additions & 24 deletions packages/website/src/scripts/playground/hash-store.js

This file was deleted.

72 changes: 72 additions & 0 deletions packages/website/src/scripts/playground/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @typedef {import('eslint').Linter.LintMessage} LintMessage
* @typedef {import("codemirror").Position} Position
*/

import { Language } from "./language";

/**
* @param {number} pos
* @returns {number}
*/
function toMarkerPos(pos) {
return pos - 1;
}

/**
* @param {LintMessage} message
* @returns {[Position, Position]}
*/
export function toMarker(message) {
const from = {
line: toMarkerPos(message.line),
ch: toMarkerPos(message.column),
};
const to = {
line: toMarkerPos(message.endLine || message.line),
ch: toMarkerPos(message.endColumn || message.column),
};
return [from, to];
}

export const INITIAL_HTML = /* html */ `<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
<li> foo </li>
</div>
</body>
</html>
`;

export const INITIAL_JAVASCRIPT = `html\`
<div>
<span>
</span>
</div>
\`
const html = /*html*/\`
<div>
<span>
</span>
</div>
\`;`;

export const INITAIL_CONFIG = JSON.stringify(
{ rules: { "@html-eslint/indent": "error" } },
null,
2
);

/**
* @param {string} language
*/
export function getInitialCode(language) {
if (language === "javascript") {
return INITIAL_JAVASCRIPT;
}
return INITIAL_HTML;
}
12 changes: 12 additions & 0 deletions packages/website/src/scripts/playground/language.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class Language {
/**
* @param {"html" | "javascript"} language
*/
constructor(language) {
this.value = language;
}

mime() {
return `text/${this.value}`;
}
}
19 changes: 15 additions & 4 deletions packages/website/src/scripts/playground/linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function allRules() {
);
}

export default class Linter {
export class Linter {
constructor() {
/**
* @type {ESLinter}
Expand Down Expand Up @@ -66,23 +66,34 @@ export default class Linter {
});
}

getParser(language) {
if (language === "js") {
return undefined;
}
return "@html-eslint/parser";
}

/**
* Lints the given code and returns the result.
* @param {string} code
* @param {string} language
* @param {boolean?} fix
* @returns {{messages: LintMessage[], output: string, fatalMessage?: LintMessage}}
*/
lint(code, fix = false) {
lint(code, language, fix = false) {
try {
let fatalMessage;
const messages = this._linter.verify(code, {
parser: "@html-eslint/parser",
parser: this.getParser(language),
rules: this._rules,
parserOptions: {
ecmaVersion: "latest",
},
});
const { output } = this._linter.verifyAndFix(
code,
{
parser: "@html-eslint/parser",
parser: this.getParser(language),
rules: this._rules,
},
{ fix }
Expand Down
Loading

0 comments on commit 8a9492a

Please sign in to comment.