diff --git a/.github/workflows/e2e-appdir.yml b/.github/workflows/e2e-appdir.yml index fd008522c0..9e858f7c52 100644 --- a/.github/workflows/e2e-appdir.yml +++ b/.github/workflows/e2e-appdir.yml @@ -41,7 +41,6 @@ jobs: env: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_BOT_AUTH_TOKEN }} NETLIFY_SITE_ID: 1d5a5c76-d445-4ae5-b694-b0d3f2e2c395 - NEXT_TEST_VERSION: canary - uses: actions/upload-artifact@v3 if: ${{ always() }} name: Upload test results diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dd88f7f780..c42269606f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,12 +15,6 @@ jobs: strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - node-version: [14, '*'] - exclude: - - os: macOS-latest - node-version: 14 - - os: windows-latest - node-version: 14 fail-fast: false steps: @@ -28,19 +22,12 @@ jobs: - name: Installing with LTS Node.js uses: actions/setup-node@v2 with: - node-version: 'lts/*' + node-version: 16 check-latest: true - name: NPM Install run: npm install - - name: Switching to Node.js ${{ matrix.node-version }} to run tests - uses: actions/setup-node@v2 - if: "${{ matrix.node-version != 'lts/*' }}" - with: - node-version: ${{ matrix.node-version }} - check-latest: true - name: Linting run: npm run format:ci - if: "${{ matrix.node-version == 'lts/*' }}" - name: Run tests against next@latest run: npm test canary: @@ -50,12 +37,6 @@ jobs: strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - node-version: [14, '*'] - exclude: - - os: macOS-latest - node-version: 14 - - os: windows-latest - node-version: 14 fail-fast: false if: github.ref_name == 'main' @@ -64,17 +45,11 @@ jobs: - name: Installing with LTS Node.js uses: actions/setup-node@v2 with: - node-version: 'lts/*' + node-version: 16 check-latest: true - name: NPM Install run: npm install - name: Install Next.js Canary run: npm install -D next@canary --legacy-peer-deps - - name: Switching to Node.js ${{ matrix.node-version }} to run tests - uses: actions/setup-node@v2 - if: "${{ matrix.node-version != 'lts/*' }}" - with: - node-version: ${{ matrix.node-version }} - check-latest: true - name: Run tests against next@canary run: npm test diff --git a/cypress/integration/default/appdir.spec.ts b/cypress/integration/default/appdir.spec.ts new file mode 100644 index 0000000000..6c024c1a8a --- /dev/null +++ b/cypress/integration/default/appdir.spec.ts @@ -0,0 +1,77 @@ +describe('appDir', () => { + it('renders ISR appdir pages as HTML by default', () => { + cy.request({ url: '/blog/erica/', followRedirect: false }).then((response) => { + expect(response.headers['content-type']).to.match(/^text\/html/) + }) + }) + + it('renders static appdir pages as HTML by default', () => { + cy.request({ url: '/blog/erica/first-post/', followRedirect: false }).then((response) => { + expect(response.headers['content-type']).to.match(/^text\/html/) + }) + }) + + it('renders dynamic appdir pages as HTML by default', () => { + cy.request({ url: '/blog/erica/random-post/', followRedirect: false }).then((response) => { + expect(response.headers['content-type']).to.match(/^text\/html/) + }) + }) + + it('returns RSC data for RSC requests to ISR pages', () => { + cy.request({ + url: '/blog/erica/', + headers: { + RSC: '1', + }, + followRedirect: false, + }).then((response) => { + expect(response.headers).to.have.property('content-type', 'application/octet-stream') + }) + }) + + it('returns RSC data for RSC requests to static pages', () => { + cy.request({ + url: '/blog/erica/first-post/', + headers: { + RSC: '1', + }, + followRedirect: false, + }).then((response) => { + expect(response.headers).to.have.property('content-type', 'application/octet-stream') + }) + }) + + it('returns RSC data for RSC requests to dynamic pages', () => { + cy.request({ + url: '/blog/erica/random-post/', + headers: { + RSC: '1', + }, + followRedirect: false, + }).then((response) => { + expect(response.headers).to.have.property('content-type', 'application/octet-stream') + }) + }) + + it('correctly redirects HTML requests for ISR pages', () => { + cy.request({ url: '/blog/erica', followRedirect: false }).then((response) => { + expect(response.status).to.equal(308) + expect(response.headers).to.have.property('location', '/blog/erica/') + }) + }) + + // This needs trailing slash handling to be fixed + it.skip('correctly redirects HTML requests for static pages', () => { + cy.request({ url: '/blog/erica/first-post', followRedirect: false }).then((response) => { + expect(response.status).to.equal(308) + expect(response.headers).to.have.property('location', '/blog/erica/first-post/') + }) + }) + + it('correctly redirects HTML requests for dynamic pages', () => { + cy.request({ url: '/blog/erica/random-post', followRedirect: false }).then((response) => { + expect(response.status).to.equal(308) + expect(response.headers).to.have.property('location', '/blog/erica/random-post/') + }) + }) +}) diff --git a/demos/default/.vscode/settings.json b/demos/default/.vscode/settings.json new file mode 100644 index 0000000000..d3fdae9a69 --- /dev/null +++ b/demos/default/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "typescript.tsdk": "../../node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true +} \ No newline at end of file diff --git a/demos/default/app/blog/[author]/[slug]/page.tsx b/demos/default/app/blog/[author]/[slug]/page.tsx new file mode 100644 index 0000000000..dd4b35de65 --- /dev/null +++ b/demos/default/app/blog/[author]/[slug]/page.tsx @@ -0,0 +1,60 @@ +import { notFound } from 'next/navigation' + +export const revalidate = null + +export const dynamicParams = true + +export default function Page({ params }) { + if (params.author === 'matt') { + return notFound() + } + return ( + <> +

/blog/[author]/[slug]

+

{JSON.stringify(params)}

+

{Date.now()}

+ + ) +} + +export function generateStaticParams({ params }: any) { + console.log('/blog/[author]/[slug] generateStaticParams', JSON.stringify(params)) + + switch (params.author) { + case 'erica': { + return [ + { + slug: 'first-post', + }, + ] + } + case 'sarah': { + return [ + { + slug: 'second-post', + }, + ] + } + case 'nick': { + return [ + { + slug: 'first-post', + }, + { + slug: 'second-post', + }, + ] + } + case 'rob': { + return [ + { + slug: 'second-post', + }, + ] + } + + default: { + throw new Error(`unexpected author param received ${params.author}`) + } + } +} diff --git a/demos/default/app/blog/[author]/layout.js b/demos/default/app/blog/[author]/layout.js new file mode 100644 index 0000000000..17fe74e233 --- /dev/null +++ b/demos/default/app/blog/[author]/layout.js @@ -0,0 +1,14 @@ +export default function Layout({ children, params }) { + return ( + <> +

{JSON.stringify(params)}

+ {children} + + ) +} + +export function generateStaticParams(params) { + console.log('/blog/[author] generateStaticParams', JSON.stringify(params)) + + return [{ author: 'nick' }, { author: 'sarah' }, { author: 'rob' }, { author: 'erica' }] +} diff --git a/demos/default/app/blog/[author]/page.js b/demos/default/app/blog/[author]/page.js new file mode 100644 index 0000000000..39489ea91f --- /dev/null +++ b/demos/default/app/blog/[author]/page.js @@ -0,0 +1,39 @@ +import Link from 'next/link' + +export default async function Page({ params }) { + await fetch('http://example.com', { + next: { revalidate: 10 }, + }) + return ( + <> +

/blog/[author]

+

{JSON.stringify(params)}

+

{Date.now()}

+ + /blog/erica + +
+ + /blog/sarah + +
+ + /blog/nick + +
+ + + /blog/erica/first-post + +
+ + /blog/sarah/second-post + +
+ + /blog/nick/first-post + +
+ + ) +} diff --git a/demos/default/app/layout.js b/demos/default/app/layout.js new file mode 100644 index 0000000000..f37ea744b8 --- /dev/null +++ b/demos/default/app/layout.js @@ -0,0 +1,10 @@ +export default function Layout({ children }) { + return ( + + + my static blog + + {children} + + ) +} diff --git a/demos/default/next.config.js b/demos/default/next.config.js index 33ae770631..890533b532 100644 --- a/demos/default/next.config.js +++ b/demos/default/next.config.js @@ -84,5 +84,6 @@ module.exports = { }, experimental: { optimizeCss: false, + appDir: true, }, } diff --git a/demos/default/pages/index.js b/demos/default/pages/index.js index 0a231445f9..555a7f7965 100644 --- a/demos/default/pages/index.js +++ b/demos/default/pages/index.js @@ -158,6 +158,12 @@ const Index = ({ shows, nodeEnv }) => { Rewrite to static (should show getStaticProps/1) +

appDir

+

Preview mode

Preview mode: