Rewrite Next, ESLint, PostCSS, and tsconfig overlay.

CSP, login/register redirects, pdfjs-dist externals, and canvas alias stay the same. package.json is unchanged.
This commit is contained in:
2026-09-20 20:19:37 +02:00
parent cdfe5812cf
commit 739e6f7f17
5 changed files with 155 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
export default defineConfig([
...nextVitals,
...nextTs,
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
]);
+71
View File
@@ -0,0 +1,71 @@
import type { NextConfig } from "next";
import {
CSP_CONNECT_HOSTS,
CSP_FRAME_HOSTS,
CSP_SCRIPT_HOSTS,
} from "./src/lib/csp-hosts";
function spaceJoin(hosts: string[]): string {
return hosts.length > 0 ? ` ${hosts.join(" ")}` : "";
}
function publicApiOrigins(): string[] {
const raw = process.env.NEXT_PUBLIC_API_URL;
if (!raw) return [];
try {
return [new URL(raw).origin];
} catch {
return [];
}
}
function contentSecurityPolicy(): string {
const connect = [...CSP_CONNECT_HOSTS, ...publicApiOrigins()];
return [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://unpkg.com https://vercel.live" +
spaceJoin(CSP_SCRIPT_HOSTS),
"style-src 'self' 'unsafe-inline' https://vercel.live https://fonts.googleapis.com",
"img-src 'self' data: https: blob:",
"font-src 'self' data: https://vercel.live https://assets.vercel.com https://fonts.gstatic.com",
"connect-src 'self' blob: https://storage.googleapis.com https://vercel.live wss://ws-us3.pusher.com" +
spaceJoin(connect),
"frame-src 'self' https://vercel.live" + spaceJoin(CSP_FRAME_HOSTS),
"worker-src 'self' blob:",
].join("; ");
}
const nextConfig: NextConfig = {
allowedDevOrigins: ["127.0.0.1", "localhost"],
serverExternalPackages: ["pdfjs-dist"],
turbopack: {
resolveAlias: {
canvas: { browser: "" },
},
},
async redirects() {
return [
{ source: "/login", destination: "/sign-in", permanent: false },
{ source: "/register", destination: "/sign-up", permanent: false },
];
},
async headers() {
return [
{
source: "/(.*)",
headers: [
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{
key: "Strict-Transport-Security",
value: "max-age=31536000; includeSubDomains",
},
{ key: "Content-Security-Policy", value: contentSecurityPolicy() },
],
},
];
},
};
export default nextConfig;
@@ -0,0 +1,5 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
+30
View File
@@ -0,0 +1,30 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}
@@ -0,0 +1,40 @@
"""Frontend Next/ESLint/PostCSS overlay lives under periscope/src."""
from __future__ import annotations
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "periscope" / "src" / "frontend"
def test_next_and_eslint_are_src():
for rel in ("next.config.ts", "eslint.config.mjs", "postcss.config.mjs", "tsconfig.json"):
path = SRC / rel
assert path.is_file(), rel
assert "Native Periscope overlay" not in path.read_text(encoding="utf-8")[:400]
def test_next_config_keeps_runtime_contract():
text = (SRC / "next.config.ts").read_text(encoding="utf-8")
assert 'from "./src/lib/csp-hosts"' in text
assert 'allowedDevOrigins: ["127.0.0.1", "localhost"]' in text
assert 'serverExternalPackages: ["pdfjs-dist"]' in text
assert 'canvas: { browser: "" }' in text
assert 'source: "/login"' in text
assert 'destination: "/sign-in"' in text
assert 'source: "/register"' in text
assert 'destination: "/sign-up"' in text
assert "X-Content-Type-Options" in text
assert "X-Frame-Options" in text
assert "Strict-Transport-Security" in text
assert "Content-Security-Policy" in text
assert "worker-src 'self' blob:" in text
assert "pdfjs-dist" in text
def test_eslint_uses_next_core_web_vitals():
text = (SRC / "eslint.config.mjs").read_text(encoding="utf-8")
assert "eslint-config-next/core-web-vitals" in text
assert "eslint-config-next/typescript" in text
assert ".next/**" in text