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

[sitecore-jss-dev-tools] Update sitecore packages metadata generation logic to account for monorepos #1949

Merged
merged 16 commits into from
Oct 22, 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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Our versioning strategy is as follows:
* `[sitecore-jss-angular]` Fix default empty field components to not render the unwanted wrapping tags ([#1937](https://github.com/Sitecore/jss/pull/1937)) ([#1940](https://github.com/Sitecore/jss/pull/1940))
* `[sitecore-jss-angular]` Fix image field style property not rendered properly ([#1944](https://github.com/Sitecore/jss/pull/1944))
* `[sitecore-jss-angular]` Fix nested dynamic placeholders not being displayed in Pages ([#1947](https://github.com/Sitecore/jss/pull/1947))
* `[sitecore-jss-dev-tools]` getMetadata() now uses `npm query` command to get the list and exact versions of packages. this solution works for monorepo setups ([#1949](https://github.com/Sitecore/jss/pull/1949))
* `[templates/nextjs-sxa]` Fix an alignment issue where components using both `me-auto` and `ms-md-auto` classes resulted in inconsistent alignment of elements. ([#1946](https://github.com/Sitecore/jss/pull/1946)) ([#1950](https://github.com/Sitecore/jss/pull/1950)) ([#1955](https://github.com/Sitecore/jss/pull/1955))

### 🎉 New Features & Improvements
Expand Down Expand Up @@ -68,7 +69,6 @@ Our versioning strategy is as follows:
* `[sitecore-jss]` GenericFieldValue model is updated to accept Date type ([#1916](https://github.com/Sitecore/jss/pull/1916))
* `[template/node-xmcloud-proxy]` `[sitecore-jss-proxy]` Introduced /api/healthz endpoint ([#1928](https://github.com/Sitecore/jss/pull/1928))
* `[sitecore-jss]` `[sitecore-jss-angular]` Render field metdata chromes in editMode metadata - in edit mode metadata in Pages, angular package field directives will render wrapping `code` elements with field metadata required for editing; ([#1926](https://github.com/Sitecore/jss/pull/1926))
* `[sitecore-jss-nextjs]` Expose MiddlewareBase class and MiddlewareBaseConfig type ([#1941](https://github.com/Sitecore/jss/pull/1941))
* `[angular-xmcloud]``[sitecore-jss-angular]` Analytics and CloudSDK integration
* `[angular-xmcloud]` Add CloudSDK initialization on client side ([#1952](https://github.com/Sitecore/jss/pull/1952))

Expand Down
169 changes: 47 additions & 122 deletions packages/sitecore-jss-dev-tools/src/templating/metadata.test.ts
Original file line number Diff line number Diff line change
@@ -1,151 +1,76 @@
/* 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/editing';
import childProcess from 'child_process';
import metadataNextjs from './../test-data/metadata/metadata-nextjs.json';
import metadataAngular from './../test-data/metadata/metadata-angular.json';
import npmQueryResultNext from './../test-data/metadata/npm-query-nextjs.json';
import npmQueryResultAngular from './../test-data/metadata/npm-query-angular.json';
import npmQueryResultNoSc from './../test-data/metadata/npm-query-no-sc.json';

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

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

afterEach(() => {
readdirSync?.restore();
readFileSync?.restore();
execSyncStub?.restore();
logStub?.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 tracked packages with exact versions from result of npm query (nextjs app)', () => {
execSyncStub = sinon.stub(childProcess, 'execSync');
execSyncStub
.withArgs('npm query [name*=@sitecore] --workspaces false')
.returns(JSON.stringify(npmQueryResultNext));
const metadata = getMetadata();
expect(metadata).to.deep.equal(metadataNextjs);
});

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 return tracked packages with exact versions from result of npm query (angular app)', () => {
execSyncStub = sinon.stub(childProcess, 'execSync');
execSyncStub
.withArgs('npm query [name*=@sitecore] --workspaces false')
.returns(JSON.stringify(npmQueryResultAngular));
const metadata = getMetadata();
expect(metadata).to.deep.equal(metadataAngular);
});

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: {} };
it('should return metadata with empty package object and log error in the console if result of npm query is not valid', () => {
execSyncStub = sinon.stub(childProcess, 'execSync');
execSyncStub.withArgs('npm query [name*=@sitecore] --workspaces false').returns('[{"name":}');
logStub = sinon.stub(console, 'error');

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
const metadata = getMetadata();
expect(logStub.calledOnceWith('Failed to retrieve sitecore packages using npm query')).to.be
.true;
expect(metadata).to.deep.equal({ packages: {} });
});

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);
it('should return metadata with empty package object and log error in the console if npm query command fails', () => {
execSyncStub = sinon.stub(childProcess, 'execSync');
execSyncStub.withArgs('npm query [name*=@sitecore] --workspaces false').throws();
logStub = sinon.stub(console, 'error');

expect(() => getMetadata()).to.throw;
const metadata = getMetadata();
expect(logStub.calledOnceWith('Failed to retrieve sitecore packages using npm query')).to.be
.true;
expect(metadata).to.deep.equal({ packages: {} });
});

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;
it('should not return packages for result of npm query not containng tracked packages', () => {
execSyncStub = sinon.stub(childProcess, 'execSync');
execSyncStub
.withArgs('npm query [name*=@sitecore] --workspaces false')
.returns(JSON.stringify(npmQueryResultNoSc));
const metadata = getMetadata();
expect(metadata).to.deep.equal({ packages: {} });
});
});
});
54 changes: 34 additions & 20 deletions packages/sitecore-jss-dev-tools/src/templating/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import fs from 'fs';
import path from 'path';
import { Metadata } from '@sitecore-jss/sitecore-jss/editing';
import { execSync } from 'child_process';

type Package = {
name: string;
version: string;
};

const trackedScopes = ['@sitecore', '@sitecore-cloudsdk', '@sitecore-feaas', '@sitecore-jss'];

/**
* 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);
}
});

let queryResult: Package[] = [];
try {
queryResult = JSON.parse(execSync('npm query [name*=@sitecore] --workspaces false').toString());
} catch (error) {
console.error('Failed to retrieve sitecore packages using npm query', error);
return metadata;
}

metadata.packages = getPackagesFromQueryResult(queryResult);

return metadata;
}

/**
* Retrieve all packages of the tracked scopes with their exact versions
* @param {Package[]} scPackages list of packages
* @returns {Record<string, string>} an object with the packages with their exact versions
*/
function getPackagesFromQueryResult(scPackages: Package[]): Record<string, string> {
const packages: Record<string, string> = {};

scPackages.forEach((scPackage) => {
if (trackedScopes.some((trackedScope) => scPackage.name.startsWith(trackedScope))) {
packages[scPackage.name] = scPackage.version;
}
});

return metadata;
return packages;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"packages": {
"@sitecore-jss/sitecore-jss-angular": "22.2.0-canary.69",
"@sitecore-jss/sitecore-jss-angular-schematics": "22.2.0-canary.69",
"@sitecore-jss/sitecore-jss-cli": "22.2.0-canary.69",
"@sitecore-jss/sitecore-jss-dev-tools": "22.2.0-canary.69",
"@sitecore-jss/sitecore-jss": "22.2.0-canary.69"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"packages": {
"@sitecore-cloudsdk/events": "0.3.1",
"@sitecore-feaas/clientside": "0.5.18",
"@sitecore-jss/sitecore-jss-nextjs": "22.2.0-canary.69",
"@sitecore/components": "1.1.11",
"@sitecore-jss/sitecore-jss-cli": "22.2.0-canary.69",
"@sitecore-jss/sitecore-jss-dev-tools": "22.2.0-canary.69",
"@sitecore-jss/sitecore-jss": "22.2.0-canary.69",
"@sitecore-jss/sitecore-jss-react": "22.2.0-canary.69",
"@sitecore-cloudsdk/personalize": "0.3.1",
"@sitecore-cloudsdk/utils": "0.3.1",
"@sitecore-cloudsdk/core": "0.3.1",
"@sitecore/byoc": "0.2.15"
}
}
Loading