Skip to content

Commit f66cde8

Browse files
committed
Reworking linter
1 parent 2db2680 commit f66cde8

File tree

7 files changed

+266
-230
lines changed

7 files changed

+266
-230
lines changed

Diff for: README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ The missile knows where it is at all times. It knows this, because it has a debu
1111
- [`gitbook/` - GitBook integration](#gitbook-integration)
1212
- [`web/` - the main implementation](#web)
1313

14+
## Deployment
15+
16+
> TODO: A guide + separate the content yaml from the code
17+
18+
You're more than welcome to fork this project for your own use.
19+
This repository is setup to be automatically deployed to GitHub Pages for every change made on the main branch.
20+
1421
## Development
1522

1623
This project uses `pnpm`, run `pnpm i` to install dependencies for all sub-projects.
@@ -30,7 +37,7 @@ Refreshing the browser seems to be manual though.
3037
#### Releasing
3138

3239
To release the integration to the org:
33-
0.
40+
3441
1. run `pnpm run publish` (**NOT** ~~`pnpm publish`~~)
3542

3643
### Web

Diff for: web/src/lib/content.ts

-61
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import contentRaw from "$content/guide.yaml";
2-
import YAML from "yaml";
32

43
export const CONTENT = contentRaw as Content;
54

@@ -11,63 +10,3 @@ export interface Step {
1110
}
1211

1312
export type Content = Record<string, Step>;
14-
15-
// TODO: An editor/validator that would use this?
16-
/** Validates content and returns an error or undefined. */
17-
export function getContentError(content: string): string | undefined {
18-
let data: Content;
19-
20-
try {
21-
data = YAML.parse(content);
22-
} catch (e) {
23-
// Return yaml error as string
24-
if (e instanceof YAML.YAMLError) {
25-
return e.message;
26-
}
27-
// re-throw anything else
28-
throw e;
29-
}
30-
31-
try {
32-
validateData(data);
33-
} catch (e) {
34-
if (typeof e === "string") return e;
35-
throw e;
36-
}
37-
}
38-
39-
function validateData(data: Content) {
40-
if (typeof data !== "object" || Array.isArray(data)) {
41-
throw "Content must be object!";
42-
}
43-
44-
if (!ensure(data, "start")) throw 'Missing "start" step!';
45-
46-
// validate steps
47-
for (const [name, step] of Object.entries(data)) {
48-
if (!("title" in step) || !step.title) {
49-
throw `Step '${name}' is missing a title!`;
50-
}
51-
52-
// validate each option if any
53-
if ("options" in step && step.options) {
54-
for (let i = 0; i < step.options.length; i++) {
55-
const option = step.options[i];
56-
57-
if (!ensure(option, "label")) {
58-
throw `Option ${i} of step '${name}' is missing a label!`;
59-
}
60-
if (!ensure(option, "target")) {
61-
throw `Option ${i} ('${option.label}') of step '${name}' is missing a target!`;
62-
}
63-
if (!ensure(data, option.target)) {
64-
throw `Option ${i} ('${option.label}') of step '${name}' is targeting step '${option.target}', which does not exist!`;
65-
}
66-
}
67-
}
68-
}
69-
}
70-
71-
function ensure(container: Record<string, unknown>, what: string) {
72-
return what in container && !!container[what];
73-
}

Diff for: web/src/modes/editor/CodeMirror.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface CodeMirrorProps {
1111
initialContent?: string;
1212
showStep?: string;
1313
onChange?: (content: string) => void;
14+
onParsed?: (content: unknown) => void;
1415
}
1516

1617
export function CodeMirror(props: CodeMirrorProps) {
@@ -20,7 +21,7 @@ export function CodeMirror(props: CodeMirrorProps) {
2021
basicSetup,
2122
oneDark,
2223
yaml(),
23-
customLinter,
24+
customLinter((c) => props.onParsed?.(c[0])),
2425
lintGutter(),
2526
keymap.of([indentWithTab]),
2627
ViewPlugin.define((view) => ({

Diff for: web/src/modes/editor/Editor.tsx

+26-7
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,52 @@ import {
77
Suspense,
88
createMemo,
99
} from "solid-js";
10-
import YAML from "yaml";
1110
import { contentToMermaid } from "../graph/mermaid";
1211
import { Button } from "$components/Button";
12+
import { download } from "./util";
13+
import { createScheduled, debounce } from "@solid-primitives/scheduled";
14+
import { Content, CONTENT } from "$lib/content";
1315

1416
import "./editor.css";
15-
import { download } from "./util";
1617

1718
const Mermaid = lazy(() =>
1819
import("../graph/Mermaid").then((m) => ({ default: m.Mermaid }))
1920
);
2021

2122
export function Editor() {
2223
const [showStep, setShowStep] = createSignal<string>();
23-
const [content, setContent] = createSignal(contentRaw);
24-
const mermaid = createMemo(() => contentToMermaid(YAML.parse(content())));
24+
const [content, setContent] = createSignal(CONTENT);
25+
26+
let rawContent = contentRaw;
27+
28+
// Update mermaid with a debounce
29+
const scheduled = createScheduled((fn) => debounce(fn, 1000));
30+
const deferredContent = createMemo<Content>((p) => {
31+
const value = content();
32+
return scheduled() ? value : p;
33+
}, content());
34+
35+
const mermaid = createMemo<string>((last) => {
36+
try {
37+
return contentToMermaid(deferredContent());
38+
} catch (e) {
39+
if (import.meta.env.DEV) console.error(e);
40+
return last;
41+
}
42+
}, "");
2543

2644
return (
2745
<div id="editor" class="full-window">
2846
<CodeMirror
29-
initialContent={content()}
30-
onChange={setContent}
47+
initialContent={contentRaw}
48+
onChange={(c) => (rawContent = c)}
49+
onParsed={setContent}
3150
showStep={showStep()}
3251
/>
3352

3453
<div class="full-window">
3554
<div class="toolbar">
36-
<Button onClick={() => download("guide.yaml", content())}>
55+
<Button onClick={() => download("guide.yaml", rawContent)}>
3756
Download YAML
3857
</Button>
3958
<Button

0 commit comments

Comments
 (0)