Skip to content

Commit bc26952

Browse files
illiakovalenkoart-alexeyenkoAutomated Buildambrauersc-ruslanmatkovskyi
authored
[Next.js][Multi-site] Multi-site middleware plugin (#1279)
* jss-cli unit test coverage: first batch * jss-cli unit test coverage: second batch * jss-cli unit tests: second batch * fix expected output for test not to fail across environments * lint * dev-tools unit test coverage first batch * dev-tools unit test coverage: second batch * resolve-scjssconfig test placeholder (for the future!) * exclude index file from jss-vue test coverage (#1266) * version v21.1.0-canary.58 [skip ci] * fix test for scjssconfig resolving * Update packages/sitecore-jss-dev-tools/src/manifest/manifest-manager.test.ts Co-authored-by: Adam Brauer <[email protected]> * Update packages/sitecore-jss-dev-tools/src/manifest/manifest-manager.test.ts Co-authored-by: Adam Brauer <[email protected]> * lint * adding test data for scjssconfig * some improvements to reject logic in resolve scjssconfig * lint numero 2 * version v21.1.0-canary.59 [skip ci] * #556667: fixed urls for sitemap * version v21.1.0-canary.60 [skip ci] * final batch * re-gen yarn.lock * yarn.lock re-update * version v21.1.0-canary.61 [skip ci] * #552985: fixed header styles * version v21.1.0-canary.62 [skip ci] * #546298: fixed style for showing hidden components * version v21.1.0-canary.63 [skip ci] * SiteResolver.resolve updates: removed 'language' from site resolution logic, return SiteInfo instead of site name * [Next.js][Multi-site] Multi-site middleware plugin * #559044: fixed rendering dynamic placeholder (#1278) * version v21.1.0-canary.64 [skip ci] * Adjust with latest site resolver changes * adjust * Add latest changes * Extra comment * extra fix * Extend unit tests * Revert cookie set change * Use response cookies instead of request * Adjust changes according to review * Adjust changes according to review * lint fix Co-authored-by: Artem Alexeyenko <[email protected]> Co-authored-by: Automated Build <[email protected]> Co-authored-by: Adam Brauer <[email protected]> Co-authored-by: Ruslan Matkovskyi <[email protected]> Co-authored-by: Ruslan Matkovskyi <[email protected]>
1 parent 5c2d3c0 commit bc26952

File tree

70 files changed

+3018
-228
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3018
-228
lines changed

lerna.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"packages/*",
55
"samples/*"
66
],
7-
"version": "21.1.0-canary.57",
7+
"version": "21.1.0-canary.64",
88
"npmClient": "yarn",
99
"useWorkspaces": true
1010
}

packages/create-sitecore-jss/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "create-sitecore-jss",
3-
"version": "21.1.0-canary.57",
3+
"version": "21.1.0-canary.64",
44
"description": "Sitecore JSS initializer",
55
"bin": "./dist/index.js",
66
"scripts": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import { MultisiteMiddleware } from '@sitecore-jss/sitecore-jss-nextjs/middleware';
3+
import { siteResolver } from 'lib/site-resolver';
4+
import { MiddlewarePlugin } from '..';
5+
6+
/**
7+
* This is the multisite middleware plugin for Next.js.
8+
* It is used to enable Sitecore multisite in Next.js.
9+
*
10+
* The `MultisiteMiddleware` will
11+
* 1. Based on provided hostname and sites information, resolve site.
12+
* 2. Rewrite the response to the specific site.
13+
* 3. Set `sc_site` cookie with site name and `x-sc-rewrite` header with rewritten path to be reused in following middlewares.
14+
*/
15+
class MultisitePlugin implements MiddlewarePlugin {
16+
private multisiteMiddleware: MultisiteMiddleware;
17+
18+
// Multisite middleware has to be executed first
19+
order = -1;
20+
21+
constructor() {
22+
this.multisiteMiddleware = new MultisiteMiddleware({
23+
// This function determines if a route should be excluded from site resolution.
24+
// Certain paths are ignored by default (e.g. files and Next.js API routes), but you may wish to exclude more.
25+
// This is an important performance consideration since Next.js Edge middleware runs on every request.
26+
excludeRoute: () => false,
27+
// This function resolves site based on hostname
28+
getSite: siteResolver.getByHost,
29+
});
30+
}
31+
32+
async exec(req: NextRequest, res?: NextResponse): Promise<NextResponse> {
33+
return this.multisiteMiddleware.getHandler()(req, res);
34+
}
35+
}
36+
37+
export const multisitePlugin = new MultisitePlugin();

packages/create-sitecore-jss/src/templates/nextjs-multisite/src/lib/site-resolver.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SiteResolver, SiteInfo } from '@sitecore-jss/sitecore-jss-nextjs';
1+
import { SiteResolver, SiteInfo } from '@sitecore-jss/sitecore-jss-nextjs/middleware';
22
import config from 'temp/config';
33

44
/*

packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PersonalizeMiddleware } from '@sitecore-jss/sitecore-jss-nextjs/middlew
33
import { MiddlewarePlugin } from '..';
44
import config from 'temp/config';
55
import { PosResolver } from 'lib/pos-resolver';
6+
import { siteResolver } from 'lib/site-resolver';
67

78
/**
89
* This is the personalize middleware plugin for Next.js.
@@ -26,7 +27,6 @@ class PersonalizePlugin implements MiddlewarePlugin {
2627
edgeConfig: {
2728
endpoint: config.graphQLEndpoint,
2829
apiKey: config.sitecoreApiKey,
29-
siteName: config.jssAppName,
3030
timeout:
3131
(process.env.PERSONALIZE_MIDDLEWARE_EDGE_TIMEOUT &&
3232
parseInt(process.env.PERSONALIZE_MIDDLEWARE_EDGE_TIMEOUT)) ||
@@ -52,6 +52,8 @@ class PersonalizePlugin implements MiddlewarePlugin {
5252
// This function resolves point of sale for cdp calls.
5353
// Point of sale may differ by locale and middleware will use request language to get the correct value every time it's invoked
5454
getPointOfSale: PosResolver.resolve,
55+
// This function resolves site based on hostname
56+
getSite: siteResolver.getByHost,
5557
});
5658
}
5759

packages/create-sitecore-jss/src/templates/nextjs-sxa/src/assets/basic/_header.scss

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
.prod-mode {
66
#header {
77
display: flex;
8-
flex-direction: column;
98

109
@include respond-to(mobile-large) {
1110
padding-bottom: 0;
@@ -33,7 +32,7 @@ header {
3332
padding-top: 0;
3433
flex-direction: column-reverse;
3534

36-
.title {
35+
.bs-title {
3736
padding-left: 0;
3837
text-align: center;
3938
margin-top: -5px;

packages/create-sitecore-jss/src/templates/nextjs-sxa/src/assets/sass/components/_component-container.scss

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
width: 100%;
55
}
66

7+
.component {
8+
position: relative;
9+
}
10+
711
.container {
812
padding: 0;
913

packages/create-sitecore-jss/src/templates/nextjs-sxa/src/lib/middleware/plugins/redirects.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
22
import { RedirectsMiddleware } from '@sitecore-jss/sitecore-jss-nextjs/middleware';
33
import config from 'temp/config';
44
import { MiddlewarePlugin } from '..';
5+
import { siteResolver } from 'lib/site-resolver';
56

67
class RedirectsPlugin implements MiddlewarePlugin {
78
private redirectsMiddleware: RedirectsMiddleware;
@@ -11,7 +12,6 @@ class RedirectsPlugin implements MiddlewarePlugin {
1112
this.redirectsMiddleware = new RedirectsMiddleware({
1213
endpoint: config.graphQLEndpoint,
1314
apiKey: config.sitecoreApiKey,
14-
siteName: config.jssAppName,
1515
// These are all the locales you support in your application.
1616
// These should match those in your next.config.js (i18n.locales).
1717
locales: ['en'],
@@ -22,6 +22,8 @@ class RedirectsPlugin implements MiddlewarePlugin {
2222
// This function determines if the middleware should be turned off.
2323
// By default it is disabled while in development mode.
2424
disabled: () => process.env.NODE_ENV === 'development',
25+
// This function resolves site based on hostname
26+
getSite: siteResolver.getByHost,
2527
});
2628
}
2729

packages/create-sitecore-jss/src/templates/nextjs-sxa/src/pages/api/sitemap.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AxiosResponse } from 'axios';
22
import type { NextApiRequest, NextApiResponse } from 'next';
3-
import { AxiosDataFetcher, GraphQLSitemapXmlService } from '@sitecore-jss/sitecore-jss-nextjs';
3+
import { AxiosDataFetcher, GraphQLSitemapXmlService, getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs';
44
import { siteResolver } from 'lib/site-resolver';
55
import config from 'temp/config';
66

@@ -53,13 +53,14 @@ const sitemapApi = async (
5353
}
5454

5555
const SitemapLinks = sitemaps
56-
.map(
57-
(item) =>
58-
`<sitemap>
59-
<loc>${item}</loc>
60-
</sitemap>
61-
`
62-
)
56+
.map((item) => {
57+
const parseUrl = item.split('/');
58+
const lastSegment = parseUrl[parseUrl.length - 1];
59+
60+
return `<sitemap>
61+
<loc>${getPublicUrl()}/${lastSegment}</loc>
62+
</sitemap>`;
63+
})
6364
.join('');
6465

6566
res.setHeader('Content-Type', 'text/xml;charset=utf-8');

packages/sitecore-jss-angular-schematics/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sitecore-jss/sitecore-jss-angular-schematics",
3-
"version": "21.1.0-canary.57",
3+
"version": "21.1.0-canary.64",
44
"description": "Scaffolding schematics for Sitecore JSS Angular apps",
55
"scripts": {
66
"build": "tsc -p tsconfig.json",

packages/sitecore-jss-angular/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sitecore-jss/sitecore-jss-angular",
3-
"version": "21.1.0-canary.57",
3+
"version": "21.1.0-canary.64",
44
"description": "",
55
"scripts": {
66
"build": "ng-packagr -p ng-package.json",
@@ -58,7 +58,7 @@
5858
"rxjs": "~6.6.6"
5959
},
6060
"dependencies": {
61-
"@sitecore-jss/sitecore-jss": "^21.1.0-canary.57"
61+
"@sitecore-jss/sitecore-jss": "^21.1.0-canary.64"
6262
},
6363
"main": "dist/bundles/sitecore-jss-sitecore-jss-angular.umd.js",
6464
"module": "dist/fesm2015/sitecore-jss-sitecore-jss-angular.js",

packages/sitecore-jss-cli/.nycrc

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
"**/*.test.ts",
88
"src/test-data",
99
"dist",
10-
"src/test.ts"
10+
"src/test.ts",
11+
"**/create.ts",
12+
"**/deploy.package.ts",
13+
"**/index.global.ts",
14+
"**/index.ts",
15+
"**/cli.global.ts"
1116
],
1217
"all": true,
1318
"reporter": [

packages/sitecore-jss-cli/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sitecore-jss/sitecore-jss-cli",
3-
"version": "21.1.0-canary.57",
3+
"version": "21.1.0-canary.64",
44
"description": "Sitecore JSS command-line",
55
"main": "dist/cjs/cli.js",
66
"module": "dist/esm/cli.js",
@@ -12,8 +12,8 @@
1212
"lint": "eslint ./src/**/*.ts",
1313
"prepublishOnly": "npm run build",
1414
"jss": "node ./dist/cjs/bin/jss.js",
15-
"test": "mocha --require ts-node/register \"./src/**/*.test.ts\"",
16-
"coverage": "nyc npm test"
15+
"test": "mocha --require ts-node/register/transpile-only \"./src/**/*.test.ts\"",
16+
"coverage": "nyc --require ts-node/register/transpile-only npm test"
1717
},
1818
"engines": {
1919
"node": ">=12",
@@ -33,7 +33,7 @@
3333
"url": "https://github.com/sitecore/jss/issues"
3434
},
3535
"dependencies": {
36-
"@sitecore-jss/sitecore-jss-dev-tools": "^21.1.0-canary.57",
36+
"@sitecore-jss/sitecore-jss-dev-tools": "^21.1.0-canary.64",
3737
"chalk": "^2.4.2",
3838
"cross-spawn": "^7.0.0",
3939
"dotenv": "^16.0.1",
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* eslint-disable no-unused-expressions */
2+
import { expect } from 'chai';
3+
import sinon from 'sinon';
4+
import { getPackageScriptCommands, makeCommand } from './cli';
5+
import * as resolvePkg from './resolve-package';
6+
import * as packageScript from './run-package-script';
7+
import { Arguments } from 'yargs';
8+
9+
describe('cli', () => {
10+
describe('getPackageScriptCommands', async () => {
11+
afterEach(() => {
12+
sinon.restore();
13+
});
14+
const packageJson = {
15+
scripts: {
16+
first: 'do --this',
17+
second: 'do --that',
18+
third: 'do --all',
19+
},
20+
};
21+
22+
it('should read scripts from package.json and return result with handlers', async () => {
23+
sinon.stub(resolvePkg, 'default').resolves(packageJson);
24+
25+
const result = await getPackageScriptCommands();
26+
const runScriptStub = sinon.stub(packageScript, 'default');
27+
const mockArgs: Arguments = {
28+
_: ['arg1', 'arg2'],
29+
$0: '',
30+
};
31+
32+
expect(Object.keys(packageJson.scripts)).to.be.deep.equal(Object.keys(result));
33+
for (const key of Object.keys(result)) {
34+
const expectedCommand = makeCommand(key);
35+
for (const field of Object.keys(expectedCommand)) {
36+
if (typeof expectedCommand[field] === 'function') {
37+
expectedCommand[field](mockArgs);
38+
expect(runScriptStub.called).to.be.true;
39+
} else {
40+
expect(result[key][field]).to.deep.equal(expectedCommand[field]);
41+
}
42+
}
43+
}
44+
});
45+
46+
it('should return empty result when package.json contents are empty', async () => {
47+
const emptyPackage = {};
48+
49+
sinon.stub(resolvePkg, 'default').resolves(emptyPackage);
50+
51+
const result = await getPackageScriptCommands();
52+
53+
expect(result).to.deep.equal(emptyPackage);
54+
});
55+
56+
it('should ignore jss script entry', async () => {
57+
const packageJson = {
58+
scripts: {
59+
jss: 'do --this',
60+
second: 'do --that',
61+
third: 'do --all',
62+
},
63+
};
64+
const { jss: _, ...expectedScripts } = packageJson.scripts;
65+
66+
sinon.stub(resolvePkg, 'default').resolves(packageJson);
67+
68+
const result = await getPackageScriptCommands();
69+
70+
expect(Object.keys(expectedScripts)).to.be.deep.equal(Object.keys(result));
71+
});
72+
});
73+
});

packages/sitecore-jss-cli/src/cli.ts

+20-13
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as commands from './scripts';
77
/**
88
* Get package script commands
99
*/
10-
async function getPackageScriptCommands() {
10+
export async function getPackageScriptCommands() {
1111
const packageJson = await resolvePackage();
1212
const result: { [key: string]: CommandModule } = {};
1313

@@ -20,25 +20,32 @@ async function getPackageScriptCommands() {
2020
return;
2121
}
2222

23-
const command = {
24-
command: script,
25-
describe: 'package.json script',
26-
builder: {},
27-
disableStrictArgs: true,
28-
handler: (argv: Arguments) => {
29-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
30-
if ((argv as any)._[0]) {
31-
runPackageScript(process.argv.slice(2));
32-
}
33-
},
34-
};
23+
const command = makeCommand(script);
3524

3625
result[script] = command;
3726
});
3827

3928
return result;
4029
}
4130

31+
/**
32+
* @param script
33+
*/
34+
export function makeCommand(script: string) {
35+
return {
36+
command: script,
37+
describe: 'package.json script',
38+
builder: {},
39+
disableStrictArgs: true,
40+
handler: (argv: Arguments) => {
41+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
42+
if ((argv as any)._[0]) {
43+
runPackageScript(process.argv.slice(2));
44+
}
45+
},
46+
};
47+
}
48+
4249
/**
4350
* implements CLI commands when executed from a local node_modules folder
4451
*/

0 commit comments

Comments
 (0)