diff --git a/apps/analog-app-e2e-cypress/src/e2e/app.cy.ts b/apps/analog-app-e2e-cypress/src/e2e/app.cy.ts index e0e041ab8..db18dbc1a 100644 --- a/apps/analog-app-e2e-cypress/src/e2e/app.cy.ts +++ b/apps/analog-app-e2e-cypress/src/e2e/app.cy.ts @@ -1,10 +1,19 @@ import * as app from '../support/app.po'; describe('My Store', () => { - beforeEach(() => cy.visit('/')); - it(`Given the user has navigated to the home page Then the app title is visible`, () => { + cy.visit('/'); app.getTitle().contains(/my store/i); }); + + it(`Given the user has navigated an invalid page then the page not found title is visible`, () => { + cy.visit('/bad'); + app.get404Title().contains(/page not found/i); + }); + + it(`Given the user has navigated an invalid nested page then the page not found title is visible`, () => { + cy.visit('/shipping/bad'); + app.getNested404Title().contains(/shipping page not found/i); + }); }); diff --git a/apps/analog-app-e2e-cypress/src/support/app.po.ts b/apps/analog-app-e2e-cypress/src/support/app.po.ts index e6566b7fd..1e77b0203 100644 --- a/apps/analog-app-e2e-cypress/src/support/app.po.ts +++ b/apps/analog-app-e2e-cypress/src/support/app.po.ts @@ -1 +1,6 @@ export const getTitle = () => cy.contains('h1', /my store/i); + +export const get404Title = () => cy.contains('h2', /page not found/i); + +export const getNested404Title = () => + cy.contains('h2', /shipping page not found/i); diff --git a/apps/analog-app/src/app/cart.service.ts b/apps/analog-app/src/app/cart.service.ts index 4978de9cd..a10797932 100644 --- a/apps/analog-app/src/app/cart.service.ts +++ b/apps/analog-app/src/app/cart.service.ts @@ -7,6 +7,7 @@ import { Product } from './products'; }) export class CartService { items: Product[] = []; + private readonly apiURL = import.meta.env.VITE_ANALOG_PUBLIC_BASE_URL; constructor(private http: HttpClient) {} @@ -25,7 +26,7 @@ export class CartService { getShippingPrices() { return this.http.get<{ type: string; price: number }[]>( - '/assets/shipping.json' + `${this.apiURL}/assets/shipping.json` ); } } diff --git a/apps/analog-app/src/app/pages/[...slug].page.ts b/apps/analog-app/src/app/pages/[...slug].page.ts new file mode 100644 index 000000000..00a2c565b --- /dev/null +++ b/apps/analog-app/src/app/pages/[...slug].page.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-page-not-found', + standalone: true, + template: `

Page Not Found

`, +}) +export default class PageNotFoundComponent {} diff --git a/apps/analog-app/src/app/pages/[...slug].server.ts b/apps/analog-app/src/app/pages/[...slug].server.ts new file mode 100644 index 000000000..a5ab19bf2 --- /dev/null +++ b/apps/analog-app/src/app/pages/[...slug].server.ts @@ -0,0 +1,9 @@ +import { PageServerLoad } from '@analogjs/router'; + +export function load({ params }: PageServerLoad) { + console.log('params', params); + + return { + loaded: true, + }; +} diff --git a/apps/analog-app/src/app/pages/shipping/[...slug].page.ts b/apps/analog-app/src/app/pages/shipping/[...slug].page.ts new file mode 100644 index 000000000..051e059d8 --- /dev/null +++ b/apps/analog-app/src/app/pages/shipping/[...slug].page.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-shipping-page-not-found', + standalone: true, + template: `

Shipping Page Not Found

`, +}) +export default class ShippingPageNotFoundComponent {} diff --git a/apps/analog-app/src/app/pages/shipping/[...slug].server.ts b/apps/analog-app/src/app/pages/shipping/[...slug].server.ts new file mode 100644 index 000000000..3df66e3d7 --- /dev/null +++ b/apps/analog-app/src/app/pages/shipping/[...slug].server.ts @@ -0,0 +1,9 @@ +import { PageServerLoad } from '@analogjs/router'; + +export function load({ params }: PageServerLoad) { + console.log('slug', params?.['slug']); + + return { + loaded: true, + }; +} diff --git a/apps/analog-app/src/public/assets/shipping.json b/apps/analog-app/src/public/assets/shipping.json index fe51488c7..d8390c4c2 100644 --- a/apps/analog-app/src/public/assets/shipping.json +++ b/apps/analog-app/src/public/assets/shipping.json @@ -1 +1,14 @@ -[] +[ + { + "type": "Overnight", + "price": 25.99 + }, + { + "type": "2-Day", + "price": 9.99 + }, + { + "type": "Postal", + "price": 2.99 + } +] diff --git a/apps/analog-app/src/vite-env.d.ts b/apps/analog-app/src/vite-env.d.ts index 11f02fe2a..c08d88d48 100644 --- a/apps/analog-app/src/vite-env.d.ts +++ b/apps/analog-app/src/vite-env.d.ts @@ -1 +1,9 @@ /// + +interface ImportMetaEnv { + readonly VITE_ANALOG_PUBLIC_BASE_URL: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/packages/router/src/lib/route-config.ts b/packages/router/src/lib/route-config.ts index 36fa6a8e0..4d3b83807 100644 --- a/packages/router/src/lib/route-config.ts +++ b/packages/router/src/lib/route-config.ts @@ -35,7 +35,9 @@ export function toRouteConfig(routeMeta: RouteMeta | undefined): RouteConfig { }; if (PAGE_ENDPOINTS[routeConfig[ANALOG_META_KEY].endpointKey]) { - const { queryParams, fragment: hash, params } = route; + const { queryParams, fragment: hash, params, parent } = route; + const segment = + parent?.url.map((segment) => segment.path).join('/') || ''; const url = new URL('', import.meta.env['VITE_ANALOG_PUBLIC_BASE_URL']); url.pathname = `/api/_analog${routeConfig[ANALOG_META_KEY].endpoint}`; url.search = `${new URLSearchParams(queryParams).toString()}`; @@ -44,6 +46,7 @@ export function toRouteConfig(routeMeta: RouteMeta | undefined): RouteConfig { Object.keys(params).forEach((param) => { url.pathname = url.pathname.replace(`[${param}]`, params[param]); }); + url.pathname = url.pathname.replace('**', segment); if ((globalThis as any).$fetch) { return (globalThis as any).$fetch(url.pathname); diff --git a/packages/router/src/lib/routes.ts b/packages/router/src/lib/routes.ts index afe38fb8c..9816fe621 100644 --- a/packages/router/src/lib/routes.ts +++ b/packages/router/src/lib/routes.ts @@ -163,6 +163,7 @@ function toRoutes(rawRoutes: RawRoute[], files: Files): Route[] { // get endpoint path const rawEndpoint = rawRoute.filename .replace(/\.page\.ts$/, '') + .replace(/\[\.{3}.+\]/, '**') // [...not-found] => ** .split(APP_DIR)[1]; // replace periods, remove (index) paths diff --git a/packages/vite-plugin-nitro/src/lib/utils/get-page-handlers.ts b/packages/vite-plugin-nitro/src/lib/utils/get-page-handlers.ts index ed614dba1..fc75fd384 100644 --- a/packages/vite-plugin-nitro/src/lib/utils/get-page-handlers.ts +++ b/packages/vite-plugin-nitro/src/lib/utils/get-page-handlers.ts @@ -19,10 +19,10 @@ export function getPageHandlers({ workspaceRoot, rootDir }: GetHandlersArgs) { const route = endpointFile .replace(path.resolve(workspaceRoot, rootDir, 'src/app'), '') .replace(/\.server\.ts$/, '') - .replace(/\[\.{3}]/g, '**') - .replace(/\[\.{3}(\w+)]/g, '**:$1') + .replace(/\[\.{3}(.+)\]/g, '**:$1') + .replace(/\[\.{3}(\w+)\]/g, '**:$1') .replace(/\/\((.*?)\)$/, '/-$1-') - .replace(/\[(\w+)]/g, ':$1') + .replace(/\[(\w+)\]/g, ':$1') .replace(/\./g, '/'); return {