Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PFX-626] - Tuneup @gasket/plugin-nextjs: ports to gasket actions #789

Merged
merged 2 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/gasket-plugin-nextjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ module.exports = {

## Adding a Sitemap

When creating a new application with this plugin, you will be prompted with a question in the CLI asking if you would like to add a [sitemap] to your application.
When creating a new application with this plugin, you will be prompted with a question in the CLI asking if you would like to add a [sitemap] to your application.

Answering yes to this question will install `next-sitemap` as a dependency, generate a next-sitemap.config.js file, and add a `sitemap` npm script to your package.json. `next-sitemap` is an npm package that generates sitemaps and a robots.txt file for Next.js applications. Learn more by reading the [next-sitemap docs].

Expand Down Expand Up @@ -233,7 +233,7 @@ module.exports = {

## Utilities

This plugin adds a middleware which attaches a `getNextRoute` function to the request object. It is intended for use in server contexts where you need to know how a request will route to a next.js page. This async function returns null if the manifest could not be parsed or if the requested URL does not match a route. If a match _is_ found, an object with these properties is returned:
This plugin hooks the `actions` lifecycle creates a `getNextRoute` action. It is intended for use in server contexts where you need to know how a request will route to a next.js page. This async function returns null if the manifest could not be parsed or if the requested URL does not match a route. If a match _is_ found, an object with these properties is returned:

| Property | Type | Description |
|----------|------|-------------|
Expand All @@ -249,7 +249,7 @@ async function someMiddleware(req, res, next) {
const { groups } = req.url.match(route.namedRegex);
console.log(`Matched ${route.page} with parameters`, groups);
}

next();
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ export const metadata = {
charset: 'UTF-8'
};

export default IndexPage
export default IndexPage;
22 changes: 22 additions & 0 deletions packages/gasket-plugin-nextjs/lib/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { createConfig } = require('./utils/config');
const nextRoute = require('./utils/next-route');

/** @type {import('@gasket/core').HookHandler<'actions'>} */
module.exports = function actions(gasket) {
return {
getNextConfig(nextConfig) {
return async function setupNextConfig(phase, { defaultConfig }) {
let baseConfig;
if (nextConfig instanceof Function) {
baseConfig = await nextConfig(phase, { defaultConfig });
} else {
baseConfig = nextConfig ?? {};
}
return createConfig(gasket, phase === 'phase-production-build', baseConfig);
};
},
async getNextRoute(req) {
return await nextRoute(gasket, req);
}
};
};
2 changes: 1 addition & 1 deletion packages/gasket-plugin-nextjs/lib/apm-transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/** @type {import('@gasket/core').HookHandler<'apmTransaction'>} */
module.exports = async function apmTransaction(gasket, transaction, { req }) {
const route = await req.getNextRoute();
const route = await gasket.actions.getNextRoute(req);

if (!route) {
return;
Expand Down
12 changes: 7 additions & 5 deletions packages/gasket-plugin-nextjs/lib/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,9 @@ function addNpmScripts({ pkg, nextServerType, nextDevProxy, typescript }) {
const fileExtension = typescript ? 'ts' : 'js';
const scripts = {
defaultServer: {
'build': 'next build',
'start': 'next start',
'start:local': `next start & GASKET_ENV=local node server.${fileExtension}`,
'local': `next dev${nextDevProxy ? ` & nodemon server.${fileExtension}` : ''}`
build: 'next build',
start: 'next start',
local: `next dev${nextDevProxy ? ` & nodemon server.${fileExtension}` : ''}`
},
customServer: {
'build': 'next build',
Expand All @@ -132,6 +131,10 @@ function addNpmScripts({ pkg, nextServerType, nextDevProxy, typescript }) {
}
};

if (nextDevProxy && nextServerType === 'defaultServer') {
scripts.defaultServer['start:local'] = `next start & GASKET_ENV=local node server.${fileExtension}`;
}

pkg.add('scripts', scripts[nextServerType]);
}

Expand All @@ -154,7 +157,6 @@ function addConfig(createContext) {
}
}


module.exports = {
timing: {
before: ['@gasket/plugin-intl'],
Expand Down
12 changes: 6 additions & 6 deletions packages/gasket-plugin-nextjs/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ declare module '@gasket/core' {

export interface GasketActions {
getNextConfig?: (config?: NextConfig | NextConfigFunction) => (phase: string, context?: { defaultConfig?: any }) => Promise<NextConfig>
getNextRoute?: (req: IncomingMessage) => Promise<null | {
page: string;
regex: RegExp;
routeKeys: Record<string, string>;
namedRegex: RegExp;
}>;
}

export interface GasketConfig {
Expand Down Expand Up @@ -60,12 +66,6 @@ declare module '@gasket/plugin-webpack' {

declare module 'http' {
export interface IncomingMessage {
getNextRoute(): Promise<null | {
page: string;
regex: RegExp;
routeKeys: Record<string, string>;
namedRegex: RegExp;
}>;
path?: string;
}
}
Expand Down
22 changes: 4 additions & 18 deletions packages/gasket-plugin-nextjs/lib/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/// <reference types="@gasket/plugin-express" />

const { name, version, description } = require('../package.json');
const apmTransaction = require('./apm-transaction');
const metadata = require('./metadata');
const configure = require('./configure');
const { createConfig } = require('./utils/config');
const actions = require('./actions');
const prompt = require('./prompt');
const create = require('./create');
const { webpackConfig } = require('./webpack-config');
const middleware = require('./middleware');
const express = require('./express');
const fastify = require('./fastify');
const workbox = require('./workbox');
Expand All @@ -20,24 +21,9 @@ const plugin = {
hooks: {
configure,
webpackConfig,
actions(gasket) {
return {
getNextConfig(nextConfig) {
return async function setupNextConfig(phase, { defaultConfig }) {
let baseConfig;
if (nextConfig instanceof Function) {
baseConfig = await nextConfig(phase, { defaultConfig });
} else {
baseConfig = nextConfig ?? {};
}
return createConfig(gasket, phase === 'phase-production-build', baseConfig);
};
}
};
},
actions,
prompt,
create,
middleware,
express,
fastify,
apmTransaction,
Expand Down
16 changes: 0 additions & 16 deletions packages/gasket-plugin-nextjs/lib/middleware.js

This file was deleted.

10 changes: 10 additions & 0 deletions packages/gasket-plugin-nextjs/test/actions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const actions = require('../lib/actions');

describe('actions', () => {

it('has expected actions', () => {
const results = actions({});
expect(results).toHaveProperty('getNextConfig');
expect(results).toHaveProperty('getNextRoute');
});
});
65 changes: 33 additions & 32 deletions packages/gasket-plugin-nextjs/test/apm-transaction.test.js
Original file line number Diff line number Diff line change
@@ -1,84 +1,85 @@
const apmTransaction = require('../lib/apm-transaction');

describe('The apmTransaction hook', () => {
let transaction;
let transaction, mockGasket;

beforeEach(() => {
transaction = {
name: 'GET *',
addLabels: jest.fn()
};

mockGasket = {
actions: {
getNextRoute: jest.fn()
}
};
});

it('retains the original name if the route is not for a page', async () => {
transaction.name = 'GET /proxy/foo';
const req = { getNextRoute: async () => null };

await apmTransaction({}, transaction, { req });

await apmTransaction(mockGasket, transaction, { req: {} });
expect(transaction.name).toEqual('GET /proxy/foo');
});

it('returns the page name if the route is for a page', async () => {
mockGasket.actions.getNextRoute.mockResolvedValueOnce({
page: '/customer/[id]',
namedRegex: /^\/customer\/(?<id>[^/]+)(?:\/)?$/
});
const req = {
url: '/customer/123',
getNextRoute: async () => ({
page: '/customer/[id]',
namedRegex: /^\/customer\/(?<id>[^/]+)(?:\/)?$/
})
url: '/customer/123'
};

await apmTransaction({}, transaction, { req });
await apmTransaction(mockGasket, transaction, { req });

expect(transaction.name).toEqual('/customer/[id]');
});

it('does not set labels if the route is not for a page', async () => {
const req = { getNextRoute: async () => null };

await apmTransaction({}, transaction, { req });
await apmTransaction(mockGasket, transaction, { req: {} });

expect(transaction.addLabels).not.toHaveBeenCalled();
});

it('sets dynamic route params as labels if the route is for a page', async () => {
mockGasket.actions.getNextRoute.mockResolvedValueOnce({
page: '/cohorts/[cohortId]',
namedRegex: /^\/cohorts\/(?<cohortId>[^/]+)(?:\/)?$/
});
const req = {
url: '/cohorts/Rad%20People',
getNextRoute: async () => ({
page: '/cohorts/[cohortId]',
namedRegex: /^\/cohorts\/(?<cohortId>[^/]+)(?:\/)?$/
})
url: '/cohorts/Rad%20People'
};

await apmTransaction({}, transaction, { req });
await apmTransaction(mockGasket, transaction, { req });

expect(transaction.addLabels).toHaveBeenCalledWith({ cohortId: 'Rad People' });
});

it('handles URL segments that are not properly URL encoded', async () => {
mockGasket.actions.getNextRoute.mockResolvedValueOnce({
page: '/cohorts/[cohortId]',
namedRegex: /^\/cohorts\/(?<cohortId>[^/]+)(?:\/)?$/
});
const req = {
url: '/cohorts/TopTen%',
getNextRoute: async () => ({
page: '/cohorts/[cohortId]',
namedRegex: /^\/cohorts\/(?<cohortId>[^/]+)(?:\/)?$/
})
url: '/cohorts/TopTen%'
};

await apmTransaction({}, transaction, { req });
await apmTransaction(mockGasket, transaction, { req });

expect(transaction.addLabels).toHaveBeenCalledWith({ cohortId: 'TopTen%' });
});

it('ignores the query string when parsing the route', async () => {
mockGasket.actions.getNextRoute.mockResolvedValueOnce({
page: '/cohorts/[cohortId]',
namedRegex: /^\/cohorts\/(?<cohortId>[^/]+)(?:\/)?$/
});
const req = {
url: '/cohorts/Rad%20People?utm_source=TDFS_DASLNC&utm_medium=parkedpages&utm_campaign=x_corp_tdfs-daslnc_base&traffic_type=TDFS_DASLNC&traffic_id=daslnc&sayfa=20&act=ul&listurun=12&sublastcat=&subcat=61&marka=&sort=2&catname=Realistik%20Belden%20Ba%F0lamal%FD',
getNextRoute: async () => ({
page: '/cohorts/[cohortId]',
namedRegex: /^\/cohorts\/(?<cohortId>[^/]+)(?:\/)?$/
})
url: '/cohorts/Rad%20People?utm_source=TDFS_DASLNC&utm_medium=parkedpages&utm_campaign=x_corp_tdfs-daslnc_base&traffic_type=TDFS_DASLNC&traffic_id=daslnc&sayfa=20&act=ul&listurun=12&sublastcat=&subcat=61&marka=&sort=2&catname=Realistik%20Belden%20Ba%F0lamal%FD'
};

await apmTransaction({}, transaction, { req });
await apmTransaction(mockGasket, transaction, { req });

expect(transaction.addLabels).toHaveBeenCalledWith({ cohortId: 'Rad People' });
});
Expand Down
19 changes: 15 additions & 4 deletions packages/gasket-plugin-nextjs/test/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,24 @@ describe('create hook', () => {
await plugin.hooks.create.handler({}, mockContext);

expect(mockContext.pkg.add).toHaveBeenCalledWith('scripts', {
'build': 'next build',
'start': 'next start',
'start:local': 'next start & GASKET_ENV=local node server.js',
'local': 'next dev'
build: 'next build',
start: 'next start',
local: 'next dev'
});
});

it('adds start:local script for next cli w/devProxy', async function () {
mockContext.nextServerType = 'defaultServer';
mockContext.nextDevProxy = true;
await plugin.hooks.create.handler({}, mockContext);

expect(mockContext.pkg.add).toHaveBeenCalledWith('scripts',
expect.objectContaining({
'start:local': 'next start & GASKET_ENV=local node server.js'
})
);
});

it('adds the appropriate npm scripts for next custom server', async function () {
mockContext.nextServerType = 'customServer';
await plugin.hooks.create.handler({}, mockContext);
Expand Down
1 change: 0 additions & 1 deletion packages/gasket-plugin-nextjs/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ describe('Plugin', function () {
'express',
'fastify',
'metadata',
'middleware',
'prompt',
'webpackConfig',
'workbox'
Expand Down
Loading