Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b3d548d
Add atom schema utilities for CSDK (#407)
MenKNas Mar 19, 2026
1274b62
[content] [react] [atoms] Introduce Atom Registry utils (#408)
yavorsk Mar 25, 2026
d4b9464
[Atom schema] Introduce utilities in CSDK to define a the callback re…
sc-nikolaoslazaridis Apr 3, 2026
5bcf374
[content] [react] [atoms] Render atom components and Design Studio in…
yavorsk Apr 17, 2026
9bbe365
Update to beta versions for beta release (#453)
yavorsk Apr 29, 2026
60c6ce3
[content] Introduce library-atoms mode for design library (#452)
yavorsk Apr 29, 2026
ba8c6fb
Introduce Zod Schemas for Content SDK Fields (#450)
sc-nikolaoslazaridis Apr 29, 2026
1aa7c1f
[Design Studio] Support Atom-based code generation (#465)
illiakovalenko May 5, 2026
299b1b8
[chore] Atoms release prep for beta.2 (#454)
yavorsk May 5, 2026
49b1142
Bump to beta.3
illiakovalenko May 6, 2026
89c0dcd
Bump peer deps to beta.3
illiakovalenko May 6, 2026
d46238d
[Render NCC wrapper] Create an SXA component in RH to fetch NCC layou…
sc-nikolaoslazaridis May 18, 2026
adbec9b
[atoms-epic] Integrate SXA component (#485)
MenKNas May 21, 2026
e79962a
Atoms json renderer migration (#514)
sc-nikolaoslazaridis Jun 15, 2026
2fcbfb0
Merge branch 'dev' into atoms-epic
sc-nikolaoslazaridis Jun 15, 2026
47c08e5
chore: set versions to beta
sc-nikolaoslazaridis Jun 15, 2026
59f8703
chore: update template
sc-nikolaoslazaridis Jun 15, 2026
6cdf840
chore: new versions
sc-nikolaoslazaridis Jun 15, 2026
f6fefbe
chore: dependencies and test
sc-nikolaoslazaridis Jun 15, 2026
52c92a7
chore: caret
sc-nikolaoslazaridis Jun 15, 2026
0ac7356
chore: minor improvements
sc-nikolaoslazaridis Jun 15, 2026
6cc1497
chore: update git ignore files to keep the atoms lock
sc-nikolaoslazaridis Jun 15, 2026
6851907
Render atoms based on layout data (#517)
sc-nikolaoslazaridis Jun 16, 2026
32cc36c
chore: config updates
sc-nikolaoslazaridis Jun 16, 2026
0384eac
chore: cli command alias
sc-nikolaoslazaridis Jun 16, 2026
b002a06
chore: cli types update
sc-nikolaoslazaridis Jun 16, 2026
89e8c52
chore: error messages
sc-nikolaoslazaridis Jun 16, 2026
e92c770
chore: remove not needed exclude entry
sc-nikolaoslazaridis Jun 16, 2026
45429b2
chore: typo
sc-nikolaoslazaridis Jun 16, 2026
a947ef2
Fix api extractor issue
MenKNas Jun 16, 2026
f750e40
Minor fixes
MenKNas Jun 16, 2026
753cd25
Fix yarn.lock
MenKNas Jun 16, 2026
0ebb1af
fix: isEditing to true when in lowCode mode (#526)
sc-nikolaoslazaridis Jun 19, 2026
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
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,5 +543,4 @@ Our versioning strategy is as follows:

### 🧹 Chores

* `[template/nextjs]` Clean package.json scripts ([#75](https://github.com/Sitecore/content-sdk/pull/75))
* Upgrade 3rd party dependencies ([#88](https://github.com/Sitecore/content-sdk/pull/88)) ([#92](https://github.com/Sitecore/content-sdk/pull/92))
* `[template/nextjs]` Clean package.json scripts ([#75](https://github.com/Sitecore/content-sdk/pull/75))
4 changes: 2 additions & 2 deletions packages/analytics-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/sitecore/content-sdk/issues"
},
"dependencies": {
"@sitecore-content-sdk/core": "^2.1.0",
"@sitecore-content-sdk/core": "2.1.0-beta.1",
"debug": "^4.4.3",
"isbot": "^5.1.39"
},
Expand Down Expand Up @@ -75,5 +75,5 @@
"api-extractor": "npm run build && api-extractor run --local --verbose",
"api-extractor:verify": "api-extractor run"
},
"version": "2.1.0"
"version": "2.1.0-beta.1"
}
6 changes: 3 additions & 3 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sitecore-content-sdk/cli",
"version": "2.1.0",
"version": "2.1.0-beta.1",
"description": "Sitecore Content SDK CLI",
"main": "dist/cjs/cli.js",
"module": "dist/esm/cli.js",
Expand Down Expand Up @@ -34,8 +34,8 @@
"url": "https://github.com/sitecore/content-sdk/issues"
},
"dependencies": {
"@sitecore-content-sdk/content": "^2.1.0",
"@sitecore-content-sdk/core": "^2.1.0",
"@sitecore-content-sdk/content": "2.1.0-beta.1",
"@sitecore-content-sdk/core": "2.1.0-beta.1",
"chokidar": "^4.0.3",
"dotenv": "^16.5.0",
"dotenv-expand": "^12.0.2",
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/scripts/project/atoms/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const ATOMS_MODULE_PATH = 'src/atoms/index';
export const LOCK_FILE_DIR = '.sitecore';
export const LOCK_FILE_NAME = 'atoms.lock.json';

17 changes: 17 additions & 0 deletions packages/cli/src/scripts/project/atoms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Argv } from 'yargs';
import * as update from './update';
import * as validate from './validate';

export const command = ['atoms', 'a'];

export const describe = 'Manage atom version locks and validate atom contracts';

/**
* @param {Argv} yargs
*/
export function builder(yargs: Argv) {
return yargs
.command([update, validate] as any)
.strict()
.demandCommand(1, 'You need to specify an atoms subcommand (update, validate)');
}
61 changes: 61 additions & 0 deletions packages/cli/src/scripts/project/atoms/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Shape of a single atom entry in the lock file.
* @internal
*/
export interface AtomLockEntry {
/** Semver version of this component. Absent when not declared on the component. */
version?: string;
/** Hash of the component's schema, used to detect changes in the schema. */
hash: string;
}

/**
* Shape of the lock file.
* @internal
*/
export interface AtomVersionsLock {
/** Catalog root version from `defineAtomsCatalog`. Absent when not declared. */
version?: string;
/** Timestamp of when the lock file was generated. */
generated: string;
/** Map of atom name to its lock entry. */
atoms: Record<string, AtomLockEntry>;
}

/**
* Atoms info extracted from the catalog, used for lock file generation and validation.
* @internal
*/
export interface AtomInfo {
/** Semver version of this component. Absent when not declared on the component. */
version: string;
/** Hash of the component's schema, used to detect changes in the schema. */
schemaHash: string;
}

/**
* Map of atom name to its info (version and schema hash) extracted from the catalog.
* @internal
*/
export type AtomsInfoMap = Record<string, AtomInfo>;

/**
* Load the raw catalog object from the project's atoms module.
* @returns The raw catalog export from the atoms module, which should include component definitions and optionally a version.
* @internal
*/
export interface CatalogLoadResult {
data?: { components?: Record<string, Record<string, unknown>>; version?: string };
componentNames?: string[];
}

/**
* Result of a lock file validation.
* @internal
*/
export interface ValidateResult {
/** Whether the lock file is valid (all hashes and versions match). */
valid: boolean;
/** List of issues found during validation, if any. */
issues: string[];
}
140 changes: 140 additions & 0 deletions packages/cli/src/scripts/project/atoms/update.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */
import { expect } from 'chai';
import sinon from 'sinon';
import * as utilsModule from './utils';
import { handler } from './update';

describe('atoms/update handler', () => {
let loadCurrentAtomsStub: sinon.SinonStub;
let loadCatalogStub: sinon.SinonStub;
let writeLockFileStub: sinon.SinonStub;
let consoleLogStub: sinon.SinonStub;

beforeEach(() => {
loadCurrentAtomsStub = sinon.stub(utilsModule, 'loadCurrentAtoms');
loadCatalogStub = sinon.stub(utilsModule, 'loadCatalog');
writeLockFileStub = sinon.stub(utilsModule, 'writeLockFile');
consoleLogStub = sinon.stub(console, 'log');
});

afterEach(() => {
sinon.restore();
});

it('should call loadCurrentAtoms and loadCatalog', async () => {
loadCurrentAtomsStub.resolves({});
loadCatalogStub.returns({ data: {} });

await handler();

expect(loadCurrentAtomsStub.calledOnce).to.be.true;
expect(loadCatalogStub.calledOnce).to.be.true;
});

it('should write lock file with correct atom entries', async () => {
loadCurrentAtomsStub.resolves({
Button: { version: '1.0.0', schemaHash: 'abc123' },
});
loadCatalogStub.returns({ data: {} });

await handler();

expect(writeLockFileStub.calledOnce).to.be.true;
const lock = writeLockFileStub.firstCall.args[0];
expect(lock.atoms.Button).to.deep.equal({ version: '1.0.0', hash: 'abc123' });
});

it('should include catalog version in lock when catalog declares one', async () => {
loadCurrentAtomsStub.resolves({});
loadCatalogStub.returns({ data: { version: '2.0.0' } });

await handler();

const lock = writeLockFileStub.firstCall.args[0];
expect(lock.version).to.equal('2.0.0');
});

it('should not include version in lock when catalog has no version', async () => {
loadCurrentAtomsStub.resolves({});
loadCatalogStub.returns({ data: {} });

await handler();

const lock = writeLockFileStub.firstCall.args[0];
expect(lock.version).to.be.undefined;
});

it('should not include version in lock when catalog.data is undefined', async () => {
loadCurrentAtomsStub.resolves({});
loadCatalogStub.returns({});

await handler();

const lock = writeLockFileStub.firstCall.args[0];
expect(lock.version).to.be.undefined;
});

it('should omit atom version entry when atom has no version', async () => {
loadCurrentAtomsStub.resolves({
Card: { version: undefined, schemaHash: 'def456' },
});
loadCatalogStub.returns({ data: {} });

await handler();

const lock = writeLockFileStub.firstCall.args[0];
expect(lock.atoms.Card).to.deep.equal({ hash: 'def456' });
expect(lock.atoms.Card.version).to.be.undefined;
});

it('should omit atom version entry when atom version is empty string', async () => {
loadCurrentAtomsStub.resolves({
Card: { version: '', schemaHash: 'def456' },
});
loadCatalogStub.returns({ data: {} });

await handler();

const lock = writeLockFileStub.firstCall.args[0];
expect(lock.atoms.Card.version).to.be.undefined;
});

it('should set generated to a valid ISO timestamp', async () => {
loadCurrentAtomsStub.resolves({});
loadCatalogStub.returns({ data: {} });

await handler();

const lock = writeLockFileStub.firstCall.args[0];
expect(lock.generated).to.be.a('string');
expect(new Date(lock.generated).toISOString()).to.equal(lock.generated);
});

it('should write a lock with multiple atoms', async () => {
loadCurrentAtomsStub.resolves({
Button: { version: '1.0.0', schemaHash: 'hash1' },
Card: { version: undefined, schemaHash: 'hash2' },
Banner: { version: '0.5.0', schemaHash: 'hash3' },
});
loadCatalogStub.returns({ data: { version: '3.0.0' } });

await handler();

const lock = writeLockFileStub.firstCall.args[0];
expect(Object.keys(lock.atoms)).to.have.length(3);
expect(lock.atoms.Button).to.deep.equal({ version: '1.0.0', hash: 'hash1' });
expect(lock.atoms.Card).to.deep.equal({ hash: 'hash2' });
expect(lock.atoms.Banner).to.deep.equal({ version: '0.5.0', hash: 'hash3' });
expect(lock.version).to.equal('3.0.0');
});

it('should log success message after writing', async () => {
loadCurrentAtomsStub.resolves({});
loadCatalogStub.returns({ data: {} });

await handler();

expect(consoleLogStub.calledWith('[atoms update] Lock file updated successfully.')).to.be.true;
});
});

49 changes: 49 additions & 0 deletions packages/cli/src/scripts/project/atoms/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { AtomLockEntry, AtomVersionsLock } from './types';
import { loadCatalog, loadCurrentAtoms, writeLockFile } from './utils';

export const command = ['update', 'u'];

export const describe =
'Regenerate the atom versions lock file from the current atom definitions. Run after intentional schema changes.';

export const builder = {
config: {
requiresArg: false,
type: 'string',
describe: 'Path to the `sitecore.cli.config` file.',
alias: 'c',
},
};

export type UpdateArgs = {
config?: string;
};

/**
* Handler for `sitecore-tools project atoms update`.
* Regenerates `.sitecore/atoms.lock.json` from current atom definitions.
*/
export async function handler() {
const catalog = loadCatalog();
const currentAtoms = await loadCurrentAtoms(catalog);
const catalogData = catalog.data as Record<string, unknown>;
const catalogVersion = typeof catalogData?.version === 'string' ? catalogData.version : undefined;

const atoms: Record<string, AtomLockEntry> = {};
for (const [name, def] of Object.entries(currentAtoms)) {
atoms[name] = {
...(def.version && { version: def.version }),
hash: def.schemaHash,
};
}

const lock: AtomVersionsLock = {
...(catalogVersion !== undefined && { version: catalogVersion }),
generated: new Date().toISOString(),
atoms,
};

writeLockFile(lock);

console.log('[atoms update] Lock file updated successfully.');
}
Loading
Loading