livemark 0.24.0 → 0.25.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/.agents/skills/change-review/SKILL.md +47 -0
- package/{skills/configuration → .agents/skills/livemark-configuration}/SKILL.md +1 -1
- package/build/actions/changelog/build.d.ts +21 -9
- package/build/actions/changelog/build.js +29 -17
- package/build/actions/changelog/build.unit.js +156 -2
- package/build/components/Callout.js +2 -2
- package/build/components/LinkButton.js +2 -2
- package/build/components/Section.js +2 -2
- package/build/components/SectionHeader.js +2 -2
- package/build/content-collections.js +5 -4
- package/build/elements/accordion.js +2 -2
- package/build/elements/alert-dialog.js +2 -2
- package/build/elements/alert.js +2 -2
- package/build/elements/aspect-ratio.js +2 -2
- package/build/elements/avatar.js +2 -2
- package/build/elements/badge.js +2 -2
- package/build/elements/breadcrumb.js +2 -2
- package/build/elements/button-group.js +2 -2
- package/build/elements/button.js +2 -2
- package/build/elements/calendar.js +2 -2
- package/build/elements/card.js +2 -2
- package/build/elements/carousel.js +2 -2
- package/build/elements/chart.js +2 -2
- package/build/elements/checkbox.js +2 -2
- package/build/elements/command.js +2 -2
- package/build/elements/context-menu.js +2 -2
- package/build/elements/dialog.js +2 -2
- package/build/elements/drawer.js +2 -2
- package/build/elements/dropdown-menu.js +2 -2
- package/build/elements/empty.js +2 -2
- package/build/elements/field.js +2 -2
- package/build/elements/hover-card.js +2 -2
- package/build/elements/input-group.js +2 -2
- package/build/elements/input-otp.js +2 -2
- package/build/elements/input.js +2 -2
- package/build/elements/item.js +2 -2
- package/build/elements/kbd.js +2 -2
- package/build/elements/label.js +2 -2
- package/build/elements/menubar.js +2 -2
- package/build/elements/native-select.js +2 -2
- package/build/elements/navigation-menu.js +2 -2
- package/build/elements/pagination.js +2 -2
- package/build/elements/popover.js +2 -2
- package/build/elements/progress.js +2 -2
- package/build/elements/radio-group.js +2 -2
- package/build/elements/resizable.js +2 -2
- package/build/elements/scroll-area.js +2 -2
- package/build/elements/select.js +2 -2
- package/build/elements/separator.js +2 -2
- package/build/elements/sheet.js +2 -2
- package/build/elements/sidebar.js +2 -2
- package/build/elements/skeleton.js +2 -2
- package/build/elements/slider.js +2 -2
- package/build/elements/spinner.js +2 -2
- package/build/elements/switch.js +2 -2
- package/build/elements/table.js +2 -2
- package/build/elements/tabs.js +2 -2
- package/build/elements/textarea.js +2 -2
- package/build/elements/toggle-group.js +2 -2
- package/build/elements/toggle.js +2 -2
- package/build/elements/tooltip.js +2 -2
- package/package.json +17 -19
- package/source/actions/changelog/build.ts +29 -16
- package/source/actions/changelog/build.unit.ts +191 -1
- package/source/components/Callout.tsx +1 -1
- package/source/components/LinkButton.tsx +1 -1
- package/source/components/Section.tsx +1 -1
- package/source/components/SectionHeader.tsx +1 -1
- package/source/content-collections.ts +4 -3
- package/source/elements/accordion.tsx +1 -1
- package/source/elements/alert-dialog.tsx +1 -1
- package/source/elements/alert.tsx +1 -1
- package/source/elements/aspect-ratio.tsx +1 -1
- package/source/elements/avatar.tsx +1 -1
- package/source/elements/badge.tsx +1 -1
- package/source/elements/breadcrumb.tsx +1 -1
- package/source/elements/button-group.tsx +1 -1
- package/source/elements/button.tsx +1 -1
- package/source/elements/calendar.tsx +1 -1
- package/source/elements/card.tsx +1 -1
- package/source/elements/carousel.tsx +1 -1
- package/source/elements/chart.tsx +1 -1
- package/source/elements/checkbox.tsx +1 -1
- package/source/elements/command.tsx +1 -1
- package/source/elements/context-menu.tsx +1 -1
- package/source/elements/dialog.tsx +1 -1
- package/source/elements/drawer.tsx +1 -1
- package/source/elements/dropdown-menu.tsx +1 -1
- package/source/elements/empty.tsx +1 -1
- package/source/elements/field.tsx +1 -1
- package/source/elements/hover-card.tsx +1 -1
- package/source/elements/input-group.tsx +1 -1
- package/source/elements/input-otp.tsx +1 -1
- package/source/elements/input.tsx +1 -1
- package/source/elements/item.tsx +1 -1
- package/source/elements/kbd.tsx +1 -1
- package/source/elements/label.tsx +1 -1
- package/source/elements/menubar.tsx +1 -1
- package/source/elements/native-select.tsx +1 -1
- package/source/elements/navigation-menu.tsx +1 -1
- package/source/elements/pagination.tsx +1 -1
- package/source/elements/popover.tsx +1 -1
- package/source/elements/progress.tsx +1 -1
- package/source/elements/radio-group.tsx +1 -1
- package/source/elements/resizable.tsx +1 -1
- package/source/elements/scroll-area.tsx +1 -1
- package/source/elements/select.tsx +1 -1
- package/source/elements/separator.tsx +1 -1
- package/source/elements/sheet.tsx +1 -1
- package/source/elements/sidebar.tsx +1 -1
- package/source/elements/skeleton.tsx +1 -1
- package/source/elements/slider.tsx +1 -1
- package/source/elements/spinner.tsx +1 -1
- package/source/elements/switch.tsx +1 -1
- package/source/elements/table.tsx +1 -1
- package/source/elements/tabs.tsx +1 -1
- package/source/elements/textarea.tsx +1 -1
- package/source/elements/toggle-group.tsx +1 -1
- package/source/elements/toggle.tsx +1 -1
- package/source/elements/tooltip.tsx +1 -1
- package/build/utils/style.d.ts +0 -2
- package/build/utils/style.js +0 -6
- package/source/utils/style.ts +0 -6
- /package/{skills/authoring → .agents/skills/livemark-authoring}/SKILL.md +0 -0
- /package/{skills/customization → .agents/skills/livemark-customization}/SKILL.md +0 -0
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livemark",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Livemark description",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Evgeny Karev",
|
|
7
|
-
"repository":
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/datisthq/livemark"
|
|
10
|
+
},
|
|
8
11
|
"bin": {
|
|
9
12
|
"livemark": "./build/entrypoints/main.js"
|
|
10
13
|
},
|
|
11
14
|
"files": [
|
|
12
15
|
"build",
|
|
13
16
|
"source",
|
|
14
|
-
"skills"
|
|
17
|
+
".agents/skills"
|
|
15
18
|
],
|
|
16
19
|
"type": "module",
|
|
17
20
|
"sideEffects": false,
|
|
@@ -20,8 +23,7 @@
|
|
|
20
23
|
"./components/*": "./source/components/*.tsx",
|
|
21
24
|
"./elements/*": "./source/elements/*.tsx",
|
|
22
25
|
"./hooks/*": "./source/hooks/*.ts",
|
|
23
|
-
"./styles/*": "./source/styles/*.css"
|
|
24
|
-
"./utils/*": "./source/utils/*.ts"
|
|
26
|
+
"./styles/*": "./source/styles/*.css"
|
|
25
27
|
},
|
|
26
28
|
"dependencies": {
|
|
27
29
|
"@base-ui/react": "1.3.0",
|
|
@@ -50,14 +52,14 @@
|
|
|
50
52
|
"@vitejs/plugin-react": "6.0.1",
|
|
51
53
|
"chokidar": "^5.0.0",
|
|
52
54
|
"class-variance-authority": "0.7.1",
|
|
53
|
-
"clsx": "2.1.1",
|
|
54
55
|
"cmdk": "1.1.1",
|
|
56
|
+
"cn": "0.2.4",
|
|
55
57
|
"commander": "14.0.3",
|
|
56
58
|
"embla-carousel-react": "8.6.0",
|
|
57
59
|
"es-toolkit": "1.44.0",
|
|
58
60
|
"github-slugger": "^2.0.0",
|
|
59
61
|
"input-otp": "1.4.2",
|
|
60
|
-
"katex": "
|
|
62
|
+
"katex": "0.18.9",
|
|
61
63
|
"lucide-react": "0.577.0",
|
|
62
64
|
"mdast-util-directive": "^3.1.0",
|
|
63
65
|
"mdast-util-to-string": "^4.0.0",
|
|
@@ -81,31 +83,28 @@
|
|
|
81
83
|
"shadcn": "3.7.0",
|
|
82
84
|
"shiki": "4.0.2",
|
|
83
85
|
"sonner": "2.0.7",
|
|
84
|
-
"tailwind-merge": "3.5.0",
|
|
85
86
|
"tailwindcss": "4.2.2",
|
|
86
87
|
"tw-animate-css": "1.4.0",
|
|
87
88
|
"unified": "^11.0.5",
|
|
88
89
|
"unist-util-visit": "^5.1.0",
|
|
89
90
|
"vaul": "1.1.2",
|
|
90
|
-
"vite": "8.
|
|
91
|
+
"vite": "8.3.1",
|
|
91
92
|
"vite-plugin-svgr": "5.2.0",
|
|
92
93
|
"zod": "4.3.6"
|
|
93
94
|
},
|
|
94
95
|
"devDependencies": {
|
|
95
96
|
"@plausible-analytics/tracker": "^0.4.4",
|
|
96
|
-
"@semantic-release/exec": "7.1.0",
|
|
97
97
|
"@types/node": "24.2.0",
|
|
98
98
|
"@typescript/native-preview": "7.0.0-dev.20260316.1",
|
|
99
|
-
"@vitest/coverage-v8": "
|
|
100
|
-
"conventional-changelog-conventionalcommits": "9.1.0",
|
|
99
|
+
"@vitest/coverage-v8": "5.0.1",
|
|
101
100
|
"lefthook": "2.1.1",
|
|
102
101
|
"livemark": "link:.",
|
|
103
|
-
"npm-check-updates": "
|
|
102
|
+
"npm-check-updates": "23.1.0",
|
|
104
103
|
"react": "19.2.4",
|
|
105
104
|
"react-dom": "19.2.4",
|
|
106
|
-
"semantic-release": "24.2.9",
|
|
107
105
|
"tempy": "3.2.0",
|
|
108
|
-
"vite-plus": "0.
|
|
106
|
+
"vite-plus": "1.0.0",
|
|
107
|
+
"vitest": "5.0.1",
|
|
109
108
|
"wrangler": "4.85.0"
|
|
110
109
|
},
|
|
111
110
|
"peerDependencies": {
|
|
@@ -113,15 +112,15 @@
|
|
|
113
112
|
"react-dom": "^19.0.0"
|
|
114
113
|
},
|
|
115
114
|
"engines": {
|
|
116
|
-
"node": "^24.
|
|
115
|
+
"node": "^24.11.0",
|
|
117
116
|
"pnpm": "^11.0.0"
|
|
118
117
|
},
|
|
119
118
|
"scripts": {
|
|
120
119
|
"build": "tsgo --project tsconfig.build.json",
|
|
121
|
-
"deps": "ncu -u",
|
|
120
|
+
"deps": "ncu -u --cooldown 7d",
|
|
122
121
|
"docs:build": "node source/entrypoints/main.ts build",
|
|
123
122
|
"docs:start": "node source/entrypoints/main.ts start",
|
|
124
|
-
"format": "vp fmt",
|
|
123
|
+
"format": "vp fmt --no-error-on-unmatched-pattern",
|
|
125
124
|
"leaks": "gitleaks git --config=.gitleaks.toml --verbose --redact",
|
|
126
125
|
"lint": "vp fmt --check && vp lint",
|
|
127
126
|
"release": "pnpm publish --access public --ignore-scripts --no-git-checks",
|
|
@@ -130,7 +129,6 @@
|
|
|
130
129
|
"test": "pnpm lint && pnpm type && pnpm unit",
|
|
131
130
|
"type": "tsgo --noEmit",
|
|
132
131
|
"unit": "vp test",
|
|
133
|
-
"vers": "pnpm version --no-git-tag-version",
|
|
134
132
|
"vulns": "semgrep --config=p/typescript --config=p/nodejs --config=p/owasp-top-ten --config=p/secrets --exclude=\"**/fixtures/**\" --metrics=off"
|
|
135
133
|
}
|
|
136
134
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto"
|
|
1
2
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises"
|
|
2
3
|
import { dirname, join, resolve } from "node:path"
|
|
3
4
|
import slugify from "@sindresorhus/slugify"
|
|
@@ -42,21 +43,31 @@ const stringifier = unified().use(remarkStringify, {
|
|
|
42
43
|
emphasis: "_",
|
|
43
44
|
})
|
|
44
45
|
|
|
45
|
-
/** Glob (absolute path) covering
|
|
46
|
-
*
|
|
47
|
-
* article scan. */
|
|
48
|
-
export function cacheIncludeGlob(config: Config) {
|
|
49
|
-
return join(
|
|
46
|
+
/** Glob (absolute path) covering the cached changelog markdown files of
|
|
47
|
+
* one changelog section. Used by content-collections to include them in
|
|
48
|
+
* the article scan. */
|
|
49
|
+
export function cacheIncludeGlob(section: ChangelogSection, config: Config) {
|
|
50
|
+
return join(changelogCacheDir(section, config), "*.md")
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
53
|
+
/** Directory holding one changelog section's cache state: the rendered
|
|
54
|
+
* `.md` entries plus a `meta.json` holding the etag for conditional
|
|
55
|
+
* GETs. Each section gets its own subdirectory of `cache/changelog/`,
|
|
56
|
+
* keyed by its prefix's slug plus a hash of the raw prefix
|
|
57
|
+
* (`/python/changelog/` → `python-changelog-<hash>`), so several
|
|
58
|
+
* changelogs never overwrite each other's entries even when their
|
|
59
|
+
* prefixes slugify alike. */
|
|
60
|
+
export function changelogCacheDir(section: ChangelogSection, config: Config) {
|
|
61
|
+
const slug = slugify(section.prefix) || "index"
|
|
62
|
+
const hash = createHash("sha256").update(section.prefix).digest("hex").slice(0, 8)
|
|
63
|
+
return join(targetDirFor(config.configPath), CACHE_SUBDIR, `${slug}-${hash}`)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Build the per-version changelog cache files of one changelog section
|
|
67
|
+
* into its own cache directory. On refresh we wipe only that
|
|
68
|
+
* directory's `.md` files, leaving meta intact. */
|
|
57
69
|
export async function buildChangelog(section: ChangelogSection, config: Config) {
|
|
58
|
-
const
|
|
59
|
-
const cacheDir = join(targetDir, CACHE_SUBDIR)
|
|
70
|
+
const cacheDir = changelogCacheDir(section, config)
|
|
60
71
|
const metaPath = join(cacheDir, "meta.json")
|
|
61
72
|
|
|
62
73
|
const entries = isGitHubUrl(section.source)
|
|
@@ -137,10 +148,12 @@ function processBody(body: string) {
|
|
|
137
148
|
return escapeMdxAngles(stringifier.stringify(tree).trimEnd())
|
|
138
149
|
}
|
|
139
150
|
|
|
140
|
-
/** Escape `<` to
|
|
151
|
+
/** Escape `<` to `<` outside fenced code blocks and inline backtick spans
|
|
141
152
|
* so commit messages with placeholders like `<subdir>` don't get parsed as
|
|
142
|
-
* JSX by MDX.
|
|
143
|
-
|
|
153
|
+
* JSX by MDX. The entity is used rather than a `\<` backslash escape because
|
|
154
|
+
* MDX's JSX-aware parser does not honour the backslash and still reads
|
|
155
|
+
* `\<name>` as an unclosed tag. */
|
|
156
|
+
export function escapeMdxAngles(markdown: string) {
|
|
144
157
|
let inFence = false
|
|
145
158
|
return markdown
|
|
146
159
|
.split("\n")
|
|
@@ -150,7 +163,7 @@ function escapeMdxAngles(markdown: string) {
|
|
|
150
163
|
return line
|
|
151
164
|
}
|
|
152
165
|
if (inFence) return line
|
|
153
|
-
return line.replace(/`[^`]*`|</g, match => (match === "<" ? "
|
|
166
|
+
return line.replace(/`[^`]*`|</g, match => (match === "<" ? "<" : match))
|
|
154
167
|
})
|
|
155
168
|
.join("\n")
|
|
156
169
|
}
|
|
@@ -1,5 +1,159 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises"
|
|
2
|
+
import { basename, dirname, join } from "node:path"
|
|
3
|
+
import { temporaryDirectoryTask } from "tempy"
|
|
1
4
|
import { describe, expect, it } from "vite-plus/test"
|
|
2
|
-
import {
|
|
5
|
+
import { partitionBySection } from "../../helpers/section.ts"
|
|
6
|
+
import { Config } from "../../models/config.ts"
|
|
7
|
+
import type { ChangelogSection } from "../../models/section.ts"
|
|
8
|
+
import { targetDirFor } from "../target/build.ts"
|
|
9
|
+
import {
|
|
10
|
+
buildChangelog,
|
|
11
|
+
cacheIncludeGlob,
|
|
12
|
+
changelogCacheDir,
|
|
13
|
+
escapeMdxAngles,
|
|
14
|
+
splitLocalChangelog,
|
|
15
|
+
} from "./build.ts"
|
|
16
|
+
|
|
17
|
+
describe("buildChangelog", () => {
|
|
18
|
+
it("keeps several changelog sections apart when they share a version", () =>
|
|
19
|
+
temporaryDirectoryTask(async root => {
|
|
20
|
+
const configPath = join(root, "livemark.config.ts")
|
|
21
|
+
const standard: ChangelogSection = {
|
|
22
|
+
type: "changelog",
|
|
23
|
+
title: "Changelog",
|
|
24
|
+
prefix: "/changelog/",
|
|
25
|
+
source: "standard/CHANGELOG.md",
|
|
26
|
+
version: true,
|
|
27
|
+
position: "sidebar",
|
|
28
|
+
}
|
|
29
|
+
const python: ChangelogSection = {
|
|
30
|
+
type: "changelog",
|
|
31
|
+
title: "Changelog",
|
|
32
|
+
prefix: "/python/changelog/",
|
|
33
|
+
source: "python/CHANGELOG.md",
|
|
34
|
+
version: true,
|
|
35
|
+
position: "sidebar",
|
|
36
|
+
}
|
|
37
|
+
const config = makeConfig(root, [standard, python])
|
|
38
|
+
|
|
39
|
+
await writeChangelog(
|
|
40
|
+
root,
|
|
41
|
+
"standard",
|
|
42
|
+
"## 0.2.0\n\nStandard two.\n\n## 0.1.0\n\nStandard one.\n",
|
|
43
|
+
)
|
|
44
|
+
await writeChangelog(root, "python", "## 0.2.0\n\nPython two.\n")
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await buildChangelog(standard, config)
|
|
48
|
+
await buildChangelog(python, config)
|
|
49
|
+
|
|
50
|
+
const standardDir = changelogCacheDir(standard, config)
|
|
51
|
+
const pythonDir = changelogCacheDir(python, config)
|
|
52
|
+
const cacheRoot = join(targetDirFor(configPath), "cache/changelog")
|
|
53
|
+
expect(dirname(standardDir)).toBe(cacheRoot)
|
|
54
|
+
expect(dirname(pythonDir)).toBe(cacheRoot)
|
|
55
|
+
expect(basename(standardDir)).toMatch(/^changelog-[0-9a-f]{8}$/)
|
|
56
|
+
expect(basename(pythonDir)).toMatch(/^python-changelog-[0-9a-f]{8}$/)
|
|
57
|
+
expect(cacheIncludeGlob(python, config)).toBe(join(pythonDir, "*.md"))
|
|
58
|
+
|
|
59
|
+
expect((await readdir(standardDir)).sort()).toEqual(["0-1-0.md", "0-2-0.md"])
|
|
60
|
+
expect(await readdir(pythonDir)).toEqual(["0-2-0.md"])
|
|
61
|
+
|
|
62
|
+
const standardEntry = await readFile(join(standardDir, "0-2-0.md"), "utf-8")
|
|
63
|
+
const pythonEntry = await readFile(join(pythonDir, "0-2-0.md"), "utf-8")
|
|
64
|
+
expect(standardEntry).toContain("path: /changelog/0-2-0/")
|
|
65
|
+
expect(standardEntry).toContain("Standard two.")
|
|
66
|
+
expect(pythonEntry).toContain("path: /python/changelog/0-2-0/")
|
|
67
|
+
expect(pythonEntry).toContain("Python two.")
|
|
68
|
+
|
|
69
|
+
const olderEntry = await readFile(join(standardDir, "0-1-0.md"), "utf-8")
|
|
70
|
+
const articles = [standardEntry, olderEntry, pythonEntry].map(markdown => ({
|
|
71
|
+
path: markdown.match(/^path: (.+)$/m)?.[1] ?? "",
|
|
72
|
+
}))
|
|
73
|
+
const buckets = partitionBySection(articles, [standard, python])
|
|
74
|
+
expect(buckets.get("/changelog/")?.map(a => a.path)).toEqual([
|
|
75
|
+
"/changelog/0-2-0/",
|
|
76
|
+
"/changelog/0-1-0/",
|
|
77
|
+
])
|
|
78
|
+
expect(buckets.get("/python/changelog/")?.map(a => a.path)).toEqual([
|
|
79
|
+
"/python/changelog/0-2-0/",
|
|
80
|
+
])
|
|
81
|
+
} finally {
|
|
82
|
+
await rm(targetDirFor(configPath), { recursive: true, force: true })
|
|
83
|
+
}
|
|
84
|
+
}))
|
|
85
|
+
|
|
86
|
+
it("cleans only its own cache directory on rebuild", () =>
|
|
87
|
+
temporaryDirectoryTask(async root => {
|
|
88
|
+
const configPath = join(root, "livemark.config.ts")
|
|
89
|
+
const standard: ChangelogSection = {
|
|
90
|
+
type: "changelog",
|
|
91
|
+
title: "Changelog",
|
|
92
|
+
prefix: "/changelog/",
|
|
93
|
+
source: "standard/CHANGELOG.md",
|
|
94
|
+
position: "sidebar",
|
|
95
|
+
}
|
|
96
|
+
const python: ChangelogSection = {
|
|
97
|
+
type: "changelog",
|
|
98
|
+
title: "Changelog",
|
|
99
|
+
prefix: "/python/changelog/",
|
|
100
|
+
source: "python/CHANGELOG.md",
|
|
101
|
+
position: "sidebar",
|
|
102
|
+
}
|
|
103
|
+
const config = makeConfig(root, [standard, python])
|
|
104
|
+
|
|
105
|
+
await writeChangelog(root, "standard", "## 0.2.0\n\nStandard two.\n")
|
|
106
|
+
await writeChangelog(root, "python", "## 0.3.0\n\nPython three.\n")
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await buildChangelog(standard, config)
|
|
110
|
+
await buildChangelog(python, config)
|
|
111
|
+
await writeChangelog(root, "standard", "## 0.4.0\n\nStandard four.\n")
|
|
112
|
+
await buildChangelog(standard, config)
|
|
113
|
+
|
|
114
|
+
expect(await readdir(changelogCacheDir(standard, config))).toEqual(["0-4-0.md"])
|
|
115
|
+
expect(await readdir(changelogCacheDir(python, config))).toEqual(["0-3-0.md"])
|
|
116
|
+
} finally {
|
|
117
|
+
await rm(targetDirFor(configPath), { recursive: true, force: true })
|
|
118
|
+
}
|
|
119
|
+
}))
|
|
120
|
+
|
|
121
|
+
it("keeps sections apart when their prefixes slugify to the same key", () =>
|
|
122
|
+
temporaryDirectoryTask(async root => {
|
|
123
|
+
const configPath = join(root, "livemark.config.ts")
|
|
124
|
+
const nested: ChangelogSection = {
|
|
125
|
+
type: "changelog",
|
|
126
|
+
title: "Changelog",
|
|
127
|
+
prefix: "/python/changelog/",
|
|
128
|
+
source: "nested/CHANGELOG.md",
|
|
129
|
+
position: "sidebar",
|
|
130
|
+
}
|
|
131
|
+
const flat: ChangelogSection = {
|
|
132
|
+
type: "changelog",
|
|
133
|
+
title: "Changelog",
|
|
134
|
+
prefix: "/python-changelog/",
|
|
135
|
+
source: "flat/CHANGELOG.md",
|
|
136
|
+
position: "sidebar",
|
|
137
|
+
}
|
|
138
|
+
const config = makeConfig(root, [nested, flat])
|
|
139
|
+
|
|
140
|
+
await writeChangelog(root, "nested", "## 0.1.0\n\nNested one.\n")
|
|
141
|
+
await writeChangelog(root, "flat", "## 0.2.0\n\nFlat two.\n")
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
await buildChangelog(nested, config)
|
|
145
|
+
await buildChangelog(flat, config)
|
|
146
|
+
|
|
147
|
+
const nestedDir = changelogCacheDir(nested, config)
|
|
148
|
+
const flatDir = changelogCacheDir(flat, config)
|
|
149
|
+
expect(nestedDir).not.toBe(flatDir)
|
|
150
|
+
expect(await readdir(nestedDir)).toEqual(["0-1-0.md"])
|
|
151
|
+
expect(await readdir(flatDir)).toEqual(["0-2-0.md"])
|
|
152
|
+
} finally {
|
|
153
|
+
await rm(targetDirFor(configPath), { recursive: true, force: true })
|
|
154
|
+
}
|
|
155
|
+
}))
|
|
156
|
+
})
|
|
3
157
|
|
|
4
158
|
describe("splitLocalChangelog", () => {
|
|
5
159
|
it("splits by h2 headings", () => {
|
|
@@ -67,3 +221,39 @@ Release notes.
|
|
|
67
221
|
expect(entries[0]?.date).toBeUndefined()
|
|
68
222
|
})
|
|
69
223
|
})
|
|
224
|
+
|
|
225
|
+
describe("escapeMdxAngles", () => {
|
|
226
|
+
it("escapes a bare angle placeholder as an HTML entity", () => {
|
|
227
|
+
// `\<` is honoured by remark but NOT by MDX's JSX-aware parser, which still
|
|
228
|
+
// reads `\<name>` as an unclosed tag. Only the entity survives both.
|
|
229
|
+
expect(escapeMdxAngles("a <name> b")).toBe("a <name> b")
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it("escapes a placeholder that follows a bare URL", () => {
|
|
233
|
+
const input = "See https://fairspec.org/profiles/latest/<name>.json, ok."
|
|
234
|
+
expect(escapeMdxAngles(input)).toBe(
|
|
235
|
+
"See https://fairspec.org/profiles/latest/<name>.json, ok.",
|
|
236
|
+
)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it("leaves inline code spans alone", () => {
|
|
240
|
+
expect(escapeMdxAngles("use `--resource <name>` here")).toBe(
|
|
241
|
+
"use `--resource <name>` here",
|
|
242
|
+
)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it("leaves fenced code blocks alone", () => {
|
|
246
|
+
const input = "```sh\nfoo <bar>\n```"
|
|
247
|
+
expect(escapeMdxAngles(input)).toBe(input)
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
function makeConfig(root: string, sections: ChangelogSection[]) {
|
|
252
|
+
const configPath = join(root, "livemark.config.ts")
|
|
253
|
+
return { ...Config.parse({ root, configPath, sections }), base: undefined }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function writeChangelog(root: string, dir: string, body: string) {
|
|
257
|
+
await mkdir(join(root, dir), { recursive: true })
|
|
258
|
+
await writeFile(join(root, dir, "CHANGELOG.md"), body, "utf-8")
|
|
259
|
+
}
|
|
@@ -59,9 +59,10 @@ for (const section of changelogSections) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const configInclude = Array.isArray(config.include) ? config.include : [config.include]
|
|
62
|
-
const include =
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
const include = [
|
|
63
|
+
...configInclude,
|
|
64
|
+
...changelogSections.map(section => cacheIncludeGlob(section, config)),
|
|
65
|
+
]
|
|
65
66
|
|
|
66
67
|
const userExclude = config.exclude
|
|
67
68
|
? Array.isArray(config.exclude)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
|
|
2
2
|
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
|
3
3
|
|
|
4
|
-
import { cn } from "
|
|
4
|
+
import { cn } from "cn"
|
|
5
5
|
|
|
6
6
|
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
|
|
7
7
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
|
|
3
3
|
|
|
4
|
-
import { cn } from "
|
|
4
|
+
import { cn } from "cn"
|
|
5
5
|
import { Button } from "./button.tsx"
|
|
6
6
|
|
|
7
7
|
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
-
import { cn } from "
|
|
2
|
+
import { cn } from "cn"
|
|
3
3
|
|
|
4
4
|
const alertVariants = cva(
|
|
5
5
|
"grid gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mergeProps } from "@base-ui/react/merge-props"
|
|
2
2
|
import { useRender } from "@base-ui/react/use-render"
|
|
3
3
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
-
import { cn } from "
|
|
4
|
+
import { cn } from "cn"
|
|
5
5
|
|
|
6
6
|
const badgeVariants = cva(
|
|
7
7
|
"h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mergeProps } from "@base-ui/react/merge-props"
|
|
2
2
|
import { useRender } from "@base-ui/react/use-render"
|
|
3
3
|
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
|
|
4
|
-
import { cn } from "
|
|
4
|
+
import { cn } from "cn"
|
|
5
5
|
|
|
6
6
|
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
|
|
7
7
|
return (
|
|
@@ -2,7 +2,7 @@ import { mergeProps } from "@base-ui/react/merge-props"
|
|
|
2
2
|
import { useRender } from "@base-ui/react/use-render"
|
|
3
3
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
4
|
|
|
5
|
-
import { cn } from "
|
|
5
|
+
import { cn } from "cn"
|
|
6
6
|
import { Separator } from "./separator.tsx"
|
|
7
7
|
|
|
8
8
|
const buttonGroupVariants = cva(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Button as ButtonPrimitive } from "@base-ui/react/button"
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
import { cn } from "
|
|
3
|
+
import { cn } from "cn"
|
|
4
4
|
|
|
5
5
|
const buttonVariants = cva(
|
|
6
6
|
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "react-day-picker"
|
|
8
8
|
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
|
9
9
|
|
|
10
|
-
import { cn } from "
|
|
10
|
+
import { cn } from "cn"
|
|
11
11
|
import { Button, buttonVariants } from "./button.tsx"
|
|
12
12
|
|
|
13
13
|
function Calendar({
|
package/source/elements/card.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from "react"
|
|
|
2
2
|
import useEmblaCarousel, { type UseEmblaCarouselType } from "embla-carousel-react"
|
|
3
3
|
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
|
4
4
|
|
|
5
|
-
import { cn } from "
|
|
5
|
+
import { cn } from "cn"
|
|
6
6
|
import { Button } from "./button.tsx"
|
|
7
7
|
|
|
8
8
|
type CarouselApi = UseEmblaCarouselType[1]
|
|
@@ -2,7 +2,7 @@ import * as React from "react"
|
|
|
2
2
|
import * as RechartsPrimitive from "recharts"
|
|
3
3
|
import type { TooltipValueType } from "recharts"
|
|
4
4
|
|
|
5
|
-
import { cn } from "
|
|
5
|
+
import { cn } from "cn"
|
|
6
6
|
|
|
7
7
|
// Format: { THEME_NAME: CSS_SELECTOR }
|
|
8
8
|
const THEMES = { light: "", dark: ".dark" } as const
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
|
|
2
2
|
import { CheckIcon } from "lucide-react"
|
|
3
3
|
|
|
4
|
-
import { cn } from "
|
|
4
|
+
import { cn } from "cn"
|
|
5
5
|
|
|
6
6
|
function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
|
|
7
7
|
return (
|
|
@@ -2,7 +2,7 @@ import * as React from "react"
|
|
|
2
2
|
import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu"
|
|
3
3
|
import { CheckIcon, ChevronRightIcon } from "lucide-react"
|
|
4
4
|
|
|
5
|
-
import { cn } from "
|
|
5
|
+
import { cn } from "cn"
|
|
6
6
|
|
|
7
7
|
function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
|
|
8
8
|
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
|
|
2
2
|
import { XIcon } from "lucide-react"
|
|
3
|
-
import { cn } from "
|
|
3
|
+
import { cn } from "cn"
|
|
4
4
|
import { Button } from "./button.tsx"
|
|
5
5
|
|
|
6
6
|
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Menu as MenuPrimitive } from "@base-ui/react/menu"
|
|
2
2
|
import { CheckIcon, ChevronRightIcon } from "lucide-react"
|
|
3
|
-
import { cn } from "
|
|
3
|
+
import { cn } from "cn"
|
|
4
4
|
|
|
5
5
|
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
|
|
6
6
|
return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
|
|
2
2
|
|
|
3
|
-
import { cn } from "
|
|
3
|
+
import { cn } from "cn"
|
|
4
4
|
|
|
5
5
|
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
|
|
6
6
|
return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
|