@grove-dev/core 0.3.1 → 0.3.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/README.md +16 -142
- package/dist/build-data.d.ts.map +1 -1
- package/dist/build-data.js +13 -4
- package/dist/build-data.js.map +1 -1
- package/dist/contributors.d.ts +3 -0
- package/dist/contributors.d.ts.map +1 -1
- package/dist/contributors.js +46 -36
- package/dist/contributors.js.map +1 -1
- package/dist/directory-client.d.ts +11 -0
- package/dist/directory-client.d.ts.map +1 -0
- package/dist/directory-client.js +11 -0
- package/dist/directory-client.js.map +1 -0
- package/dist/directory-display.d.ts +27 -0
- package/dist/directory-display.d.ts.map +1 -0
- package/dist/directory-display.js +104 -0
- package/dist/directory-display.js.map +1 -0
- package/dist/directory-format.d.ts +29 -0
- package/dist/directory-format.d.ts.map +1 -0
- package/dist/directory-format.js +72 -0
- package/dist/directory-format.js.map +1 -0
- package/dist/directory-lenses.d.ts +59 -0
- package/dist/directory-lenses.d.ts.map +1 -0
- package/dist/directory-lenses.js +199 -0
- package/dist/directory-lenses.js.map +1 -0
- package/dist/directory-repo.d.ts +20 -0
- package/dist/directory-repo.d.ts.map +1 -0
- package/dist/directory-repo.js +31 -0
- package/dist/directory-repo.js.map +1 -0
- package/dist/directory-scores.d.ts +37 -0
- package/dist/directory-scores.d.ts.map +1 -0
- package/dist/directory-scores.js +76 -0
- package/dist/directory-scores.js.map +1 -0
- package/dist/directory-search.d.ts +119 -0
- package/dist/directory-search.d.ts.map +1 -0
- package/dist/directory-search.js +398 -0
- package/dist/directory-search.js.map +1 -0
- package/dist/directory-taxonomy.d.ts +19 -0
- package/dist/directory-taxonomy.d.ts.map +1 -0
- package/dist/directory-taxonomy.js +36 -0
- package/dist/directory-taxonomy.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -6
- package/dist/index.js.map +1 -1
- package/dist/llms.js +1 -1
- package/dist/llms.js.map +1 -1
- package/dist/prepare.d.ts +20 -0
- package/dist/prepare.d.ts.map +1 -0
- package/dist/prepare.js +75 -0
- package/dist/prepare.js.map +1 -0
- package/dist/schema.d.ts +56 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +38 -0
- package/dist/schema.js.map +1 -1
- package/dist/site-artifacts.d.ts +15 -0
- package/dist/site-artifacts.d.ts.map +1 -0
- package/dist/site-artifacts.js +122 -0
- package/dist/site-artifacts.js.map +1 -0
- package/dist/validate.js +4 -2
- package/dist/validate.js.map +1 -1
- package/package.json +9 -2
- package/dist/build-data.test.d.ts +0 -2
- package/dist/build-data.test.d.ts.map +0 -1
- package/dist/build-data.test.js +0 -196
- package/dist/build-data.test.js.map +0 -1
- package/dist/contributors.test.d.ts +0 -2
- package/dist/contributors.test.d.ts.map +0 -1
- package/dist/contributors.test.js +0 -79
- package/dist/contributors.test.js.map +0 -1
- package/dist/decisions.test.d.ts +0 -2
- package/dist/decisions.test.d.ts.map +0 -1
- package/dist/decisions.test.js +0 -205
- package/dist/decisions.test.js.map +0 -1
- package/dist/enrich.test.d.ts +0 -2
- package/dist/enrich.test.d.ts.map +0 -1
- package/dist/enrich.test.js +0 -105
- package/dist/enrich.test.js.map +0 -1
- package/dist/github-client.test.d.ts +0 -2
- package/dist/github-client.test.d.ts.map +0 -1
- package/dist/github-client.test.js +0 -176
- package/dist/github-client.test.js.map +0 -1
- package/dist/github.test.d.ts +0 -2
- package/dist/github.test.d.ts.map +0 -1
- package/dist/github.test.js +0 -73
- package/dist/github.test.js.map +0 -1
- package/dist/health.test.d.ts +0 -2
- package/dist/health.test.d.ts.map +0 -1
- package/dist/health.test.js +0 -183
- package/dist/health.test.js.map +0 -1
- package/dist/importer.test.d.ts +0 -2
- package/dist/importer.test.d.ts.map +0 -1
- package/dist/importer.test.js +0 -173
- package/dist/importer.test.js.map +0 -1
- package/dist/llms.test.d.ts +0 -2
- package/dist/llms.test.d.ts.map +0 -1
- package/dist/llms.test.js +0 -39
- package/dist/llms.test.js.map +0 -1
- package/dist/public-api.test.d.ts +0 -2
- package/dist/public-api.test.d.ts.map +0 -1
- package/dist/public-api.test.js +0 -13
- package/dist/public-api.test.js.map +0 -1
- package/dist/sitemap.test.d.ts +0 -2
- package/dist/sitemap.test.d.ts.map +0 -1
- package/dist/sitemap.test.js +0 -35
- package/dist/sitemap.test.js.map +0 -1
- package/dist/slug.test.d.ts +0 -2
- package/dist/slug.test.d.ts.map +0 -1
- package/dist/slug.test.js +0 -101
- package/dist/slug.test.js.map +0 -1
- package/dist/validate.test.d.ts +0 -2
- package/dist/validate.test.d.ts.map +0 -1
- package/dist/validate.test.js +0 -272
- package/dist/validate.test.js.map +0 -1
package/dist/importer.test.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @grove-dev/core — markdown.ts importer unit tests.
|
|
3
|
-
*
|
|
4
|
-
* The `markdown.ts` module houses `parseAwesomeMarkdown`,
|
|
5
|
-
* `detectGithubRepo`, `importAwesomeList`, and
|
|
6
|
-
* `writeImportedRecords`. `importer.ts` re-exports the last two.
|
|
7
|
-
*
|
|
8
|
-
* Tests cover:
|
|
9
|
-
* - detectGithubRepo: URL + slug derivation, edge cases
|
|
10
|
-
* - parseAwesomeMarkdown: section grouping, link extraction,
|
|
11
|
-
* dedup, empty input
|
|
12
|
-
* - writeImportedRecords: real tmpdir round-trip
|
|
13
|
-
*/
|
|
14
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
15
|
-
import { mkdtemp, writeFile, readFile, rm } from "node:fs/promises";
|
|
16
|
-
import { tmpdir } from "node:os";
|
|
17
|
-
import { join } from "node:path";
|
|
18
|
-
import { detectGithubRepo, parseAwesomeMarkdown, importAwesomeList, writeImportedRecords, } from "./markdown.js";
|
|
19
|
-
describe("detectGithubRepo", () => {
|
|
20
|
-
it("returns the canonical GitHub URL for a github.com URL", () => {
|
|
21
|
-
expect(detectGithubRepo("https://github.com/owner/repo")).toBe("https://github.com/owner/repo");
|
|
22
|
-
});
|
|
23
|
-
it("strips a trailing .git from the repo name", () => {
|
|
24
|
-
expect(detectGithubRepo("https://github.com/owner/repo.git")).toBe("https://github.com/owner/repo");
|
|
25
|
-
});
|
|
26
|
-
it("returns undefined for an empty / non-URL string", () => {
|
|
27
|
-
expect(detectGithubRepo("")).toBeUndefined();
|
|
28
|
-
expect(detectGithubRepo("No link here.")).toBeUndefined();
|
|
29
|
-
});
|
|
30
|
-
it("returns undefined for a non-github URL (e.g. gitlab)", () => {
|
|
31
|
-
expect(detectGithubRepo("https://gitlab.com/owner/repo")).toBeUndefined();
|
|
32
|
-
});
|
|
33
|
-
it("returns undefined for reserved GitHub paths (issues, pulls, etc.)", () => {
|
|
34
|
-
// `https://github.com/owner/issues` is a tab on the repo page,
|
|
35
|
-
// not a repo. detectGithubRepo must return undefined for
|
|
36
|
-
// these so they don't get imported as a record.
|
|
37
|
-
expect(detectGithubRepo("https://github.com/owner/issues")).toBeUndefined();
|
|
38
|
-
expect(detectGithubRepo("https://github.com/owner/pulls")).toBeUndefined();
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
describe("parseAwesomeMarkdown", () => {
|
|
42
|
-
it("returns an empty result for empty input", () => {
|
|
43
|
-
const r = parseAwesomeMarkdown("");
|
|
44
|
-
expect(r.records).toEqual([]);
|
|
45
|
-
expect(r.report.imported).toBe(0);
|
|
46
|
-
expect(r.report.skipped).toBe(0);
|
|
47
|
-
});
|
|
48
|
-
it("groups links by section heading and extracts name + url", () => {
|
|
49
|
-
const md = [
|
|
50
|
-
"# Awesome",
|
|
51
|
-
"",
|
|
52
|
-
"## Tools",
|
|
53
|
-
"",
|
|
54
|
-
"- [Repo One](https://github.com/owner/one) - A great tool",
|
|
55
|
-
"- [Repo Two](https://github.com/owner/two)",
|
|
56
|
-
"",
|
|
57
|
-
"## Libraries",
|
|
58
|
-
"",
|
|
59
|
-
"- [Repo Three](https://github.com/owner/three) - A nice library",
|
|
60
|
-
].join("\n");
|
|
61
|
-
const r = parseAwesomeMarkdown(md);
|
|
62
|
-
expect(r.report.imported).toBe(3);
|
|
63
|
-
expect(r.records.map((rec) => rec.name)).toEqual([
|
|
64
|
-
"Repo One",
|
|
65
|
-
"Repo Two",
|
|
66
|
-
"Repo Three",
|
|
67
|
-
]);
|
|
68
|
-
// Category comes from the section heading.
|
|
69
|
-
expect(r.records[0]?.category).toBe("Tools");
|
|
70
|
-
expect(r.records[2]?.category).toBe("Libraries");
|
|
71
|
-
});
|
|
72
|
-
it("dedupes repeated names (same label slug → -2, -3, ...)", () => {
|
|
73
|
-
// The parser's collision counter is NAME-based (via
|
|
74
|
-
// `uniqueSlug`), not URL-based. Two lines with different
|
|
75
|
-
// names both pointing at the same URL produce two records;
|
|
76
|
-
// two lines with the same name produce the second with a
|
|
77
|
-
// -2 suffix. Pinning the actual behaviour so a future
|
|
78
|
-
// switch to URL-based dedup is visible.
|
|
79
|
-
const md = [
|
|
80
|
-
"## Tools",
|
|
81
|
-
"",
|
|
82
|
-
"- [A](https://github.com/owner/repo) - first",
|
|
83
|
-
"- [A](https://github.com/owner/other) - second",
|
|
84
|
-
].join("\n");
|
|
85
|
-
const r = parseAwesomeMarkdown(md);
|
|
86
|
-
expect(r.report.imported).toBe(2);
|
|
87
|
-
expect(r.records.map((rec) => rec.slug)).toEqual(["a", "a-2"]);
|
|
88
|
-
});
|
|
89
|
-
it("skips lines that are not list items (text between sections)", () => {
|
|
90
|
-
const md = [
|
|
91
|
-
"## Tools",
|
|
92
|
-
"",
|
|
93
|
-
"Some prose describing the section.",
|
|
94
|
-
"",
|
|
95
|
-
"- [Real](https://github.com/owner/real) - A real repo",
|
|
96
|
-
].join("\n");
|
|
97
|
-
const r = parseAwesomeMarkdown(md);
|
|
98
|
-
expect(r.records).toHaveLength(1);
|
|
99
|
-
expect(r.records[0]?.name).toBe("Real");
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe("writeImportedRecords — filesystem round-trip", () => {
|
|
103
|
-
let cwd;
|
|
104
|
-
beforeEach(async () => {
|
|
105
|
-
cwd = await mkdtemp(join(tmpdir(), "grove-importer-test-"));
|
|
106
|
-
});
|
|
107
|
-
afterEach(async () => {
|
|
108
|
-
await rm(cwd, { recursive: true, force: true });
|
|
109
|
-
});
|
|
110
|
-
it("writes one YAML file per record to the target directory", async () => {
|
|
111
|
-
const result = {
|
|
112
|
-
records: [
|
|
113
|
-
{
|
|
114
|
-
name: "First",
|
|
115
|
-
slug: "first",
|
|
116
|
-
description: "first record",
|
|
117
|
-
category: "tools",
|
|
118
|
-
links: { github: "https://github.com/owner/first" },
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
name: "Second",
|
|
122
|
-
slug: "second",
|
|
123
|
-
description: "second record",
|
|
124
|
-
category: "tools",
|
|
125
|
-
links: { github: "https://github.com/owner/second" },
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
report: { imported: 2, skipped: 0, categories: [], duplicateSlugs: 0, tocSkipped: 0, anchorLinksSkipped: 0 },
|
|
129
|
-
};
|
|
130
|
-
const { written, dir } = await writeImportedRecords(result, cwd, "project-directory");
|
|
131
|
-
expect(written).toBe(2);
|
|
132
|
-
expect(dir).toContain("data/records");
|
|
133
|
-
const first = await readFile(join(dir, "first.yml"), "utf8");
|
|
134
|
-
expect(first).toContain("name: First");
|
|
135
|
-
expect(first).toContain("kind: project");
|
|
136
|
-
expect(first).toContain("slug: first");
|
|
137
|
-
});
|
|
138
|
-
it("returns 0 and writes nothing for an empty input list", async () => {
|
|
139
|
-
const result = {
|
|
140
|
-
records: [],
|
|
141
|
-
report: { imported: 0, skipped: 0, categories: [], duplicateSlugs: 0, tocSkipped: 0, anchorLinksSkipped: 0 },
|
|
142
|
-
};
|
|
143
|
-
const { written } = await writeImportedRecords(result, cwd, "project-directory");
|
|
144
|
-
expect(written).toBe(0);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
describe("importAwesomeList — end-to-end on a local README.md", () => {
|
|
148
|
-
let cwd;
|
|
149
|
-
beforeEach(async () => {
|
|
150
|
-
cwd = await mkdtemp(join(tmpdir(), "grove-import-test-"));
|
|
151
|
-
});
|
|
152
|
-
afterEach(async () => {
|
|
153
|
-
await rm(cwd, { recursive: true, force: true });
|
|
154
|
-
});
|
|
155
|
-
it("parses a local markdown file and returns ImportedRecord[]", async () => {
|
|
156
|
-
const md = [
|
|
157
|
-
"# Awesome Stuff",
|
|
158
|
-
"",
|
|
159
|
-
"## Frameworks",
|
|
160
|
-
"",
|
|
161
|
-
"- [Framework A](https://github.com/owner/framework-a) - A nice framework",
|
|
162
|
-
"- [Framework B](https://github.com/owner/framework-b)",
|
|
163
|
-
].join("\n");
|
|
164
|
-
const readme = join(cwd, "README.md");
|
|
165
|
-
await writeFile(readme, md);
|
|
166
|
-
const result = await importAwesomeList(readme);
|
|
167
|
-
expect(result.report.imported).toBe(2);
|
|
168
|
-
expect(result.records).toHaveLength(2);
|
|
169
|
-
expect(result.records[0]?.name).toBe("Framework A");
|
|
170
|
-
expect(result.records[0]?.category).toBe("Frameworks");
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
//# sourceMappingURL=importer.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"importer.test.js","sourceRoot":"","sources":["../src/importer.test.ts"],"names":[],"mappings":"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"}
|
package/dist/llms.test.d.ts
DELETED
package/dist/llms.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"llms.test.d.ts","sourceRoot":"","sources":["../src/llms.test.ts"],"names":[],"mappings":""}
|
package/dist/llms.test.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { buildLlmsFullTxt, buildLlmsTxt } from "./llms.js";
|
|
3
|
-
const config = {
|
|
4
|
-
blueprint: "project-directory",
|
|
5
|
-
site: {
|
|
6
|
-
name: "Open Apps",
|
|
7
|
-
tagline: "Open-source apps with real codebases.",
|
|
8
|
-
url: "https://open-apps.dev.mn",
|
|
9
|
-
},
|
|
10
|
-
routes: { directory: "apps" },
|
|
11
|
-
labels: { singular: "app", plural: "apps" },
|
|
12
|
-
};
|
|
13
|
-
const input = {
|
|
14
|
-
generatedAt: "2026-06-24T00:00:00.000Z",
|
|
15
|
-
records: [
|
|
16
|
-
{
|
|
17
|
-
slug: "immich",
|
|
18
|
-
name: "Immich",
|
|
19
|
-
description: "Self-hosted photo backup.",
|
|
20
|
-
category: "tools",
|
|
21
|
-
stack: "flutter",
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
};
|
|
25
|
-
describe("LLM outputs", () => {
|
|
26
|
-
it("uses the configured directory route in the compact index", () => {
|
|
27
|
-
const text = buildLlmsTxt(input, config);
|
|
28
|
-
expect(text).toContain("Directory: https://open-apps.dev.mn/apps");
|
|
29
|
-
expect(text).not.toContain("/projects");
|
|
30
|
-
});
|
|
31
|
-
it("uses configured route and plural label in the full index", () => {
|
|
32
|
-
const text = buildLlmsFullTxt(input, config);
|
|
33
|
-
expect(text).toContain("> Source: https://open-apps.dev.mn/apps");
|
|
34
|
-
expect(text).toContain("## Apps");
|
|
35
|
-
expect(text).toContain("- url: https://open-apps.dev.mn/apps/immich");
|
|
36
|
-
expect(text).not.toContain("/projects");
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
//# sourceMappingURL=llms.test.js.map
|
package/dist/llms.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"llms.test.js","sourceRoot":"","sources":["../src/llms.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG3D,MAAM,MAAM,GAAG;IACb,SAAS,EAAE,mBAAmB;IAC9B,IAAI,EAAE;QACJ,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uCAAuC;QAChD,GAAG,EAAE,0BAA0B;KAChC;IACD,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;IAC7B,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE;CAC7B,CAAC;AAEjB,MAAM,KAAK,GAAG;IACZ,WAAW,EAAE,0BAA0B;IACvC,OAAO,EAAE;QACP;YACE,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,2BAA2B;YACxC,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,SAAS;SACjB;KACF;CACF,CAAC;AAEF,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAEzC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,0CAA0C,CAAC,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAE7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,yCAAyC,CAAC,CAAC;QAClE,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,6CAA6C,CAAC,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.test.d.ts","sourceRoot":"","sources":["../src/public-api.test.ts"],"names":[],"mappings":""}
|
package/dist/public-api.test.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { recordsFileSchema, resourceSchema } from "./index.js";
|
|
3
|
-
describe("public schema API", () => {
|
|
4
|
-
it("exports the complete resource validators for consumer migrations", () => {
|
|
5
|
-
expect(recordsFileSchema).toBe(resourceSchema);
|
|
6
|
-
expect(recordsFileSchema.safeParse({
|
|
7
|
-
kind: "project",
|
|
8
|
-
slug: "example",
|
|
9
|
-
name: "Example",
|
|
10
|
-
}).success).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
//# sourceMappingURL=public-api.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.test.js","sourceRoot":"","sources":["../src/public-api.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/C,MAAM,CACJ,iBAAiB,CAAC,SAAS,CAAC;YAC1B,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC,OAAO,CACX,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/sitemap.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sitemap.test.d.ts","sourceRoot":"","sources":["../src/sitemap.test.ts"],"names":[],"mappings":""}
|
package/dist/sitemap.test.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { buildSitemap } from "./sitemap.js";
|
|
6
|
-
const roots = [];
|
|
7
|
-
afterEach(async () => {
|
|
8
|
-
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
9
|
-
});
|
|
10
|
-
describe("buildSitemap", () => {
|
|
11
|
-
it("uses the configured directory route for index and record URLs", async () => {
|
|
12
|
-
const cwd = await mkdtemp(join(tmpdir(), "grove-sitemap-"));
|
|
13
|
-
roots.push(cwd);
|
|
14
|
-
const config = {
|
|
15
|
-
blueprint: "project-directory",
|
|
16
|
-
site: {
|
|
17
|
-
name: "Open Apps",
|
|
18
|
-
tagline: "Open-source apps with real codebases.",
|
|
19
|
-
url: "https://open-apps.dev.mn",
|
|
20
|
-
},
|
|
21
|
-
routes: { directory: "apps" },
|
|
22
|
-
labels: { singular: "app", plural: "apps" },
|
|
23
|
-
paths: { publicDir: "public" },
|
|
24
|
-
};
|
|
25
|
-
const result = await buildSitemap({
|
|
26
|
-
generatedAt: "2026-06-24T00:00:00.000Z",
|
|
27
|
-
items: [{ slug: "immich" }],
|
|
28
|
-
}, cwd, config);
|
|
29
|
-
const xml = await readFile(result.path, "utf8");
|
|
30
|
-
expect(xml).toContain("https://open-apps.dev.mn/apps");
|
|
31
|
-
expect(xml).toContain("https://open-apps.dev.mn/apps/immich");
|
|
32
|
-
expect(xml).not.toContain("/projects");
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
//# sourceMappingURL=sitemap.test.js.map
|
package/dist/sitemap.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sitemap.test.js","sourceRoot":"","sources":["../src/sitemap.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,MAAM,KAAK,GAAa,EAAE,CAAC;AAE3B,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/F,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,MAAM,MAAM,GAAG;YACb,SAAS,EAAE,mBAAmB;YAC9B,IAAI,EAAE;gBACJ,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,uCAAuC;gBAChD,GAAG,EAAE,0BAA0B;aAChC;YACD,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC7B,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE;YAC3C,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;SAChB,CAAC;QAEjB,MAAM,MAAM,GAAG,MAAM,YAAY,CAC/B;YACE,WAAW,EAAE,0BAA0B;YACvC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;SAC5B,EACD,GAAG,EACH,MAAM,CACP,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAEhD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;QACvD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sCAAsC,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/slug.test.d.ts
DELETED
package/dist/slug.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slug.test.d.ts","sourceRoot":"","sources":["../src/slug.test.ts"],"names":[],"mappings":""}
|
package/dist/slug.test.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @grove-dev/core — slug helpers unit tests.
|
|
3
|
-
*
|
|
4
|
-
* Coverage:
|
|
5
|
-
* - slugify: empty input, unicode, smart quotes, length cap
|
|
6
|
-
* - uniqueSlug: collision counter, repeated calls with same input
|
|
7
|
-
* (idempotency), unknown-fallback when slugify yields ""
|
|
8
|
-
*/
|
|
9
|
-
import { describe, it, expect } from "vitest";
|
|
10
|
-
import { slugify, uniqueSlug } from "./slug.js";
|
|
11
|
-
describe("slugify", () => {
|
|
12
|
-
it("lower-cases and replaces runs of non-alphanumerics with single hyphens", () => {
|
|
13
|
-
expect(slugify("Hello World")).toBe("hello-world");
|
|
14
|
-
expect(slugify("Foo Bar Baz")).toBe("foo-bar-baz");
|
|
15
|
-
expect(slugify("a__b--c")).toBe("a-b-c");
|
|
16
|
-
});
|
|
17
|
-
it("strips straight ASCII single quotes (the only quote the regex matches)", () => {
|
|
18
|
-
// The regex /['']/g contains two ASCII apostrophes (0x27) — not
|
|
19
|
-
// the curly U+2018 / U+2019 characters a casual read of the
|
|
20
|
-
// source might suggest. Pinning this behaviour here so a
|
|
21
|
-
// well-meaning future refactor (e.g. "let's also strip smart
|
|
22
|
-
// quotes") doesn't silently change every slug with a
|
|
23
|
-
// possessive in the name. The actual product input rarely
|
|
24
|
-
// has straight apostrophes because record names go through a
|
|
25
|
-
// curator; the smart-quote path below is the one that matters
|
|
26
|
-
// for copy-paste from docs and READMEs.
|
|
27
|
-
expect(slugify("Don't Stop")).toBe("dont-stop");
|
|
28
|
-
expect(slugify("It's a Test")).toBe("its-a-test");
|
|
29
|
-
});
|
|
30
|
-
it("does NOT strip smart quotes (U+2018 / U+2019) — they become hyphens", () => {
|
|
31
|
-
// The opposite of the previous test. Smart quotes are NOT in
|
|
32
|
-
// the regex's character class, so they fall through to the
|
|
33
|
-
// [^a-z0-9]+ replace and become a single "-". A user pasting
|
|
34
|
-
// a record name with a curly apostrophe from a Markdown
|
|
35
|
-
// document gets a slug with a hyphen at that position, not a
|
|
36
|
-
// silently-dropped character. Document the behaviour here so
|
|
37
|
-
// it cannot drift.
|
|
38
|
-
expect(slugify("Don\u2019t Stop")).toBe("don-t-stop");
|
|
39
|
-
expect(slugify("It\u2019s a Test")).toBe("it-s-a-test");
|
|
40
|
-
});
|
|
41
|
-
it("trims leading and trailing hyphens", () => {
|
|
42
|
-
expect(slugify("---hello---")).toBe("hello");
|
|
43
|
-
expect(slugify("!@#hello!@#")).toBe("hello");
|
|
44
|
-
});
|
|
45
|
-
it("returns an empty string for non-ASCII-only input (e.g. emoji, accented)", () => {
|
|
46
|
-
// Accented characters are dropped by the [^a-z0-9]+ replace and
|
|
47
|
-
// then the leading/trailing hyphen trim removes the surrounding
|
|
48
|
-
// hyphens. Emoji is the same. This test pins the exact empty
|
|
49
|
-
// string — not "item" — because uniqueSlug is the function that
|
|
50
|
-
// falls back to "item", not slugify itself.
|
|
51
|
-
expect(slugify("café")).toBe("caf");
|
|
52
|
-
expect(slugify("🚀 rocket")).toBe("rocket");
|
|
53
|
-
expect(slugify("über")).toBe("ber");
|
|
54
|
-
});
|
|
55
|
-
it("truncates to 80 characters", () => {
|
|
56
|
-
const long = "a".repeat(200);
|
|
57
|
-
expect(slugify(long).length).toBe(80);
|
|
58
|
-
});
|
|
59
|
-
it("returns an empty string for empty / whitespace-only input", () => {
|
|
60
|
-
expect(slugify("")).toBe("");
|
|
61
|
-
expect(slugify(" ")).toBe("");
|
|
62
|
-
expect(slugify("---")).toBe("");
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe("uniqueSlug", () => {
|
|
66
|
-
it("returns the slug unchanged on first occurrence", () => {
|
|
67
|
-
const seen = new Map();
|
|
68
|
-
expect(uniqueSlug("Hello World", seen)).toBe("hello-world");
|
|
69
|
-
expect(seen.get("hello-world")).toBe(1);
|
|
70
|
-
});
|
|
71
|
-
it("appends -2, -3, ... on subsequent collisions", () => {
|
|
72
|
-
const seen = new Map();
|
|
73
|
-
expect(uniqueSlug("Hello", seen)).toBe("hello");
|
|
74
|
-
expect(uniqueSlug("Hello", seen)).toBe("hello-2");
|
|
75
|
-
expect(uniqueSlug("Hello", seen)).toBe("hello-3");
|
|
76
|
-
// The seen map reflects the latest count, not just the touched
|
|
77
|
-
// counter — second occurrence is "hello-2" so the next collision
|
|
78
|
-
// should be "hello-3", not "hello-2" again.
|
|
79
|
-
expect(seen.get("hello")).toBe(3);
|
|
80
|
-
});
|
|
81
|
-
it("repeated calls with the same input are independent (caller owns the seen map)", () => {
|
|
82
|
-
// Two callers passing the same input string but two different
|
|
83
|
-
// seen maps each get a fresh first-occurrence behaviour. This
|
|
84
|
-
// pins the rule that the seen map is the source of truth — the
|
|
85
|
-
// function does not maintain internal state.
|
|
86
|
-
const a = new Map();
|
|
87
|
-
const b = new Map();
|
|
88
|
-
expect(uniqueSlug("Foo", a)).toBe("foo");
|
|
89
|
-
expect(uniqueSlug("Foo", b)).toBe("foo");
|
|
90
|
-
expect(uniqueSlug("Foo", a)).toBe("foo-2");
|
|
91
|
-
expect(uniqueSlug("Foo", b)).toBe("foo-2");
|
|
92
|
-
});
|
|
93
|
-
it("falls back to 'item' when slugify returns empty (emoji / accents only)", () => {
|
|
94
|
-
const seen = new Map();
|
|
95
|
-
// slugify("🚀") → "" → uniqueSlug substitutes "item"
|
|
96
|
-
expect(uniqueSlug("🚀", seen)).toBe("item");
|
|
97
|
-
// second collision: "item-2"
|
|
98
|
-
expect(uniqueSlug("🚀", seen)).toBe("item-2");
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
//# sourceMappingURL=slug.test.js.map
|
package/dist/slug.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slug.test.js","sourceRoot":"","sources":["../src/slug.test.ts"],"names":[],"mappings":"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"}
|
package/dist/validate.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate.test.d.ts","sourceRoot":"","sources":["../src/validate.test.ts"],"names":[],"mappings":""}
|