@eventcatalog/core 2.5.2 → 2.5.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @eventcatalog/core
2
2
 
3
+ ## 2.5.4
4
+
5
+ ### Patch Changes
6
+
7
+ - b6052d9: fix(core): added client side url builder
8
+
9
+ ## 2.5.3
10
+
11
+ ### Patch Changes
12
+
13
+ - a7f1bd0: fix(core): fixed generate script for plugins
14
+
3
15
  ## 2.5.2
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -5,7 +5,8 @@
5
5
  [![MIT License][license-badge]][license]
6
6
  [![PRs Welcome][prs-badge]][prs]
7
7
  <img src="https://img.shields.io/github/actions/workflow/status/event-catalog/eventcatalog/verify-build.yml"/>
8
- [![Star on GitHub][github-star-badge]][github-star]
8
+ [![](https://dcbadge.limes.pink/api/server/https://discord.gg/3rjaZMmrAm?style=flat)](https://discord.gg/3rjaZMmrAm) [<img src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" height="20px" />](https://www.linkedin.com/in/david-boyne/) [![blog](https://img.shields.io/badge/blog-EDA--Visuals-brightgreen)](https://eda-visuals.boyney.io/?utm_source=event-catalog-gihub)
9
+
9
10
 
10
11
 
11
12
 
@@ -233,7 +234,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
233
234
 
234
235
  This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
235
236
 
236
-
237
237
  # License
238
238
 
239
239
  MIT.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eventcatalog/core",
3
3
  "type": "module",
4
- "version": "2.5.2",
4
+ "version": "2.5.4",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -1,53 +1,109 @@
1
- import { readFile, writeFile } from 'node:fs/promises';
2
- import { createRequire } from 'module';
1
+ import { readFile, writeFile, rm } from 'node:fs/promises';
3
2
  import path from 'node:path';
4
3
 
5
- const generate = async () => {
6
- // Fix for the file
7
- const rawFile = await readFile(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.js'), 'utf8');
4
+ /**
5
+ * Very strange behaviour when importing ESM files from catalogs into core.
6
+ * Core (node) does not know how to handle ESM files, so we have to try and convert them.
7
+ *
8
+ * This needs sorting out! Sorry if you are reading this, but it unblocked me for now!
9
+ * @param {*} content
10
+ * @returns
11
+ */
12
+ function convertESMtoCJS(content) {
13
+ // Replace import statements with require
14
+ content = content.replace(/import\s+([a-zA-Z0-9{},\s*]+)\s+from\s+['"]([^'"]+)['"];/g, (match, imports, modulePath) => {
15
+ return `const ${imports.trim()} = require('${modulePath}');`;
16
+ });
8
17
 
9
- const require = createRequire(process.env.PROJECT_DIR);
18
+ // Replace export default with module.exports
19
+ content = content.replace(/export\s+default\s+/g, 'module.exports = ');
10
20
 
11
- // Convert export default to module.exports (Needed for dynamic require)
12
- if (rawFile.includes('export default')) {
13
- const fixedFile = rawFile.replace('export default', 'module.exports =');
14
- await writeFile(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.js'), fixedFile);
15
- }
21
+ // Replace named exports with module.exports
22
+ content = content.replace(/export\s+{([^}]+)}/g, (match, exports) => {
23
+ return `module.exports = {${exports.trim()}};`;
24
+ });
25
+
26
+ // Remove declarations of __filename and __dirname
27
+ content = content.replace(/^\s*(const|let|var)\s+__(filename|dirname)\s+=\s+.*;?\s*$/gm, '');
16
28
 
17
- const config = require(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.js'));
29
+ return content;
30
+ }
18
31
 
19
- const { generators = [] } = config;
32
+ // TODO: Do we actually need this? Can we clean this up
33
+ function getDefaultExport(importedModule) {
34
+ if (importedModule === null || typeof importedModule !== 'object') {
35
+ throw new Error('Invalid module');
36
+ }
20
37
 
21
- if (!generators.length) {
22
- console.log('No configured generators found, skipping generation');
23
- return;
38
+ if (typeof importedModule.default === 'object' && importedModule.default !== null) {
39
+ return importedModule.default.default || importedModule.default;
24
40
  }
25
41
 
26
- console.log('Running generators...');
42
+ if (typeof importedModule.default !== 'undefined') {
43
+ return importedModule.default;
44
+ }
27
45
 
28
- // Tidy up
29
- await writeFile(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.js'), rawFile);
46
+ return importedModule;
47
+ }
30
48
 
31
- const plugins = generators.map((generator) => {
32
- let plugin = generator[0];
33
- const pluginConfig = generator[1];
49
+ async function cleanup() {
50
+ await rm(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.cjs'));
51
+ }
34
52
 
35
- if (plugin.startsWith('./')) {
36
- plugin = path.join(process.env.PROJECT_DIR, plugin);
37
- }
53
+ const generate = async () => {
54
+ try {
55
+ // Fix for the file
56
+ const rawFile = await readFile(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.js'), 'utf8');
57
+
58
+ // Have to conver the ESM to CJS...
59
+ const configAsCommonJS = convertESMtoCJS(rawFile);
60
+ await writeFile(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.cjs'), configAsCommonJS);
38
61
 
39
- if (plugin.includes('<rootDir>')) {
40
- plugin = plugin.replace('<rootDir>', process.env.PROJECT_DIR);
62
+ const configAsCJS = await import(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.cjs'));
63
+ const config = configAsCJS.default;
64
+
65
+ const { generators = [] } = config;
66
+
67
+ if (!generators.length) {
68
+ console.log('No configured generators found, skipping generation');
69
+ return;
41
70
  }
42
71
 
43
- const importedGenerator = require(plugin);
72
+ // Tidy up
73
+ await writeFile(path.join(process.env.PROJECT_DIR, 'eventcatalog.config.js'), rawFile);
44
74
 
45
- console.log(`Generating EventCatalog docs using: ${plugin}`);
75
+ for (const generator of generators) {
76
+ let plugin = generator[0];
77
+ const pluginConfig = generator[1];
46
78
 
47
- return importedGenerator({ eventCatalogConfig: config }, pluginConfig);
48
- });
79
+ if (plugin.startsWith('./')) {
80
+ plugin = path.join(process.env.PROJECT_DIR, plugin);
81
+ }
82
+
83
+ if (plugin.includes('<rootDir>')) {
84
+ plugin = plugin.replace('<rootDir>', process.env.PROJECT_DIR);
85
+ }
86
+
87
+ try {
88
+ const importedGenerator = await import(plugin);
49
89
 
50
- await Promise.all(plugins);
90
+ // TODO: Fix this...
91
+ const generator = getDefaultExport(importedGenerator);
92
+
93
+ await generator({ eventCatalogConfig: {} }, pluginConfig);
94
+
95
+ // Use importedGenerator here
96
+ } catch (error) {
97
+ console.error('Error loading plugin:', error);
98
+ await cleanup();
99
+ return;
100
+ }
101
+ }
102
+ } catch (error) {
103
+ // Failed to generate clean up...
104
+ console.error(error);
105
+ await cleanup();
106
+ }
51
107
  };
52
108
 
53
109
  generate();
@@ -5,6 +5,8 @@ import { getNodesAndEdges as getNodesAndEdgesForEvent } from '@utils/events/node
5
5
  import { getNodesAndEdges as getNodesAndEdgesForCommand } from '@utils/commands/node-graph';
6
6
  import { getNodesAndEdges as getNodesAndEdgesForDomain } from '@utils/domains/node-graph';
7
7
  import { getNodesAndEdges as getNodesAndEdgesForFlows } from '@utils/flows/node-graph';
8
+ import { buildUrl } from '@utils/url-builder';
9
+ import config from '@eventcatalog';
8
10
 
9
11
  interface Props {
10
12
  id: string;
@@ -77,6 +79,13 @@ if (collection === 'flows') {
77
79
  nodes = eventNodes;
78
80
  edges = eventEdges;
79
81
  }
82
+
83
+ const getDocUrlForCollection = () => {
84
+ return buildUrl(`/docs/${collection}/${id}/${version}`);
85
+ };
86
+ const getVisualiserUrlForCollection = () => {
87
+ return buildUrl(`/visualiser/${collection}/${id}/${version}`);
88
+ };
80
89
  ---
81
90
 
82
91
  <div>
@@ -88,6 +97,7 @@ if (collection === 'flows') {
88
97
  hrefLabel={href.label}
89
98
  href={href.url}
90
99
  linkTo={linkTo}
100
+ urlHasTrailingSlash={config.trailingSlash}
91
101
  client:load
92
102
  />
93
103
  </div>
@@ -21,7 +21,7 @@ import type { CollectionEntry } from 'astro:content';
21
21
  import { navigate } from 'astro:transitions/client';
22
22
  import type { CollectionTypes } from '@types';
23
23
  import DownloadButton from './DownloadButton';
24
- import { buildUrl } from '@utils/url-builder';
24
+ import { buildUrl } from '@utils/url-builder-client';
25
25
 
26
26
  interface Props {
27
27
  nodes: any;
@@ -32,13 +32,17 @@ interface Props {
32
32
  includeControls?: boolean;
33
33
  linkTo: 'docs' | 'visualiser';
34
34
  includeKey?: boolean;
35
+ urlHasTrailingSlash?: boolean;
35
36
  }
36
37
 
37
- const getDocUrlForCollection = (collectionItem: CollectionEntry<CollectionTypes>) => {
38
- return buildUrl(`/docs/${collectionItem.collection}/${collectionItem.data.id}/${collectionItem.data.version}`);
38
+ const getDocUrlForCollection = (collectionItem: CollectionEntry<CollectionTypes>, trailingSlash?: boolean) => {
39
+ return buildUrl(`/docs/${collectionItem.collection}/${collectionItem.data.id}/${collectionItem.data.version}`, trailingSlash);
39
40
  };
40
- const getVisualiserUrlForCollection = (collectionItem: CollectionEntry<CollectionTypes>) => {
41
- return buildUrl(`/visualiser/${collectionItem.collection}/${collectionItem.data.id}/${collectionItem.data.version}`);
41
+ const getVisualiserUrlForCollection = (collectionItem: CollectionEntry<CollectionTypes>, trailingSlash?: boolean) => {
42
+ return buildUrl(
43
+ `/visualiser/${collectionItem.collection}/${collectionItem.data.id}/${collectionItem.data.version}`,
44
+ trailingSlash
45
+ );
42
46
  };
43
47
 
44
48
  // const NodeGraphBuilder = ({ title, subtitle, includeBackground = true, includeControls = true }: Props) => {
@@ -49,6 +53,7 @@ const NodeGraphBuilder = ({
49
53
  includeBackground = true,
50
54
  linkTo = 'docs',
51
55
  includeKey = true,
56
+ urlHasTrailingSlash,
52
57
  }: Props) => {
53
58
  const nodeTypes = useMemo(
54
59
  () => ({
@@ -67,10 +72,18 @@ const NodeGraphBuilder = ({
67
72
 
68
73
  const handleNodeClick = (_: any, node: Node) => {
69
74
  if (node.type === 'events' || node.type === 'commands') {
70
- navigate(linkTo === 'docs' ? getDocUrlForCollection(node.data.message) : getVisualiserUrlForCollection(node.data.message));
75
+ navigate(
76
+ linkTo === 'docs'
77
+ ? getDocUrlForCollection(node.data.message, urlHasTrailingSlash)
78
+ : getVisualiserUrlForCollection(node.data.message, urlHasTrailingSlash)
79
+ );
71
80
  }
72
81
  if (node.type === 'services') {
73
- navigate(linkTo === 'docs' ? getDocUrlForCollection(node.data.service) : getVisualiserUrlForCollection(node.data.service));
82
+ navigate(
83
+ linkTo === 'docs'
84
+ ? getDocUrlForCollection(node.data.service, urlHasTrailingSlash)
85
+ : getVisualiserUrlForCollection(node.data.service, urlHasTrailingSlash)
86
+ );
74
87
  }
75
88
  };
76
89
 
@@ -132,6 +145,7 @@ interface NodeGraphProps {
132
145
  linkTo: 'docs' | 'visualiser';
133
146
  includeKey?: boolean;
134
147
  footerLabel?: string;
148
+ urlHasTrailingSlash?: boolean;
135
149
  }
136
150
 
137
151
  const NodeGraph = ({
@@ -144,6 +158,7 @@ const NodeGraph = ({
144
158
  hrefLabel = 'Open in visualizer',
145
159
  includeKey = true,
146
160
  footerLabel,
161
+ urlHasTrailingSlash,
147
162
  }: NodeGraphProps) => {
148
163
  const [elem, setElem] = useState(null);
149
164
 
@@ -158,7 +173,14 @@ const NodeGraph = ({
158
173
  <div>
159
174
  {createPortal(
160
175
  <ReactFlowProvider>
161
- <NodeGraphBuilder edges={edges} nodes={nodes} title={title} linkTo={linkTo} includeKey={includeKey} />
176
+ <NodeGraphBuilder
177
+ edges={edges}
178
+ nodes={nodes}
179
+ title={title}
180
+ linkTo={linkTo}
181
+ includeKey={includeKey}
182
+ urlHasTrailingSlash={urlHasTrailingSlash}
183
+ />
162
184
 
163
185
  <div className="flex justify-between">
164
186
  {footerLabel && (
@@ -0,0 +1,22 @@
1
+ const cleanUrl = (url: string) => {
2
+ return url.replace(/\/+/g, '/');
3
+ };
4
+
5
+ // Custom URL builder as Astro does not support this stuff out the box.
6
+ // Used on client components to build URLs
7
+ export const buildUrl = (url: string, trailingSlash = false, ignoreTrailingSlash = false) => {
8
+ let newUrl = url;
9
+
10
+ // If the base URL is not the root, we need to append it
11
+ if (import.meta.env.BASE_URL !== '/') {
12
+ newUrl = `${import.meta.env.BASE_URL}${url}`;
13
+ }
14
+
15
+ // Should we add a trailing slash to the url?
16
+ if (trailingSlash && !ignoreTrailingSlash) {
17
+ if (url.endsWith('/')) return newUrl;
18
+ return cleanUrl(`${newUrl}/`);
19
+ }
20
+
21
+ return cleanUrl(newUrl);
22
+ };