Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Deploy to Cloudflare Pages

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
name: Deploy to Cloudflare Pages
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: bun install

- name: Build web package
run: cd packages/web && bun run build

- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
13 changes: 8 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ name: Run Tests

on:
pull_request:
branches: [ main ]
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Bun
uses: oven-sh/setup-bun@v1

- name: Install dependencies
run: bun install


- name: Run type check
run: bun run tsc

- name: Run tests
run: bun test
run: bun test
10 changes: 5 additions & 5 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
"packages/lang": {
"name": "@scorelang/lang",
"dependencies": {
"@types/node": "^22.15.30",
"cli-table3": "^0.6.5",
"tiny-invariant": "^1.3.3",
"ts-pattern": "^5.7.0",
},
"devDependencies": {
"@types/bun": "latest",
"@types/bun": "^1.2.15",
},
"peerDependencies": {
"typescript": "^5",
Expand All @@ -23,6 +24,7 @@
"version": "0.0.0",
"dependencies": {
"@scorelang/lang": "*",
"@types/bun": "^1.2.15",
"@types/node": "^22.15.30",
"class-variance-authority": "^0.7.1",
"clsx": "^2.0.0",
Expand Down Expand Up @@ -239,7 +241,7 @@

"@types/babel__traverse": ["@types/[email protected]", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="],

"@types/bun": ["@types/[email protected].13", "", { "dependencies": { "bun-types": "1.2.13" } }, "sha512-u6vXep/i9VBxoJl3GjZsl/BFIsvML8DfVDO0RYLEwtSZSp981kEO1V5NwRcO1CPJ7AmvpbnDCiMKo3JvbDEjAg=="],
"@types/bun": ["@types/[email protected].15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="],

"@types/estree": ["@types/[email protected]", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],

Expand Down Expand Up @@ -305,7 +307,7 @@

"browserslist": ["[email protected]", "", { "dependencies": { "caniuse-lite": "^1.0.30001716", "electron-to-chromium": "^1.5.149", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw=="],

"bun-types": ["[email protected].13", "", { "dependencies": { "@types/node": "*" } }, "sha512-rRjA1T6n7wto4gxhAO/ErZEtOXyEZEmnIHQfl0Dt1QQSB4QV0iP6BZ9/YB5fZaHFQ2dwHFrmPaRQ9GGMX01k9Q=="],
"bun-types": ["[email protected].15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],

"bytes": ["[email protected]", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],

Expand Down Expand Up @@ -795,8 +797,6 @@

"@typescript-eslint/typescript-estree/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],

"bun-types/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A=="],

"chokidar/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],

"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
Expand Down
5 changes: 3 additions & 2 deletions packages/lang/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
"type": "module",
"private": true,
"devDependencies": {
"@types/bun": "latest"
"@types/bun": "^1.2.15"
},
"peerDependencies": {
"typescript": "^5"
},
"dependencies": {
"@types/node": "^22.15.30",
"cli-table3": "^0.6.5",
"tiny-invariant": "^1.3.3",
"ts-pattern": "^5.7.0"
Expand All @@ -18,7 +19,7 @@
"packages/*"
],
"scripts": {
"build": "bun build ./src/index.ts --compile --outfile scorelang",
"build": "bun build ./src/cli.ts --compile --outfile scorelang",
"tsc": "tsc --noEmit",
"lint": "eslint ."
}
Expand Down
8 changes: 6 additions & 2 deletions packages/lang/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ export interface Statement extends Node {
}

export class Team {
constructor(private token: Token, private name: string) {}
constructor(private token: Token, private name: string) {
this.name; // disable-lint
}

tokenLiteral(): string {
return this.token.value;
}
}

export class Score {
constructor(private token: Token, private value: number) {}
constructor(private token: Token, private value: number) {
this.token; // disable-lint
}

tokenLiteral(): number {
return this.value;
Expand Down
30 changes: 30 additions & 0 deletions packages/lang/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { match } from "ts-pattern";
import { Evaluator } from "./evaluator";
import Lexer from "./lexer";
import { Parser } from "./parser";
import invariant from "tiny-invariant";
import { calculatePointsTable, printPointsTable } from "./utils";

async function main() {
// Try to read from file argument if provided
const args = process.argv.slice(2);
const input = await match(args.length > 0)
.with(true, async () => await Bun.file(args[0]!).text())
.with(false, async () => await Bun.stdin.text())
.exhaustive();

invariant(input, "invariant: input should not be empty");

const lexer = new Lexer(input);
const parser = new Parser(lexer);
const program = parser.parse();
const evaluator = new Evaluator();
const { results } = evaluator.evaluate(program);

const pointsTable = calculatePointsTable(results);
printPointsTable(pointsTable);
}

if (import.meta.main) {
await main();
}
33 changes: 1 addition & 32 deletions packages/lang/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,6 @@
import { match } from "ts-pattern";
import { Evaluator, type GameResult } from "./evaluator";
import Lexer from "./lexer";
import { Parser } from "./parser";
import invariant from "tiny-invariant";
import { calculatePointsTable, printPointsTable } from "./utils";

// Re-export types and utilities
// Re-export types and utilities
export type { GameResult } from "./evaluator";
export { calculatePointsTable } from "./utils";
export { Evaluator } from "./evaluator";
export { Lexer } from "./lexer";
export { Parser } from "./parser";

async function main() {
// Try to read from file argument if provided
const args = process.argv.slice(2);
const input = await match(args.length > 0)
.with(true, async () => await Bun.file(args[0]!).text())
.with(false, async () => await Bun.stdin.text())
.exhaustive();

invariant(input, "invariant: input should not be empty");

const lexer = new Lexer(input);
const parser = new Parser(lexer);
const program = parser.parse();
const evaluator = new Evaluator();
const { results } = evaluator.evaluate(program);

const pointsTable = calculatePointsTable(results);
printPointsTable(pointsTable);
}

if (import.meta.main) {
await main();
}
4 changes: 2 additions & 2 deletions packages/lang/src/lexer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe("Lexer", () => {
const token = lexer.nextToken();
expect(token).toEqual(expectedToken);
});
})
});

it("should tokenize a string with team names with multiple spaces", () => {
const input = `Manchester United 2-0 Arsenal;`;
Expand All @@ -72,5 +72,5 @@ describe("Lexer", () => {
const token = lexer.nextToken();
expect(token).toEqual(expectedToken);
});
})
});
});
2 changes: 1 addition & 1 deletion packages/lang/src/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class Lexer {
while (this.isLetter(this.ch) || this.ch === " ") {
this.readChar();
}
return this.program.slice(startPosition, this.position).trim()
return this.program.slice(startPosition, this.position).trim();
}

private readNumber(): string {
Expand Down
2 changes: 1 addition & 1 deletion packages/lang/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { GameResult } from "./evaluator";
import Table from "cli-table3";

type TeamStats = {
points: number;
Expand Down Expand Up @@ -67,7 +68,6 @@ export const calculatePointsTable = (results: GameResult[]) => {
};

export const printPointsTable = (pointsTable: Map<string, TeamStats>) => {
const Table = require("cli-table3");
const sortedTeams = [...pointsTable.entries()].sort(
(a, b) => b[1].points - a[1].points
);
Expand Down
4 changes: 3 additions & 1 deletion tsconfig.json → packages/lang/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
"noPropertyAccessFromIndexSignature": false,

"types": ["node", "bun"]
}
}
6 changes: 6 additions & 0 deletions packages/web/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Security headers for Cloudflare Pages
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
X-XSS-Protection: 1; mode=block
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"@scorelang/lang": "*",
"@types/bun": "^1.2.15",
"@types/node": "^22.15.30",
"class-variance-authority": "^0.7.1",
"clsx": "^2.0.0",
Expand Down
26 changes: 26 additions & 0 deletions packages/web/src/worker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export default {
async fetch(request: Request, env: any): Promise<Response> {
const url = new URL(request.url);

// Serve static files from the public directory
if (url.pathname.startsWith('/assets/') || url.pathname.endsWith('.css') || url.pathname.endsWith('.js')) {
// Let Cloudflare serve static assets
return env.ASSETS.fetch(request);
}

// For all other routes, serve the index.html (SPA routing)
const indexResponse = await env.ASSETS.fetch(new Request(new URL('/index.html', request.url)));

if (indexResponse.ok) {
return new Response(indexResponse.body, {
status: 200,
headers: {
'Content-Type': 'text/html',
...indexResponse.headers,
},
});
}

return new Response('Not found', { status: 404 });
},
};
4 changes: 2 additions & 2 deletions packages/web/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"types": ["vite/client"],
"types": ["vite/client", "node", "bun"],
"module": "ESNext",
"skipLibCheck": true,

Expand All @@ -20,7 +20,7 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"erasableSyntaxOnly": false,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
Expand Down
2 changes: 1 addition & 1 deletion packages/web/tsconfig.node.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"erasableSyntaxOnly": false,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
Expand Down
11 changes: 8 additions & 3 deletions packages/web/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})
build: {
outDir: "dist",
sourcemap: true,
},
publicDir: "public",
});
21 changes: 21 additions & 0 deletions packages/web/wrangler.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "scorelang",
"main": "src/worker.tsx",
"compatibility_date": "2024-09-23",
"compatibility_flags": ["nodejs_compat"],
"observability": {
"enabled": true
},
"routes": [
{
"pattern": "score.zoid.dev",
"custom_domain": true
}
],
"assets": {
"directory": "./dist",
"binding": "ASSETS"
},
"vars": {}
}
Loading