Public: anyone can read this; the crew can change it.

Zero dependencies, kept: a test fails the moment a package or a remote asset is added #25

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

mason/listing-launch:mason/23-zero-dependencies-kept-a-test-fails-the → main 2 commits fork 2 threads · 2 unresolved

  • Merged. mason/23-zero-dependencies-kept-a-test-fails-the was merged into main as dd8bc17.

Review

Sign in to review this change.
2 files changed +95 −0
README.md +4
@@ -117,6 +117,10 @@ every open pull gets a preview at its own address with its own copy of the
117117 data. Everything the app keeps lives under `FORGE_APP_DATA`, which is what
118118 makes "production data" and "preview data" two different things.
119119
120 There are no packages to install: `package.json` declares none, every
121 import is relative, `bun:` or `node:`, and no page loads anything from
122 another host. `src/deps.test.ts` fails the moment that stops being true.
123
120124 ```sh
121125 PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts # run it here
122126 bun test # what `forge.json`'s app.test runs on every push
src/deps.test.ts added +91
@@ -0,0 +1,91 @@
1 import { expect, test } from "bun:test";
2 import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3 import { join } from "node:path";
4
5 const root = join(import.meta.dir, "..");
6
7 const walk = (dir: string): string[] =>
8 readdirSync(dir).flatMap((name) => {
9 const path = join(dir, name);
10 return statSync(path).isDirectory() ? walk(path) : [path];
11 });
12
13 const read = (path: string) => readFileSync(path, "utf8");
14
15 test("package.json declares no packages", () => {
16 const pkg = JSON.parse(read(join(root, "package.json")));
17 for (const key of ["dependencies", "devDependencies", "peerDependencies"]) {
18 expect(pkg[key], key).toBeUndefined();
19 }
20 });
21
22 test("every import in src/ and scripts/ is relative, bun: or node:", () => {
23 const files = [...walk(join(root, "src")), ...walk(join(root, "scripts"))].filter((f) => f.endsWith(".ts") && !f.endsWith("deps.test.ts")); // its samples are bad on purpose
24 const spec = /^\s*(?:import|export)\b[^"'`;]*?\bfrom\s*["']([^"']+)["']|^\s*import\s*["']([^"']+)["']|\bimport\(\s*["']([^"']+)["']\s*\)/gm;
25 const bad: string[] = [];
26 for (const file of files) {
27 for (const m of read(file).matchAll(spec)) {
28 const s = m[1] ?? m[2] ?? m[3]!;
29 if (!(s.startsWith("./") || s.startsWith("../") || s.startsWith("bun:") || s.startsWith("node:"))) {
30 bad.push(`${file.slice(root.length + 1)}: ${s}`);
31 }
32 }
33 }
34 expect(bad).toEqual([]);
35 });
36
37 // Markup attributes, CSS and network calls only: https:// in prose or in typed
outdated

Scope too narrow: this misses markup built in src/routes.ts, src/photos.ts, src/generate.ts, src/server.ts. Walk all non-test .ts under src/ as well as static/.

38 // links is fine. The app's own links are relative paths, so any remote href is a violation.
39 const REMOTE = String.raw`(?:https?:|wss?:)?//`;
40 const remoteRefs: RegExp[] = [
41 new RegExp(String.raw`\b(?:src|srcset|poster|action|data|href)\s*=\s*["']?\s*${REMOTE}`, "gi"),
outdated

No pattern covers fetch/WebSocket/EventSource/import to a remote URL in static/*.js or src. Add one. Also add a positive-control test that these regexes flag a known-bad sample, so a broken regex can't pass silently.

42 new RegExp(String.raw`@import\s+(?:url\(\s*)?["']?\s*${REMOTE}`, "gi"),
43 new RegExp(String.raw`\burl\(\s*["']?\s*${REMOTE}`, "gi"),
44 new RegExp(String.raw`\b(?:fetch|import|importScripts|sendBeacon|open)\s*\(\s*(?:[^,)"'\x60]+,\s*)?["'\x60]\s*${REMOTE}`, "gi"),
45 new RegExp(String.raw`\bnew\s+(?:WebSocket|EventSource|Worker|SharedWorker)\s*\(\s*["'\x60]\s*${REMOTE}`, "gi"),
46 ];
47
48 const findRemote = (text: string): string[] => remoteRefs.flatMap((re) => [...text.matchAll(re)].map((m) => m[0]));
49
50 test("the remote-reference scan flags bad samples and passes good ones", () => {
51 for (const bad of [
52 '<script src="https://x/y.js">',
53 '<link href="//cdn.x/a.css">',
54 '@import "//x";',
55 "@import url(https://x/a.css);",
56 "a { background: url(https://x) }",
57 'fetch("https://x/api")',
58 'fetch("/ok"); fetch(`https://x/${id}`)',
59 'new WebSocket("wss://x/live")',
60 'new EventSource("http://x/stream")',
61 'await import("https://x/m.js")',
62 ]) {
63 expect(findRemote(bad), bad).not.toEqual([]);
64 }
65 for (const good of [
66 '<script src="/static/live.js">',
67 '<a href="/listings/1">',
68 "a { background: url(/static/a.svg) }",
69 '@import "/static/b.css";',
70 'fetch("/listings/1")',
71 'new EventSource("/live")',
72 "<p>See https://example.com for details</p>",
73 ]) {
74 expect(findRemote(good), good).toEqual([]);
75 }
76 });
77
78 test("no page, CSS or JS reaches a remote URL", () => {
79 const files = [...walk(join(root, "src")), ...walk(join(root, "static"))].filter(
80 (f) => /\.(ts|js|css|html)$/.test(f) && !f.endsWith(".test.ts"),
81 );
82 expect(files.length).toBeGreaterThan(0);
83 const found = files.flatMap((f) => findRemote(read(f)).map((m) => `${f.slice(root.length + 1)}: ${m}`));
84 expect(found).toEqual([]);
85 });
86
87 test("no lockfile or node_modules: nothing is installed", () => {
88 for (const name of ["bun.lock", "bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "node_modules"]) {
89 expect(existsSync(join(root, name)), name).toBe(false);
90 }
91 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs