Skip to content

Commit e95889d

Browse files
committed
Fix for wrong escape in JSON
1 parent 8e1f0da commit e95889d

File tree

8 files changed

+17096
-7
lines changed

8 files changed

+17096
-7
lines changed

explorer-v2/build-system/pre-build/astrojs-compiler-service4b.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ export function parse(code, options) {
1616
// eslint-disable-next-line node/no-unsupported-features/es-builtins -- ignore
1717
service = globalThis['@astrojs/compiler'];
1818
}
19-
const ast = JSON.parse(service.parse(code, options).ast);
20-
return { ast };
19+
return service.parse(code, options);
2120
}
2221

2322
/** setup */

src/parser/astro-parser/astrojs-compiler-service.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from "fs"
22
import Go from "./wasm_exec"
3-
import type { ParseOptions, ParseResult } from "@astrojs/compiler"
3+
import type { ParseOptions } from "@astrojs/compiler"
44
const go = new Go()
55
const wasmBuffer = fs.readFileSync(
66
require.resolve("@astrojs/compiler/astro.wasm"),
@@ -15,7 +15,11 @@ const service = (globalThis as any)["@astrojs/compiler"] as {
1515
/**
1616
* Parse code by `@astrojs/compiler`
1717
*/
18-
export function parse(code: string, options: ParseOptions): ParseResult {
19-
const ast = JSON.parse(service.parse(code, options).ast)
20-
return { ast }
18+
export function parse(
19+
code: string,
20+
options: ParseOptions,
21+
): {
22+
ast: string
23+
} {
24+
return service.parse(code, options)
2125
}

src/parser/astro-parser/parse.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import { ParseError } from "../../errors"
2121
* Parse code by `@astrojs/compiler`
2222
*/
2323
export function parse(code: string, ctx: Context): ParseResult {
24-
const ast = service.parse(code, { position: true }).ast
24+
const ast = parseByService(code).ast
25+
2526
const htmlElement = ast.children.find(
2627
(n): n is ElementNode => n.type === "element" && n.name === "html",
2728
)
@@ -32,6 +33,31 @@ export function parse(code: string, ctx: Context): ParseResult {
3233
return { ast }
3334
}
3435

36+
/**
37+
* Parse code by `@astrojs/compiler`
38+
*/
39+
function parseByService(code: string): ParseResult {
40+
const jsonAst = service.parse(code, { position: true }).ast
41+
42+
try {
43+
const ast = JSON.parse(jsonAst)
44+
return { ast }
45+
} catch {
46+
// Adjust because you may get the wrong escape as JSON.
47+
const ast = JSON.parse(
48+
jsonAst.replace(/\\./gu, (m) => {
49+
try {
50+
JSON.parse(`"${m}"`)
51+
return m
52+
} catch {
53+
return `\\${m}`
54+
}
55+
}),
56+
)
57+
return { ast }
58+
}
59+
}
60+
3561
/**
3662
* Adjust <html> element node
3763
*/
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
---
2+
import { Sprite } from 'astro-icon';
3+
4+
export interface Props {
5+
id: string;
6+
placeholder: string;
7+
selector: string;
8+
}
9+
10+
const { id, placeholder, selector = '#integrations > *' } = Astro.props as Props;
11+
12+
const inlineStyle = `
13+
${selector}[hidden] {
14+
display: none;
15+
}
16+
17+
`
18+
---
19+
20+
<section>
21+
<div>
22+
<input type="text" {id} {placeholder} aria-label="Search">
23+
<button aria-hidden="true">
24+
<Sprite pack="heroicons-outline" name="search" size={24} />
25+
</button>
26+
</div>
27+
</section>
28+
29+
<style set:html={inlineStyle}></style>
30+
31+
<script src="https://polyfill.io/v3/polyfill.min.js?features=URLSearchParams" />
32+
<script type="module" define:vars={{selector, id}}>
33+
/* https://gomakethings.com/how-to-create-a-search-page-for-a-static-website-with-vanilla-js/ */
34+
const stopWords = ['a', 'an', 'and', 'are', 'aren\'t', 'as', 'by', 'can', 'cannot', 'can\'t', 'could', 'couldn\'t', 'how', 'is', 'isn\'t', 'it', 'its', 'it\'s', 'that', 'the', 'their', 'there', 'they', 'they\'re', 'them', 'to', 'too', 'us', 'very', 'was', 'we', 'well', 'were', 'what', 'whatever', 'when', 'whenever', 'where', 'with', 'would', 'yet', 'you', 'your', 'yours', 'yourself', 'yourselves', 'the'];
35+
36+
const input = document.getElementById(id);
37+
const button = document.querySelector(`#${id} + button`);
38+
const items = document.querySelectorAll(selector);
39+
40+
const FPS_30 = 1000 / 30;
41+
42+
function debounce(fn, timeout = 150) {
43+
let timer;
44+
return (...args) => {
45+
clearTimeout(timer);
46+
timer = setTimeout(() => fn.apply(this, args), timeout);
47+
}
48+
}
49+
50+
const updateOpen = () => {
51+
requestAnimationFrame(() => {
52+
(!!input.value || document.activeElement === input)
53+
? input.parentElement.setAttribute('open', '')
54+
: input.parentElement.removeAttribute('open');
55+
});
56+
}
57+
58+
const update = () => {
59+
updateOpen();
60+
61+
const regexMap = input.value.toLowerCase()
62+
.split(' ')
63+
.filter(word => word.length && !stopWords.includes(word))
64+
.map(word => new RegExp(word, 'i'));
65+
66+
for (const item of items) {
67+
const isMatch = !regexMap.length || regexMap.some(regex => !!item.textContent.match(regex));
68+
isMatch ? item.removeAttribute('hidden') : item.setAttribute('hidden', '');
69+
}
70+
71+
updateURL()
72+
}
73+
74+
const updateURL = () => {
75+
const q = input.value?.trim()
76+
77+
if (q) {
78+
const params = new URLSearchParams()
79+
params.set('q', q)
80+
window.history.replaceState(null, null, `?${params.toString()}`)
81+
} else {
82+
const [href] = window.location.href.split('?')
83+
window.history.replaceState(null, null, href)
84+
}
85+
}
86+
87+
const hydrateQuery = () => {
88+
const params = new URLSearchParams(location.search)
89+
input.value = params.get('q') || ''
90+
}
91+
92+
input.addEventListener('input', debounce(update, FPS_30), true);
93+
button.addEventListener('click', input.focus, true);
94+
input.addEventListener('focus', updateOpen, true);
95+
input.addEventListener('blur', updateOpen, true);
96+
97+
hydrateQuery()
98+
update();
99+
</script>
100+
101+
<style>
102+
section {
103+
display: flex;
104+
justify-content: flex-end;
105+
font-size: 16px;
106+
width: calc(25ch + 2.5rem);
107+
}
108+
109+
div {
110+
display: flex;
111+
border-radius: 9999px;
112+
padding: 0.25rem;
113+
background: var(--color-white);
114+
}
115+
116+
input {
117+
line-height: 2.5rem;
118+
width: 25ch;
119+
max-width: 25ch;
120+
padding-left: 0.75rem;
121+
background: transparent;
122+
font-size: 16px;
123+
outline: none;
124+
}
125+
126+
input::placeholder {
127+
color: var(--color-midnight);
128+
opacity: 0.75;
129+
}
130+
131+
button {
132+
display: flex;
133+
align-items: center;
134+
justify-content: center;
135+
width: 2.5rem;
136+
height: 2.5rem;
137+
border-radius: 9999px;
138+
background: var(--color-dusk);
139+
color: var(--color-white);
140+
}
141+
142+
@media (max-width: 768px) {
143+
div, section {
144+
width: 100%;
145+
}
146+
147+
input {
148+
flex: 1;
149+
max-width: unset;
150+
}
151+
}
152+
153+
@media(hover: hover) {
154+
div {
155+
background: transparent;
156+
}
157+
158+
input {
159+
max-width: 0;
160+
padding-left: 0;
161+
}
162+
163+
input, button {
164+
transition: 250ms ease;
165+
}
166+
167+
input:focus,
168+
input:not(:placeholder-shown) {
169+
max-width: initial;
170+
padding: 0 .5rem;
171+
padding-left: 0.75rem;
172+
}
173+
174+
button {
175+
background: transparent;
176+
color: var(--color-midnight);
177+
}
178+
179+
div:hover, div[open] {
180+
background: var(--color-white);
181+
}
182+
183+
div:hover > input {
184+
max-width: 25ch;
185+
padding: 0 .5rem;
186+
padding-left: 0.75rem;
187+
}
188+
189+
div:hover > button,
190+
input:focus + button,
191+
input:not(:placeholder-shown) + button {
192+
background: var(--color-dusk);
193+
color: var(--color-white);
194+
}
195+
}
196+
</style>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"ruleId": "no-undef",
4+
"code": "Astro",
5+
"line": 10,
6+
"column": 61
7+
}
8+
]

0 commit comments

Comments
 (0)