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

[XM Cloud][Next.js] Editing Configuration Endpoint #1724

Merged
merged 20 commits into from
Feb 7, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
yavorsk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Our versioning strategy is as follows:
* `[sitecore-jss]` `[templates/nextjs-xmcloud]` Load the content styles for the RichText component ([#1670](https://github.com/Sitecore/jss/pull/1670))([#1683](https://github.com/Sitecore/jss/pull/1683)) ([#1684](https://github.com/Sitecore/jss/pull/1684)) ([#1693](https://github.com/Sitecore/jss/pull/1693))
* `[templates/react]` `[sitecore-jss-react]` Replace package 'deep-equal' with 'fast-deep-equal'. No functionality change only performance improvement ([#1719](https://github.com/Sitecore/jss/pull/1719)) ([#1665](https://github.com/Sitecore/jss/pull/1665))
* `[templates/nextjs-xmcloud]` `[sitecore-jss]` `[sitecore-jss-nextjs]` `[sitecore-jss-react]` Add support for loading appropriate stylesheets whenever a theme is applied to BYOC and SXA components by introducing new function getComponentLibraryStylesheetLinks, which replaces getFEAASLibraryStylesheetLinks (which has been marked as deprecated) ([#1722](https://github.com/Sitecore/jss/pull/1722))
* `[templates/nextjs-xmcloud]` `[sitecore-jss-nextjs]` Add protected endpoint which provides configuration information (the sitecore packages used by the app and all registered components) to be used to determine feature compatibility on Pages side. ([#1724](https://github.com/Sitecore/jss/pull/1724))
* `[templates/nextjs]` `[templates/nextjs-styleguide]` Modify all GraphQLRequestClient import statements so that it gets imported from the /graphql submodule ([#1728](https://github.com/Sitecore/jss/pull/1728))

### 🐛 Bug Fixes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { EditingConfigMiddleware } from '@sitecore-jss/sitecore-jss-nextjs/editing';
import { components } from 'temp/componentBuilder';
import metadata from 'temp/metadata.json';

/**
* This Next.js API route is used by Sitecore editors (Pages) in XM Cloud
* to determine feature compatibility and configuration.
*/

const handler = new EditingConfigMiddleware({
components,
metadata,
}).getHandler();

export default handler;
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ import './generate-config';
COMPONENT BUILDER GENERATION
*/
import './generate-component-builder';

/*
META DATA GENERATION
*/
import './generate-metadata';
yavorsk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from 'fs';
import path from 'path';
import { Metadata, getMetadata } from '@sitecore-jss/sitecore-jss-dev-tools';

/*
METADATA GENERATION
Generates the /src/temp/metadata.json file which contains application
configuration metadata that is used for Sitecore XM Cloud integration.
*/
generateMetadata();

function generateMetadata(): void {
const metadata: Metadata = getMetadata();
writeMetadata(metadata);
}

/**
* Writes the metadata object to disk.
* @param {Metadata} metadata metadata to write.
*/
function writeMetadata(metadata: Metadata): void {
const filePath = path.resolve('src/temp/metadata.json');
console.log(`Writing metadata to ${filePath}`);
fs.writeFileSync(filePath, JSON.stringify(metadata, null, 2), { encoding: 'utf8' });
}
1 change: 1 addition & 0 deletions packages/sitecore-jss-dev-tools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export {
DisconnectedServerOptions,
} from './disconnected-server/create-default-disconnected-server';
export { ScJssConfig, JssConfiguration, resolveScJssConfig } from './resolve-scjssconfig';
export { Metadata } from '@sitecore-jss/sitecore-jss/utils';

export * from './templating';
export * from './manifest';
Expand Down
1 change: 1 addition & 0 deletions packages/sitecore-jss-dev-tools/src/templating/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { ComponentFile, PackageDefinition, getComponentList } from './components
export { PluginDefinition, generatePlugins, ModuleType } from './plugins';
export { scaffoldFile } from './scaffold';
export { strip } from './strip';
export { getMetadata } from './metadata';
151 changes: 151 additions & 0 deletions packages/sitecore-jss-dev-tools/src/templating/metadata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/* eslint-disable quotes */
/* eslint-disable no-unused-expressions */
import { expect } from 'chai';
import fs from 'fs';
import path from 'path';
import { getMetadata } from './metadata';
import sinon, { SinonStub } from 'sinon';
import { Metadata } from '@sitecore-jss/sitecore-jss/utils';

describe('metadata', () => {
afterEach(() => {
sinon.restore();
});

describe('getMetadata', () => {
let readdirSync: SinonStub;
let readFileSync: SinonStub;

afterEach(() => {
readdirSync?.restore();
readFileSync?.restore();
});

it('should return packages metadata from @sitecore scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore']);
readdirSync.withArgs(path.join('node_modules', '@sitecore')).returns(['byoc', 'components']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore', 'byoc', 'package.json'))
.returns('{"name": "@sitecore/byoc","version": "0.2.8"}');
readFileSync
.withArgs(path.join('node_modules', '@sitecore', 'components', 'package.json'))
.returns('{"name": "@sitecore/components","version": "1.1.6"}');

const expected: Metadata = {
packages: {
'@sitecore/byoc': '0.2.8',
'@sitecore/components': '1.1.6',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should return packages metadata from @sitecore-jss scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-jss']);
readdirSync
.withArgs(path.join('node_modules', '@sitecore-jss'))
.returns(['sitecore-jss-cli', 'sitecore-jss-nextjs']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-jss', 'sitecore-jss-cli', 'package.json'))
.returns('{"name": "@sitecore-jss/sitecore-jss-cli","version": "21.7.0-canary.55"}');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-jss', 'sitecore-jss-nextjs', 'package.json'))
.returns('{"name": "@sitecore-jss/sitecore-jss-nextjs","version": "21.7.0-canary.55"}');

const expected: Metadata = {
packages: {
'@sitecore-jss/sitecore-jss-cli': '21.7.0-canary.55',
'@sitecore-jss/sitecore-jss-nextjs': '21.7.0-canary.55',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should return packages metadata from @sitecore-cloudsdk scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-cloudsdk']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-cloudsdk')).returns(['core']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-cloudsdk', 'core', 'package.json'))
.returns('{"name": "@sitecore-cloudsdk/core","version": "0.1.5"}');

const expected: Metadata = {
packages: {
'@sitecore-cloudsdk/core': '0.1.5',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should return packages metadata from @sitecore-feaas scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-feaas']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-feaas')).returns(['clientside']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-feaas', 'clientside', 'package.json'))
.returns('{"name": "@sitecore-feaas/clientside","version": "0.5.12"}');

const expected: Metadata = {
packages: {
'@sitecore-feaas/clientside': '0.5.12',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should not return packages metadata for not tracked scopes', () => {
const scope = '@nottracked-scope';
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns([scope]);

const expected: Metadata = { packages: {} };

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should throw if package.json not found', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-feaas']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-feaas')).returns(['clientside']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-feaas', 'clientside', 'package.json'))
.returns(null);

expect(() => getMetadata()).to.throw;
});

it('should throw if json not valid', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-feaas']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-feaas')).returns(['clientside']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-feaas', 'clientside', 'package.json'))
.returns('{"name": "@sitecore-feaas/clientside","version": "0.5.12"');

expect(() => getMetadata()).to.throw;
});
});
});
31 changes: 31 additions & 0 deletions packages/sitecore-jss-dev-tools/src/templating/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import fs from 'fs';
import path from 'path';
import { Metadata } from '@sitecore-jss/sitecore-jss/utils';

/**
* Get application metadata
*/
export function getMetadata(): Metadata {
const metadata: Metadata = { packages: {} };
const trackedScopes = ['@sitecore', '@sitecore-cloudsdk', '@sitecore-feaas', '@sitecore-jss'];
const dirs = fs.readdirSync('node_modules');

dirs.forEach((dir: any) => {
if (trackedScopes.includes(dir)) {
const packageNames = fs.readdirSync(path.join('node_modules', dir));
packageNames.forEach((pkg: any) => {
try {
const json = JSON.parse(
fs.readFileSync(path.join('node_modules', dir, pkg, 'package.json'), 'utf8')
);

metadata.packages[json.name] = json.version;
} catch (e) {
console.error(`Failed to read/parse package.json for ${pkg}`, e);
}
});
}
});

return metadata;
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('generate-component-builder', () => {
"import * as Text from 'src/custom-components/Text';\n" +
"import * as barModule from 'bar';\n" +
'\n' +
'const components = new Map();\n' +
'export const components = new Map();\n' +
"components.set('Foo', Foo);\n" +
'\n' +
"components.set('TextComponent', Text);\n" +
Expand Down Expand Up @@ -129,7 +129,7 @@ describe('generate-component-builder', () => {
" element: (isEditing?: boolean) => isEditing ? require('car.dynamic')?.default : dynamic(carModule.module)\n" +
'}\n' +
'\n' +
'const components = new Map();\n' +
'export const components = new Map();\n' +
"components.set('Foo', Foo);\n" +
'\n' +
"components.set('TextComponent', Text);\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ ${componentFiles
})
.join('\n')}

const components = new Map();
export const components = new Map();
${packages
.map((p) =>
p.components.map(
Expand Down
Loading