@metaobjectsdev/docs-site 0.23.1 → 0.24.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.
@@ -1 +1 @@
1
- {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGzF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,GAAE,SAAS,oBAAoB,EAAO,GACnD,OAAO,CAAC,WAAW,CAAC,CAmCtB;AAoBD,oGAAoG;AACpG,wBAAgB,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAKjE"}
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGzF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,GAAE,SAAS,oBAAoB,EAAO,GACnD,OAAO,CAAC,WAAW,CAAC,CA2CtB;AAuCD,oGAAoG;AACpG,wBAAgB,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAKjE"}
package/dist/load.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mkdtempSync, readdirSync, rmSync, statSync, symlinkSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
- import { basename, join, resolve } from "node:path";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
4
  import { MetaDataLoader, composeRegistry, coreTypesProvider, dbProvider, docProvider, promptProvider, uiProvider } from "@metaobjectsdev/metadata";
5
5
  import { FileSource } from "@metaobjectsdev/metadata/core";
6
6
  /**
@@ -15,14 +15,19 @@ import { FileSource } from "@metaobjectsdev/metadata/core";
15
15
  export async function loadModel(sourceDirs, extraProviders = []) {
16
16
  const staging = mkdtempSync(join(tmpdir(), "metadocs-"));
17
17
  try {
18
- const usedBasenames = new Set();
18
+ // Source groups are keyed by the staging entry's name, so two source dirs
19
+ // sharing a basename need distinct names — `metaobjects/` plus a shared
20
+ // model's `../shared-model/metaobjects` is the ordinary shape of a
21
+ // multi-source project, and refusing it would make the feature unusable
22
+ // with `--site`. Collisions are qualified by their parent directory (the
23
+ // name a reader recognises) before falling back to a counter.
24
+ const used = new Set();
25
+ const groupNames = [];
19
26
  for (const dir of sourceDirs) {
20
- const baseName = basename(dir);
21
- if (usedBasenames.has(baseName)) {
22
- throw new Error(`duplicate source dir basename: ${baseName}`);
23
- }
24
- usedBasenames.add(baseName);
25
- symlinkSync(resolve(dir), join(staging, baseName));
27
+ const name = uniqueGroupName(dir, used);
28
+ used.add(name);
29
+ groupNames.push(name);
30
+ symlinkSync(resolve(dir), join(staging, name));
26
31
  }
27
32
  const registry = composeRegistry([coreTypesProvider, dbProvider, docProvider, promptProvider, uiProvider, ...extraProviders]);
28
33
  // Feed files in files-before-subdirs order (the same order the sdk's loadMemory
@@ -40,13 +45,37 @@ export async function loadModel(sourceDirs, extraProviders = []) {
40
45
  return {
41
46
  root: result.root,
42
47
  warnings: result.warnings.map((w) => w.message),
43
- sourceDirs: sourceDirs.map((d) => basename(resolve(d))),
48
+ // The staging entry names, NOT raw basenames: `treeOf` matches a node's
49
+ // source path segment against this list, and a collision-qualified name
50
+ // is what that segment actually is.
51
+ sourceDirs: groupNames,
44
52
  };
45
53
  }
46
54
  finally {
47
55
  rmSync(staging, { recursive: true, force: true });
48
56
  }
49
57
  }
58
+ /**
59
+ * A staging-directory entry name for `dir` that no earlier source dir already
60
+ * took. The basename when it is free (so a single-source project's group name
61
+ * is unchanged); otherwise `<parent>-<basename>`, then a counter — deterministic
62
+ * for a given source list, which keeps the emitted site byte-stable.
63
+ */
64
+ function uniqueGroupName(dir, used) {
65
+ const abs = resolve(dir);
66
+ const base = basename(abs);
67
+ if (!used.has(base))
68
+ return base;
69
+ const parent = basename(dirname(abs));
70
+ const qualified = parent === "" || parent === base ? base : `${parent}-${base}`;
71
+ if (!used.has(qualified))
72
+ return qualified;
73
+ for (let n = 2;; n++) {
74
+ const candidate = `${qualified}-${n}`;
75
+ if (!used.has(candidate))
76
+ return candidate;
77
+ }
78
+ }
50
79
  /** Metadata files under `dir`, files-before-subdirs with each level sorted — the
51
80
  * overlay-safe order the sdk's loadMemory uses, so a base loads before an overlay
52
81
  * nested under it. Symlinks (the staging dir uses them) are followed. */
package/dist/load.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"load.js","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEnJ,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAQ3D;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,UAAoB,EACpB,iBAAkD,EAAE;IAEpD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;IACzD,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,EAAE,CAAC,CAAC;YAChE,CAAC;YACD,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC5B,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;QAC9H,gFAAgF;QAChF,iFAAiF;QACjF,8EAA8E;QAC9E,2DAA2D;QAC3D,iEAAiE;QACjE,yEAAyE;QACzE,iFAAiF;QACjF,MAAM,KAAK,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,IAAI,cAAc,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,CACvE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CACpC,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC/C,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;SACxD,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED;;0EAE0E;AAC1E,SAAS,2BAA2B,CAAC,GAAW;IAC9C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,+DAA+D;QACzF,IAAI,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACnC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1E,CAAC;IACD,KAAK,CAAC,IAAI,EAAE,CAAC;IACb,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,2BAA2B,CAAC,GAAG,CAAC,CAAC,CAAC;IAChF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,MAAM,CAAC,IAAc,EAAE,KAAkB;IACvD,MAAM,GAAG,GAAG,IAAI,CAAC,MAA8B,CAAC;IAChD,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,OAAO,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/docs-site",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "description": "HTML documentation-site generator for metaobjects models — a browsable multi-page site (nav, search, per-object/package pages, kind-aware ER diagrams). Powers the `meta docs --site` surface.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -48,8 +48,8 @@
48
48
  "access": "public"
49
49
  },
50
50
  "dependencies": {
51
- "@metaobjectsdev/metadata": "0.23.1",
52
- "@metaobjectsdev/render": "0.23.1",
51
+ "@metaobjectsdev/metadata": "0.24.0",
52
+ "@metaobjectsdev/render": "0.24.0",
53
53
  "yaml": "^2.9.0"
54
54
  },
55
55
  "devDependencies": {
package/src/load.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mkdtempSync, readdirSync, rmSync, statSync, symlinkSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
- import { basename, join, resolve } from "node:path";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
4
  import { MetaDataLoader, composeRegistry, coreTypesProvider, dbProvider, docProvider, promptProvider, uiProvider } from "@metaobjectsdev/metadata";
5
5
  import type { MetaData, MetaRoot, MetaDataTypeProvider } from "@metaobjectsdev/metadata";
6
6
  import { FileSource } from "@metaobjectsdev/metadata/core";
@@ -26,14 +26,19 @@ export async function loadModel(
26
26
  ): Promise<LoadedModel> {
27
27
  const staging = mkdtempSync(join(tmpdir(), "metadocs-"));
28
28
  try {
29
- const usedBasenames = new Set<string>();
29
+ // Source groups are keyed by the staging entry's name, so two source dirs
30
+ // sharing a basename need distinct names — `metaobjects/` plus a shared
31
+ // model's `../shared-model/metaobjects` is the ordinary shape of a
32
+ // multi-source project, and refusing it would make the feature unusable
33
+ // with `--site`. Collisions are qualified by their parent directory (the
34
+ // name a reader recognises) before falling back to a counter.
35
+ const used = new Set<string>();
36
+ const groupNames: string[] = [];
30
37
  for (const dir of sourceDirs) {
31
- const baseName = basename(dir);
32
- if (usedBasenames.has(baseName)) {
33
- throw new Error(`duplicate source dir basename: ${baseName}`);
34
- }
35
- usedBasenames.add(baseName);
36
- symlinkSync(resolve(dir), join(staging, baseName));
38
+ const name = uniqueGroupName(dir, used);
39
+ used.add(name);
40
+ groupNames.push(name);
41
+ symlinkSync(resolve(dir), join(staging, name));
37
42
  }
38
43
  const registry = composeRegistry([coreTypesProvider, dbProvider, docProvider, promptProvider, uiProvider, ...extraProviders]);
39
44
  // Feed files in files-before-subdirs order (the same order the sdk's loadMemory
@@ -53,13 +58,35 @@ export async function loadModel(
53
58
  return {
54
59
  root: result.root,
55
60
  warnings: result.warnings.map((w) => w.message),
56
- sourceDirs: sourceDirs.map((d) => basename(resolve(d))),
61
+ // The staging entry names, NOT raw basenames: `treeOf` matches a node's
62
+ // source path segment against this list, and a collision-qualified name
63
+ // is what that segment actually is.
64
+ sourceDirs: groupNames,
57
65
  };
58
66
  } finally {
59
67
  rmSync(staging, { recursive: true, force: true });
60
68
  }
61
69
  }
62
70
 
71
+ /**
72
+ * A staging-directory entry name for `dir` that no earlier source dir already
73
+ * took. The basename when it is free (so a single-source project's group name
74
+ * is unchanged); otherwise `<parent>-<basename>`, then a counter — deterministic
75
+ * for a given source list, which keeps the emitted site byte-stable.
76
+ */
77
+ function uniqueGroupName(dir: string, used: ReadonlySet<string>): string {
78
+ const abs = resolve(dir);
79
+ const base = basename(abs);
80
+ if (!used.has(base)) return base;
81
+ const parent = basename(dirname(abs));
82
+ const qualified = parent === "" || parent === base ? base : `${parent}-${base}`;
83
+ if (!used.has(qualified)) return qualified;
84
+ for (let n = 2; ; n++) {
85
+ const candidate = `${qualified}-${n}`;
86
+ if (!used.has(candidate)) return candidate;
87
+ }
88
+ }
89
+
63
90
  /** Metadata files under `dir`, files-before-subdirs with each level sorted — the
64
91
  * overlay-safe order the sdk's loadMemory uses, so a base loads before an overlay
65
92
  * nested under it. Symlinks (the staging dir uses them) are followed. */