From f26b23c943c9d74b4a0ec2d718f3c6c12bebba70 Mon Sep 17 00:00:00 2001 From: Aiji Uejima Date: Tue, 29 Jun 2021 23:54:34 +0900 Subject: [PATCH] feat(withSplit): passable rewrite rules manually --- src/make-rewrites.spec.ts | 68 +++++++ src/make-rewrites.ts | 56 ++++++ src/merge-rewrites.spec.ts | 367 ++++++++++++++++++++++++++----------- src/with-split.spec.ts | 58 ++++++ src/with-split.ts | 63 +------ 5 files changed, 455 insertions(+), 157 deletions(-) create mode 100644 src/make-rewrites.spec.ts create mode 100644 src/make-rewrites.ts diff --git a/src/make-rewrites.spec.ts b/src/make-rewrites.spec.ts new file mode 100644 index 0000000..1ae6e31 --- /dev/null +++ b/src/make-rewrites.spec.ts @@ -0,0 +1,68 @@ +import { makeRewrites } from './make-rewrites' + +describe('makeRewrites', () => { + it('must return inactiveRewiteRule when no challenger', () => { + return makeRewrites({ main: '' }, 'top', true, undefined)().then((res) => { + expect(res).toEqual({ + beforeFiles: [{ destination: '/top', source: '/' }] + }) + }) + }) + it('must return inactiveRewiteRule when active flag is not true', () => { + return makeRewrites( + { main: '', challenger: 'https://example.com' }, + 'top', + false, + undefined + )().then((res) => { + expect(res).toEqual({ + beforeFiles: [{ destination: '/top', source: '/' }] + }) + }) + }) + it('must return activeRewiteRule when has mappings and active flag is true', () => { + return makeRewrites( + { main: '', challenger: 'https://example.com' }, + 'top', + true, + undefined + )().then((res) => { + expect(res).toEqual({ + beforeFiles: [ + { + destination: '/top/', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/' + }, + { + destination: '/:path*', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/:path*/' + }, + { + destination: 'https://example.com/top/', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*' + }, + { destination: '/_split-challenge', source: '/:path*/' } + ] + }) + }) + }) +}) \ No newline at end of file diff --git a/src/make-rewrites.ts b/src/make-rewrites.ts new file mode 100644 index 0000000..229d896 --- /dev/null +++ b/src/make-rewrites.ts @@ -0,0 +1,56 @@ +import { Rewrite, RouteHas } from 'next/dist/lib/load-custom-routes' +import { mergeRewrites } from './merge-rewrites' +const rule = ( + source: string, + destination: string, + additional = {} +): Rewrite => ({ + source, + destination, + ...additional +}) +const has = (value: string): RouteHas[] => [ + { + type: 'cookie', + key: 'next-with-split', + value + } +] + +export type Mappings = { [branch: string]: string } + +export type Rewrites = { + beforeFiles?: Rewrite[] + afterFiles?: Rewrite[] + fallback?: Rewrite[] +} + +export const makeRewrites = + ( + mappings: Mappings, + rootPage: string, + active: boolean, + originalRewrite: (() => Promise) | undefined + ) => + async (): Promise => { + const rewrite = await originalRewrite?.() + if (!active || Object.keys(mappings).length < 2) + return mergeRewrites(rewrite, { + beforeFiles: [rule('/', `/${rootPage}`)] + }) + + return mergeRewrites(rewrite, { + beforeFiles: [ + ...Object.entries(mappings) + .map(([branch, origin]) => [ + rule('/', `${origin}/${rootPage}/`, { has: has(branch) }), + rule('/:path*/', `${origin}/:path*`, { has: has(branch) }), + ...(origin + ? [rule('/:path*', `${origin}/:path*`, { has: has(branch) })] + : []) + ]) + .flat(), + rule('/:path*/', '/_split-challenge') + ] + }) + } diff --git a/src/merge-rewrites.spec.ts b/src/merge-rewrites.spec.ts index 7ff1fbb..dc1b0c7 100644 --- a/src/merge-rewrites.spec.ts +++ b/src/merge-rewrites.spec.ts @@ -26,131 +26,292 @@ const activeRewrite = { } describe('mergeRewrites', () => { - it('', () => { - expect(mergeRewrites(undefined, inactiveRewrite)).toEqual(inactiveRewrite) - - expect(mergeRewrites(undefined, activeRewrite)).toEqual(activeRewrite) + describe('originalRewrite is undefind', () => { + it('must return untouched rewrite', () => { + expect(mergeRewrites(undefined, inactiveRewrite)).toEqual(inactiveRewrite) + expect(mergeRewrites(undefined, activeRewrite)).toEqual(activeRewrite) + }) + }) - expect( - mergeRewrites( - [ + describe('originalRewrite is array type rewrite', () => { + it('must return inserted beforeFiles when inactive', () => { + expect( + mergeRewrites( + [ + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + } + ], + inactiveRewrite + ) + ).toEqual({ + beforeFiles: [ + { destination: '/top', source: '/' }, { destination: '/foo/', source: '/bar', has: [{ key: 'baz', type: 'cookie', value: 'qux' }] } - ], - inactiveRewrite - ) - ).toEqual({ - beforeFiles: [ - { destination: '/top', source: '/' }, - { - destination: '/foo/', - source: '/bar', - has: [{ key: 'baz', type: 'cookie', value: 'qux' }] - } - ] - }) + ] + }) - expect( - mergeRewrites( - [ + expect( + mergeRewrites( + [ + { + destination: '/foo/', + source: '/bar' + } + ], + inactiveRewrite + ) + ).toEqual({ + beforeFiles: [ + { destination: '/top', source: '/' }, { destination: '/foo/', - source: '/bar', - has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + source: '/bar' } - ], - activeRewrite - ) - ).toEqual({ - beforeFiles: [ - { - destination: '/top/', - has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], - source: '/' - }, - { - destination: 'https://example.com/:path*', - has: [ - { key: 'next-with-split', type: 'cookie', value: 'challenger' } - ], - source: '/:path*' - }, - { - destination: '/foo/', - source: '/bar', - has: [ - { key: 'next-with-split', type: 'cookie', value: 'main' }, - { key: 'baz', type: 'cookie', value: 'qux' } - ] - }, - { destination: '/_split-challenge', source: '/:path*/' } - ] + ] + }) }) - expect( - mergeRewrites( - { - beforeFiles: [ + it('must return inserted beforeFiles before challenge-split with has cookie condition when active', () => { + expect( + mergeRewrites( + [ { destination: '/foo/', source: '/bar', has: [{ key: 'baz', type: 'cookie', value: 'qux' }] } - ] - }, - inactiveRewrite - ) - ).toEqual({ - beforeFiles: [ - { destination: '/top', source: '/' }, - { - destination: '/foo/', - source: '/bar', - has: [{ key: 'baz', type: 'cookie', value: 'qux' }] - } - ] - }) + ], + activeRewrite + ) + ).toEqual({ + beforeFiles: [ + { + destination: '/top/', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*' + }, + { + destination: '/foo/', + source: '/bar', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'main' }, + { key: 'baz', type: 'cookie', value: 'qux' } + ] + }, + { destination: '/_split-challenge', source: '/:path*/' } + ] + }) - expect( - mergeRewrites( - { - beforeFiles: [ + expect( + mergeRewrites( + [ { destination: '/foo/', - source: '/bar', - has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + source: '/bar' } - ] - }, - activeRewrite - ) - ).toEqual({ - beforeFiles: [ - { - destination: '/top/', - has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], - source: '/' - }, - { - destination: 'https://example.com/:path*', - has: [ - { key: 'next-with-split', type: 'cookie', value: 'challenger' } ], - source: '/:path*' - }, - { - destination: '/foo/', - source: '/bar', - has: [ - { key: 'next-with-split', type: 'cookie', value: 'main' }, - { key: 'baz', type: 'cookie', value: 'qux' } - ] - }, - { destination: '/_split-challenge', source: '/:path*/' } - ] + activeRewrite + ) + ).toEqual({ + beforeFiles: [ + { + destination: '/top/', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*' + }, + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }] + }, + { destination: '/_split-challenge', source: '/:path*/' } + ] + }) + }) + }) + + describe('originalRewrite is object type rewrite', () => { + it('must return inserted beforeFiles when inactive', () => { + expect( + mergeRewrites( + { + beforeFiles: [ + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + } + ] + }, + inactiveRewrite + ) + ).toEqual({ + beforeFiles: [ + { destination: '/top', source: '/' }, + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + } + ] + }) + + expect( + mergeRewrites( + { + afterFiles: [ + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + } + ] + }, + inactiveRewrite + ) + ).toEqual({ + beforeFiles: [{ destination: '/top', source: '/' }], + afterFiles: [ + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + } + ] + }) + }) + + it('must return inserted beforeFiles before challenge-split with has cookie condition when active', () => { + expect( + mergeRewrites( + { + beforeFiles: [ + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'baz', type: 'cookie', value: 'qux' }] + } + ] + }, + activeRewrite + ) + ).toEqual({ + beforeFiles: [ + { + destination: '/top/', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*' + }, + { + destination: '/foo/', + source: '/bar', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'main' }, + { key: 'baz', type: 'cookie', value: 'qux' } + ] + }, + { destination: '/_split-challenge', source: '/:path*/' } + ] + }) + + expect( + mergeRewrites( + { + beforeFiles: [ + { + destination: '/foo/', + source: '/bar' + } + ] + }, + activeRewrite + ) + ).toEqual({ + beforeFiles: [ + { + destination: '/top/', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*' + }, + { + destination: '/foo/', + source: '/bar', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }] + }, + { destination: '/_split-challenge', source: '/:path*/' } + ] + }) + + expect( + mergeRewrites( + { + afterFiles: [ + { + destination: '/foo/', + source: '/bar' + } + ] + }, + activeRewrite + ) + ).toEqual({ + beforeFiles: [ + { + destination: '/top/', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*' + }, + { destination: '/_split-challenge', source: '/:path*/' } + ], + afterFiles: [ + { + destination: '/foo/', + source: '/bar' + } + ] + }) }) }) }) diff --git a/src/with-split.spec.ts b/src/with-split.spec.ts index 1805d27..0fd9d53 100644 --- a/src/with-split.spec.ts +++ b/src/with-split.spec.ts @@ -12,6 +12,8 @@ jest.mock('./check-existing-split-challenge', () => ({ } })) +jest.spyOn(console, 'warn').mockImplementation((mes) => console.log(mes)) + describe('withSplit', () => { const OLD_ENV = process.env beforeEach(() => { @@ -172,4 +174,60 @@ describe('withSplit', () => { }) }) }) + + it('must overwrite trailingSlash: true when passed trailingSlash: false', () => { + const conf = withSplit({ trailingSlash: false }) + expect(conf.trailingSlash).toEqual(true) + }) + + it('must warn when runs on challenger branch and active flag is true', () => { + process.env = { + ...process.env, + VERCEL_GIT_COMMIT_REF: 'challenger' + } + const conf = withSplit({ + splits: { + active: true, + branchMappings: { challenger: 'https://example.com' } + } + }) + return conf.rewrites().then((res) => { + expect(res).toEqual({ + beforeFiles: [ + { + destination: '/top/', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/' + }, + { + destination: '/:path*', + has: [{ key: 'next-with-split', type: 'cookie', value: 'main' }], + source: '/:path*/' + }, + { + destination: 'https://example.com/top/', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*/' + }, + { + destination: 'https://example.com/:path*', + has: [ + { key: 'next-with-split', type: 'cookie', value: 'challenger' } + ], + source: '/:path*' + }, + { destination: '/_split-challenge', source: '/:path*/' } + ] + }) + }) + }) }) diff --git a/src/with-split.ts b/src/with-split.ts index 8f16cd1..9776f8f 100644 --- a/src/with-split.ts +++ b/src/with-split.ts @@ -1,58 +1,8 @@ +import { Rewrite } from 'next/dist/lib/load-custom-routes' import { checkExistingIndex } from './check-existing-index' import { checkExistingSplitChallenge } from './check-existing-split-challenge' import { info, warn } from './log' -import { Rewrite, RouteHas } from 'next/dist/lib/load-custom-routes' - -const rule = ( - source: string, - destination: string, - additional = {} -): Rewrite => ({ - source, - destination, - ...additional -}) -const has = (value = 'original'): RouteHas[] => [ - { - type: 'cookie', - key: 'next-with-split', - value - } -] - -type Mappings = { [branch: string]: string } - -type Rewrites = { - beforeFiles?: Rewrite[] - afterFiles?: Rewrite[] - fallback?: Rewrite[] -} - -const makeRewrites = - (mappings: Mappings, rootPage: string, active: boolean) => - async (): Promise => { - if (!active || Object.keys(mappings).length < 2) - return { - beforeFiles: [rule('/', `/${rootPage}`)] - } - - return { - beforeFiles: [ - ...Object.entries(mappings) - .map(([branch, origin]) => - [ - rule('/', `${origin}/${rootPage}/`, { has: has(branch) }), - rule('/:path*/', `${origin}/:path*`, { has: has(branch) }), - ...(origin - ? [rule('/:path*', `${origin}/:path*`, { has: has(branch) })] - : []) - ] - ) - .flat(), - rule('/:path*/', '/_split-challenge') - ] - } - } +import { Mappings, Rewrites, makeRewrites } from './make-rewrites' type Options = { branchMappings: Mappings @@ -72,7 +22,7 @@ type WithSplitArgs = { splits?: Partial env?: Record trailingSlash?: boolean - rewrites?: Promise + rewrites?: () => Promise [x: string]: unknown } @@ -124,6 +74,11 @@ export const withSplit = (args: WithSplitArgs): WithSplitResult => { }, trailingSlash: true, assetPrefix: mappings[process.env.VERCEL_GIT_COMMIT_REF ?? ''] ?? '', - rewrites: makeRewrites(mappings, options.rootPage, options.active) + rewrites: makeRewrites( + mappings, + options.rootPage, + options.active, + nextConfig.rewrites + ) } }