|
import { linter } from "@codemirror/lint"; |
|
// This is practically the same as CodeMirror's Diagnostic type |
|
import type { Diagnostic } from "app/worker/types"; |
|
import { syntaxTree } from "@codemirror/language"; |
|
import { EditorView } from "@codemirror/view"; |
|
|
|
/** |
|
* Inner method for scanning for bad JSX code, this is exported |
|
* so we can test this code. |
|
*/ |
|
export function scanForBadJSX(view: EditorView) { |
|
let hasPragma = false; |
|
const diagnostics: Diagnostic[] = []; |
|
syntaxTree(view.state) |
|
.cursor() |
|
.iterate((node) => { |
|
// "The JSX pragma needs to be in the leading comments of the module" |
|
// according to the docs, so we should be able to count on this |
|
// BlockComment appearing before the JSXElement |
|
// |
|
// Also, it is is required to be a BlockComment, not single line comment, |
|
// so we only scan for those: |
|
// https://mariusschulz.com/blog/per-file-jsx-factories-in-typescript |
|
if (node.name === "BlockComment") { |
|
const commentContent = view.state.sliceDoc(node.from, node.to); |
|
if (commentContent.includes("@jsxImportSource")) { |
|
hasPragma = true; |
|
} |
|
} |
|
|
|
// The CodeMirror mode outputs multiple nodes within JSX content, |
|
// like JSXAttribute and such, but we only look for JSXElement |
|
// because scanning for all of the elements will produce overlapping |
|
// error annotations. |
|
if (node.name === "JSXElement" && !hasPragma) { |
|
diagnostics.push({ |
|
from: node.from, |
|
to: node.to, |
|
severity: "error", |
|
message: "To use JSX, you need to specify a @jsxImportSource.", |
|
actions: [ |
|
{ |
|
name: "Use React", |
|
apply(view, _from, _to) { |
|
view.dispatch({ |
|
changes: { |
|
from: 0, |
|
insert: `/** @jsxImportSource https://esm.sh/react */\nimport { renderToString } from "npm:react-dom/server";\n\n`, |
|
}, |
|
}); |
|
}, |
|
}, |
|
{ |
|
name: "Use Preact", |
|
apply(view, _from, _to) { |
|
view.dispatch({ |
|
changes: { |
|
from: 0, |
|
insert: `/** @jsxImportSource https://esm.sh/preact */\nimport { render } from "npm:preact-render-to-string";\n\n`, |
|
}, |
|
}); |
|
}, |
|
}, |
|
{ |
|
name: "Use Hono", |
|
apply(view, _from, _to) { |
|
view.dispatch({ |
|
changes: { |
|
from: 0, |
|
insert: `/** @jsxImportSource https://esm.sh/hono@latest/jsx **/\n\n`, |
|
}, |
|
}); |
|
}, |
|
}, |
|
], |
|
}); |
|
} |
|
}); |
|
return diagnostics; |
|
} |
|
|
|
/** |
|
* Look for val code that includes JSX elements, |
|
* but does not include a jsxImportSource pragma, and give |
|
* the end-user a quick way to insert the required pragma. |
|
* |
|
* See Deno docs for more information: |
|
* https://docs.deno.com/runtime/manual/advanced/jsx_dom/jsx |
|
*/ |
|
export const linterJSX = linter((view): readonly Diagnostic[] => { |
|
return scanForBadJSX(view); |
|
}); |