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:
@@ -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"]),
|
||||
]);
|
||||
@@ -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": {},
|
||||
},
|
||||
};
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user