@eventcatalog/core 2.7.2 → 2.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/package.json +4 -1
- package/src/components/Lists/SpecificationsList.astro +2 -2
- package/src/content/config.ts +1 -0
- package/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +31 -9
- package/src/pages/docs/[type]/[id]/[version]/index.astro +2 -2
- package/src/utils/collections/file-diffs.ts +132 -0
- package/src/utils/collections/util.ts +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @eventcatalog/core
|
|
2
2
|
|
|
3
|
+
## 2.7.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0189ebb: fix(core): ignore trailing slash for icons
|
|
8
|
+
|
|
9
|
+
## 2.7.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 1c56b8d: feat(core): added automatic diffs for changelogs for json, yml and avro files
|
|
14
|
+
|
|
3
15
|
## 2.7.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventcatalog/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.7.
|
|
4
|
+
"version": "2.7.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"astro-pagefind": "^1.5.0",
|
|
51
51
|
"astro-seo": "^0.8.4",
|
|
52
52
|
"dagre": "^0.8.5",
|
|
53
|
+
"diff": "^7.0.0",
|
|
54
|
+
"diff2html": "^3.4.48",
|
|
53
55
|
"glob": "^10.4.1",
|
|
54
56
|
"html-to-image": "^1.11.11",
|
|
55
57
|
"lodash.merge": "4.6.2",
|
|
@@ -67,6 +69,7 @@
|
|
|
67
69
|
},
|
|
68
70
|
"devDependencies": {
|
|
69
71
|
"@parcel/watcher": "^2.4.1",
|
|
72
|
+
"@types/diff": "^5.2.2",
|
|
70
73
|
"@types/lodash.merge": "4.6.9",
|
|
71
74
|
"@types/react": "^18.3.3",
|
|
72
75
|
"@types/react-dom": "^18.3.0",
|
|
@@ -21,7 +21,7 @@ const numberOfSpecifications = Object.keys(specVersions).length;
|
|
|
21
21
|
href={buildUrl(`/docs/${collectionItem.collection}/${collectionItem.data.id}/${collectionItem.data.version}/spec`)}
|
|
22
22
|
class="text-sm flex items-center space-x-1 hover:underline hover:text-purple-500"
|
|
23
23
|
>
|
|
24
|
-
<img src={buildUrl('/icons/openapi.svg')} class="h-4 w-4" />
|
|
24
|
+
<img src={buildUrl('/icons/openapi.svg', true)} class="h-4 w-4" />
|
|
25
25
|
<span>OpenAPI spec</span>
|
|
26
26
|
</a>
|
|
27
27
|
)
|
|
@@ -32,7 +32,7 @@ const numberOfSpecifications = Object.keys(specVersions).length;
|
|
|
32
32
|
href={buildUrl(`/docs/${collectionItem.collection}/${collectionItem.data.id}/${collectionItem.data.version}/asyncapi`)}
|
|
33
33
|
class="text-sm flex items-center space-x-1 hover:underline hover:text-purple-500"
|
|
34
34
|
>
|
|
35
|
-
<img src={buildUrl('/icons/asyncapi.svg')} class="h-4 w-4" />
|
|
35
|
+
<img src={buildUrl('/icons/asyncapi.svg', true)} class="h-4 w-4" />
|
|
36
36
|
<span>AsyncAPI spec</span>
|
|
37
37
|
</a>
|
|
38
38
|
)
|
package/src/content/config.ts
CHANGED
|
@@ -6,8 +6,11 @@ import type { PageTypes } from '@types';
|
|
|
6
6
|
import { getChangeLogs } from '@utils/changelogs/changelogs';
|
|
7
7
|
import { RectangleGroupIcon, ServerIcon, BoltIcon, ChatBubbleLeftIcon } from '@heroicons/react/24/outline';
|
|
8
8
|
import { pageDataLoader } from '@utils/pages/pages';
|
|
9
|
+
import 'diff2html/bundles/css/diff2html.min.css';
|
|
9
10
|
|
|
10
11
|
import { buildUrl } from '@utils/url-builder';
|
|
12
|
+
import { getVersions, getPreviousVersion } from '@utils/collections/util';
|
|
13
|
+
import { getDiffsForCurrentAndPreviousVersion } from '@utils/collections/file-diffs';
|
|
11
14
|
|
|
12
15
|
export async function getStaticPaths() {
|
|
13
16
|
const itemTypes: PageTypes[] = ['events', 'commands', 'services', 'domains'];
|
|
@@ -22,6 +25,8 @@ export async function getStaticPaths() {
|
|
|
22
25
|
},
|
|
23
26
|
props: {
|
|
24
27
|
type: itemTypes[index],
|
|
28
|
+
allVersionsForCollection: getVersions(items).versions,
|
|
29
|
+
allCollectionItems: items,
|
|
25
30
|
...item,
|
|
26
31
|
},
|
|
27
32
|
}))
|
|
@@ -29,6 +34,7 @@ export async function getStaticPaths() {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
const props = Astro.props;
|
|
37
|
+
let collectionItem = props;
|
|
32
38
|
const logs = await getChangeLogs(props);
|
|
33
39
|
|
|
34
40
|
const { data } = props;
|
|
@@ -44,15 +50,30 @@ const renderedLogs = await logs.map(async (log) => {
|
|
|
44
50
|
|
|
45
51
|
const logsToRender = await Promise.all(renderedLogs);
|
|
46
52
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const logListPromise = logsToRender.map(async (log, index) => {
|
|
54
|
+
const currentLogVersion = log.data.version;
|
|
55
|
+
const previousLogVersion = log.data.version ? getPreviousVersion(log.data.version, props.allVersionsForCollection) : '';
|
|
56
|
+
return {
|
|
57
|
+
id: log.id,
|
|
58
|
+
url: buildUrl(`/docs/${props.collection}/${props.data.id}`),
|
|
59
|
+
isLatest: log.data.version === latestVersion,
|
|
60
|
+
version: log.data.version,
|
|
61
|
+
createdAt: log.data.createdAt,
|
|
62
|
+
badges: log.data.badges || [],
|
|
63
|
+
Content: log.Content,
|
|
64
|
+
diffs:
|
|
65
|
+
currentLogVersion && previousLogVersion
|
|
66
|
+
? await getDiffsForCurrentAndPreviousVersion(
|
|
67
|
+
currentLogVersion,
|
|
68
|
+
previousLogVersion,
|
|
69
|
+
collectionItem.data.id,
|
|
70
|
+
props.allCollectionItems
|
|
71
|
+
)
|
|
72
|
+
: [],
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const logList = await Promise.all(logListPromise);
|
|
56
77
|
|
|
57
78
|
const getBadge = () => {
|
|
58
79
|
if (props.collection === 'services') {
|
|
@@ -154,6 +175,7 @@ const badges = [getBadge()];
|
|
|
154
175
|
<div class="prose prose-md !max-w-none py-2">
|
|
155
176
|
<log.Content />
|
|
156
177
|
</div>
|
|
178
|
+
{log.diffs && log.diffs.map((diff) => <div id="diff-container" set:html={diff} />)}
|
|
157
179
|
</div>
|
|
158
180
|
</div>
|
|
159
181
|
</li>
|
|
@@ -89,7 +89,7 @@ const getSpecificationBadges = () => {
|
|
|
89
89
|
backgroundColor: 'white',
|
|
90
90
|
textColor: 'gray',
|
|
91
91
|
content: 'OpenAPI Spec',
|
|
92
|
-
iconURL: buildUrl('/icons/openapi.svg'),
|
|
92
|
+
iconURL: buildUrl('/icons/openapi.svg', true),
|
|
93
93
|
class: 'text-black hover:underline',
|
|
94
94
|
id: 'open-api-badge',
|
|
95
95
|
url: buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.version}/spec`),
|
|
@@ -101,7 +101,7 @@ const getSpecificationBadges = () => {
|
|
|
101
101
|
backgroundColor: 'white',
|
|
102
102
|
textColor: 'gray',
|
|
103
103
|
content: 'AsyncAPI Spec',
|
|
104
|
-
iconURL: buildUrl('/icons/asyncapi.svg'),
|
|
104
|
+
iconURL: buildUrl('/icons/asyncapi.svg', true),
|
|
105
105
|
class: 'text-black hover:underline',
|
|
106
106
|
id: 'asyncapi-badge',
|
|
107
107
|
url: buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.version}/asyncapi`),
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { diffLines, type Change } from 'diff';
|
|
4
|
+
import { html, parse } from 'diff2html';
|
|
5
|
+
import { getItemsFromCollectionByIdAndSemverOrLatest } from './util';
|
|
6
|
+
import type { CollectionEntry } from 'astro:content';
|
|
7
|
+
import type { CollectionTypes } from '@types';
|
|
8
|
+
|
|
9
|
+
const FILE_EXTENSIONS_TO_INCLUDE = ['.json', '.avro', '.yaml', '.yml'];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generates a diff string in a unified diff format for two versions of a file.
|
|
13
|
+
* @param fileName - The name of the file being compared
|
|
14
|
+
* @param oldStr - The content of the old version of the file
|
|
15
|
+
* @param newStr - The content of the new version of the file
|
|
16
|
+
* @returns A string representing the diff in unified format
|
|
17
|
+
*/
|
|
18
|
+
function generateDiffString(fileName: string, oldStr: string, newStr: string): string {
|
|
19
|
+
const diff = diffLines(oldStr, newStr);
|
|
20
|
+
|
|
21
|
+
// Check if there are any changes
|
|
22
|
+
const hasChanges = diff.some((part) => part.added || part.removed);
|
|
23
|
+
|
|
24
|
+
if (!hasChanges) return '';
|
|
25
|
+
|
|
26
|
+
let diffString = `diff --git a/${fileName} b/${fileName}\n`;
|
|
27
|
+
diffString += `--- a/${fileName}\n`;
|
|
28
|
+
diffString += `+++ b/${fileName}\n`;
|
|
29
|
+
|
|
30
|
+
diff.forEach((part: Change) => {
|
|
31
|
+
const prefix = part.added ? '+' : part.removed ? '-' : ' ';
|
|
32
|
+
const lines = part.value.split('\n');
|
|
33
|
+
// Remove the last element if it's an empty string (which happens if the last line ends with a newline)
|
|
34
|
+
if (lines[lines.length - 1] === '') lines.pop();
|
|
35
|
+
|
|
36
|
+
lines.forEach((line: string) => {
|
|
37
|
+
diffString += `${prefix}${line}\n`;
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return diffString;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Retrieves the list of files for diffing in a collection.
|
|
46
|
+
* @param collection - The collection entry to get files from
|
|
47
|
+
* @returns An array of objects containing file names and their directory paths
|
|
48
|
+
*/
|
|
49
|
+
export async function getFilesForDiffInCollection(
|
|
50
|
+
collection: CollectionEntry<CollectionTypes>
|
|
51
|
+
): Promise<Array<{ file: string; dir: string }>> {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
const pathToFolder = collection.catalog?.absoluteFilePath;
|
|
54
|
+
if (!pathToFolder) return [];
|
|
55
|
+
|
|
56
|
+
const dir = dirname(pathToFolder);
|
|
57
|
+
const allFilesInDirectory = await readdir(dir);
|
|
58
|
+
|
|
59
|
+
return allFilesInDirectory
|
|
60
|
+
.filter((file) => FILE_EXTENSIONS_TO_INCLUDE.some((ext) => file.endsWith(ext)))
|
|
61
|
+
.map((file) => ({ file, dir }));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generates diffs for files between two versions of a collection.
|
|
66
|
+
* @param collections - Array of all collection entries
|
|
67
|
+
* @param id - The ID of the collection to compare
|
|
68
|
+
* @param versionA - The first version to compare
|
|
69
|
+
* @param versionB - The second version to compare
|
|
70
|
+
* @returns An array of diff strings for matching files between versions
|
|
71
|
+
*/
|
|
72
|
+
async function getFilesDiffsBetweenVersions(
|
|
73
|
+
id: string,
|
|
74
|
+
collections: CollectionEntry<CollectionTypes>[],
|
|
75
|
+
versionA: string,
|
|
76
|
+
versionB: string
|
|
77
|
+
): Promise<string[]> {
|
|
78
|
+
const [collectionA, collectionB] = await Promise.all([
|
|
79
|
+
getItemsFromCollectionByIdAndSemverOrLatest(collections, id, versionA),
|
|
80
|
+
getItemsFromCollectionByIdAndSemverOrLatest(collections, id, versionB),
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
if (collectionA.length === 0 || collectionB.length === 0) return [];
|
|
84
|
+
|
|
85
|
+
const [filesForCollectionA, filesForCollectionB] = await Promise.all([
|
|
86
|
+
getFilesForDiffInCollection(collectionA[0]),
|
|
87
|
+
getFilesForDiffInCollection(collectionB[0]),
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
if (filesForCollectionA.length === 0 || filesForCollectionB.length === 0) return [];
|
|
91
|
+
|
|
92
|
+
const matchingFiles = filesForCollectionA.filter((fileA) => filesForCollectionB.some((fileB) => fileB.file === fileA.file));
|
|
93
|
+
|
|
94
|
+
const filesToDiff = matchingFiles.map((file) => ({
|
|
95
|
+
file: file.file,
|
|
96
|
+
dirA: filesForCollectionA.find((f) => f.file === file.file)?.dir,
|
|
97
|
+
dirB: filesForCollectionB.find((f) => f.file === file.file)?.dir,
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
const diffs = await Promise.all(
|
|
101
|
+
filesToDiff.map(async (file) => {
|
|
102
|
+
const [contentA, contentB] = await Promise.all([
|
|
103
|
+
file.dirA ? readFile(join(file.dirA, file.file), 'utf-8') : '',
|
|
104
|
+
file.dirB ? readFile(join(file.dirB, file.file), 'utf-8') : '',
|
|
105
|
+
]);
|
|
106
|
+
return generateDiffString(file.file, contentB, contentA);
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return diffs.filter((diff) => diff !== '');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Generates HTML diffs for files in a collection between the current and previous version.
|
|
115
|
+
* @param id - The ID of the collection
|
|
116
|
+
* @param version - The current version of the collection
|
|
117
|
+
* @param allCollectionItems - Array of all collection items
|
|
118
|
+
* @param versions - Array of all available versions
|
|
119
|
+
* @returns An array of HTML strings representing the diffs, or null if no previous version exists
|
|
120
|
+
*/
|
|
121
|
+
export async function getDiffsForCurrentAndPreviousVersion(
|
|
122
|
+
currentVersion: string,
|
|
123
|
+
previousVersion: string,
|
|
124
|
+
collectionId: string,
|
|
125
|
+
allCollectionItems: CollectionEntry<CollectionTypes>[]
|
|
126
|
+
): Promise<string[] | null> {
|
|
127
|
+
const diffs = await getFilesDiffsBetweenVersions(collectionId, allCollectionItems, currentVersion, previousVersion);
|
|
128
|
+
|
|
129
|
+
if (diffs.length === 0) return [];
|
|
130
|
+
|
|
131
|
+
return diffs.map((diff) => html(parse(diff), { drawFileList: false, matching: 'lines', outputFormat: 'side-by-side' }));
|
|
132
|
+
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { CollectionTypes } from '@types';
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
|
-
import { coerce, satisfies as satisfiesRange
|
|
3
|
+
import { coerce, satisfies as satisfiesRange } from 'semver';
|
|
4
|
+
|
|
5
|
+
export const getPreviousVersion = (version: string, versions: string[]) => {
|
|
6
|
+
const index = versions.indexOf(version);
|
|
7
|
+
return index === -1 ? null : versions[index + 1];
|
|
8
|
+
};
|
|
4
9
|
|
|
5
10
|
export const getVersions = (data: CollectionEntry<CollectionTypes>[]) => {
|
|
6
11
|
const allVersions = data.map((item) => item.data.version).sort();
|