Add ESLint and Prettier

Prettier owns formatting: .prettierrc.json, format and format:check scripts, and
a one-off pass over the repo so the check is green from the start. Embedded
language formatting is off deliberately - the renderers build HTML in html
tagged templates and letting Prettier reflow those would change the markup
customers receive. docs/ is excluded because it is hand-wrapped prose, and shell
scripts have no Prettier parser.

ESLint covers the JavaScript: the two browser scripts, the build and check
scripts, and its own config. no-var is scoped out for the browser scripts on
purpose; they are plain ES5-shaped IIFEs and a hoisting difference would only
show up in a browser.

ESLint cannot lint the TypeScript here: typescript@7 is the native port with no
JavaScript compiler API, and typescript-eslint requires a 4.8-6.1 peer, so it
refuses to install and forcing it through would leave a parser that cannot read
the compiler. tsc --noEmit stays the TypeScript gate; README records the two
ways to change that.

lint and format:check now run in deploy.sh, and VS Code formats on save with
Prettier and surfaces ESLint findings.
This commit is contained in:
tanshu committed 2026-09-22 23:15:08 +05:30
1 parent ce8079355b
commit e64f65642f
40 files changed
+1469 -172

No files matched your search

+30 -7
View File
@@ -15,23 +15,43 @@ import { SetStore } from '../src/storage/sets.ts';
import { SectionStore } from '../src/storage/sections.ts';
const root = path.resolve(process.env['MENU_ROOT'] ?? '.dev');
const configPath = path.resolve(
process.env['MENU_SITE_CONFIG'] ?? 'config/hopsngrains.site.json',
);
const configPath = path.resolve(process.env['MENU_SITE_CONFIG'] ?? 'config/hopsngrains.site.json');
const dataDir = path.join(root, 'data');
const config = parseSiteConfig(JSON.parse(await readFile(configPath, 'utf8')));
function menuPage(title: string, section: string, page: number, total: number, seed: number): string {
function menuPage(
title: string,
section: string,
page: number,
total: number,
seed: number,
): string {
const items = Array.from({ length: 7 }, (_, index) => {
const y = 420 + index * 130;
const width = 180 + ((seed * 37 + index * 53) % 220);
return `
<text x="120" y="${y}" font-family="Georgia, serif" font-size="34" fill="#1b1f22">${
['Wood-fired pizza', 'Smash burger', 'Tenderloin steak', 'Craft platter', 'Fish and chips', 'Risotto', 'Cheesecake'][index % 7]
[
'Wood-fired pizza',
'Smash burger',
'Tenderloin steak',
'Craft platter',
'Fish and chips',
'Risotto',
'Cheesecake',
][index % 7]
}</text>
<text x="120" y="${y + 34}" font-family="Georgia, serif" font-size="24" fill="#6b7075">${
['thin crust, basil', 'double patty, cheddar', 'pepper sauce', 'for two', 'tartare', 'wild mushroom', 'berry coulis'][index % 7]
[
'thin crust, basil',
'double patty, cheddar',
'pepper sauce',
'for two',
'tartare',
'wild mushroom',
'berry coulis',
][index % 7]
}</text>
<text x="1000" y="${y}" text-anchor="end" font-family="Georgia, serif" font-size="34" fill="#1b1f22">${
240 + ((seed * 91 + index * 71) % 620)
@@ -81,7 +101,10 @@ const store = new SetStore({
for (const [index, entry] of plan.entries()) {
const section = sections.bySlug(entry.section);
if (section === undefined) continue;
const set = await store.create(entry.section, { label: entry.label, validUntil: entry.validUntil });
const set = await store.create(entry.section, {
label: entry.label,
validUntil: entry.validUntil,
});
for (let page = 1; page <= entry.pages; page += 1) {
const svg = menuPage(entry.label, section.label, page, entry.pages, index + page);
await store.addPage(set.id, {