afterbefore 0.1.18 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -33
- package/dist/chunk-XRQ4MAEV.js +204 -0
- package/dist/chunk-XRQ4MAEV.js.map +1 -0
- package/dist/next.d.ts +5 -0
- package/dist/next.js +28 -0
- package/dist/next.js.map +1 -0
- package/dist/overlay/index.d.ts +5 -0
- package/dist/overlay/index.js +1176 -0
- package/dist/overlay/index.js.map +1 -0
- package/dist/server/middleware.d.ts +8 -0
- package/dist/server/middleware.js +29 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/route.d.ts +10 -0
- package/dist/server/route.js +30 -0
- package/dist/server/route.js.map +1 -0
- package/package.json +36 -22
- package/dist/cli.js +0 -2667
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts +0 -91
- package/dist/index.js +0 -2602
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Run it from a feature branch in any Next.js app router project:
|
|
|
17
17
|
npx afterbefore
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
It spins up two dev servers (your branch and the base branch)
|
|
20
|
+
It spins up two dev servers (your branch and the base branch) and captures screenshots of affected routes.
|
|
21
21
|
|
|
22
22
|
Output lands in `.afterbefore/`:
|
|
23
23
|
|
|
@@ -25,10 +25,7 @@ Output lands in `.afterbefore/`:
|
|
|
25
25
|
.afterbefore/
|
|
26
26
|
└── feature-branch_2026-02-26/
|
|
27
27
|
├── about-before.png
|
|
28
|
-
|
|
29
|
-
├── about-diff.png
|
|
30
|
-
├── about-compare.png
|
|
31
|
-
└── about-slider.html # interactive slider
|
|
28
|
+
└── about-after.png
|
|
32
29
|
```
|
|
33
30
|
|
|
34
31
|
## How it works
|
|
@@ -38,9 +35,7 @@ Output lands in `.afterbefore/`:
|
|
|
38
35
|
3. Builds an import graph by parsing ES module imports across your codebase
|
|
39
36
|
4. Walks the graph backward from each changed file to find which `page.tsx` files are affected
|
|
40
37
|
5. Creates a git worktree for the base branch and runs `npm install`
|
|
41
|
-
6. Starts two Next.js dev servers in parallel (base branch + current branch)
|
|
42
|
-
7. Captures full-page screenshots of each affected route on both servers using Playwright
|
|
43
|
-
8. Compares screenshots with pixelmatch and generates diff images
|
|
38
|
+
6. Starts two Next.js dev servers in parallel (base branch + current branch) and captures full-page screenshots of each affected route using Playwright
|
|
44
39
|
|
|
45
40
|
Layouts work the same way. Edit `app/dashboard/layout.tsx` and it captures every page under `app/dashboard/`.
|
|
46
41
|
|
|
@@ -52,33 +47,13 @@ If only global files changed (like `globals.css` or `tailwind.config.ts`) and no
|
|
|
52
47
|
|------|---------|-------------|
|
|
53
48
|
| `--base <ref>` | `main` | Base branch to compare against |
|
|
54
49
|
| `--output <dir>` | `.afterbefore` | Output directory |
|
|
55
|
-
| `--post` | `false` | Post results as a GitHub PR comment |
|
|
56
|
-
| `--threshold <percent>` | `0.1` | Ignore diffs below this percentage |
|
|
57
50
|
| `--max-routes <count>` | `6` | Cap the number of routes captured (0 = unlimited) |
|
|
58
51
|
| `--width <pixels>` | `1280` | Viewport width |
|
|
59
52
|
| `--height <pixels>` | `720` | Viewport height |
|
|
60
|
-
| `--device <name>` | — | Playwright device, e.g. `"iPhone 14"` |
|
|
61
53
|
| `--delay <ms>` | `0` | Extra wait time after page load |
|
|
62
|
-
| `--
|
|
63
|
-
| `--
|
|
64
|
-
| `--
|
|
65
|
-
| `--max-sections <count>` | `10` | Max sections per page state |
|
|
66
|
-
|
|
67
|
-
## Post to PR
|
|
68
|
-
|
|
69
|
-
The `--post` flag uses the GitHub CLI (`gh`) to add a comment to your open pull request with a markdown summary of the results. On subsequent runs, it updates the same comment instead of creating a new one.
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
npx afterbefore --post
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
You need `gh` installed and authenticated.
|
|
76
|
-
|
|
77
|
-
## Auto-detection
|
|
78
|
-
|
|
79
|
-
**Tabs.** If a page has ARIA tab controls (`role="tablist"` and `role="tab"`), afterbefore clicks through each tab and captures the state. This is on by default. Disable it with `--no-auto-tabs`.
|
|
80
|
-
|
|
81
|
-
**Sections.** With `--auto-sections`, pages with 3+ headings get captured section by section. Each heading-labeled section is matched by text between before and after, so you can see exactly which section changed.
|
|
54
|
+
| `--max-depth <n>` | `10` | Max import graph traversal depth |
|
|
55
|
+
| `--dry-run` | `false` | Show affected routes without capturing |
|
|
56
|
+
| `--verbose` | `false` | Show detailed import graph traversal |
|
|
82
57
|
|
|
83
58
|
## Configuration file
|
|
84
59
|
|
|
@@ -102,8 +77,6 @@ For pages that need interaction before capture (opening a modal, clicking a drop
|
|
|
102
77
|
|
|
103
78
|
Actions run in order before the screenshot is taken. Supported actions: `click` (CSS selector), `scroll` (CSS selector), `wait` (milliseconds).
|
|
104
79
|
|
|
105
|
-
When scenarios are defined for a route, auto-tabs are disabled for that route.
|
|
106
|
-
|
|
107
80
|
## Requirements
|
|
108
81
|
|
|
109
82
|
- Next.js with the app router (`app/` directory)
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// src/server/save.ts
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
var VALID_TYPES = ["before", "after"];
|
|
6
|
+
var VALID_MODES = ["fullpage", "viewport", "area"];
|
|
7
|
+
var DATA_URL_PREFIX = "data:image/png;base64,";
|
|
8
|
+
async function handleSave(req) {
|
|
9
|
+
let body;
|
|
10
|
+
try {
|
|
11
|
+
body = await req.json();
|
|
12
|
+
} catch {
|
|
13
|
+
return NextResponse.json(
|
|
14
|
+
{ error: "Invalid JSON body" },
|
|
15
|
+
{ status: 400 }
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const { type, mode, image } = body;
|
|
19
|
+
if (!VALID_TYPES.includes(type)) {
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ error: `Invalid type: must be "before" or "after"` },
|
|
22
|
+
{ status: 400 }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (!VALID_MODES.includes(mode)) {
|
|
26
|
+
return NextResponse.json(
|
|
27
|
+
{ error: `Invalid mode: must be "fullpage", "viewport", or "area"` },
|
|
28
|
+
{ status: 400 }
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (typeof image !== "string" || !image.startsWith(DATA_URL_PREFIX)) {
|
|
32
|
+
return NextResponse.json(
|
|
33
|
+
{ error: "Invalid image: must be a data:image/png;base64 data URL" },
|
|
34
|
+
{ status: 400 }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const base64 = image.slice(DATA_URL_PREFIX.length);
|
|
38
|
+
const buffer = Buffer.from(base64, "base64");
|
|
39
|
+
const dir = join(process.cwd(), ".afterbefore");
|
|
40
|
+
const filename = `${type}.png`;
|
|
41
|
+
const filepath = join(dir, filename);
|
|
42
|
+
const relativePath = `.afterbefore/${filename}`;
|
|
43
|
+
try {
|
|
44
|
+
await mkdir(dir, { recursive: true });
|
|
45
|
+
await writeFile(filepath, buffer);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ error: "Failed to write screenshot", detail: String(err) },
|
|
49
|
+
{ status: 500 }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return NextResponse.json({ success: true, path: relativePath });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/server/push.ts
|
|
56
|
+
import { NextResponse as NextResponse2 } from "next/server";
|
|
57
|
+
import { execFile } from "child_process";
|
|
58
|
+
import { access } from "fs/promises";
|
|
59
|
+
import { join as join2 } from "path";
|
|
60
|
+
import { promisify } from "util";
|
|
61
|
+
var execFileAsync = promisify(execFile);
|
|
62
|
+
var DIR = ".afterbefore";
|
|
63
|
+
async function run(cmd, args) {
|
|
64
|
+
return execFileAsync(cmd, args, { cwd: process.cwd() });
|
|
65
|
+
}
|
|
66
|
+
async function ghAvailable() {
|
|
67
|
+
try {
|
|
68
|
+
await run("gh", ["--version"]);
|
|
69
|
+
return true;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function getPrInfo() {
|
|
75
|
+
try {
|
|
76
|
+
const { stdout } = await run("gh", [
|
|
77
|
+
"pr",
|
|
78
|
+
"view",
|
|
79
|
+
"--json",
|
|
80
|
+
"number,url,headRepository,headRefName"
|
|
81
|
+
]);
|
|
82
|
+
return JSON.parse(stdout);
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function fileExists(path) {
|
|
88
|
+
try {
|
|
89
|
+
await access(path);
|
|
90
|
+
return true;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function handlePush(req) {
|
|
96
|
+
if (!await ghAvailable()) {
|
|
97
|
+
return NextResponse2.json(
|
|
98
|
+
{ success: false, error: "GitHub CLI (gh) is not installed or not in PATH" },
|
|
99
|
+
{ status: 500 }
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const pr = await getPrInfo();
|
|
103
|
+
if (!pr) {
|
|
104
|
+
return NextResponse2.json(
|
|
105
|
+
{ success: false, error: "No PR found for current branch" },
|
|
106
|
+
{ status: 404 }
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const dir = join2(process.cwd(), DIR);
|
|
110
|
+
const beforePath = join2(dir, "before.png");
|
|
111
|
+
const afterPath = join2(dir, "after.png");
|
|
112
|
+
const [hasBefore, hasAfter] = await Promise.all([
|
|
113
|
+
fileExists(beforePath),
|
|
114
|
+
fileExists(afterPath)
|
|
115
|
+
]);
|
|
116
|
+
if (!hasBefore || !hasAfter) {
|
|
117
|
+
const missing = [
|
|
118
|
+
!hasBefore && "before.png",
|
|
119
|
+
!hasAfter && "after.png"
|
|
120
|
+
].filter(Boolean);
|
|
121
|
+
return NextResponse2.json(
|
|
122
|
+
{ success: false, error: `Missing screenshots: ${missing.join(", ")}` },
|
|
123
|
+
{ status: 400 }
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
await run("git", ["add", beforePath, afterPath]);
|
|
128
|
+
await run("git", [
|
|
129
|
+
"commit",
|
|
130
|
+
"-m",
|
|
131
|
+
"chore: add before/after screenshots"
|
|
132
|
+
]);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
const msg = String(err);
|
|
135
|
+
if (!msg.includes("nothing to commit")) {
|
|
136
|
+
return NextResponse2.json(
|
|
137
|
+
{ success: false, error: "Failed to commit screenshots", detail: msg },
|
|
138
|
+
{ status: 500 }
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await run("git", ["push"]);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return NextResponse2.json(
|
|
146
|
+
{ success: false, error: "Failed to push to remote", detail: String(err) },
|
|
147
|
+
{ status: 500 }
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const owner = pr.headRepository.owner.login;
|
|
151
|
+
const repo = pr.headRepository.name;
|
|
152
|
+
const branch = pr.headRefName;
|
|
153
|
+
const rawBase = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
154
|
+
const beforeUrl = `${rawBase}/${DIR}/before.png`;
|
|
155
|
+
const afterUrl = `${rawBase}/${DIR}/after.png`;
|
|
156
|
+
const ts = Date.now();
|
|
157
|
+
const commentBody = [
|
|
158
|
+
"## Before / After",
|
|
159
|
+
"",
|
|
160
|
+
"| Before | After |",
|
|
161
|
+
"|--------|-------|",
|
|
162
|
+
`|  |  |`
|
|
163
|
+
].join("\n");
|
|
164
|
+
let commentUrl;
|
|
165
|
+
try {
|
|
166
|
+
const { stdout } = await run("gh", [
|
|
167
|
+
"pr",
|
|
168
|
+
"comment",
|
|
169
|
+
String(pr.number),
|
|
170
|
+
"--body",
|
|
171
|
+
commentBody
|
|
172
|
+
]);
|
|
173
|
+
commentUrl = stdout.trim() || void 0;
|
|
174
|
+
} catch (err) {
|
|
175
|
+
return NextResponse2.json(
|
|
176
|
+
{ success: false, error: "Failed to post PR comment", detail: String(err) },
|
|
177
|
+
{ status: 500 }
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return NextResponse2.json({
|
|
181
|
+
success: true,
|
|
182
|
+
prNumber: pr.number,
|
|
183
|
+
prUrl: pr.url,
|
|
184
|
+
commentUrl
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/server/open.ts
|
|
189
|
+
import { NextResponse as NextResponse3 } from "next/server";
|
|
190
|
+
import { exec } from "child_process";
|
|
191
|
+
import { join as join3 } from "path";
|
|
192
|
+
async function handleOpen(_req) {
|
|
193
|
+
const dir = join3(process.cwd(), ".afterbefore");
|
|
194
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
|
|
195
|
+
exec(`${cmd} "${dir}"`);
|
|
196
|
+
return NextResponse3.json({ success: true });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export {
|
|
200
|
+
handleSave,
|
|
201
|
+
handlePush,
|
|
202
|
+
handleOpen
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=chunk-XRQ4MAEV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/save.ts","../src/server/push.ts","../src/server/open.ts"],"sourcesContent":["import { NextRequest, NextResponse } from \"next/server\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nconst VALID_TYPES = [\"before\", \"after\"] as const;\ntype ScreenshotType = (typeof VALID_TYPES)[number];\n\nconst VALID_MODES = [\"fullpage\", \"viewport\", \"area\"] as const;\n\nconst DATA_URL_PREFIX = \"data:image/png;base64,\";\n\ninterface SaveRequestBody {\n type: ScreenshotType;\n mode: string;\n image: string;\n}\n\nexport async function handleSave(req: NextRequest): Promise<NextResponse> {\n let body: SaveRequestBody;\n try {\n body = await req.json();\n } catch {\n return NextResponse.json(\n { error: \"Invalid JSON body\" },\n { status: 400 },\n );\n }\n\n const { type, mode, image } = body;\n\n if (!VALID_TYPES.includes(type as ScreenshotType)) {\n return NextResponse.json(\n { error: `Invalid type: must be \"before\" or \"after\"` },\n { status: 400 },\n );\n }\n\n if (!VALID_MODES.includes(mode as (typeof VALID_MODES)[number])) {\n return NextResponse.json(\n { error: `Invalid mode: must be \"fullpage\", \"viewport\", or \"area\"` },\n { status: 400 },\n );\n }\n\n if (typeof image !== \"string\" || !image.startsWith(DATA_URL_PREFIX)) {\n return NextResponse.json(\n { error: \"Invalid image: must be a data:image/png;base64 data URL\" },\n { status: 400 },\n );\n }\n\n const base64 = image.slice(DATA_URL_PREFIX.length);\n const buffer = Buffer.from(base64, \"base64\");\n\n const dir = join(process.cwd(), \".afterbefore\");\n const filename = `${type}.png`;\n const filepath = join(dir, filename);\n const relativePath = `.afterbefore/${filename}`;\n\n try {\n await mkdir(dir, { recursive: true });\n await writeFile(filepath, buffer);\n } catch (err) {\n return NextResponse.json(\n { error: \"Failed to write screenshot\", detail: String(err) },\n { status: 500 },\n );\n }\n\n return NextResponse.json({ success: true, path: relativePath });\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { execFile } from \"node:child_process\";\nimport { access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nconst DIR = \".afterbefore\";\n\ninterface PrInfo {\n number: number;\n url: string;\n headRepository: { owner: { login: string }; name: string };\n headRefName: string;\n}\n\nasync function run(\n cmd: string,\n args: string[],\n): Promise<{ stdout: string; stderr: string }> {\n return execFileAsync(cmd, args, { cwd: process.cwd() });\n}\n\nasync function ghAvailable(): Promise<boolean> {\n try {\n await run(\"gh\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function getPrInfo(): Promise<PrInfo | null> {\n try {\n const { stdout } = await run(\"gh\", [\n \"pr\",\n \"view\",\n \"--json\",\n \"number,url,headRepository,headRefName\",\n ]);\n return JSON.parse(stdout) as PrInfo;\n } catch {\n return null;\n }\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function handlePush(req: NextRequest): Promise<NextResponse> {\n // 1. Check gh CLI availability\n if (!(await ghAvailable())) {\n return NextResponse.json(\n { success: false, error: \"GitHub CLI (gh) is not installed or not in PATH\" },\n { status: 500 },\n );\n }\n\n // 2. Check for active PR\n const pr = await getPrInfo();\n if (!pr) {\n return NextResponse.json(\n { success: false, error: \"No PR found for current branch\" },\n { status: 404 },\n );\n }\n\n // 3. Check that screenshot files exist\n const dir = join(process.cwd(), DIR);\n const beforePath = join(dir, \"before.png\");\n const afterPath = join(dir, \"after.png\");\n\n const [hasBefore, hasAfter] = await Promise.all([\n fileExists(beforePath),\n fileExists(afterPath),\n ]);\n\n if (!hasBefore || !hasAfter) {\n const missing = [\n !hasBefore && \"before.png\",\n !hasAfter && \"after.png\",\n ].filter(Boolean);\n return NextResponse.json(\n { success: false, error: `Missing screenshots: ${missing.join(\", \")}` },\n { status: 400 },\n );\n }\n\n // 4. Stage, commit, and push the screenshots\n try {\n await run(\"git\", [\"add\", beforePath, afterPath]);\n await run(\"git\", [\n \"commit\",\n \"-m\",\n \"chore: add before/after screenshots\",\n ]);\n } catch (err) {\n // Commit may fail if files are already committed with no changes.\n // That's fine -- we still want to push and comment.\n const msg = String(err);\n if (!msg.includes(\"nothing to commit\")) {\n return NextResponse.json(\n { success: false, error: \"Failed to commit screenshots\", detail: msg },\n { status: 500 },\n );\n }\n }\n\n try {\n await run(\"git\", [\"push\"]);\n } catch (err) {\n return NextResponse.json(\n { success: false, error: \"Failed to push to remote\", detail: String(err) },\n { status: 500 },\n );\n }\n\n // 5. Build raw GitHub URLs for the images\n const owner = pr.headRepository.owner.login;\n const repo = pr.headRepository.name;\n const branch = pr.headRefName;\n const rawBase = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;\n\n const beforeUrl = `${rawBase}/${DIR}/before.png`;\n const afterUrl = `${rawBase}/${DIR}/after.png`;\n\n // Cache-bust with timestamp so GitHub doesn't serve stale images\n const ts = Date.now();\n const commentBody = [\n \"## Before / After\",\n \"\",\n \"| Before | After |\",\n \"|--------|-------|\",\n `|  |  |`,\n ].join(\"\\n\");\n\n // 6. Post PR comment\n let commentUrl: string | undefined;\n try {\n const { stdout } = await run(\"gh\", [\n \"pr\",\n \"comment\",\n String(pr.number),\n \"--body\",\n commentBody,\n ]);\n // gh pr comment prints the comment URL to stdout\n commentUrl = stdout.trim() || undefined;\n } catch (err) {\n return NextResponse.json(\n { success: false, error: \"Failed to post PR comment\", detail: String(err) },\n { status: 500 },\n );\n }\n\n return NextResponse.json({\n success: true,\n prNumber: pr.number,\n prUrl: pr.url,\n commentUrl,\n });\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { exec } from \"node:child_process\";\nimport { join } from \"node:path\";\n\nexport async function handleOpen(_req: NextRequest): Promise<NextResponse> {\n const dir = join(process.cwd(), \".afterbefore\");\n\n const cmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"explorer\"\n : \"xdg-open\";\n\n exec(`${cmd} \"${dir}\"`);\n\n return NextResponse.json({ success: true });\n}\n"],"mappings":"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","names":["NextResponse","join","NextResponse","join"]}
|
package/dist/next.d.ts
ADDED
package/dist/next.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/next.ts
|
|
2
|
+
function withAfterBefore(config = {}) {
|
|
3
|
+
if (process.env.NODE_ENV !== "development") return config;
|
|
4
|
+
return {
|
|
5
|
+
...config,
|
|
6
|
+
async rewrites() {
|
|
7
|
+
const existing = await config.rewrites?.() ?? [];
|
|
8
|
+
const rules = [
|
|
9
|
+
{
|
|
10
|
+
source: "/__afterbefore/:path*",
|
|
11
|
+
destination: "/api/__afterbefore/:path*"
|
|
12
|
+
}
|
|
13
|
+
];
|
|
14
|
+
if (Array.isArray(existing)) {
|
|
15
|
+
return [...rules, ...existing];
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
beforeFiles: [...rules, ...existing.beforeFiles ?? []],
|
|
19
|
+
afterFiles: existing.afterFiles ?? [],
|
|
20
|
+
fallback: existing.fallback ?? []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
withAfterBefore
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=next.js.map
|
package/dist/next.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/next.ts"],"sourcesContent":["import type { NextConfig } from \"next\";\n\ntype Rewrite = { source: string; destination: string };\n\nexport function withAfterBefore(config: NextConfig = {}): NextConfig {\n if (process.env.NODE_ENV !== \"development\") return config;\n\n return {\n ...config,\n async rewrites() {\n const existing = (await config.rewrites?.()) ?? [];\n const rules: Rewrite[] = [\n {\n source: \"/__afterbefore/:path*\",\n destination: \"/api/__afterbefore/:path*\",\n },\n ];\n\n if (Array.isArray(existing)) {\n return [...rules, ...existing];\n }\n\n return {\n beforeFiles: [...rules, ...(existing.beforeFiles ?? [])],\n afterFiles: existing.afterFiles ?? [],\n fallback: existing.fallback ?? [],\n };\n },\n };\n}\n"],"mappings":";AAIO,SAAS,gBAAgB,SAAqB,CAAC,GAAe;AACnE,MAAI,QAAQ,IAAI,aAAa,cAAe,QAAO;AAEnD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,WAAW;AACf,YAAM,WAAY,MAAM,OAAO,WAAW,KAAM,CAAC;AACjD,YAAM,QAAmB;AAAA,QACvB;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,QACf;AAAA,MACF;AAEA,UAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,eAAO,CAAC,GAAG,OAAO,GAAG,QAAQ;AAAA,MAC/B;AAEA,aAAO;AAAA,QACL,aAAa,CAAC,GAAG,OAAO,GAAI,SAAS,eAAe,CAAC,CAAE;AAAA,QACvD,YAAY,SAAS,cAAc,CAAC;AAAA,QACpC,UAAU,SAAS,YAAY,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|