Skip to content

Commit 67857f8

Browse files
committed
RELEASING: Releasing 2 package(s)
Releases: [email protected] @repo/[email protected]
1 parent 5b915a3 commit 67857f8

17 files changed

+963
-13
lines changed

PLUGIN_GUIDE.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Plugin System Guide
2+
3+
## Overview
4+
5+
The plugin system allows extending git-json-resolver with custom merge strategies while maintaining JSON config compatibility.
6+
7+
## Creating a Plugin
8+
9+
### 1. Plugin Structure
10+
11+
```typescript
12+
import { StrategyPlugin, StrategyStatus } from "git-json-resolver";
13+
14+
// Augment types for TypeScript support
15+
declare module "git-json-resolver" {
16+
interface PluginStrategies {
17+
"my-strategy": string;
18+
}
19+
}
20+
21+
// Individual strategy function (can be imported directly)
22+
export const myStrategy = ({ ours, theirs, base, path }) => {
23+
// Custom logic here
24+
return { status: StrategyStatus.OK, value: result };
25+
};
26+
27+
// Plugin interface for dynamic loading
28+
const plugin: StrategyPlugin = {
29+
strategies: {
30+
"my-strategy": myStrategy,
31+
},
32+
init: async (config) => {
33+
// Optional initialization
34+
}
35+
};
36+
37+
export default plugin;
38+
```
39+
40+
### 2. Publishing
41+
42+
```bash
43+
npm publish my-git-json-resolver-plugin
44+
```
45+
46+
## Using Plugins
47+
48+
### Direct Import (Recommended)
49+
50+
```typescript
51+
import { myStrategy } from "my-plugin";
52+
import { resolveConflicts } from "git-json-resolver";
53+
54+
await resolveConflicts({
55+
customStrategies: {
56+
"my-strategy": myStrategy,
57+
},
58+
rules: {
59+
version: ["my-strategy"]
60+
}
61+
});
62+
```
63+
64+
### Dynamic Loading (JSON Config)
65+
66+
```json
67+
{
68+
"plugins": ["my-plugin"],
69+
"pluginConfig": {
70+
"my-plugin": {
71+
"customOption": "value"
72+
}
73+
},
74+
"rules": {
75+
"version": ["my-strategy"]
76+
}
77+
}
78+
```
79+
80+
### TypeScript Config
81+
82+
```typescript
83+
// git-json-resolver.config.ts
84+
import type { Config } from "git-json-resolver";
85+
86+
const config: Config = {
87+
plugins: ["my-plugin"],
88+
pluginConfig: {
89+
"my-plugin": { customOption: "value" }
90+
},
91+
rules: {
92+
version: ["my-strategy"] // TypeScript will know about this strategy
93+
}
94+
};
95+
96+
export default config;
97+
```
98+
99+
## Plugin Development Best Practices
100+
101+
1. **Naming**: Use descriptive strategy names
102+
2. **Fallback**: Return `CONTINUE` when strategy doesn't apply
103+
3. **Validation**: Check input types before processing
104+
4. **Error Handling**: Return `FAIL` for unrecoverable errors
105+
5. **Type Safety**: Augment `PluginStrategies` interface
106+
107+
## Example Strategies
108+
109+
- `semantic-version`: Compare semantic versions
110+
- `timestamp-latest`: Choose most recent timestamp
111+
- `array-union`: Merge arrays with unique values
112+
- `file-size-larger`: Choose larger file content
113+
- `priority-field`: Use priority-based resolution

README.md

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ A Git-aware conflict resolver for **JSON-first structured data**.
2828
- 🔄 **Backup and restore** functionality
2929
- 📊 **Configurable logging** (memory or file-based)
3030
- 🔀 **Git merge driver** support for seamless Git integration
31+
- 🔧 **Plugin system** for custom strategies with JSON config support
3132

3233
## Installation
3334

@@ -114,7 +115,9 @@ await resolveConflicts({
114115
});
115116
```
116117

117-
### Config File (`git-json-resolver.config.js`)
118+
### Config File
119+
120+
**JavaScript Config (`git-json-resolver.config.js`)**
118121

119122
```js
120123
module.exports = {
@@ -151,6 +154,37 @@ module.exports = {
151154
};
152155
```
153156

157+
**JSON Config (`git-json-resolver.config.json`) - ⚠️ Experimental**
158+
159+
```json
160+
{
161+
"$schema": "https://cdn.jsdelivr.net/npm/git-json-resolver@latest/schema/config.schema.json",
162+
"defaultStrategy": ["merge", "ours"],
163+
"plugins": ["my-plugin"],
164+
"pluginConfig": {
165+
"my-plugin": {
166+
"option": "value"
167+
}
168+
},
169+
"rules": {
170+
"package.json": {
171+
"version": ["semantic-version", "theirs"],
172+
"dependencies": ["ours"]
173+
}
174+
},
175+
"byStrategy": {
176+
"ours": ["dependencies.*", "devDependencies.*"],
177+
"theirs!": ["version", "name"]
178+
}
179+
}
180+
```
181+
182+
**⚠️ JSON Config Limitations:**
183+
- No TypeScript intellisense for plugin strategies
184+
- Limited validation for custom strategy names
185+
- No compile-time type checking
186+
- **Recommended:** Use `.js` or `.ts` config for better developer experience
187+
154188
## Supported Strategies
155189

156190
- **merge** → deep merge objects/arrays where possible
@@ -217,7 +251,115 @@ All non-JSON formats are converted to JSON → resolved → converted back to or
217251
- **Glob patterns**: `"dependencies.*"`, `"**.config.**"`
218252
- **Wildcards**: `"*.json"`, `"src/**/*.config.js"`
219253

220-
### Custom Strategies
254+
### Plugin System
255+
256+
**For TypeScript/JavaScript configs, you can use either approach:**
257+
258+
#### 1. Direct Import (Recommended)
259+
260+
```ts
261+
import { strategies } from "my-plugin";
262+
// or import { semanticVersion, timestampLatest } from "my-plugin";
263+
import { resolveConflicts } from "git-json-resolver";
264+
265+
await resolveConflicts({
266+
customStrategies: {
267+
...strategies,
268+
// or "semantic-version": semanticVersion,
269+
},
270+
rules: {
271+
version: ["semantic-version", "theirs"]
272+
}
273+
});
274+
```
275+
276+
#### 2. Dynamic Loading
277+
278+
```ts
279+
// Also works in .js/.ts configs
280+
const config = {
281+
plugins: ["my-plugin"],
282+
pluginConfig: {
283+
"my-plugin": { option: "value" }
284+
},
285+
rules: {
286+
version: ["semantic-version", "theirs"]
287+
}
288+
};
289+
```
290+
291+
#### 3. JSON Config (Dynamic Loading Only)
292+
293+
```json
294+
{
295+
"plugins": ["my-plugin"],
296+
"pluginConfig": {
297+
"my-plugin": { "option": "value" }
298+
},
299+
"rules": {
300+
"version": ["semantic-version", "theirs"]
301+
}
302+
}
303+
```
304+
305+
**⚠️ If plugin doesn't provide global types:**
306+
307+
```ts
308+
import type { Config } from "git-json-resolver";
309+
310+
const config: Config<AllStrategies | "semantic-version" | "timestamp-latest"> = {
311+
// ... your config
312+
};
313+
```
314+
315+
**Creating a Plugin**
316+
317+
```ts
318+
import { StrategyPlugin, StrategyStatus, StrategyFn } from "git-json-resolver";
319+
320+
// Augment types for TypeScript support
321+
declare module "git-json-resolver" {
322+
interface PluginStrategies {
323+
"semantic-version": string;
324+
"timestamp-latest": string;
325+
}
326+
}
327+
328+
// Individual strategy functions (can be imported directly)
329+
export const semanticVersion: StrategyFn = ({ ours, theirs }) => {
330+
if (isNewerVersion(theirs, ours)) {
331+
return { status: StrategyStatus.OK, value: theirs };
332+
}
333+
return { status: StrategyStatus.CONTINUE };
334+
};
335+
336+
export const timestampLatest: StrategyFn = ({ ours, theirs }) => {
337+
const oursTime = new Date(ours as string).getTime();
338+
const theirsTime = new Date(theirs as string).getTime();
339+
return {
340+
status: StrategyStatus.OK,
341+
value: oursTime > theirsTime ? ours : theirs
342+
};
343+
};
344+
345+
// Export strategies object for direct import
346+
export const strategies = {
347+
"semantic-version": semanticVersion,
348+
"timestamp-latest": timestampLatest,
349+
};
350+
351+
// Plugin interface for dynamic loading
352+
const plugin: StrategyPlugin = {
353+
strategies,
354+
init: async (config) => {
355+
console.log('Plugin initialized with:', config);
356+
}
357+
};
358+
359+
export default plugin;
360+
```
361+
362+
### Custom Strategies (Inline)
221363

222364
```ts
223365
import { StrategyStatus } from "git-json-resolver";
@@ -245,6 +387,10 @@ const config = {
245387
- **Per-file logs**: Separate log files for each processed file
246388
- **Debug mode**: Detailed conflict information and strategy traces
247389

390+
## Plugin Development
391+
392+
See [PLUGIN_GUIDE.md](./PLUGIN_GUIDE.md) for detailed plugin development documentation.
393+
248394
## Contributing
249395

250396
Contributions welcome 🙌
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"$schema": "https://cdn.jsdelivr.net/npm/git-json-resolver@latest/schema/config.schema.json",
3+
"defaultStrategy": ["merge", "ours"],
4+
"plugins": ["example-plugin"],
5+
"pluginConfig": {
6+
"example-plugin": {
7+
"semverPreferStable": true,
8+
"timestampFormat": "ISO"
9+
}
10+
},
11+
"rules": {
12+
"package.json": {
13+
"version": ["semantic-version", "theirs"],
14+
"dependencies": ["ours"],
15+
"scripts": ["merge"]
16+
},
17+
"*.config.json": {
18+
"lastModified": ["timestamp-latest"],
19+
"features": ["array-union"]
20+
}
21+
},
22+
"byStrategy": {
23+
"ours": ["devDependencies.*", "peerDependencies.*"],
24+
"theirs!": ["name", "description"]
25+
},
26+
"include": ["**/*.json", "**/*.yaml"],
27+
"exclude": ["**/node_modules/**", "**/dist/**"],
28+
"matcher": "picomatch",
29+
"debug": false,
30+
"writeConflictSidecar": true,
31+
"backupDir": ".merge-backups"
32+
}

examples/plugin/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@example/plugin",
3+
"version": "1.0.0",
4+
"description": "A Plugin example",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "",
10+
"license": "ISC",
11+
"devDependencies": {
12+
"@repo/typescript-config": "workspace:*",
13+
"@types/node": "^24.3.0",
14+
"git-json-resolver": "workspace:*",
15+
"typescript": "^5.9.2"
16+
}
17+
}

0 commit comments

Comments
 (0)