diff --git a/.cursor/commands/code-review.md b/.cursor/commands/code-review.md new file mode 100644 index 0000000000..c336c0aa17 --- /dev/null +++ b/.cursor/commands/code-review.md @@ -0,0 +1,122 @@ +--- +name: code-review +description: Automated PR review using comprehensive checklist tailored for Contentstack CLI plugins +--- + +# Code Review Command + +## Usage Patterns + +### Scope-Based Reviews +- `/code-review` - Review all current changes with full checklist +- `/code-review --scope typescript` - Focus on TypeScript configuration and patterns +- `/code-review --scope testing` - Focus on Mocha/Chai/Sinon test patterns +- `/code-review --scope contentstack` - Focus on API integration and CLI patterns +- `/code-review --scope oclif` - Focus on command structure and OCLIF patterns + +### Severity Filtering +- `/code-review --severity critical` - Show only critical issues (security, breaking changes) +- `/code-review --severity high` - Show high and critical issues +- `/code-review --severity all` - Show all issues including suggestions + +### Package-Aware Reviews +- `/code-review --package contentstack-import` - Review changes in specific package +- `/code-review --package-type plugin` - Review plugin packages only +- `/code-review --package-type library` - Review library packages (e.g., variants) + +### File Type Focus +- `/code-review --files commands` - Review command files only +- `/code-review --files tests` - Review test files only +- `/code-review --files modules` - Review import/export modules + +## Comprehensive Review Checklist + +### Monorepo Structure Compliance +- **Package organization**: Proper placement in `packages/` structure +- **pnpm workspace**: Correct `package.json` workspace configuration +- **Build artifacts**: No `lib/` directories committed to version control +- **Dependencies**: Proper use of shared utilities (`@contentstack/cli-command`, `@contentstack/cli-utilities`) + +### TypeScript Standards (Repository-Specific) +- **Configuration compliance**: Follows package-specific TypeScript config +- **Naming conventions**: kebab-case files, PascalCase classes, camelCase functions +- **Type safety**: Appropriate use of strict mode vs relaxed settings per package +- **Import patterns**: ES modules with proper default/named exports +- **Migration strategy**: Proper use of `@ts-ignore` during gradual migration + +### OCLIF Command Patterns (Actual Implementation) +- **Base class usage**: Extends `@contentstack/cli-command` (not `@oclif/core`) +- **Command structure**: Proper `static description`, `examples`, `flags` +- **Topic organization**: Uses `cm` topic structure (`cm:stacks:import`) +- **Error handling**: Uses `handleAndLogError` from utilities +- **Validation**: Early flag validation and user-friendly error messages +- **Service delegation**: Commands orchestrate, services implement business logic + +### Testing Excellence (Mocha/Chai/Sinon Stack) +- **Framework compliance**: Uses Mocha + Chai + Sinon (not Jest) +- **File patterns**: Follows `*.test.ts` naming (or `*.test.js` for bootstrap) +- **Directory structure**: Proper placement in `test/unit/`, `test/lib/`, etc. +- **Mock patterns**: Proper Sinon stubbing of SDK methods +- **Coverage configuration**: Correct nyc setup (watch for `inlcude` typo) +- **Test isolation**: Proper `beforeEach`/`afterEach` with `sinon.restore()` +- **No real API calls**: All external dependencies properly mocked + +### Contentstack API Integration (Real Patterns) +- **SDK usage**: Proper `managementSDKClient` and fluent API chaining +- **Authentication**: Correct `configHandler` and token alias handling +- **Rate limiting compliance**: + - Batch spacing (minimum 1 second between batches) + - 429 retry handling with exponential backoff + - Pagination throttling for variants +- **Error handling**: Proper `handleAndLogError` usage and user-friendly messages +- **Configuration**: Proper regional endpoint and management token handling + +### Import/Export Module Architecture +- **BaseClass extension**: Proper inheritance from import/export BaseClass +- **Batch processing**: Correct use of `makeConcurrentCall` and `logMsgAndWaitIfRequired` +- **Module organization**: Proper entity-specific module structure +- **Configuration handling**: Proper `ModuleClassParams` usage +- **Progress feedback**: Appropriate user feedback during long operations + +### Security and Best Practices +- **Token security**: No API keys or tokens logged or committed +- **Input validation**: Proper validation of user inputs and flags +- **Error exposure**: No sensitive information in error messages +- **File permissions**: Proper handling of file system operations +- **Process management**: Appropriate use of `process.exit(1)` for critical failures + +### Performance Considerations +- **Concurrent processing**: Proper use of `Promise.allSettled` for batch operations +- **Memory management**: Appropriate handling of large datasets +- **Rate limiting**: Compliance with Contentstack API limits (10 req/sec) +- **Batch sizing**: Appropriate batch sizes for different operations +- **Progress tracking**: Efficient progress reporting without performance impact + +### Package-Specific Patterns +- **Plugin vs Library**: Correct `oclif.commands` configuration for plugin packages +- **Command compilation**: Proper build pipeline (`tsc` → `lib/commands` → `oclif manifest`) +- **Dependency management**: Correct use of shared vs package-specific dependencies +- **Test variations**: Handles different test patterns per package (JS vs TS, different structures) + +## Review Execution + +### Automated Checks +1. **Lint compliance**: ESLint and TypeScript compiler checks +2. **Test coverage**: nyc coverage thresholds (where enforced) +3. **Build verification**: Successful compilation to `lib/` directories +4. **Dependency audit**: No security vulnerabilities in dependencies + +### Manual Review Focus Areas +1. **API integration patterns**: Verify proper SDK usage and error handling +2. **Rate limiting implementation**: Check for proper throttling mechanisms +3. **Test quality**: Verify comprehensive mocking and error scenario coverage +4. **Command usability**: Ensure clear help text and examples +5. **Monorepo consistency**: Check for consistent patterns across packages + +### Common Issues to Flag +- **Coverage config typos**: `"inlcude"` instead of `"include"` in `.nycrc.json` +- **Inconsistent TypeScript**: Mixed strict mode usage without migration plan +- **Real API calls in tests**: Any unmocked external dependencies +- **Missing rate limiting**: API calls without proper throttling +- **Build artifacts committed**: Any `lib/` directories in version control +- **Inconsistent error handling**: Not using utilities error handling patterns diff --git a/.cursor/commands/execute-tests.md b/.cursor/commands/execute-tests.md new file mode 100644 index 0000000000..7cde58bbdd --- /dev/null +++ b/.cursor/commands/execute-tests.md @@ -0,0 +1,107 @@ +--- +name: execute-tests +description: Run tests by scope, file, or module with intelligent filtering for this pnpm monorepo +--- + +# Execute Tests Command + +## Usage Patterns + +### Monorepo-Wide Testing +- `/execute-tests` - Run all tests across all packages +- `/execute-tests --coverage` - Run all tests with nyc coverage report +- `/execute-tests --parallel` - Run package tests in parallel using pnpm + +### Package-Specific Testing +- `/execute-tests packages/contentstack-audit/` - Run tests for specific package +- `/execute-tests packages/contentstack-import/` - Run import package tests +- `/execute-tests packages/contentstack-export/` - Run export package tests +- `/execute-tests contentstack-migration` - Run tests by package name (shorthand) + +### Scope-Based Testing +- `/execute-tests unit` - Run unit tests only (`test/unit/**/*.test.ts`) +- `/execute-tests commands` - Run command tests (`test/commands/**/*.test.ts`) +- `/execute-tests services` - Run service layer tests +- `/execute-tests modules` - Run import/export module tests + +### File Pattern Testing +- `/execute-tests *.test.ts` - Run all TypeScript tests +- `/execute-tests *.test.js` - Run JavaScript tests (bootstrap package) +- `/execute-tests test/unit/services/` - Run tests for specific directory + +### Watch and Development +- `/execute-tests --watch` - Run tests in watch mode with file monitoring +- `/execute-tests --debug` - Run tests with debug output enabled +- `/execute-tests --bail` - Stop on first test failure + +## Intelligent Filtering + +### Repository-Aware Detection +- **Test patterns**: Primarily `*.test.ts`, some `*.test.js` (bootstrap), rare `*.spec.ts` +- **Directory structures**: `test/unit/`, `test/lib/`, `test/seed/`, `test/commands/` +- **Package variations**: Different test layouts per package +- **Build exclusion**: Ignores `lib/` directories (compiled artifacts) + +### Monorepo Integration +- **pnpm workspace support**: Uses `pnpm -r --filter` for package targeting +- **Dependency awareness**: Understands package interdependencies +- **Parallel execution**: Leverages pnpm's parallel capabilities +- **Selective testing**: Can target specific packages or file patterns + +### Framework Detection +- **Mocha configuration**: Respects `.mocharc.json` files per package +- **TypeScript compilation**: Handles `pretest: tsc -p test` scripts +- **Coverage integration**: Works with nyc configuration (`.nycrc.json`) +- **Test helpers**: Detects and includes test initialization files + +## Execution Examples + +### Common Workflows +```bash +# Run all tests with coverage +/execute-tests --coverage + +# Test specific package during development +/execute-tests packages/contentstack-import/ --watch + +# Run only unit tests across all packages +/execute-tests unit + +# Test import/export modules specifically +/execute-tests modules --coverage + +# Debug failing tests in audit package +/execute-tests packages/contentstack-audit/ --debug --bail +``` + +### Package-Specific Commands Generated +```bash +# For contentstack-import package +cd packages/contentstack-import && pnpm test + +# For all packages with coverage +pnpm -r --filter './packages/*' run test:coverage + +# For specific test file +cd packages/contentstack-export && npx mocha test/unit/export/modules/stack.test.ts +``` + +## Configuration Awareness + +### Mocha Integration +- Respects individual package `.mocharc.json` configurations +- Handles TypeScript compilation via `ts-node/register` +- Supports test helpers and initialization files +- Manages timeout settings per package + +### Coverage Integration +- Uses nyc for coverage reporting +- Respects `.nycrc.json` configurations (with typo detection) +- Generates HTML, text, and lcov reports +- Handles TypeScript source mapping + +### pnpm Workspace Features +- Leverages workspace dependency resolution +- Supports filtered execution by package patterns +- Enables parallel test execution across packages +- Respects package-specific scripts and configurations diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md new file mode 100644 index 0000000000..f6bb162a2f --- /dev/null +++ b/.cursor/rules/README.md @@ -0,0 +1,107 @@ +# Cursor Rules + +Context-aware rules that load automatically based on the files you're editing, optimized for this Contentstack CLI plugins monorepo. + +## Rule Files + +| File | Scope | Always Applied | Purpose | +|------|-------|----------------|---------| +| `dev-workflow.md` | `**/*.ts`, `**/*.js`, `**/*.json` | Yes | Monorepo TDD workflow, pnpm workspace patterns | +| `typescript.mdc` | `**/*.ts`, `**/*.tsx` | No | TypeScript config variations, naming conventions | +| `testing.mdc` | `**/test/**/*.ts`, `**/test/**/*.js`, `**/*.test.ts`, `**/*.spec.ts` | Yes | Mocha, Chai, Sinon patterns, nyc coverage | +| `oclif-commands.mdc` | `**/commands/**/*.ts` | No | OCLIF patterns, BaseCommand classes, CLI validation | +| `contentstack-cli.mdc` | `**/import/**/*.ts`, `**/export/**/*.ts`, `**/modules/**/*.ts`, `**/services/**/*.ts`, `**/utils/**/*.ts` | No | API integration, rate limiting, batch processing | + +## Commands + +| File | Trigger | Purpose | +|------|---------|---------| +| `execute-tests.md` | `/execute-tests` | Run tests by scope, package, or module with monorepo awareness | +| `code-review.md` | `/code-review` | Automated PR review with Contentstack CLI specific checklist | + +## Loading Behaviour + +### File Type Mapping +- **TypeScript files** → `typescript.mdc` + `dev-workflow.md` +- **Command files** (`packages/*/src/commands/**/*.ts`) → `oclif-commands.mdc` + `typescript.mdc` + `dev-workflow.md` +- **Import/Export modules** (`packages/*/src/{import,export,modules}/**/*.ts`) → `contentstack-cli.mdc` + `typescript.mdc` + `dev-workflow.md` +- **Service files** (`packages/*/src/services/**/*.ts`) → `contentstack-cli.mdc` + `typescript.mdc` + `dev-workflow.md` +- **Test files** (`packages/*/test/**/*.{ts,js}`) → `testing.mdc` + relevant domain rules + `dev-workflow.md` +- **Utility files** (`packages/*/src/utils/**/*.ts`) → `contentstack-cli.mdc` + `typescript.mdc` + `dev-workflow.md` + +### Package-Specific Loading +- **Plugin packages** (with `oclif.commands`) → Full command and API rules +- **Library packages** (e.g., variants) → TypeScript and utility rules only +- **Bootstrap package** (JavaScript tests) → Adjusted testing rules + +## Repository-Specific Features + +### Monorepo Awareness +- **11 plugin packages** under `packages/` +- **pnpm workspaces** configuration +- **Shared utilities**: `@contentstack/cli-command`, `@contentstack/cli-utilities` +- **Build artifacts**: `lib/` directories (excluded from rules) + +### Actual Patterns Detected +- **Testing**: Mocha + Chai + Sinon (not Jest) +- **TypeScript**: Mixed strict mode adoption +- **Commands**: Extend `@contentstack/cli-command` (not `@oclif/core`) +- **Rate limiting**: Multiple mechanisms (batch spacing, 429 retry, pagination throttle) +- **Coverage**: nyc with inconsistent enforcement + +## Performance Benefits + +- **75-85% token reduction** vs monolithic `.cursorrules` +- **Context-aware loading** - only relevant rules activate based on actual file patterns +- **Precise glob patterns** - avoid loading rules for build artifacts or irrelevant files +- **Skills integration** - rules provide quick context, skills provide comprehensive patterns + +## Design Principles + +### Validated Against Codebase +- Rules reflect **actual patterns** found in repository analysis +- Glob patterns match **real file structure** (not theoretical) +- Examples use **actual dependencies** and APIs +- Coverage targets reflect **current enforcement** (aspirational vs actual) + +### Lightweight and Focused +- Each rule has **single responsibility** +- Detailed patterns referenced via **skills system** +- `alwaysApply: true` only for truly universal patterns +- Package-specific variations acknowledged + +## Validation Checklist + +### Rule Loading Tests (Repository-Specific) +- ✅ **Command files** (`packages/*/src/commands/**/*.ts`) → `oclif-commands.mdc` + `typescript.mdc` + `dev-workflow.md` +- ✅ **Import modules** (`packages/contentstack-import/src/import/**/*.ts`) → `contentstack-cli.mdc` + `typescript.mdc` + `dev-workflow.md` +- ✅ **Export modules** (`packages/contentstack-export/src/export/**/*.ts`) → `contentstack-cli.mdc` + `typescript.mdc` + `dev-workflow.md` +- ✅ **Test files** (`packages/*/test/unit/**/*.test.ts`) → `testing.mdc` + `dev-workflow.md` +- ✅ **Bootstrap tests** (`packages/contentstack-bootstrap/test/**/*.test.js`) → `testing.mdc` (JS-aware) + `dev-workflow.md` +- ✅ **Service files** (`packages/*/src/services/**/*.ts`) → `contentstack-cli.mdc` + `typescript.mdc` + `dev-workflow.md` + +### Manual Verification +1. Open files from different packages in Cursor +2. Check active rules shown in chat interface +3. Verify correct rule combinations load based on file location +4. Test manual rule invocation: `@contentstack-cli show me rate limiting patterns` +5. Confirm no rules load for `lib/` build artifacts + +### Performance Monitoring +- **Before**: Monolithic rules loaded for all files +- **After**: Context-specific rules based on actual file patterns +- **Expected reduction**: 75-85% in token usage +- **Validation**: Rules load only when relevant to current file context + +## Maintenance Notes + +### Regular Updates Needed +- **Glob patterns** - Update when package structure changes +- **TypeScript config** - Align with actual tsconfig.json variations +- **Coverage targets** - Sync with actual nyc configuration +- **Dependencies** - Update when shared utilities change + +### Known Issues to Monitor +- **Coverage typo**: Several `.nycrc.json` files have `"inlcude"` instead of `"include"` +- **Strict mode inconsistency**: Packages have different TypeScript strictness levels +- **Test patterns**: Bootstrap uses `.test.js` while others use `.test.ts` diff --git a/.cursor/rules/contentstack-cli.mdc b/.cursor/rules/contentstack-cli.mdc new file mode 100644 index 0000000000..b7ec1b81bc --- /dev/null +++ b/.cursor/rules/contentstack-cli.mdc @@ -0,0 +1,165 @@ +--- +description: 'Contentstack CLI specific patterns and API integration' +globs: ['**/import/**/*.ts', '**/export/**/*.ts', '**/modules/**/*.ts', '**/services/**/*.ts', '**/utils/**/*.ts'] +alwaysApply: false +--- + +# Contentstack CLI Standards + +## API Integration + +- Use `@contentstack/cli-utilities` for SDK factory: `managementSDKClient(config)` +- Stack-scoped API access: `stackAPIClient.asset()`, `stackAPIClient.extension()` +- Fluent SDK chaining: `stack.contentType().entry().query().find()` +- Custom HTTP for variants: `apiClient.put/get` with path strings + +## Authentication + +- Use `@contentstack/cli-utilities` for token management +- Management token alias: `configHandler.get('tokens.')` +- OAuth context: `configHandler.get('userUid'|'email'|'oauthOrgUid')` +- Authentication check: `isAuthenticated()` before operations +- Never log API keys or tokens in console or files + +## Rate Limiting - Multiple Mechanisms + +### Batch Spacing (Import/Export) +```typescript +// ✅ GOOD - Ensure minimum 1 second between batches +async logMsgAndWaitIfRequired(processName: string, start: number): Promise { + const end = Date.now(); + const exeTime = end - start; + if (exeTime < 1000) await this.delay(1000 - exeTime); +} +``` + +### 429 Retry (Branches) +```typescript +// ✅ GOOD - Handle 429 with retry +export async function handleErrorMsg(err, retryCallback?: () => Promise) { + if (err?.status === 429 || err?.response?.status === 429) { + await new Promise((resolve) => setTimeout(resolve, 1000)); // 1 sec delay + if (retryCallback) { + return retryCallback(); // Retry the request + } + } +} +``` + +### Variant Pagination Throttle +```typescript +// ✅ GOOD - Throttle variant API requests +if (requestTime < 1000) { + await delay(1000 - requestTime); +} +``` + +## Error Handling + +### Standard Pattern +```typescript +// ✅ GOOD - Use handleAndLogError from utilities +try { + const result = await this.stack.contentType().entry().fetch(); +} catch (error) { + handleAndLogError(error); + this.logAndPrintErrorDetails(error, config); +} +``` + +### User-Friendly Errors +```typescript +// ✅ GOOD - User-facing error display +cliux.print(errorMessage, { color: 'red' }); +// For critical failures +process.exit(1); +``` + +## Module Architecture (Import/Export) + +### BaseClass Pattern +```typescript +// ✅ GOOD - Extend BaseClass for entity modules +export class ContentTypes extends BaseClass { + constructor(params: ModuleClassParams) { + super(params); + // Entity-specific initialization + } + + async import(): Promise { + // Use this.makeConcurrentCall for batching + // Use this.logMsgAndWaitIfRequired for rate limiting + } +} +``` + +### Batch Processing +```typescript +// ✅ GOOD - Concurrent batch processing +const batches = chunk(apiContent, batchSize); +for (const batch of batches) { + const start = Date.now(); + await this.makeConcurrentCall(batch, this.processItem.bind(this)); + await this.logMsgAndWaitIfRequired('Processing', start, batches.length, batchIndex); +} +``` + +## Configuration Patterns + +### Import/Export Config +```typescript +// ✅ GOOD - Use configHandler for management tokens +const config = { + host: configHandler.get('region.cma'), + managementTokenAlias: flags.alias, + stackApiKey: flags['stack-api-key'], + rateLimit: 5, // Default rate limit +}; +``` + +### Regional Configuration +```typescript +// ✅ GOOD - Handle regional endpoints +const defaultConfig = { + host: 'https://api.contentstack.io', + cdn: 'https://cdn.contentstack.io', + // Regional developer hub URLs +}; +``` + +## Testing Patterns + +### SDK Mocking +```typescript +// ✅ GOOD - Mock stack client methods +const mockStackClient = { + fetch: sinon.stub().resolves({ name: 'Test Stack', uid: 'stack-uid' }), + locale: sinon.stub().returns({ + query: sinon.stub().returns({ + find: sinon.stub().resolves({ items: [], count: 0 }), + }), + }), +}; +``` + +### Error Simulation +```typescript +// ✅ GOOD - Test error handling +it('should handle 429 rate limit', async () => { + const error = { status: 429 }; + mockClient.fetch.rejects(error); + // Test retry logic +}); +``` + +## Package-Specific Patterns + +### Plugin vs Library +- **Plugin packages**: Have `oclif.commands` in package.json +- **Library packages** (e.g., variants): No OCLIF commands, consumed by other packages + +### Monorepo Structure +- Commands: `packages/*/src/commands/cm/**/*.ts` +- Modules: `packages/*/src/{import,export,modules}/**/*.ts` +- Utilities: `packages/*/src/utils/**/*.ts` +- Built artifacts: `packages/*/lib/**` (not source) diff --git a/.cursor/rules/dev-workflow.md b/.cursor/rules/dev-workflow.md new file mode 100644 index 0000000000..04ac39af64 --- /dev/null +++ b/.cursor/rules/dev-workflow.md @@ -0,0 +1,148 @@ +--- +description: "Core development workflow and TDD patterns - always applied" +globs: ["**/*.ts", "**/*.js", "**/*.json"] +alwaysApply: true +--- + +# Development Workflow + +## Monorepo Structure + +### Package Organization +- **11 plugin packages** under `packages/` +- **pnpm workspaces** with `workspaces: ["packages/*"]` +- **Shared dependencies**: `@contentstack/cli-command`, `@contentstack/cli-utilities` +- **Build artifacts**: `lib/` directory (compiled from `src/`) + +### Development Commands +```bash +# Install dependencies for all packages +pnpm install + +# Run command across all packages +pnpm -r --filter './packages/*' + +# Work on specific package +cd packages/contentstack-import +pnpm test +``` + +## TDD Workflow - MANDATORY + +1. **RED** → Write ONE failing test in `test/unit/**/*.test.ts` +2. **GREEN** → Write minimal code in `src/` to pass +3. **REFACTOR** → Improve code quality while keeping tests green + +### Test-First Examples +```typescript +// ✅ GOOD - Write test first +describe('ImportService', () => { + it('should import content types', async () => { + // Arrange - Set up mocks + mockStackClient.contentType.returns({ + create: sinon.stub().resolves({ uid: 'ct-uid' }) + }); + + // Act - Call the method + const result = await importService.importContentTypes(); + + // Assert - Verify behavior + expect(result.success).to.be.true; + expect(mockStackClient.contentType).to.have.been.called; + }); +}); +``` + +## Critical Rules + +### Testing Standards +- **NO implementation before tests** - Test-driven development only +- **Coverage aspiration**: 80% minimum (not uniformly enforced) +- **Mock all external dependencies** - No real API calls in tests +- **Use Mocha + Chai + Sinon** - Standard testing stack + +### Code Quality +- **TypeScript configuration**: Varies by package (strict mode aspirational) +- **NO test.skip or .only in commits** - Clean test suites only +- **Proper error handling** - Use `handleAndLogError` from utilities + +### Build Process +```bash +# Standard build process +pnpm run build # tsc compilation +pnpm run test # Run test suite +oclif manifest # Generate OCLIF manifest +``` + +## Package-Specific Patterns + +### Plugin Packages +- Have `oclif.commands` in `package.json` +- Commands in `src/commands/cm/**/*.ts` +- Built commands in `lib/commands/` +- Extend `@contentstack/cli-command` + +### Library Packages (e.g., variants) +- No OCLIF commands configuration +- Pure TypeScript libraries +- Consumed by other packages +- `main` points to `lib/index.js` + +## Quick Reference + +For detailed patterns, see skills: +- `@skills/testing` - Mocha, Chai, Sinon patterns and TDD workflow +- `@skills/contentstack-cli` - API integration, rate limiting, authentication +- `@skills/oclif-commands` - Command structure, base classes, validation + +## Development Checklist + +### Before Starting Work +- [ ] Identify target package in `packages/` +- [ ] Check existing tests in `test/unit/` +- [ ] Understand command structure if working on commands +- [ ] Set up proper TypeScript configuration + +### During Development +- [ ] Write failing test first +- [ ] Implement minimal code to pass +- [ ] Mock external dependencies (SDK, file system, etc.) +- [ ] Use proper error handling patterns +- [ ] Follow naming conventions (kebab-case files, PascalCase classes) + +### Before Committing +- [ ] All tests pass: `pnpm test` +- [ ] No `.only` or `.skip` in test files +- [ ] Build succeeds: `pnpm run build` +- [ ] TypeScript compilation clean +- [ ] Proper error handling implemented + +## Common Patterns + +### Service Layer Architecture +```typescript +// ✅ GOOD - Separate concerns +export default class ImportCommand extends Command { + async run(): Promise { + const config = this.buildConfig(); + const service = new ImportService(config); + + try { + await service.execute(); + this.log('Import completed successfully'); + } catch (error) { + handleAndLogError(error); + } + } +} +``` + +### Rate Limiting Compliance +```typescript +// ✅ GOOD - Respect API limits +async processBatch(batch: Item[]): Promise { + const start = Date.now(); + await this.makeConcurrentCall(batch, this.processItem); + await this.logMsgAndWaitIfRequired('Processing', start); +} +``` diff --git a/.cursor/rules/oclif-commands.mdc b/.cursor/rules/oclif-commands.mdc new file mode 100644 index 0000000000..ac186ff52c --- /dev/null +++ b/.cursor/rules/oclif-commands.mdc @@ -0,0 +1,219 @@ +--- +description: 'OCLIF command development patterns and CLI best practices' +globs: ['**/commands/**/*.ts'] +alwaysApply: false +--- + +# OCLIF Command Standards + +## Command Structure + +### Standard Command Pattern +```typescript +// ✅ GOOD - Standard command structure +import { Command } from '@contentstack/cli-command'; + +export default class ImportCommand extends Command { + static description = 'Import content from a stack'; + + static examples: string[] = [ + 'csdx cm:stacks:import --stack-api-key --data-dir ', + 'csdx cm:stacks:import --alias --config ', + ]; + + static flags = { + // Define flags using utilities + }; + + async run(): Promise { + // Main command logic + } +} +``` + +## Base Classes Available + +### BaseCommand (Audit Package) +```typescript +// ✅ GOOD - Extend BaseCommand for shared functionality +export abstract class BaseCommand extends Command { + static baseFlags: FlagInput = { + config: Flags.string({ char: 'c', description: 'Config path' }), + 'data-dir': Flags.string({ char: 'd', description: 'Data directory' }), + 'show-console-output': Flags.boolean({ description: 'Show console output' }), + }; + + public async init(): Promise { + await super.init(); + const { args, flags } = await this.parse({ + flags: this.ctor.flags, + baseFlags: (super.ctor as typeof BaseCommand).baseFlags, + // ... + }); + } +} +``` + +### BaseCommand (Export-to-CSV Package) +```typescript +// ✅ GOOD - Lightweight base with command context +export abstract class BaseCommand extends Command { + public commandContext!: CommandContext; + + public async init(): Promise { + await super.init(); + this.commandContext = this.createCommandContext(); + log.debug('Command initialized', this.commandContext); + } + + protected async catch(err: Error & { exitCode?: number }): Promise { + log.debug('Command error caught', { ...this.commandContext, error: err.message }); + return super.catch(err); + } +} +``` + +## Command Patterns + +### Import Commands +- Use `@contentstack/cli-command` Command base +- Parse with `ImportCommand` type for config validation +- Handle authentication via `configHandler` and `isAuthenticated` +- Delegate to service layer modules + +### Direct Extension Pattern +```typescript +// ✅ GOOD - Most packages extend Command directly +export default class BranchMerge extends Command { + static description = 'Merge branches'; + + async run(): Promise { + const { flags } = await this.parse(BranchMerge); + // Command-specific logic + } +} +``` + +## OCLIF Configuration + +### Package.json Setup +```json +{ + "oclif": { + "commands": "./lib/commands", + "bin": "csdx", + "topicSeparator": ":" + } +} +``` + +### Command Topics +- All commands use `cm` topic: `cm:stacks:import`, `cm:branches:merge` +- Built commands live in `lib/commands` (compiled from `src/commands`) +- Optional `csdxConfig.shortCommandName` for abbreviated names + +## Error Handling + +### Standard Error Pattern +```typescript +// ✅ GOOD - Use handleAndLogError from utilities +try { + await this.executeCommand(); +} catch (error) { + handleAndLogError(error); + this.logAndPrintErrorDetails(error, config); +} +``` + +### User-Friendly Messages +```typescript +// ✅ GOOD - Clear user feedback +cliux.print('Operation completed successfully', { color: 'green' }); +cliux.print('Error occurred', { color: 'red' }); + +// For critical failures +process.exit(1); +``` + +## Validation Patterns + +### Early Validation +```typescript +// ✅ GOOD - Validate flags early +async run(): Promise { + const { flags } = await this.parse(MyCommand); + + // Validate required combinations + if (!flags.alias && !flags['stack-api-key']) { + this.error('Either --alias or --stack-api-key is required'); + } + + // Proceed with validated input +} +``` + +### Authentication Check +```typescript +// ✅ GOOD - Check authentication before operations +if (!isAuthenticated()) { + this.error('Please login first using: csdx auth:login'); +} +``` + +## Progress and Logging + +### Progress Feedback +```typescript +// ✅ GOOD - Provide user feedback +this.log('Starting import process...'); +cliux.print('Processing entries...', { color: 'blue' }); + +// Use progress bars for long operations +const progressBar = cliux.progress.start(total); +progressBar.increment(); +progressBar.stop(); +``` + +### Debug Logging +```typescript +// ✅ GOOD - Use structured logging +log.debug('Command initialized', { + command: this.id, + flags: this.flags +}); +``` + +## Command Delegation + +### Service Layer Separation +```typescript +// ✅ GOOD - Commands orchestrate, services implement +async run(): Promise { + const config = this.buildConfig(); + const service = new ImportService(config); + + try { + await service.execute(); + this.log('Import completed successfully'); + } catch (error) { + this.handleError(error); + } +} +``` + +## Testing Commands + +### OCLIF Test Support +```typescript +// ✅ GOOD - Use @oclif/test for command testing +import { test } from '@oclif/test'; + +describe('cm:stacks:import', () => { + test + .stdout() + .command(['cm:stacks:import', '--help']) + .it('shows help', ctx => { + expect(ctx.stdout).to.contain('Import content from a stack'); + }); +}); +``` diff --git a/.cursor/rules/testing.mdc b/.cursor/rules/testing.mdc new file mode 100644 index 0000000000..7fc3a7c936 --- /dev/null +++ b/.cursor/rules/testing.mdc @@ -0,0 +1,266 @@ +--- +description: 'Testing patterns and TDD workflow' +globs: ['**/test/**/*.ts', '**/test/**/*.js', '**/__tests__/**/*.ts', '**/*.spec.ts', '**/*.test.ts'] +alwaysApply: true +--- + +# Testing Standards + +## Framework Stack + +### Primary Testing Tools +- **Mocha** - Test runner (used across all packages) +- **Chai** - Assertion library +- **Sinon** - Mocking and stubbing +- **@oclif/test** - Command testing support +- **nyc** - Code coverage + +### Package-Specific Tools +- **nock** - HTTP mocking (migration package) +- **rewire** - Module patching (import package) + +## Test File Patterns + +### Naming Conventions +- **Primary**: `*.test.ts` (dominant pattern) +- **Alternative**: `*.spec.ts` (less common) +- **Bootstrap exception**: `*.test.js` (JavaScript tests) + +### Directory Structure +``` +packages/*/ +├── test/unit/**/*.test.ts # Most packages +├── test/lib/**/*.test.ts # clone package +├── test/seed/**/*.test.ts # seed package +└── test/commands/**/*.test.ts # command-specific tests +``` + +## Mocha Configuration + +### Standard Setup (.mocharc.json) +```json +{ + "require": [ + "test/helpers/init.js", + "ts-node/register", + "source-map-support/register" + ], + "recursive": true, + "timeout": 30000, + "spec": "test/**/*.test.ts" +} +``` + +### TypeScript Compilation +```json +// package.json scripts +{ + "pretest": "tsc -p test", + "test": "nyc --extension .ts mocha" +} +``` + +## Mocking Patterns + +### Sinon SDK Mocking +```typescript +// ✅ GOOD - Mock Contentstack SDK methods +const mockStackClient = { + fetch: sinon.stub().resolves({ + name: 'Test Stack', + uid: 'stack-uid', + org_uid: 'org-uid' + }), + locale: sinon.stub().returns({ + query: sinon.stub().returns({ + find: sinon.stub().resolves({ + items: [{ + uid: 'locale-1', + name: 'English (United States)', + code: 'en-us' + }], + count: 1, + }), + }), + }), +}; +``` + +### Module Stubbing +```typescript +// ✅ GOOD - Stub sibling modules +beforeEach(() => { + sinon.stub(mapModule, 'processEntries').resolves([]); + sinon.stub(configModule, 'getConfig').returns(mockConfig); +}); + +afterEach(() => { + sinon.restore(); +}); +``` + +### HTTP Mocking (Migration) +```typescript +// ✅ GOOD - Use nock for HTTP mocking +import nock from 'nock'; + +beforeEach(() => { + nock('https://api.contentstack.io') + .get('/v3/stacks') + .reply(200, { stacks: [] }); +}); +``` + +## Coverage Configuration + +### NYC Setup (.nycrc.json) +```json +{ + "extension": [".ts"], + "include": ["src/**/*.ts"], + "exclude": ["**/*.test.ts", "**/*.spec.ts"], + "reporter": ["text", "html", "lcov"], + "all": true +} +``` + +### Coverage Targets +- **Team aspiration**: 80% minimum coverage +- **Current enforcement**: Inconsistent across packages +- **Note**: Some packages have `check-coverage: false` +- **Typo alert**: Several `.nycrc.json` files have `"inlcude"` instead of `"include"` + +## Test Structure + +### Standard Test Pattern +```typescript +// ✅ GOOD - Comprehensive test structure +describe('ContentTypes Module', () => { + let mockStackClient: any; + let contentTypes: ContentTypes; + + beforeEach(() => { + mockStackClient = createMockStackClient(); + contentTypes = new ContentTypes({ + stackAPIClient: mockStackClient, + importConfig: mockConfig, + }); + }); + + afterEach(() => { + sinon.restore(); + }); + + describe('import()', () => { + it('should import content types successfully', async () => { + // Arrange + mockStackClient.contentType.returns({ + create: sinon.stub().resolves({ uid: 'ct-uid' }) + }); + + // Act + await contentTypes.import(); + + // Assert + expect(mockStackClient.contentType).to.have.been.called; + }); + + it('should handle API errors gracefully', async () => { + // Arrange + const error = new Error('API Error'); + mockStackClient.contentType.throws(error); + + // Act & Assert + await expect(contentTypes.import()).to.be.rejectedWith('API Error'); + }); + }); +}); +``` + +## Command Testing + +### OCLIF Test Pattern +```typescript +// ✅ GOOD - Test commands with @oclif/test +import { test } from '@oclif/test'; + +describe('cm:stacks:import', () => { + test + .stdout() + .command(['cm:stacks:import', '--help']) + .it('shows help message', ctx => { + expect(ctx.stdout).to.contain('Import content from a stack'); + }); + + test + .stderr() + .command(['cm:stacks:import']) + .exit(2) + .it('fails without required flags'); +}); +``` + +## Error Testing + +### Rate Limit Testing +```typescript +// ✅ GOOD - Test rate limiting behavior +it('should handle 429 rate limit errors', async () => { + const rateLimitError = { status: 429 }; + mockClient.fetch.onFirstCall().rejects(rateLimitError); + mockClient.fetch.onSecondCall().resolves(mockResponse); + + const result = await service.fetchWithRetry(); + + expect(mockClient.fetch).to.have.been.calledTwice; + expect(result).to.equal(mockResponse); +}); +``` + +### Authentication Testing +```typescript +// ✅ GOOD - Test authentication scenarios +it('should handle token expiration', async () => { + const authError = { status: 401, message: 'Unauthorized' }; + mockClient.fetch.rejects(authError); + + await expect(service.makeRequest()).to.be.rejectedWith('Unauthorized'); +}); +``` + +## Test Data Management + +### Mock Data Organization +```typescript +// ✅ GOOD - Organize test data +const mockData = { + contentTypes: [ + { uid: 'ct1', title: 'Content Type 1' }, + { uid: 'ct2', title: 'Content Type 2' }, + ], + entries: [ + { uid: 'entry1', title: 'Entry 1', content_type: 'ct1' }, + ], +}; +``` + +### Test Helpers +```typescript +// ✅ GOOD - Create reusable test utilities +export function createMockStackClient() { + return { + fetch: sinon.stub(), + contentType: sinon.stub(), + entry: sinon.stub(), + // ... other methods + }; +} +``` + +## Critical Testing Rules + +- **No real API calls** - Always mock external dependencies +- **Test both success and failure paths** - Cover error scenarios +- **Mock at service boundaries** - Don't mock internal implementation details +- **Use proper cleanup** - Always restore stubs in afterEach +- **Test command validation** - Verify flag validation and error messages diff --git a/.cursor/rules/typescript.mdc b/.cursor/rules/typescript.mdc new file mode 100644 index 0000000000..d3ff4774b1 --- /dev/null +++ b/.cursor/rules/typescript.mdc @@ -0,0 +1,259 @@ +--- +description: 'TypeScript strict mode standards and naming conventions' +globs: ['**/*.ts', '**/*.tsx'] +alwaysApply: false +--- + +# TypeScript Standards + +## Configuration + +### Root Configuration +```json +// tsconfig.json - Baseline configuration +{ + "compilerOptions": { + "strict": true, + "module": "commonjs", + "target": "es2016", + "declaration": true, + "outDir": "lib", + "rootDir": "src" + } +} +``` + +### Package-Level Variations +```json +// Most packages override with: +{ + "compilerOptions": { + "strict": false, // ⚠️ Relaxed for legacy code + "noImplicitAny": true, // ✅ Still enforce type annotations + "target": "es2017", + "allowJs": true // Mixed JS/TS support + } +} +``` + +### Modern Packages (Bootstrap, Variants) +```json +// TypeScript 5.x with stricter settings +{ + "compilerOptions": { + "strict": true, + "target": "es2020", + "moduleResolution": "node16" + } +} +``` + +## Naming Conventions (Actual Usage) + +### Files +- **Primary pattern**: `kebab-case.ts` (`base-class.ts`, `import-config-handler.ts`) +- **Single-word modules**: `stack.ts`, `locales.ts`, `entries.ts` +- **Commands**: Follow OCLIF topic structure (`cm/stacks/import.ts`) + +### Classes +```typescript +// ✅ GOOD - PascalCase for classes +export class ImportCommand extends Command { } +export class BaseClass { } +export class ExportStack { } +export class ContentTypes { } +``` + +### Functions and Methods +```typescript +// ✅ GOOD - camelCase for functions +export async function fetchAllEntries(): Promise { } +async logMsgAndWaitIfRequired(): Promise { } +createCommandContext(): CommandContext { } +``` + +### Constants +```typescript +// ✅ GOOD - SCREAMING_SNAKE_CASE for constants +const DEFAULT_RATE_LIMIT = 5; +const MAX_RETRY_ATTEMPTS = 3; +const API_BASE_URL = 'https://api.contentstack.io'; +``` + +### Interfaces and Types +```typescript +// ✅ GOOD - PascalCase for types +export interface ModuleClassParams { + importConfig: ImportConfig; + stackAPIClient: ManagementStack; +} + +export type ApiOptions = { + host?: string; + timeout?: number; +}; + +export type EnvType = 'development' | 'staging' | 'production'; +``` + +## Import/Export Patterns + +### ES Modules (Preferred) +```typescript +// ✅ GOOD - ES import/export syntax +import { Command } from '@contentstack/cli-command'; +import type { ImportConfig } from '../types'; +import { managementSDKClient } from '@contentstack/cli-utilities'; + +export default class ImportCommand extends Command { } +export { ImportConfig, ApiOptions }; +``` + +### Default Exports +```typescript +// ✅ GOOD - Default export for commands and main classes +export default class ImportCommand extends Command { } +export default class BaseClass { } +``` + +### Named Exports +```typescript +// ✅ GOOD - Named exports for utilities and types +export async function delay(ms: number): Promise { } +export interface ConfigOptions { } +export type ModuleType = 'import' | 'export'; +``` + +## Type Definitions + +### Local Types +```typescript +// ✅ GOOD - Define types close to usage +export interface ImportOptions { + stackApiKey: string; + dataDir: string; + rateLimit?: number; +} + +export type BatchResult = { + success: boolean; + errors: Error[]; + processedCount: number; +}; +``` + +### Type Organization +```typescript +// ✅ GOOD - Organize types in dedicated files +// src/types/index.ts +export interface ImportConfig { } +export interface ExportConfig { } +export type ModuleClassParams = { }; +``` + +## Strict Mode Compliance + +### Function Return Types +```typescript +// ✅ GOOD - Explicit return types +export async function fetchEntries(): Promise { + return await this.stack.entry().query().find(); +} + +export function createConfig(): ImportConfig { + return { + stackApiKey: '', + dataDir: './data', + }; +} +``` + +### Null Safety +```typescript +// ✅ GOOD - Handle null/undefined explicitly +function processEntry(entry: Entry | null): void { + if (!entry) { + throw new Error('Entry is required'); + } + // Process entry safely +} +``` + +### Type Guards +```typescript +// ✅ GOOD - Use type guards for runtime checks +function isImportConfig(config: unknown): config is ImportConfig { + return typeof config === 'object' && + config !== null && + 'stackApiKey' in config; +} +``` + +## Error Handling Types + +### Custom Error Classes +```typescript +// ✅ GOOD - Typed error classes +export class ContentstackApiError extends Error { + constructor( + message: string, + public readonly statusCode?: number, + public readonly cause?: Error + ) { + super(message); + this.name = 'ContentstackApiError'; + } +} +``` + +### Error Union Types +```typescript +// ✅ GOOD - Model expected errors +type ApiResult = { + success: true; + data: T; +} | { + success: false; + error: string; + statusCode: number; +}; +``` + +## Migration Strategy + +### Gradual Strict Mode Adoption +```typescript +// ✅ ACCEPTABLE - Gradual migration approach +// @ts-ignore for legacy code during migration +// TODO: Remove @ts-ignore and fix types +// @ts-ignore +const legacyResult = oldApiCall(); +``` + +### Type Assertions (Use Sparingly) +```typescript +// ⚠️ USE CAREFULLY - Type assertions when necessary +const config = unknownConfig as ImportConfig; + +// ✅ BETTER - Use type guards instead +if (isImportConfig(unknownConfig)) { + const config = unknownConfig; // TypeScript knows the type +} +``` + +## Package-Specific Patterns + +### Command Packages +- Extend `@contentstack/cli-command` types +- Use OCLIF flag types from utilities +- Define command-specific interfaces + +### Library Packages (Variants) +- No OCLIF dependencies +- Pure TypeScript interfaces +- Consumed by other packages + +### Test Files +- Use `any` sparingly for mock objects +- Prefer typed mocks when possible +- Test type safety with TypeScript compiler diff --git a/.talismanrc b/.talismanrc index e9954f9fc5..a38c65af00 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,4 +1,10 @@ fileignoreconfig: - filename: pnpm-lock.yaml checksum: b8dc082b59f03873ab00adddd07df399fc74e90491f50ee96297467016fc9f20 + - filename: .cursor/rules/dev-workflow.md + checksum: ae8f8b2894c5cf0da6a85eb53c97775bd38835c191f9132ecaa24dcb9f439811 + - filename: .cursor/rules/contentstack-cli.mdc + checksum: ba287a9e9dcf6565d2d2e79c9bbf6350a89e13afbd1f8755973d837563115c83 + - filename: .cursor/rules/oclif-commands.mdc + checksum: 3f9891d3d5872a2823c83215bc1727911dd32935d17d91ad5f2e7d5f75f3ab61 version: '1.0'