feat: a named author, an About page and a Payload blog #140
No files matched your search
@@ -83,9 +83,21 @@ route to `/cms-api` is sufficient and complete. Do not add the exclusion.
|
||||
## File Structure
|
||||
|
||||
**Moved (Task 2)** — `app/*` → `app/(frontend)/*`, unchanged in content:
|
||||
`layout.tsx`, `page.tsx`, `globals.css`, `robots.ts`, `opengraph-image.tsx`,
|
||||
`icon.png`, `apple-icon.png`, `rankings/`, `admissions/`, `compare/`,
|
||||
`schools/`, `school/`, `api/`, `sitemaps/`, `sitemap.xml/`.
|
||||
`layout.tsx`, `page.tsx`, `globals.css`, `rankings/`, `admissions/`,
|
||||
`compare/`, `schools/`, `school/`, `api/`, `sitemaps/`, `sitemap.xml/`.
|
||||
|
||||
**Deliberately NOT moved — they stay at the `app/` root:** `robots.ts`,
|
||||
`opengraph-image.tsx`, `icon.png`, `apple-icon.png`.
|
||||
|
||||
Next.js metadata file conventions only produce stable root URLs at the `app/`
|
||||
root. Inside a route group they are treated as segment-scoped: verified during
|
||||
execution, moving them into `(frontend)` renamed `/icon.png` to
|
||||
`/icon-4usi79.png`, `/apple-icon.png` to `/apple-icon-4usi79.png`,
|
||||
`/opengraph-image` to `/opengraph-image-4usi79`, and dropped `/robots.txt`
|
||||
entirely. That would have broken the `/icon.png` cache-control rule and the
|
||||
`outputFileTracingIncludes['/opengraph-image']` entry in `next.config.mjs`,
|
||||
and silently removed the site's robots.txt. Route handlers (`sitemap.xml/`,
|
||||
`sitemaps/`, `api/`) are unaffected and move normally.
|
||||
|
||||
**Created:**
|
||||
|
||||
@@ -264,20 +276,25 @@ Write down the passing test count. It must be identical at Step 5.
|
||||
|
||||
```bash
|
||||
cd nextjs-app/app && mkdir -p "(frontend)"
|
||||
git mv layout.tsx page.tsx globals.css robots.ts opengraph-image.tsx \
|
||||
icon.png apple-icon.png \
|
||||
git mv layout.tsx page.tsx globals.css \
|
||||
rankings admissions compare schools school api sitemaps sitemap.xml \
|
||||
"(frontend)/"
|
||||
```
|
||||
|
||||
Verify nothing is left behind — `app/` should now contain only `(frontend)`:
|
||||
`robots.ts`, `opengraph-image.tsx`, `icon.png` and `apple-icon.png` stay at the
|
||||
`app/` root — see "Deliberately NOT moved" in the File Structure section above.
|
||||
Moving them silently changes their URLs and drops robots.txt.
|
||||
|
||||
Verify the split — `app/` should now contain `(frontend)` plus exactly the four
|
||||
metadata conventions:
|
||||
|
||||
```bash
|
||||
cd /Users/tudor/projects/school_compare/nextjs-app && ls app
|
||||
cd /Users/tudor/projects/school_compare/nextjs-app && ls -A app
|
||||
```
|
||||
|
||||
If anything else appears, move it too. `git status --short` is the
|
||||
authoritative check: untracked files do not show in `git diff --stat`.
|
||||
Expected: `(frontend)`, `apple-icon.png`, `icon.png`, `opengraph-image.tsx`,
|
||||
`robots.ts`. If a route directory is still there, move it. `git status --short`
|
||||
is the authoritative check: untracked files do not show in `git diff --stat`.
|
||||
|
||||
- [ ] **Step 3: Update the four test imports**
|
||||
|
||||
@@ -304,13 +321,27 @@ import { GET } from '@/app/(frontend)/api/[...path]/route';
|
||||
|
||||
- [ ] **Step 4: Find any other references to the old paths**
|
||||
|
||||
Two searches, because import specifiers are not the only way a file names a
|
||||
path. Quote the `--include` globs or zsh expands them.
|
||||
|
||||
```bash
|
||||
cd /Users/tudor/projects/school_compare && grep -rn "app/layout\|app/page\|@/app/" \
|
||||
--include=*.ts --include=*.tsx --include=*.js --include=*.mjs \
|
||||
cd /Users/tudor/projects/school_compare
|
||||
# 1. Import specifiers
|
||||
grep -rn "app/layout\|app/page\|@/app/" \
|
||||
--include="*.ts" --include="*.tsx" --include="*.js" --include="*.mjs" \
|
||||
nextjs-app --exclude-dir=node_modules --exclude-dir=.next | grep -v "app/(frontend)"
|
||||
|
||||
# 2. Filesystem paths — readFileSync/path.join targets, which search 1 misses.
|
||||
# __tests__/components/darkThemeSafety.test.ts reads app/globals.css this way
|
||||
# and fails to *run* when the path is stale, so it shows as a failed suite
|
||||
# rather than a failed assertion.
|
||||
grep -rn "'app'" nextjs-app/__tests__ --include="*.ts" --include="*.tsx" \
|
||||
| grep -v "(frontend)"
|
||||
```
|
||||
|
||||
Expected: no output. Fix anything that appears.
|
||||
Expected: no output from either. Fix anything that appears — note that
|
||||
`renderSchoolDetail.tsx` names `app/layout.tsx` in a comment, which should be
|
||||
updated for accuracy even though nothing breaks.
|
||||
|
||||
- [ ] **Step 5: Verify tests, types and build**
|
||||
|
||||
@@ -595,7 +626,7 @@ proxy's catch-all."
|
||||
indexed and must not be reachable through search results.
|
||||
|
||||
**Files:**
|
||||
- Modify: `nextjs-app/app/(frontend)/robots.ts`
|
||||
- Modify: `nextjs-app/app/robots.ts`
|
||||
- Modify: `nextjs-app/next.config.mjs`
|
||||
- Modify: `nextjs-app/__tests__/app/nextConfig.test.ts`
|
||||
- Create: `nextjs-app/__tests__/app/robots.test.ts`
|
||||
@@ -609,7 +640,7 @@ indexed and must not be reachable through search results.
|
||||
Create `nextjs-app/__tests__/app/robots.test.ts`:
|
||||
|
||||
```ts
|
||||
import robots from '@/app/(frontend)/robots';
|
||||
import robots from '@/app/robots';
|
||||
|
||||
describe('robots.txt', () => {
|
||||
it('disallows the admin panel and the CMS API', () => {
|
||||
@@ -651,7 +682,7 @@ rule exists.
|
||||
|
||||
- [ ] **Step 3: Add the robots disallow entries**
|
||||
|
||||
In `nextjs-app/app/(frontend)/robots.ts`, change the `disallow` array to:
|
||||
In `nextjs-app/app/robots.ts`, change the `disallow` array to:
|
||||
|
||||
```ts
|
||||
disallow: ['/api/', '/_next/', '/admin/', '/cms-api/'],
|
||||
@@ -1527,7 +1558,7 @@ without a webhook."
|
||||
- Create: `nextjs-app/components/blog/CalloutBlock.tsx` + `.module.css`
|
||||
- Create: `nextjs-app/app/(frontend)/blog/rss.xml/route.ts`
|
||||
- Create: `nextjs-app/app/(frontend)/content-sitemap.xml/route.ts`
|
||||
- Modify: `nextjs-app/app/(frontend)/robots.ts`
|
||||
- Modify: `nextjs-app/app/robots.ts`
|
||||
- Modify: `nextjs-app/lib/jsonld.ts`
|
||||
- Create: `nextjs-app/__tests__/app/blogMetadata.test.ts`
|
||||
- Modify: `e2e/tests/journeys.spec.ts`
|
||||
@@ -2123,7 +2154,7 @@ ${urls.map(({ loc, lastmod }) =>
|
||||
|
||||
- [ ] **Step 9: List both sitemaps in robots.txt**
|
||||
|
||||
In `nextjs-app/app/(frontend)/robots.ts`, change the `sitemap` key to:
|
||||
In `nextjs-app/app/robots.ts`, change the `sitemap` key to:
|
||||
|
||||
```ts
|
||||
sitemap: [absoluteUrl('/sitemap.xml'), absoluteUrl('/content-sitemap.xml')],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// environment provides — under jsdom this suite fails on import, not on an
|
||||
// assertion.
|
||||
import { NextRequest } from 'next/server';
|
||||
import { GET } from '@/app/api/[...path]/route';
|
||||
import { GET } from '@/app/(frontend)/api/[...path]/route';
|
||||
|
||||
function request(path: string) {
|
||||
return new NextRequest(`http://localhost:3000/api/${path}`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { metadata as homeMetadata } from '@/app/page';
|
||||
import { metadata as rankingsMetadata } from '@/app/rankings/page';
|
||||
import { metadata as admissionsMetadata } from '@/app/admissions/page';
|
||||
import { generateMetadata as compareMetadata } from '@/app/compare/page';
|
||||
import { metadata as homeMetadata } from '@/app/(frontend)/page';
|
||||
import { metadata as rankingsMetadata } from '@/app/(frontend)/rankings/page';
|
||||
import { metadata as admissionsMetadata } from '@/app/(frontend)/admissions/page';
|
||||
import { generateMetadata as compareMetadata } from '@/app/(frontend)/compare/page';
|
||||
|
||||
describe('canonical URLs', () => {
|
||||
it('the homepage canonicalises to the bare root', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { generateMetadata as placeMeta } from '@/app/schools/[place]/page';
|
||||
import { generateMetadata as placeMeta } from '@/app/(frontend)/schools/[place]/page';
|
||||
|
||||
jest.mock('@/lib/places', () => ({
|
||||
...jest.requireActual('@/lib/places'),
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('dark-theme safety', () => {
|
||||
* simply missed.
|
||||
*/
|
||||
describe('third-party surfaces under themed text', () => {
|
||||
const GLOBALS = path.join(__dirname, '..', '..', 'app', 'globals.css');
|
||||
const GLOBALS = path.join(__dirname, '..', '..', 'app', '(frontend)', 'globals.css');
|
||||
|
||||
/** Leaflet surfaces our own code writes token-coloured text onto. */
|
||||
const LEAFLET_POPUP_SURFACES = [
|
||||
@@ -171,7 +171,7 @@ describe('third-party surfaces under themed text', () => {
|
||||
*/
|
||||
describe('destination tokens', () => {
|
||||
const css = fs.readFileSync(
|
||||
path.join(__dirname, '..', '..', 'app', 'globals.css'), 'utf8');
|
||||
path.join(__dirname, '..', '..', 'app', '(frontend)', 'globals.css'), 'utf8');
|
||||
|
||||
const TOKENS = [
|
||||
'--dest-sixthform', '--dest-sfcollege', '--dest-fecollege',
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
import { nationalAveragesFixture } from './schoolFixtures';
|
||||
|
||||
// The shell calls useComparison(), which throws outside the provider. In the
|
||||
// app this wrapper comes from app/layout.tsx.
|
||||
// app this wrapper comes from app/(frontend)/layout.tsx.
|
||||
function withProviders(ui: ReactNode) {
|
||||
return <ComparisonProvider>{ui}</ComparisonProvider>;
|
||||
}
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user