mikro-orm-markdown 0.1.0-alpha.5 → 0.1.0
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 +9 -0
- package/README.ko.md +35 -1
- package/README.md +38 -1
- package/dist/cli.cjs +40 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +40 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +40 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +40 -9
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.0] - 2026-06-30
|
|
9
|
+
|
|
10
|
+
First stable release. All alpha features are considered production-ready.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `AGENTS.md` with project overview, source layout, development commands, and conventions for AI agent context
|
|
15
|
+
- Planned Features section in README (en/ko) documenting `--check` mode, `--watch` mode, and `EntitySchema` support as upcoming work
|
|
16
|
+
|
|
8
17
|
## [0.1.0-alpha.5] - 2026-06-29
|
|
9
18
|
|
|
10
19
|
### Added
|
package/README.ko.md
CHANGED
|
@@ -216,7 +216,7 @@ erDiagram
|
|
|
216
216
|
Post {
|
|
217
217
|
integer id PK
|
|
218
218
|
string title
|
|
219
|
-
|
|
219
|
+
string body
|
|
220
220
|
integer author_id FK
|
|
221
221
|
}
|
|
222
222
|
Author {
|
|
@@ -453,6 +453,40 @@ erDiagram
|
|
|
453
453
|
|
|
454
454
|
> `elk`와 theme 지원 여부는 뷰어마다 다릅니다. `--mermaid-layout`이나 `--mermaid-theme`을 지정하지 않으면 frontmatter는 출력되지 않습니다.
|
|
455
455
|
|
|
456
|
+
## 추후 구현 예정 기능
|
|
457
|
+
|
|
458
|
+
다음 기능들은 향후 릴리즈에서 지원할 예정입니다.
|
|
459
|
+
|
|
460
|
+
### `--check` 모드
|
|
461
|
+
|
|
462
|
+
출력 파일이 현재 엔티티 메타데이터와 다를 경우 non-zero 코드로 종료하는 `--check` 플래그입니다. 파일을 직접 수정하지 않으며, CI 파이프라인에서 `ERD.md`가 엔티티 변경 사항과 함께 항상 커밋되었는지 검증하는 용도로 사용합니다.
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
mikro-orm-markdown --config ./mikro-orm.config.ts --check
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### `--watch` 모드
|
|
469
|
+
|
|
470
|
+
엔티티 소스 파일을 감지하여 변경이 발생할 때마다 출력 문서를 자동으로 재생성하는 `--watch` 플래그입니다. CLI를 매번 수동으로 실행하지 않아도 ERD를 최신 상태로 유지할 수 있어 개발 중에 유용합니다.
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
mikro-orm-markdown --config ./mikro-orm.config.ts --watch
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### `EntitySchema` 지원
|
|
477
|
+
|
|
478
|
+
현재는 decorator 기반 엔티티(`@Entity()` 클래스)만 지원합니다. 향후 릴리즈에서는 TypeScript decorator 없이 Plain Object로 스키마를 정의하는 [`EntitySchema`](https://mikro-orm.io/docs/entity-schema) 방식도 지원할 예정입니다.
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
const PostSchema = new EntitySchema({
|
|
482
|
+
name: 'Post',
|
|
483
|
+
properties: {
|
|
484
|
+
id: { primary: true, type: 'integer' },
|
|
485
|
+
title: { type: 'string' },
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
```
|
|
489
|
+
|
|
456
490
|
## 라이선스
|
|
457
491
|
|
|
458
492
|
MIT
|
package/README.md
CHANGED
|
@@ -216,7 +216,7 @@ erDiagram
|
|
|
216
216
|
Post {
|
|
217
217
|
integer id PK
|
|
218
218
|
string title
|
|
219
|
-
|
|
219
|
+
string body
|
|
220
220
|
integer author_id FK
|
|
221
221
|
}
|
|
222
222
|
Author {
|
|
@@ -453,6 +453,43 @@ erDiagram
|
|
|
453
453
|
|
|
454
454
|
> Viewer support for `elk` and theme values varies. If no `--mermaid-layout` or `--mermaid-theme` is provided, no frontmatter is emitted.
|
|
455
455
|
|
|
456
|
+
## Planned Features
|
|
457
|
+
|
|
458
|
+
The following features are planned for future releases.
|
|
459
|
+
|
|
460
|
+
### `--check` mode
|
|
461
|
+
|
|
462
|
+
A `--check` flag that exits with a non-zero code when the output file is out of sync with the current entity metadata — without writing any files.
|
|
463
|
+
Intended for CI pipelines to enforce that `ERD.md` is always committed alongside entity changes.
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
mikro-orm-markdown --config ./mikro-orm.config.ts --check
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### `--watch` mode
|
|
470
|
+
|
|
471
|
+
A `--watch` flag that monitors entity source files and regenerates the output document automatically on each change.
|
|
472
|
+
Useful during active development when you want the ERD to stay current without running the CLI manually.
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
mikro-orm-markdown --config ./mikro-orm.config.ts --watch
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### `EntitySchema` support
|
|
479
|
+
|
|
480
|
+
Currently only decorator-based entities (`@Entity()` classes) are supported.
|
|
481
|
+
A future release will add support for [`EntitySchema`](https://mikro-orm.io/docs/entity-schema)-defined entities, which describe the schema as a plain object without TypeScript decorators.
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
const PostSchema = new EntitySchema({
|
|
485
|
+
name: 'Post',
|
|
486
|
+
properties: {
|
|
487
|
+
id: { primary: true, type: 'integer' },
|
|
488
|
+
title: { type: 'string' },
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
456
493
|
## License
|
|
457
494
|
|
|
458
495
|
MIT
|
package/dist/cli.cjs
CHANGED
|
@@ -53,7 +53,7 @@ var import_commander = require("commander");
|
|
|
53
53
|
|
|
54
54
|
// src/docs/jsdoc.ts
|
|
55
55
|
var import_ts_morph = require("ts-morph");
|
|
56
|
-
function loadJsDoc(filePaths) {
|
|
56
|
+
function loadJsDoc(filePaths, onWarn) {
|
|
57
57
|
const entities = /* @__PURE__ */ new Map();
|
|
58
58
|
const props = /* @__PURE__ */ new Map();
|
|
59
59
|
const classNames = /* @__PURE__ */ new Set();
|
|
@@ -70,8 +70,12 @@ function loadJsDoc(filePaths) {
|
|
|
70
70
|
});
|
|
71
71
|
for (const filePath of filePaths) {
|
|
72
72
|
try {
|
|
73
|
-
project.addSourceFilesAtPaths(filePath);
|
|
74
|
-
|
|
73
|
+
const sourceFiles2 = project.addSourceFilesAtPaths(filePath);
|
|
74
|
+
if (sourceFiles2.length === 0 && !hasGlobPattern(filePath)) {
|
|
75
|
+
onWarn?.(`No JSDoc source file matched path: ${filePath}`);
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
onWarn?.(`Could not load JSDoc source path "${filePath}": ${formatUnknownError(err)}`);
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
const sourceFiles = project.getSourceFiles();
|
|
@@ -102,11 +106,18 @@ function loadJsDoc(filePaths) {
|
|
|
102
106
|
props.set(className, propMap);
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
|
-
} catch {
|
|
109
|
+
} catch (err) {
|
|
110
|
+
onWarn?.(`Could not parse JSDoc source file "${sourceFile.getFilePath()}": ${formatUnknownError(err)}`);
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
113
|
return { entities, props, sourceFileCount: sourceFiles.length, classNames };
|
|
109
114
|
}
|
|
115
|
+
function formatUnknownError(err) {
|
|
116
|
+
return err instanceof Error ? err.message : String(err);
|
|
117
|
+
}
|
|
118
|
+
function hasGlobPattern(filePath) {
|
|
119
|
+
return /[*?[\]{}]/.test(filePath);
|
|
120
|
+
}
|
|
110
121
|
function parseEntityJsDoc(jsDocs) {
|
|
111
122
|
const namespaces = [];
|
|
112
123
|
const erdNamespaces = [];
|
|
@@ -776,7 +787,8 @@ function isCrossNamespaceInGroup(jsDoc, groupName, isDefault) {
|
|
|
776
787
|
if (isDefault || !jsDoc) {
|
|
777
788
|
return false;
|
|
778
789
|
}
|
|
779
|
-
|
|
790
|
+
const hasHomeNamespace = jsDoc.namespaces.length > 0 || jsDoc.describeNamespaces.length > 0;
|
|
791
|
+
return hasHomeNamespace && jsDoc.erdNamespaces.includes(groupName) && !jsDoc.namespaces.includes(groupName) && !jsDoc.describeNamespaces.includes(groupName);
|
|
780
792
|
}
|
|
781
793
|
|
|
782
794
|
// src/provider.ts
|
|
@@ -806,7 +818,7 @@ function renderMarkdown(docModel, mermaid) {
|
|
|
806
818
|
sections.push(escapeMarkdownParagraph(docModel.description));
|
|
807
819
|
}
|
|
808
820
|
if (docModel.groups.length > 1) {
|
|
809
|
-
sections.push(renderTableOfContents(docModel.groups));
|
|
821
|
+
sections.push(renderTableOfContents(docModel.groups, resolveGroupAnchors(docModel)));
|
|
810
822
|
}
|
|
811
823
|
for (const group of docModel.groups) {
|
|
812
824
|
sections.push(renderGroupSection(group, mermaid));
|
|
@@ -820,12 +832,31 @@ function renderBulletSection(header, items, renderItem) {
|
|
|
820
832
|
}
|
|
821
833
|
return lines.join("\n");
|
|
822
834
|
}
|
|
823
|
-
function renderTableOfContents(groups) {
|
|
835
|
+
function renderTableOfContents(groups, anchors) {
|
|
824
836
|
return renderBulletSection("## Contents", groups, (group) => {
|
|
825
837
|
const label = escapeMarkdownInline(group.name).replace(/[[\]]/g, "\\$&");
|
|
826
|
-
return `- [${label}](#${toMarkdownAnchor(group.name)})`;
|
|
838
|
+
return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
|
|
827
839
|
});
|
|
828
840
|
}
|
|
841
|
+
function resolveGroupAnchors(docModel) {
|
|
842
|
+
const seenAnchors = /* @__PURE__ */ new Map();
|
|
843
|
+
const groupAnchors = /* @__PURE__ */ new Map();
|
|
844
|
+
resolveHeadingAnchor(docModel.title, seenAnchors);
|
|
845
|
+
resolveHeadingAnchor("Contents", seenAnchors);
|
|
846
|
+
for (const group of docModel.groups) {
|
|
847
|
+
groupAnchors.set(group, resolveHeadingAnchor(group.name, seenAnchors));
|
|
848
|
+
for (const entity of group.textEntities) {
|
|
849
|
+
resolveHeadingAnchor(entity.model.className, seenAnchors);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return groupAnchors;
|
|
853
|
+
}
|
|
854
|
+
function resolveHeadingAnchor(value, seenAnchors) {
|
|
855
|
+
const baseAnchor = toMarkdownAnchor(value);
|
|
856
|
+
const previousCount = seenAnchors.get(baseAnchor) ?? 0;
|
|
857
|
+
seenAnchors.set(baseAnchor, previousCount + 1);
|
|
858
|
+
return previousCount === 0 ? baseAnchor : `${baseAnchor}-${previousCount}`;
|
|
859
|
+
}
|
|
829
860
|
function renderGroupSection(group, mermaid) {
|
|
830
861
|
const parts = [`## ${escapeMarkdownInline(group.name)}`];
|
|
831
862
|
if (group.erdEntities.length > 0) {
|
|
@@ -992,7 +1023,7 @@ async function generateMarkdown(options) {
|
|
|
992
1023
|
const { orm, title = "Database Schema", description, src, onWarn, mermaid } = options;
|
|
993
1024
|
const effectiveOrm = await withTsMorphMetadataProvider(orm, onWarn);
|
|
994
1025
|
const { metas, sourcePaths } = await loadEntityMetadataWithTsMorphFallback(orm, effectiveOrm);
|
|
995
|
-
const jsDocResult = loadJsDoc(resolveJsDocSources(sourcePaths, src, onWarn));
|
|
1026
|
+
const jsDocResult = loadJsDoc(resolveJsDocSources(sourcePaths, src, onWarn), onWarn);
|
|
996
1027
|
if (src !== void 0 && src.length > 0) {
|
|
997
1028
|
assertExplicitJsDocSourceCoverage(metas, jsDocResult, src, onWarn);
|
|
998
1029
|
}
|