Skip to content
Draft
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
19 changes: 12 additions & 7 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@

fileignoreconfig:
- filename: package-lock.json
checksum: fa2c81c72305bc19fa4f64457af1000aa2f0c179ecb7e46a5fc9d8b760c705a7
- filename: pnpm-lock.yaml
checksum: faaef9e003c5ba45e56ab1ca24a4fc285f53a2b9ecc6d3dbd11e7130a31db4a2
- filename: packages/contentstack-clone/src/core/util/clone-handler.ts
checksum: 0a7ed55e96aa5a94084538b4a480608593b04c316bf02cf54cdd3a647e9b714c
- filename: package-lock.json
checksum: d42b12a21c38a527fb850cbe8500160120ccb58e57aa0b4f8098b835e7ca0b4d
- filename: pnpm-lock.yaml
checksum: c442a57252ba95771003f170786afed30888f14f67dc59766352be4a57d5ad55
- filename: packages/contentstack-clone/src/core/util/clone-handler.ts
checksum: 0a7ed55e96aa5a94084538b4a480608593b04c316bf02cf54cdd3a647e9b714c
- filename: packages/contentstack-config/src/interfaces/index.ts
checksum: 0c8e8a6e478151f6f6a1c50a5ce66c64e93d5d1d1bcd22344a9496cdc19f63b8
- filename: packages/contentstack-config/src/utils/region-handler.ts
checksum: 0ce1a0f3e4228f5429661a32611fda32f04da038337b74bad93bc717406481f4
- filename: packages/contentstack/README.md
checksum: 2f6da4ed14592fcb5c5fb8bc70bf57dd9fed512ed580b3edfad23055d8f05f38
version: '1.0'
7 changes: 3 additions & 4 deletions packages/contentstack-audit/src/audit-base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { v4 as uuid } from 'uuid';
import isEmpty from 'lodash/isEmpty';
import { join, resolve } from 'path';
import cloneDeep from 'lodash/cloneDeep';
import { cliux, sanitizePath, TableFlags, TableHeader, log, configHandler, CLIProgressManager, clearProgressModuleSetting } from '@contentstack/cli-utilities';
import { cliux, sanitizePath, TableFlags, TableHeader, log, configHandler, CLIProgressManager, clearProgressModuleSetting, readContentTypeSchemas } from '@contentstack/cli-utilities';
import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';
import config from './config';
import { print } from './util/log';
Expand Down Expand Up @@ -480,10 +480,9 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
* `gfSchema`. The values of these properties are the parsed JSON data from two different files.
*/
getCtAndGfSchema() {
const ctPath = join(
const ctDirPath = join(
this.sharedConfig.basePath,
this.sharedConfig.moduleConfig['content-types'].dirName,
this.sharedConfig.moduleConfig['content-types'].fileName,
);
const gfPath = join(
this.sharedConfig.basePath,
Expand All @@ -492,7 +491,7 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
);

const gfSchema = existsSync(gfPath) ? (JSON.parse(readFileSync(gfPath, 'utf8')) as ContentTypeStruct[]) : [];
const ctSchema = existsSync(ctPath) ? (JSON.parse(readFileSync(ctPath, 'utf8')) as ContentTypeStruct[]) : [];
const ctSchema = readContentTypeSchemas(ctDirPath) as ContentTypeStruct[];

return { ctSchema, gfSchema };
}
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-audit/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const config = {
moduleConfig: {
'content-types': {
name: 'content type',
fileName: 'schema.json',
fileName: 'schema.json', // Not used - reads from individual files
dirName: 'content_types',
},
'global-fields': {
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-bootstrap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ $ npm install -g @contentstack/cli-cm-bootstrap
$ csdx COMMAND
running command...
$ csdx (--version)
@contentstack/cli-cm-bootstrap/2.0.0-beta.6 darwin-arm64 node-v24.12.0
@contentstack/cli-cm-bootstrap/2.0.0-beta.6 darwin-arm64 node-v23.11.0
$ csdx --help [COMMAND]
USAGE
$ csdx COMMAND
Expand Down
1,797 changes: 1,797 additions & 0 deletions packages/contentstack-bulk-publish/README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/contentstack-export/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ $ npm install -g @contentstack/cli-cm-export
$ csdx COMMAND
running command...
$ csdx (--version)
@contentstack/cli-cm-export/2.0.0-beta.7 darwin-arm64 node-v24.12.0
@contentstack/cli-cm-export/2.0.0-beta.7 darwin-arm64 node-v23.11.0
$ csdx --help [COMMAND]
USAGE
$ csdx COMMAND
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,5 @@ export default class ContentTypesExport extends BaseClass {
await executeTask(contentTypes, writeWithProgress.bind(this), {
concurrency: this.exportConfig.writeConcurrency,
});

const schemaFilePath = path.join(this.contentTypesDirPath, 'schema.json');
log.debug(`Writing aggregate schema to: ${schemaFilePath}`, this.exportConfig.context);

return fsUtil.writeFile(schemaFilePath, contentTypes);
}
}
11 changes: 5 additions & 6 deletions packages/contentstack-export/src/export/modules/entries.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'path';
import { ContentstackClient, FsUtility, handleAndLogError, messageHandler, log } from '@contentstack/cli-utilities';
import { ContentstackClient, FsUtility, handleAndLogError, messageHandler, log, readContentTypeSchemas } from '@contentstack/cli-utilities';
import { Export, ExportProjects } from '@contentstack/cli-variants';
import { sanitizePath } from '@contentstack/cli-utilities';

Expand Down Expand Up @@ -30,7 +30,7 @@ export default class EntriesExport extends BaseClass {
private variantEntries!: any;
private entriesDirPath: string;
private localesFilePath: string;
private schemaFilePath: string;
private contentTypesDirPath: string;
private entriesFileHelper: FsUtility;
private projectInstance: ExportProjects;
public exportVariantEntry: boolean = false;
Expand All @@ -51,11 +51,10 @@ export default class EntriesExport extends BaseClass {
sanitizePath(exportConfig.modules.locales.dirName),
sanitizePath(exportConfig.modules.locales.fileName),
);
this.schemaFilePath = path.resolve(
this.contentTypesDirPath = path.resolve(
sanitizePath(exportConfig.exportDir),
sanitizePath(exportConfig.branchName || ''),
sanitizePath(exportConfig.modules.content_types.dirName),
'schema.json',
);
this.projectInstance = new ExportProjects(this.exportConfig);
this.exportConfig.context.module = MODULE_CONTEXTS.ENTRIES;
Expand All @@ -70,7 +69,7 @@ export default class EntriesExport extends BaseClass {
const [locales, contentTypes, entryRequestOptions, totalEntriesCount, variantInfo] =
await this.withLoadingSpinner('ENTRIES: Analyzing content structure and entries...', async () => {
const locales = fsUtil.readFile(this.localesFilePath) as Array<Record<string, unknown>>;
const contentTypes = fsUtil.readFile(this.schemaFilePath) as Array<Record<string, unknown>>;
const contentTypes = readContentTypeSchemas(this.contentTypesDirPath);

if (!Array.isArray(locales) || locales?.length === 0) {
log.debug(`No locales found in ${this.localesFilePath}`, this.exportConfig.context);
Expand All @@ -83,7 +82,7 @@ export default class EntriesExport extends BaseClass {
return [locales, contentTypes, [], 0, null];
}
log.debug(
`Loaded ${contentTypes?.length} content types from ${this.schemaFilePath}`,
`Loaded ${contentTypes?.length} content types from individual files in ${this.contentTypesDirPath}`,
this.exportConfig.context,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ describe('ExportContentTypes', () => {
// Stub FsUtility methods
sinon.stub(FsUtility.prototype, 'writeFile').resolves();
sinon.stub(FsUtility.prototype, 'makeDirectory').resolves();
// Stub FsUtility.prototype.readdir and readFile for readContentTypeSchemas support
sinon.stub(FsUtility.prototype, 'readdir').returns([]);
sinon.stub(FsUtility.prototype, 'readFile').returns(undefined);
});

afterEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ describe('EntriesExport', () => {
createFolderStub.callsFake(() => {
// Do nothing - prevent actual directory creation
});

// Stub FsUtility.prototype.readdir and readFile for readContentTypeSchemas support
// readContentTypeSchemas creates its own FsUtility instance, so we need to stub the prototype
sandbox.stub(FsUtility.prototype, 'readdir').returns([]);
sandbox.stub(FsUtility.prototype, 'readFile').returns(undefined);

entriesExport = new EntriesExport({
exportConfig: mockExportConfig,
Expand Down Expand Up @@ -173,16 +178,15 @@ describe('EntriesExport', () => {
mockExportConfig.modules.locales.dirName,
mockExportConfig.modules.locales.fileName,
);
const expectedSchemaPath = path.resolve(
const expectedContentTypesDirPath = path.resolve(
mockExportConfig.exportDir,
mockExportConfig.branchName || '',
mockExportConfig.modules.content_types.dirName,
'schema.json',
);

expect(entriesExport.entriesDirPath).to.equal(expectedEntriesPath);
expect(entriesExport.localesFilePath).to.equal(expectedLocalesPath);
expect(entriesExport.schemaFilePath).to.equal(expectedSchemaPath);
expect(entriesExport.contentTypesDirPath).to.equal(expectedContentTypesDirPath);
});

it('should initialize ExportProjects instance', () => {
Expand All @@ -197,27 +201,29 @@ describe('EntriesExport', () => {

describe('start() method - Early Returns', () => {
it('should return early when no content types are found', async () => {
mockFsUtil.readFile
.onFirstCall()
.returns([{ code: 'en-us' }]) // locales
.onSecondCall()
.returns([]); // content types
// Stub mockFsUtil.readFile for locales
mockFsUtil.readFile.returns([{ code: 'en-us' }]);

// Stub FsUtility.prototype for readContentTypeSchemas to return empty
(FsUtility.prototype.readdir as sinon.SinonStub).returns([]); // No content type files

await entriesExport.start();

// Should not attempt to fetch entries
expect(mockStackAPIClient.contentType.called).to.be.false;
// Should read both locales and content types files
expect(mockFsUtil.readFile.calledTwice).to.be.true;
// Should read locales file
expect(mockFsUtil.readFile.called).to.be.true;
});

it('should handle empty locales array gracefully', async () => {
const contentTypes = [{ uid: 'ct-1', title: 'Content Type 1' }];
mockFsUtil.readFile
.onFirstCall()
.returns([]) // empty locales
.onSecondCall()
.returns(contentTypes);
const contentTypes = [{ uid: 'ct-1', title: 'Content Type 1', schema: [] as any }];

// Stub mockFsUtil.readFile for locales
mockFsUtil.readFile.returns([]); // empty locales

// Stub FsUtility.prototype for readContentTypeSchemas to return content types
(FsUtility.prototype.readdir as sinon.SinonStub).returns(['ct-1.json']);
(FsUtility.prototype.readFile as sinon.SinonStub).returns(contentTypes[0]);

await entriesExport.start();

Expand All @@ -226,14 +232,14 @@ describe('EntriesExport', () => {
});

it('should handle non-array locales gracefully', async () => {
const contentTypes = [{ uid: 'ct-1', title: 'Content Type 1' }];
// Use empty array instead of null to avoid Object.keys error
// The code checks !Array.isArray first, so empty array will work
mockFsUtil.readFile
.onFirstCall()
.returns([]) // empty locales array
.onSecondCall()
.returns(contentTypes);
const contentTypes = [{ uid: 'ct-1', title: 'Content Type 1', schema: [] as any }];

// Stub mockFsUtil.readFile for locales
mockFsUtil.readFile.returns([]); // empty locales array

// Stub FsUtility.prototype for readContentTypeSchemas to return content types
(FsUtility.prototype.readdir as sinon.SinonStub).returns(['ct-1.json']);
(FsUtility.prototype.readFile as sinon.SinonStub).returns(contentTypes[0]);

// Mock entry query for when entries are processed
const mockEntryQuery = {
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-import-setup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ $ npm install -g @contentstack/cli-cm-import-setup
$ csdx COMMAND
running command...
$ csdx (--version)
@contentstack/cli-cm-import-setup/2.0.0-beta.3 darwin-arm64 node-v24.12.0
@contentstack/cli-cm-import-setup/2.0.0-beta.3 darwin-arm64 node-v23.11.0
$ csdx --help [COMMAND]
USAGE
$ csdx COMMAND
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-import/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ $ npm install -g @contentstack/cli-cm-import
$ csdx COMMAND
running command...
$ csdx (--version)
@contentstack/cli-cm-import/2.0.0-beta.6 darwin-arm64 node-v24.12.0
@contentstack/cli-cm-import/2.0.0-beta.6 darwin-arm64 node-v23.11.0
$ csdx --help [COMMAND]
USAGE
$ csdx COMMAND
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import * as path from 'path';
import { find, cloneDeep, map } from 'lodash';
import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
import { sanitizePath, log, handleAndLogError, readContentTypeSchemas } from '@contentstack/cli-utilities';
import { ImportConfig, ModuleClassParams } from '../../types';
import BaseClass, { ApiOptions } from './base-class';
import { updateFieldRules } from '../../utils/content-type-helper';
Expand Down Expand Up @@ -474,7 +474,7 @@ export default class ContentTypesImport extends BaseClass {
const [cts, gfs, pendingGfs, pendingExt] = await this.withLoadingSpinner(
'CONTENT TYPES: Analyzing import data...',
async () => {
const cts = fsUtil.readFile(path.join(this.cTsFolderPath, 'schema.json'));
const cts = readContentTypeSchemas(this.cTsFolderPath);
const gfs = fsUtil.readFile(path.resolve(this.gFsFolderPath, this.gFsConfig.fileName));
const pendingGfs = fsUtil.readFile(this.gFsPendingPath);
const pendingExt = fsUtil.readFile(this.extPendingPath);
Expand Down
17 changes: 11 additions & 6 deletions packages/contentstack-import/src/import/modules/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import * as path from 'path';
import { writeFileSync } from 'fs';
import { isEmpty, values, cloneDeep, find, indexOf, forEach, remove } from 'lodash';
import { FsUtility, sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
import { FsUtility, sanitizePath, log, handleAndLogError, readContentTypeSchemas } from '@contentstack/cli-utilities';
import {
fsUtil,
lookupExtension,
Expand Down Expand Up @@ -230,7 +230,8 @@ export default class EntriesImport extends BaseClass {
return this.withLoadingSpinner('ENTRIES: Analyzing import data...', async () => {
log.debug('Loading content types for entry analysis', this.importConfig.context);

this.cTs = fsUtil.readFile(path.join(this.cTsPath, 'schema.json')) as Record<string, unknown>[];
this.cTs = readContentTypeSchemas(this.cTsPath);

if (!this.cTs || isEmpty(this.cTs)) {
return [0, 0, 0, 0, 0];
}
Expand Down Expand Up @@ -1201,15 +1202,19 @@ export default class EntriesImport extends BaseClass {
log.debug(`Found ${cTsWithFieldRules.length} content types with field rules to update`, this.importConfig.context);

try {
// Read content types from individual files
const cTs = readContentTypeSchemas(this.cTsPath) || [];
for (let cTUid of cTsWithFieldRules) {
log.debug(`Processing field rules for content type: ${cTUid}`, this.importConfig.context);

const cTs: Record<string, any>[] = fsUtil.readFile(path.join(this.cTsPath, 'schema.json')) as Record<
string,
unknown
>[];

const contentType: any = find(cTs, { uid: cTUid });

if (!contentType) {
log.debug(`Content type ${cTUid} not found in schemas`, this.importConfig.context);
continue;
}

if (contentType.field_rules) {
log.debug(
`Found ${contentType.field_rules.length} field rules for content type: ${cTUid}`,
Expand Down
Loading
Loading