Skip to content

Commit 7b422b2

Browse files
committed
merged latest changes
2 parents 9e61946 + 3ff93e9 commit 7b422b2

50 files changed

Lines changed: 2981 additions & 838 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ Context-aware rules that load automatically based on the files you're editing, o
99
| `dev-workflow.md` | `**/*.ts`, `**/*.js`, `**/*.json` | Yes | Monorepo TDD workflow, pnpm workspace patterns (6 packages) |
1010
| `typescript.mdc` | `**/*.ts`, `**/*.tsx` | No | TypeScript configurations and naming conventions |
1111
| `testing.mdc` | `**/test/**/*.ts`, `**/test/**/*.js`, `**/__tests__/**/*.ts`, `**/*.spec.ts`, `**/*.test.ts` | Yes | Mocha, Chai test patterns and test structure |
12-
| `oclif-commands.mdc` | `**/commands/**/*.ts` | No | OCLIF command patterns and CLI validation |
12+
| `oclif-commands.mdc` | `**/commands/**/*.ts`, `**/base-command.ts` | No | OCLIF command patterns and CLI validation |
13+
| `contentstack-core.mdc` | `packages/contentstack/src/**/*.ts`, `packages/contentstack/src/**/*.js` | No | Core package plugin aggregation, hooks, and entry point patterns |
1314

1415
## Commands
1516

@@ -23,6 +24,8 @@ Context-aware rules that load automatically based on the files you're editing, o
2324
### File Type Mapping
2425
- **TypeScript files**`typescript.mdc` + `dev-workflow.md`
2526
- **Command files** (`packages/*/src/commands/**/*.ts`) → `oclif-commands.mdc` + `typescript.mdc` + `dev-workflow.md`
27+
- **Base command files** (`packages/*/src/base-command.ts`) → `oclif-commands.mdc` + `typescript.mdc` + `dev-workflow.md`
28+
- **Core package files** (`packages/contentstack/src/**/*.ts`) → `contentstack-core.mdc` + `typescript.mdc` + `dev-workflow.md`
2629
- **Test files** (`packages/*/test/**/*.{ts,js}`) → `testing.mdc` + `dev-workflow.md`
2730
- **Utility files** (`packages/*/src/utils/**/*.ts`) → `typescript.mdc` + `dev-workflow.md`
2831

@@ -76,5 +79,6 @@ Context-aware rules that load automatically based on the files you're editing, o
7679
For detailed patterns:
7780
- **Testing**: See `testing.mdc` for Mocha/Chai test structure
7881
- **Commands**: See `oclif-commands.mdc` for command development
82+
- **Core Package**: See `contentstack-core.mdc` for plugin aggregation and hook patterns
7983
- **Development**: See `dev-workflow.md` for TDD and monorepo workflow
8084
- **TypeScript**: See `typescript.mdc` for type safety patterns
Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
---
2+
description: "Contentstack core CLI package patterns — plugin aggregation, hooks, and entry point"
3+
globs: ["packages/contentstack/src/**/*.ts", "packages/contentstack/src/**/*.js"]
4+
alwaysApply: false
5+
---
6+
7+
# Contentstack Core Package Standards
8+
9+
## Overview
10+
11+
The `@contentstack/cli` core package is the entry point for the entire CLI. Unlike plugin packages (auth, config), it:
12+
- **Aggregates all plugins** — declared in `oclif.plugins` array in `package.json`
13+
- **Implements hooks** — `init` and `prerun` hooks in `src/hooks/` for global behaviors
14+
- **Shares interfaces** — Core types used across all plugins in `src/interfaces/`
15+
- **Provides utilities** — Helper classes like `CsdxContext` in `src/utils/`
16+
- **Has no command files** — Commands are provided by plugin packages
17+
18+
## Architecture
19+
20+
### Entry Point
21+
22+
```typescript
23+
// ✅ GOOD - bin/run.js (CommonJS)
24+
// This is the executable entry point referenced in package.json "bin"
25+
// Standard OCLIF entry point pattern
26+
```
27+
28+
### Package Configuration
29+
30+
The `oclif` configuration in `package.json`:
31+
```json
32+
{
33+
"oclif": {
34+
"bin": "csdx",
35+
"topicSeparator": ":",
36+
"helpClass": "./lib/help.js",
37+
"plugins": [
38+
"@oclif/plugin-help",
39+
"@oclif/plugin-not-found",
40+
"@oclif/plugin-plugins",
41+
"@contentstack/cli-config",
42+
"@contentstack/cli-auth"
43+
// ... more plugins
44+
],
45+
"hooks": {
46+
"init": [
47+
"./lib/hooks/init/context-init",
48+
"./lib/hooks/init/utils-init"
49+
],
50+
"prerun": [
51+
"./lib/hooks/prerun/init-context-for-command",
52+
"./lib/hooks/prerun/command-deprecation-check",
53+
"./lib/hooks/prerun/default-rate-limit-check",
54+
"./lib/hooks/prerun/latest-version-warning"
55+
]
56+
},
57+
"topics": {
58+
"auth": { "description": "Perform authentication-related activities" },
59+
"config": { "description": "Perform configuration related activities" },
60+
"cm": { "description": "Perform content management activities" }
61+
}
62+
}
63+
}
64+
```
65+
66+
## Hook Lifecycle
67+
68+
### OCLIF Hook Execution Order
69+
70+
1. **CLI initialization** → Node process starts
71+
2. **`init` hooks** → Set up global context and utilities (executed once)
72+
3. **Command detection** → OCLIF matches command name to plugin
73+
4. **`prerun` hooks** → Validate state, check auth, prepare for command execution (per command)
74+
5. **Command execution** → Plugin command's `run()` method executes
75+
76+
### Init Hooks
77+
78+
Init hooks run once during CLI startup. Use them for expensive setup operations.
79+
80+
```typescript
81+
// ✅ GOOD - src/hooks/init/context-init.ts
82+
// Initialize CLI context that commands depend on
83+
import { CsdxContext } from '../../utils';
84+
import { configHandler } from '@contentstack/cli-utilities';
85+
86+
export default function (opts): void {
87+
// Store command ID for session-based log organization
88+
if (opts.id) {
89+
configHandler.set('currentCommandId', opts.id);
90+
}
91+
// Make context available to all commands via this.config.context
92+
this.config.context = new CsdxContext(opts, this.config);
93+
}
94+
```
95+
96+
### Prerun Hooks
97+
98+
Prerun hooks run before each command. Use them for validation and state checks.
99+
100+
```typescript
101+
// ✅ GOOD - src/hooks/prerun/auth-guard.ts
102+
// Validate authentication before running protected commands
103+
104+
import { cliux, isAuthenticated, managementSDKClient } from '@contentstack/cli-utilities';
105+
106+
export default async function (opts): Promise<void> {
107+
const { context: { region = null } = {} } = this.config;
108+
109+
// Validate region is set (required for all non-region commands)
110+
if (opts.Command.id !== 'config:set:region') {
111+
if (!region) {
112+
cliux.error('No region found, please set a region via config:set:region');
113+
this.exit();
114+
return;
115+
}
116+
}
117+
118+
// Example: Validate auth for protected commands
119+
if (isProtectedCommand(opts.Command.id)) {
120+
if (!isAuthenticated()) {
121+
cliux.error('Please log in to execute this command');
122+
this.exit();
123+
}
124+
}
125+
}
126+
```
127+
128+
### Hook Patterns
129+
130+
#### Accessing Configuration
131+
```typescript
132+
// ✅ GOOD - Access global config in hooks
133+
export default function (opts): void {
134+
const { config } = this; // OCLIF Config object
135+
const { context, region } = config; // Custom properties set by other hooks
136+
}
137+
```
138+
139+
#### Async Hooks
140+
```typescript
141+
// ✅ GOOD - Async hooks for operations requiring I/O
142+
export default async function (opts): Promise<void> {
143+
const client = await managementSDKClient({ host: this.config.region.cma });
144+
const user = await client.getUser();
145+
// Hook runs to completion before command starts
146+
}
147+
```
148+
149+
#### Early Exit
150+
```typescript
151+
// ✅ GOOD - Exit hook execution when validation fails
152+
export default function (opts): void {
153+
if (!isValid()) {
154+
cliux.error('Validation failed');
155+
this.exit(); // Stops command from executing
156+
return;
157+
}
158+
}
159+
```
160+
161+
## Context Object
162+
163+
The `CsdxContext` class wraps OCLIF config and adds CLI-specific state.
164+
165+
```typescript
166+
// ✅ GOOD - Accessing context in commands
167+
import { CLIConfig } from '../interfaces';
168+
169+
export default class MyCommand extends Command {
170+
async run(): Promise<void> {
171+
const config: CLIConfig = this.config;
172+
const { context } = config;
173+
174+
// Available context properties:
175+
// - context.id: unique session identifier
176+
// - context.user: authenticated user info (authtoken, email)
177+
// - context.region: current region configuration
178+
// - context.config: regional configuration
179+
// - context.plugin: current plugin metadata
180+
}
181+
}
182+
```
183+
184+
## Shared Interfaces
185+
186+
Interfaces in `src/interfaces/index.ts` are exported and consumed by all plugins.
187+
188+
```typescript
189+
// ✅ GOOD - Define shared types
190+
export interface Context {
191+
id: string;
192+
user: {
193+
authtoken: string;
194+
email: string;
195+
};
196+
region: Region;
197+
plugin: Plugin;
198+
config: any;
199+
}
200+
201+
export interface CLIConfig extends Config {
202+
context: Context;
203+
}
204+
205+
export interface Region {
206+
name: string;
207+
cma: string; // Content Management API endpoint
208+
cda: string; // Content Delivery API endpoint
209+
}
210+
```
211+
212+
## Utilities
213+
214+
Core utilities in `src/utils/` provide shared functionality.
215+
216+
```typescript
217+
// ✅ GOOD - src/utils/context-handler.ts
218+
// Wrapper around context initialization and access
219+
export class CsdxContext {
220+
constructor(opts: any, config: any) {
221+
this.id = opts.id || generateId();
222+
this.region = config.region;
223+
this.user = extractUserFromToken();
224+
}
225+
}
226+
227+
// Export utilities for use in hooks and contexts
228+
export { CsdxContext };
229+
```
230+
231+
## Plugin Registration
232+
233+
Plugins are registered via `oclif.plugins` in `package.json`. Each plugin package must:
234+
235+
1. **Provide commands** — via `oclif.commands` in its `package.json`
236+
2. **Be installed** — as a dependency in the core package
237+
3. **Be listed** — in `oclif.plugins` array for auto-discovery
238+
239+
```json
240+
{
241+
"dependencies": {
242+
"@contentstack/cli-config": "~1.20.0-beta.1",
243+
"@contentstack/cli-auth": "~1.8.0-beta.1"
244+
},
245+
"oclif": {
246+
"plugins": [
247+
"@contentstack/cli-config",
248+
"@contentstack/cli-auth"
249+
]
250+
}
251+
}
252+
```
253+
254+
### Plugin Discovery
255+
256+
OCLIF automatically discovers commands in:
257+
1. Built-in plugins (`@oclif/plugin-help`, etc.)
258+
2. Core package commands (none in contentstack core)
259+
3. Registered plugins (listed in `oclif.plugins`)
260+
261+
## Differences from Plugin Packages
262+
263+
| Aspect | Core Package | Plugin Package |
264+
|--------|--------------|----------------|
265+
| **OCLIF config** | No `commands` field | Has `oclif.commands: "./lib/commands"` |
266+
| **Source structure** | `src/hooks/`, `src/interfaces/`, `src/utils/` | `src/commands/`, `src/services/` |
267+
| **Entry point** | `bin/run.js` | None |
268+
| **Dependencies** | References all plugins | Depends on `@contentstack/cli-command` |
269+
| **Execution role** | Aggregates and initializes | Implements business logic |
270+
271+
## Build Process
272+
273+
The core package build includes hook compilation and OCLIF manifest generation.
274+
275+
```bash
276+
# In package.json scripts
277+
"build": "pnpm compile && oclif manifest && oclif readme"
278+
```
279+
280+
### Build Steps
281+
282+
1. **compile** — TypeScript → JavaScript in `lib/`
283+
2. **oclif manifest** — Generate `oclif.manifest.json` for plugin discovery
284+
3. **oclif readme** — Generate README with available commands
285+
286+
### Build Artifacts
287+
288+
- `lib/` — Compiled hooks, utilities, interfaces
289+
- `oclif.manifest.json` — Plugin and command registry
290+
- `bin/run.js` — Executable entry point
291+
- `README.md` — Generated command documentation
292+
293+
## Testing Hooks
294+
295+
Hooks cannot be tested with standard command testing. Test hook behavior by:
296+
297+
1. **Unit test hook functions** — Import and invoke directly
298+
2. **Integration test via CLI** — Run commands that trigger hooks
299+
3. **Mock OCLIF config** — Provide mocked `this.config` object
300+
301+
```typescript
302+
// ✅ GOOD - Test hook function directly
303+
import contextInit from '../src/hooks/init/context-init';
304+
305+
describe('context-init hook', () => {
306+
it('should set context on config', () => {
307+
const mockConfig = { context: null };
308+
const hookContext = { config: mockConfig };
309+
const opts = { id: 'test-command' };
310+
311+
contextInit.call(hookContext, opts);
312+
313+
expect(mockConfig.context).to.exist;
314+
});
315+
});
316+
```
317+
318+
## Error Handling in Hooks
319+
320+
Hooks should fail fast and provide clear error messages to users.
321+
322+
```typescript
323+
// ✅ GOOD - Clear error messages with user guidance
324+
export default function (opts): void {
325+
if (!isRegionSet()) {
326+
cliux.error('No region configured');
327+
cliux.print('Run: csdx config:set:region --region us', { color: 'blue' });
328+
this.exit();
329+
}
330+
}
331+
```
332+
333+
## Best Practices
334+
335+
### Hook Organization
336+
- Keep hooks focused on a single concern (validation, initialization, etc.)
337+
- Use descriptive names that indicate when they run (`prerun-`, `init-`)
338+
- Initialize dependencies in `init` hooks, not in `prerun` hooks
339+
340+
### Performance
341+
- Minimize work in `init` hooks (they run once per CLI session)
342+
- Cache expensive operations in context for reuse
343+
- Avoid repeated API calls across hooks
344+
345+
### Ordering
346+
- Place hooks that prepare data before hooks that consume it
347+
- Auth validation (`auth-guard`) should run after region validation
348+
- Version warnings can run last (non-critical)
349+
350+
### Context Usage
351+
- Store computed values in context to avoid recalculation
352+
- Make context available to all commands via `this.config.context`
353+
- Document context properties that plugins should expect
354+
355+
### Plugin Development
356+
- Ensure plugins depend on `@contentstack/cli-command`, not the core package
357+
- Commands should extend the shared Command base class
358+
- Plugins should not modify or depend on core hooks directly

.cursor/rules/oclif-commands.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: 'OCLIF command development patterns and CLI best practices'
3-
globs: ['**/commands/**/*.ts']
3+
globs: ['**/commands/**/*.ts', '**/base-command.ts']
44
alwaysApply: false
55
---
66

0 commit comments

Comments
 (0)