Skip to content

Commit 65e9b5d

Browse files
committed
Better error handling
1 parent 948cf43 commit 65e9b5d

File tree

8 files changed

+170
-112
lines changed

8 files changed

+170
-112
lines changed

.changeset/sixty-otters-shop.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@code-hike/lighter": patch
3+
---
4+
5+
Better error handling

lib/dist/index.cjs.js

+37-28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/dist/index.esm.mjs

+37-28
Original file line numberDiff line numberDiff line change
@@ -2602,9 +2602,11 @@ async function fetchJSON(endpoint) {
26022602
}
26032603

26042604
const sourceToGrammarPromise = new Map();
2605-
let shoudUseFileSystem = true;
26062605

2607-
function loadGrammarByScope(scope) {
2606+
let shouldUseFileSystemPromise = undefined;
2607+
let shouldUseFileSystem = undefined;
2608+
2609+
async function loadGrammarByScope(scope) {
26082610
if (sourceToGrammarPromise.has(scope)) {
26092611
return sourceToGrammarPromise.get(scope);
26102612
}
@@ -2614,34 +2616,41 @@ function loadGrammarByScope(scope) {
26142616
return Promise.resolve(undefined);
26152617
}
26162618

2617-
let promise;
2618-
if (shoudUseFileSystem) {
2619-
try {
2620-
// TODO this is not failing here, because it's async
2621-
promise = loadGrammarFromFile(lang);
2622-
} catch (e) {
2623-
shoudUseFileSystem = false;
2624-
}
2619+
let grammarPromise;
2620+
2621+
if (shouldUseFileSystemPromise == null) {
2622+
grammarPromise = loadGrammarFromFile(lang);
2623+
shouldUseFileSystemPromise = grammarPromise
2624+
.then(() => true)
2625+
.catch(() => false);
26252626
}
26262627

2627-
if (!shoudUseFileSystem) {
2628-
const deps = getLanguagesToLoad(lang.id);
2629-
console.log("loading from network", lang.id);
2630-
const fetchPromise = fetchJSON(`grammars?lang=${lang.id}`);
2631-
deps.forEach((l) => {
2632-
if (!sourceToGrammarPromise.has(l.scopeName)) {
2633-
const subPromise = fetchPromise.then((gs) =>
2634-
gs.find((g) => g.scopeName === l.scopeName)
2635-
);
2636-
sourceToGrammarPromise.set(l.scopeName, subPromise);
2637-
}
2638-
});
2628+
if (shouldUseFileSystem == null) {
2629+
shouldUseFileSystem = await shouldUseFileSystemPromise;
2630+
}
26392631

2640-
promise = fetchPromise.then((gs) =>
2641-
gs.find((g) => g.scopeName === lang.scopeName)
2642-
);
2632+
if (shouldUseFileSystem) {
2633+
const promise = grammarPromise || loadGrammarFromFile(lang);
2634+
sourceToGrammarPromise.set(scope, promise);
2635+
return promise;
26432636
}
26442637

2638+
// if (!shouldUseFileSystem)
2639+
const deps = getLanguagesToLoad(lang.id);
2640+
console.log("loading from network", lang.id);
2641+
const fetchPromise = fetchJSON(`grammars?lang=${lang.id}`);
2642+
deps.forEach((l) => {
2643+
if (!sourceToGrammarPromise.has(l.scopeName)) {
2644+
const subPromise = fetchPromise.then((gs) =>
2645+
gs.find((g) => g.scopeName === l.scopeName)
2646+
);
2647+
sourceToGrammarPromise.set(l.scopeName, subPromise);
2648+
}
2649+
});
2650+
2651+
const promise = fetchPromise.then((gs) =>
2652+
gs.find((g) => g.scopeName === lang.scopeName)
2653+
);
26452654
sourceToGrammarPromise.set(scope, promise);
26462655
return promise;
26472656
}
@@ -2911,14 +2920,14 @@ async function loadTheme(name) {
29112920
}
29122921

29132922
async function highlight(code, lang, theme = "dark-plus") {
2923+
const grammarsPromise = loadGrammars(lang);
2924+
29142925
let loadedTheme = theme;
29152926
if (typeof theme === "string") {
2916-
// TODO load theme in parallel with grammars
29172927
loadedTheme = await loadTheme(theme);
29182928
}
2919-
2920-
await loadGrammars(lang);
29212929
const fixedTheme = fixTheme(loadedTheme);
2930+
await grammarsPromise;
29222931
const lines = toTokens(code, lang, fixedTheme);
29232932
return {
29242933
lines,

lib/src/grammars.js

+35-26
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import { languages, idDepsById } from "./languages.mjs";
33
import { fetchJSON } from "./network";
44

55
const sourceToGrammarPromise = new Map();
6-
let shoudUseFileSystem = true;
76

8-
export function loadGrammarByScope(scope) {
7+
let shouldUseFileSystemPromise = undefined;
8+
let shouldUseFileSystem = undefined;
9+
10+
export async function loadGrammarByScope(scope) {
911
if (sourceToGrammarPromise.has(scope)) {
1012
return sourceToGrammarPromise.get(scope);
1113
}
@@ -15,34 +17,41 @@ export function loadGrammarByScope(scope) {
1517
return Promise.resolve(undefined);
1618
}
1719

18-
let promise;
19-
if (shoudUseFileSystem) {
20-
try {
21-
// TODO this is not failing here, because it's async
22-
promise = loadGrammarFromFile(lang);
23-
} catch (e) {
24-
shoudUseFileSystem = false;
25-
}
20+
let grammarPromise;
21+
22+
if (shouldUseFileSystemPromise == null) {
23+
grammarPromise = loadGrammarFromFile(lang);
24+
shouldUseFileSystemPromise = grammarPromise
25+
.then(() => true)
26+
.catch(() => false);
2627
}
2728

28-
if (!shoudUseFileSystem) {
29-
const deps = getLanguagesToLoad(lang.id);
30-
console.log("loading from network", lang.id);
31-
const fetchPromise = fetchJSON(`grammars?lang=${lang.id}`);
32-
deps.forEach((l) => {
33-
if (!sourceToGrammarPromise.has(l.scopeName)) {
34-
const subPromise = fetchPromise.then((gs) =>
35-
gs.find((g) => g.scopeName === l.scopeName)
36-
);
37-
sourceToGrammarPromise.set(l.scopeName, subPromise);
38-
}
39-
});
40-
41-
promise = fetchPromise.then((gs) =>
42-
gs.find((g) => g.scopeName === lang.scopeName)
43-
);
29+
if (shouldUseFileSystem == null) {
30+
shouldUseFileSystem = await shouldUseFileSystemPromise;
4431
}
4532

33+
if (shouldUseFileSystem) {
34+
const promise = grammarPromise || loadGrammarFromFile(lang);
35+
sourceToGrammarPromise.set(scope, promise);
36+
return promise;
37+
}
38+
39+
// if (!shouldUseFileSystem)
40+
const deps = getLanguagesToLoad(lang.id);
41+
console.log("loading from network", lang.id);
42+
const fetchPromise = fetchJSON(`grammars?lang=${lang.id}`);
43+
deps.forEach((l) => {
44+
if (!sourceToGrammarPromise.has(l.scopeName)) {
45+
const subPromise = fetchPromise.then((gs) =>
46+
gs.find((g) => g.scopeName === l.scopeName)
47+
);
48+
sourceToGrammarPromise.set(l.scopeName, subPromise);
49+
}
50+
});
51+
52+
const promise = fetchPromise.then((gs) =>
53+
gs.find((g) => g.scopeName === lang.scopeName)
54+
);
4655
sourceToGrammarPromise.set(scope, promise);
4756
return promise;
4857
}

lib/src/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import { fixTheme } from "./themes";
44
import { loadTheme } from "./load";
55

66
export async function highlight(code, lang, theme = "dark-plus") {
7+
const grammarsPromise = loadGrammars(lang);
8+
79
let loadedTheme = theme;
810
if (typeof theme === "string") {
9-
// TODO load theme in parallel with grammars
1011
loadedTheme = await loadTheme(theme);
1112
}
12-
13-
await loadGrammars(lang);
1413
const fixedTheme = fixTheme(loadedTheme);
14+
await grammarsPromise;
1515
const lines = toTokens(code, lang, fixedTheme);
1616
return {
1717
lines,

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
]
99
},
1010
"scripts": {
11+
"dev": "yarn workspace @code-hike/lighter watch & yarn workspace lighter-web dev",
1112
"changeset": "changeset",
1213
"version": "changeset version",
1314
"build": "yarn workspace @code-hike/lighter build",

web/pages/api/grammars.js

+28-14
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,55 @@ import { NextResponse } from "next/server";
22
import { getLanguagesToLoad } from "../../../lib/src/grammars";
33

44
export const config = {
5-
runtime: "experimental-edge",
5+
runtime: "edge",
66
};
77

8-
// api/grammars?lang=js
8+
// api/grammars?lang=js&v=0.1.8
99
export default async (req) => {
1010
if (req.method === "OPTIONS") {
11-
res.status(200).end();
12-
return;
11+
return new Response(null, { status: 200 });
1312
}
1413

1514
const url = new URL(req.url);
1615
const lang = url.searchParams.get("lang");
1716
const version = url.searchParams.get("v");
1817
console.log("fetching grammars", lang, version);
1918

20-
const languages = getLanguagesToLoad(lang);
19+
try {
20+
const grammars = await getGrammars(lang, version);
21+
const res = NextResponse.json(grammars);
22+
23+
res.headers.set("Cache-Control", "s-maxage=1, stale-while-revalidate");
24+
res.headers.set("Access-Control-Allow-Methods", "GET");
25+
res.headers.set("Access-Control-Allow-Origin", "*");
26+
27+
return res;
28+
} catch (e) {
29+
console.log("error fetching grammars", e);
30+
return new Response(e.message, { status: 500 });
31+
}
32+
};
2133

34+
async function getGrammars(lang, version) {
35+
const languages = getLanguagesToLoad(lang);
2236
const grammars = await Promise.all(
2337
languages.map(async (language) => {
2438
const r = await fetch(
2539
`https://unpkg.com/@code-hike/lighter@${version}/grammars/${language.path}`
2640
);
41+
42+
if (!r.ok) {
43+
throw new Error(
44+
`https://unpkg.com/@code-hike/lighter@${version}/grammars/${language.path} ${r.status} ${r.statusText}`
45+
);
46+
}
47+
2748
const grammar = await r.json();
2849

2950
grammar.names = [language.id, ...(language.aliases || [])];
3051
grammar.embeddedLangs = language.embeddedLangs || [];
3152
return grammar;
3253
})
3354
);
34-
35-
const res = NextResponse.json(grammars);
36-
37-
res.headers.set("Cache-Control", "s-maxage=1, stale-while-revalidate");
38-
res.headers.set("Access-Control-Allow-Methods", "GET");
39-
res.headers.set("Access-Control-Allow-Origin", "*");
40-
41-
return res;
42-
};
55+
return grammars;
56+
}

0 commit comments

Comments
 (0)