livemark 0.25.0 → 0.27.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/README.md +3 -2
- package/build/actions/config/define.d.ts +12 -0
- package/build/actions/config/define.unit.js +8 -1
- package/build/actions/escape/copy.d.ts +11 -0
- package/build/actions/escape/copy.js +29 -0
- package/build/actions/escape/copy.unit.d.ts +1 -0
- package/build/actions/escape/copy.unit.js +37 -0
- package/build/actions/escape/list.d.ts +12 -0
- package/build/actions/escape/list.js +27 -0
- package/build/actions/escape/list.unit.d.ts +1 -0
- package/build/actions/escape/list.unit.js +36 -0
- package/build/actions/target/build.d.ts +9 -26
- package/build/actions/target/build.js +50 -71
- package/build/actions/target/build.unit.d.ts +1 -0
- package/build/actions/target/build.unit.js +76 -0
- package/build/actions/target/clear.d.ts +4 -0
- package/build/actions/target/clear.js +14 -0
- package/build/actions/target/clear.unit.d.ts +1 -0
- package/build/actions/target/clear.unit.js +23 -0
- package/build/commands/build.js +1 -2
- package/build/commands/clear.d.ts +5 -0
- package/build/commands/clear.js +15 -0
- package/build/commands/escape.d.ts +4 -3
- package/build/commands/escape.js +45 -48
- package/build/commands/preview.js +1 -2
- package/build/commands/start.js +1 -2
- package/build/components/Abbr.d.ts +1 -1
- package/build/components/Abbr.js +2 -4
- package/build/components/AnsiCode.d.ts +1 -1
- package/build/components/AnsiCode.js +2 -2
- package/build/components/Article.d.ts +2 -1
- package/build/components/Article.js +7 -14
- package/build/components/BackToTop.d.ts +1 -1
- package/build/components/Banner.d.ts +2 -2
- package/build/components/Banner.js +7 -3
- package/build/components/BlogIndex.d.ts +1 -1
- package/build/components/BlogIndex.js +2 -11
- package/build/components/Breadcrumbs.d.ts +1 -1
- package/build/components/Breadcrumbs.js +2 -3
- package/build/components/Callout.d.ts +1 -1
- package/build/components/Callout.js +5 -9
- package/build/components/Card.d.ts +1 -1
- package/build/components/Card.js +2 -3
- package/build/components/Cards.d.ts +1 -1
- package/build/components/ChangelogIndex.d.ts +1 -1
- package/build/components/ChangelogIndex.js +2 -11
- package/build/components/ChoiceList.d.ts +15 -0
- package/build/components/ChoiceList.js +13 -0
- package/build/components/CodeBlock.d.ts +1 -1
- package/build/components/CodeBlock.js +3 -16
- package/build/components/CodeTabs.d.ts +1 -1
- package/build/components/CodeTabs.js +2 -3
- package/build/components/Column.d.ts +1 -1
- package/build/components/Columns.d.ts +1 -1
- package/build/components/Content.d.ts +1 -1
- package/build/components/ContentTab.d.ts +1 -1
- package/build/components/ContentTabs.d.ts +1 -1
- package/build/components/ContentTabs.js +2 -3
- package/build/components/DefaultCatchBoundary.d.ts +1 -1
- package/build/components/DefaultCatchBoundary.js +5 -7
- package/build/components/DefinitionList.d.ts +3 -3
- package/build/components/Details.d.ts +1 -1
- package/build/components/Details.js +2 -5
- package/build/components/FileTree.d.ts +1 -1
- package/build/components/FileTree.js +3 -5
- package/build/components/Footer.d.ts +1 -1
- package/build/components/Footer.js +2 -5
- package/build/components/FootnoteRef.d.ts +1 -1
- package/build/components/FootnoteRef.js +2 -4
- package/build/components/Header.d.ts +6 -0
- package/build/components/Header.js +26 -0
- package/build/components/HeaderMenu.d.ts +2 -0
- package/build/components/HeaderMenu.js +27 -0
- package/build/components/HeaderNav.d.ts +6 -0
- package/build/components/HeaderNav.js +74 -0
- package/build/components/HeaderSearch.d.ts +4 -0
- package/build/components/HeaderSearch.js +33 -0
- package/build/components/Heading.d.ts +6 -6
- package/build/components/InlineBadge.d.ts +2 -2
- package/build/components/InlineBadge.js +1 -1
- package/build/components/InlineIcon.d.ts +1 -1
- package/build/components/InlineToc.d.ts +1 -1
- package/build/components/InlineToc.js +2 -4
- package/build/components/Layout.d.ts +2 -1
- package/build/components/Layout.js +5 -58
- package/build/components/LinkButton.d.ts +2 -2
- package/build/components/LinkButton.js +1 -1
- package/build/components/MenuSheet.d.ts +22 -0
- package/build/components/MenuSheet.js +27 -0
- package/build/components/Mermaid.d.ts +1 -1
- package/build/components/MermaidRenderer.d.ts +1 -1
- package/build/components/MermaidRenderer.js +20 -8
- package/build/components/NotFound.d.ts +1 -1
- package/build/components/NotFound.js +3 -6
- package/build/components/PackageTabs.d.ts +1 -1
- package/build/components/PackageTabs.js +3 -7
- package/build/components/PageToolbar.d.ts +1 -1
- package/build/components/PageToolbar.js +2 -10
- package/build/components/PrevNext.d.ts +1 -1
- package/build/components/PrevNext.js +2 -10
- package/build/components/Root.d.ts +4 -0
- package/build/components/Root.js +8 -0
- package/build/components/Search.d.ts +5 -2
- package/build/components/Search.js +11 -9
- package/build/components/SearchDialog.d.ts +1 -1
- package/build/components/SearchDialog.js +10 -133
- package/build/components/Section.d.ts +1 -1
- package/build/components/SectionHeader.d.ts +1 -1
- package/build/components/SheetPanel.d.ts +12 -0
- package/build/components/SheetPanel.js +19 -0
- package/build/components/Sidebar.d.ts +1 -1
- package/build/components/Sidebar.js +12 -14
- package/build/components/SidebarArticles.d.ts +4 -1
- package/build/components/SidebarArticles.js +5 -9
- package/build/components/SidebarLinks.d.ts +4 -1
- package/build/components/SidebarLinks.js +6 -9
- package/build/components/SidebarPosts.d.ts +4 -1
- package/build/components/SidebarPosts.js +7 -23
- package/build/components/SidebarReleases.d.ts +4 -1
- package/build/components/SidebarReleases.js +5 -15
- package/build/components/SidebarSectionPicker.d.ts +8 -0
- package/build/components/SidebarSectionPicker.js +31 -0
- package/build/components/SidebarSections.d.ts +1 -1
- package/build/components/SidebarSections.js +5 -9
- package/build/components/SiteLink.d.ts +1 -1
- package/build/components/SiteTitle.d.ts +1 -1
- package/build/components/SiteTitle.js +2 -6
- package/build/components/SortableTable.d.ts +1 -1
- package/build/components/SoundCloud.d.ts +1 -1
- package/build/components/SoundCloud.js +2 -2
- package/build/components/TagIndex.d.ts +1 -1
- package/build/components/TagIndex.js +2 -11
- package/build/components/Theme.d.ts +2 -1
- package/build/components/Theme.js +35 -15
- package/build/components/Toc.d.ts +5 -4
- package/build/components/Toc.js +35 -35
- package/build/components/YouTube.d.ts +1 -1
- package/build/components/YouTube.js +2 -2
- package/build/components/ZoomImage.d.ts +1 -1
- package/build/content/article.js +4 -11
- package/build/content-collections.js +6 -4
- package/build/elements/accordion.d.ts +4 -4
- package/build/elements/accordion.js +2 -3
- package/build/elements/alert-dialog.d.ts +12 -12
- package/build/elements/alert-dialog.js +2 -4
- package/build/elements/alert.d.ts +4 -4
- package/build/elements/alert.js +2 -2
- package/build/elements/aspect-ratio.d.ts +1 -1
- package/build/elements/avatar.d.ts +6 -6
- package/build/elements/badge.d.ts +1 -1
- package/build/elements/badge.js +3 -1
- package/build/elements/breadcrumb.d.ts +6 -6
- package/build/elements/breadcrumb.js +2 -4
- package/build/elements/button-group.d.ts +2 -2
- package/build/elements/button-group.js +5 -5
- package/build/elements/button.d.ts +2 -2
- package/build/elements/button.js +9 -7
- package/build/elements/calendar.d.ts +2 -2
- package/build/elements/calendar.js +2 -2
- package/build/elements/card.d.ts +7 -7
- package/build/elements/card.js +4 -4
- package/build/elements/carousel.d.ts +5 -5
- package/build/elements/carousel.js +3 -7
- package/build/elements/chart.d.ts +6 -6
- package/build/elements/chart.js +5 -11
- package/build/elements/checkbox.d.ts +1 -1
- package/build/elements/collapsible.d.ts +3 -3
- package/build/elements/command.d.ts +9 -9
- package/build/elements/command.js +5 -11
- package/build/elements/context-menu.d.ts +15 -15
- package/build/elements/context-menu.js +6 -9
- package/build/elements/dialog.d.ts +10 -10
- package/build/elements/dialog.js +2 -6
- package/build/elements/drawer.d.ts +14 -12
- package/build/elements/drawer.js +14 -15
- package/build/elements/dropdown-menu.d.ts +15 -15
- package/build/elements/dropdown-menu.js +7 -10
- package/build/elements/empty.d.ts +6 -6
- package/build/elements/empty.js +2 -2
- package/build/elements/field.d.ts +10 -10
- package/build/elements/field.js +3 -4
- package/build/elements/hover-card.d.ts +3 -3
- package/build/elements/hover-card.js +2 -2
- package/build/elements/input-group.d.ts +7 -7
- package/build/elements/input-group.js +5 -5
- package/build/elements/input-otp.d.ts +4 -4
- package/build/elements/input.d.ts +1 -1
- package/build/elements/input.js +2 -2
- package/build/elements/item.d.ts +9 -9
- package/build/elements/item.js +2 -2
- package/build/elements/kbd.d.ts +2 -2
- package/build/elements/label.d.ts +1 -1
- package/build/elements/menubar.d.ts +16 -16
- package/build/elements/menubar.js +9 -11
- package/build/elements/native-select.d.ts +3 -3
- package/build/elements/native-select.js +2 -4
- package/build/elements/navigation-menu.d.ts +8 -8
- package/build/elements/navigation-menu.js +3 -5
- package/build/elements/pagination.d.ts +7 -7
- package/build/elements/pagination.js +4 -10
- package/build/elements/popover.d.ts +6 -6
- package/build/elements/popover.js +2 -2
- package/build/elements/progress.d.ts +5 -5
- package/build/elements/progress.js +2 -3
- package/build/elements/radio-group.d.ts +2 -2
- package/build/elements/resizable.d.ts +3 -3
- package/build/elements/scroll-area.d.ts +2 -2
- package/build/elements/scroll-area.js +2 -4
- package/build/elements/select.d.ts +9 -9
- package/build/elements/select.js +4 -9
- package/build/elements/separator.d.ts +1 -1
- package/build/elements/sheet.d.ts +8 -8
- package/build/elements/sheet.js +2 -6
- package/build/elements/sidebar.d.ts +19 -19
- package/build/elements/sidebar.js +15 -37
- package/build/elements/skeleton.d.ts +1 -1
- package/build/elements/slider.d.ts +2 -1
- package/build/elements/slider.js +2 -3
- package/build/elements/sonner.d.ts +1 -1
- package/build/elements/spinner.d.ts +1 -1
- package/build/elements/switch.d.ts +1 -1
- package/build/elements/table.d.ts +8 -8
- package/build/elements/tabs.d.ts +4 -4
- package/build/elements/tabs.js +3 -3
- package/build/elements/textarea.d.ts +1 -1
- package/build/elements/textarea.js +2 -2
- package/build/elements/toggle-group.d.ts +3 -2
- package/build/elements/toggle-group.js +3 -3
- package/build/elements/toggle.d.ts +1 -1
- package/build/elements/toggle.js +2 -2
- package/build/elements/tooltip.d.ts +4 -4
- package/build/elements/tooltip.js +2 -3
- package/build/head.d.ts +4 -0
- package/build/head.js +6 -0
- package/build/helpers/agent-skills.d.ts +22 -0
- package/build/helpers/agent-skills.js +46 -0
- package/build/helpers/agent-skills.unit.d.ts +1 -0
- package/build/helpers/agent-skills.unit.js +49 -0
- package/build/helpers/article-search.d.ts +19 -0
- package/build/helpers/article-search.js +134 -0
- package/build/helpers/article.d.ts +13 -0
- package/build/helpers/article.js +17 -1
- package/build/helpers/dependency-dir.d.ts +5 -0
- package/build/helpers/dependency-dir.js +20 -0
- package/build/helpers/dependency-dir.unit.d.ts +1 -0
- package/build/helpers/dependency-dir.unit.js +25 -0
- package/build/helpers/dynamic-icon-resolver.d.ts +1 -1
- package/build/helpers/dynamic-icon.d.ts +1 -1
- package/build/helpers/head.d.ts +58 -0
- package/build/helpers/head.js +80 -0
- package/build/helpers/head.unit.d.ts +1 -0
- package/build/helpers/head.unit.js +110 -0
- package/build/helpers/llms.d.ts +33 -0
- package/build/helpers/llms.js +72 -0
- package/build/helpers/llms.unit.d.ts +1 -0
- package/build/helpers/llms.unit.js +98 -0
- package/build/helpers/nav-items.d.ts +11 -0
- package/build/helpers/nav-items.js +65 -0
- package/build/helpers/render-inline-markdown.d.ts +1 -0
- package/build/helpers/render-inline-markdown.js +34 -0
- package/build/helpers/render-inline-markdown.unit.d.ts +1 -0
- package/build/helpers/render-inline-markdown.unit.js +20 -0
- package/build/helpers/search-highlight.d.ts +8 -0
- package/build/helpers/search-highlight.js +59 -0
- package/build/helpers/search-highlight.unit.d.ts +1 -0
- package/build/helpers/search-highlight.unit.js +36 -0
- package/build/helpers/target-export.d.ts +9 -0
- package/build/helpers/target-export.js +24 -0
- package/build/helpers/target-export.unit.d.ts +1 -0
- package/build/helpers/target-export.unit.js +25 -0
- package/build/helpers/theme.d.ts +6 -2
- package/build/helpers/theme.js +40 -7
- package/build/helpers/theme.unit.d.ts +1 -0
- package/build/helpers/theme.unit.js +30 -0
- package/build/hooks/hydrated.d.ts +2 -0
- package/build/hooks/hydrated.js +15 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -1
- package/build/models/config.d.ts +52 -6
- package/build/models/config.js +17 -1
- package/build/models/section.d.ts +4 -2
- package/build/models/section.js +1 -1
- package/build/plugins/vite-agents.d.ts +17 -0
- package/build/plugins/vite-agents.js +109 -0
- package/build/plugins/vite-agents.unit.d.ts +1 -0
- package/build/plugins/vite-agents.unit.js +20 -0
- package/build/plugins/vite-livemark.d.ts +4 -5
- package/build/plugins/vite-livemark.js +56 -90
- package/build/plugins/vite-robots.d.ts +4 -2
- package/build/plugins/vite-robots.js +15 -2
- package/build/plugins/vite-robots.unit.js +5 -1
- package/build/program.js +4 -2
- package/build/routes/$.d.ts +4 -1
- package/build/routes/$.js +20 -9
- package/build/routes/__root.js +29 -63
- package/build/settings.js +2 -1
- package/build/vite.config.js +3 -1
- package/package.json +69 -66
- package/{.agents/skills → skills}/livemark-authoring/SKILL.md +2 -2
- package/{.agents/skills → skills}/livemark-configuration/SKILL.md +16 -5
- package/skills/livemark-customization/SKILL.md +146 -0
- package/source/actions/config/define.unit.ts +9 -0
- package/source/actions/escape/copy.ts +35 -0
- package/source/actions/escape/copy.unit.ts +51 -0
- package/source/actions/escape/list.ts +30 -0
- package/source/actions/escape/list.unit.ts +42 -0
- package/source/actions/target/build.ts +49 -69
- package/source/actions/target/build.unit.ts +95 -0
- package/source/actions/target/clear.ts +14 -0
- package/source/actions/target/clear.unit.ts +25 -0
- package/source/commands/build.ts +0 -1
- package/source/commands/clear.ts +15 -0
- package/source/commands/escape.ts +42 -59
- package/source/commands/preview.ts +0 -1
- package/source/commands/start.ts +0 -1
- package/source/components/AnsiCode.tsx +1 -1
- package/source/components/Article.tsx +8 -4
- package/source/components/Banner.tsx +12 -2
- package/source/components/BlogIndex.tsx +1 -1
- package/source/components/Callout.tsx +4 -6
- package/source/components/ChangelogIndex.tsx +1 -1
- package/source/components/ChoiceList.tsx +69 -0
- package/source/components/CodeBlock.tsx +6 -6
- package/source/components/CodeTabs.tsx +1 -1
- package/source/components/ContentTabs.tsx +1 -1
- package/source/components/DefaultCatchBoundary.tsx +4 -6
- package/source/components/Details.tsx +1 -1
- package/source/components/FileTree.tsx +1 -1
- package/source/components/Header.tsx +52 -0
- package/source/components/HeaderMenu.tsx +41 -0
- package/source/components/HeaderNav.tsx +145 -0
- package/source/components/HeaderSearch.tsx +77 -0
- package/source/components/InlineBadge.tsx +1 -1
- package/source/components/InlineToc.tsx +1 -1
- package/source/components/Layout.tsx +16 -160
- package/source/components/LinkButton.tsx +9 -1
- package/source/components/MenuSheet.tsx +92 -0
- package/source/components/MermaidRenderer.tsx +19 -8
- package/source/components/NotFound.tsx +3 -5
- package/source/components/PackageTabs.tsx +2 -2
- package/source/components/PageToolbar.tsx +11 -3
- package/source/components/PrevNext.tsx +2 -2
- package/source/components/Root.tsx +13 -0
- package/source/components/Search.tsx +28 -8
- package/source/components/SearchDialog.tsx +15 -189
- package/source/components/SheetPanel.tsx +69 -0
- package/source/components/Sidebar.tsx +27 -14
- package/source/components/SidebarArticles.tsx +4 -3
- package/source/components/SidebarLinks.tsx +3 -2
- package/source/components/SidebarPosts.tsx +3 -2
- package/source/components/SidebarReleases.tsx +3 -2
- package/source/components/SidebarSectionPicker.tsx +70 -0
- package/source/components/SiteTitle.tsx +5 -5
- package/source/components/SoundCloud.tsx +1 -1
- package/source/components/TagIndex.tsx +1 -1
- package/source/components/Theme.tsx +55 -29
- package/source/components/Toc.tsx +76 -55
- package/source/components/YouTube.tsx +1 -1
- package/source/content/article.ts +8 -13
- package/source/content-collections.ts +5 -4
- package/source/elements/alert-dialog.tsx +1 -1
- package/source/elements/alert.tsx +1 -1
- package/source/elements/badge.tsx +4 -0
- package/source/elements/button-group.tsx +4 -4
- package/source/elements/button.tsx +10 -6
- package/source/elements/calendar.tsx +1 -1
- package/source/elements/card.tsx +3 -3
- package/source/elements/command.tsx +7 -4
- package/source/elements/context-menu.tsx +5 -5
- package/source/elements/dialog.tsx +1 -1
- package/source/elements/drawer.tsx +59 -43
- package/source/elements/dropdown-menu.tsx +6 -6
- 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 +4 -4
- package/source/elements/input.tsx +1 -1
- package/source/elements/item.tsx +1 -1
- package/source/elements/menubar.tsx +8 -8
- package/source/elements/native-select.tsx +1 -1
- package/source/elements/popover.tsx +1 -1
- package/source/elements/select.tsx +3 -3
- package/source/elements/sidebar.tsx +8 -21
- package/source/elements/tabs.tsx +2 -2
- package/source/elements/textarea.tsx +1 -1
- package/source/elements/toggle-group.tsx +2 -2
- package/source/elements/toggle.tsx +1 -1
- package/source/env.d.ts +1 -0
- package/source/head.ts +7 -0
- package/source/helpers/agent-skills.ts +59 -0
- package/source/helpers/agent-skills.unit.ts +56 -0
- package/source/helpers/article-search.tsx +192 -0
- package/source/helpers/article.ts +17 -0
- package/source/helpers/dependency-dir.ts +20 -0
- package/source/helpers/dependency-dir.unit.ts +39 -0
- package/source/helpers/head.ts +105 -0
- package/source/helpers/head.unit.ts +118 -0
- package/source/helpers/llms.ts +109 -0
- package/source/helpers/llms.unit.ts +115 -0
- package/source/helpers/nav-items.tsx +102 -0
- package/source/helpers/render-inline-markdown.ts +44 -0
- package/source/helpers/render-inline-markdown.unit.ts +30 -0
- package/source/helpers/search-highlight.ts +73 -0
- package/source/helpers/search-highlight.unit.ts +42 -0
- package/source/helpers/target-export.ts +28 -0
- package/source/helpers/target-export.unit.ts +37 -0
- package/source/helpers/theme.ts +40 -6
- package/source/helpers/theme.unit.ts +32 -0
- package/source/hooks/hydrated.ts +18 -0
- package/source/index.ts +1 -0
- package/source/models/config.ts +24 -0
- package/source/models/section.ts +3 -0
- package/source/plugins/vite-agents.ts +135 -0
- package/source/plugins/vite-agents.unit.ts +26 -0
- package/source/plugins/vite-livemark.ts +54 -95
- package/source/plugins/vite-robots.ts +18 -3
- package/source/plugins/vite-robots.unit.ts +7 -0
- package/source/program.ts +2 -0
- package/source/routes/$.tsx +27 -9
- package/source/routes/__root.tsx +51 -71
- package/source/settings.ts +1 -0
- package/source/styles/general.css +59 -11
- package/source/styles/markdown.css +13 -7
- package/source/styles/shape.css +14 -0
- package/source/vite.config.ts +2 -0
- package/.agents/skills/change-review/SKILL.md +0 -47
- package/.agents/skills/livemark-customization/SKILL.md +0 -118
|
@@ -31,4 +31,13 @@ describe("defineConfig", () => {
|
|
|
31
31
|
const config = defineConfig({})
|
|
32
32
|
expect(config.include).toBe("**/*.md")
|
|
33
33
|
})
|
|
34
|
+
|
|
35
|
+
it("should accept an inline Markdown banner", () => {
|
|
36
|
+
const config = defineConfig({ banner: "Hi · [x](/y/)" })
|
|
37
|
+
expect(config.banner).toBe("Hi · [x](/y/)")
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("should leave banner unset by default", () => {
|
|
41
|
+
expect(defineConfig({}).banner).toBeUndefined()
|
|
42
|
+
})
|
|
34
43
|
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { dirname, join } from "node:path"
|
|
3
|
+
import { ESCAPABLE_FILES, ESCAPABLE_FOLDERS } from "../../settings.ts"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Copy a livemark default into `<root>/.livemark/<path>`.
|
|
7
|
+
*
|
|
8
|
+
* Throws when the path is not escapable, is not a livemark file, or the
|
|
9
|
+
* destination already exists. Returns the destination path.
|
|
10
|
+
*/
|
|
11
|
+
export function copyEscape(props: { root: string; sourceRoot: string; path: string }) {
|
|
12
|
+
const isTopLevelFile = !props.path.includes("/") && ESCAPABLE_FILES.includes(props.path)
|
|
13
|
+
const [first] = props.path.split("/")
|
|
14
|
+
const isFolderPath =
|
|
15
|
+
!!first && ESCAPABLE_FOLDERS.includes(first) && !props.path.includes("..")
|
|
16
|
+
if (!isTopLevelFile && !isFolderPath) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`'${props.path}' is not escapable (folders: ${ESCAPABLE_FOLDERS.join(", ")}; files: ${ESCAPABLE_FILES.join(", ")})`,
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const src = join(props.sourceRoot, props.path)
|
|
23
|
+
if (!existsSync(src) || !statSync(src).isFile()) {
|
|
24
|
+
throw new Error(`'${props.path}' is not a livemark file`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const dest = join(props.root, ".livemark", props.path)
|
|
28
|
+
if (existsSync(dest)) {
|
|
29
|
+
throw new Error(`.livemark/${props.path} already exists`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
mkdirSync(dirname(dest), { recursive: true })
|
|
33
|
+
writeFileSync(dest, readFileSync(src))
|
|
34
|
+
return dest
|
|
35
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { temporaryDirectoryTask } from "tempy"
|
|
4
|
+
import { describe, expect, it } from "vite-plus/test"
|
|
5
|
+
import { copyEscape } from "./copy.ts"
|
|
6
|
+
|
|
7
|
+
describe("copyEscape", () => {
|
|
8
|
+
it("should copy a default into .livemark/", () =>
|
|
9
|
+
temporaryDirectoryTask(async tmpDir => {
|
|
10
|
+
const sourceRoot = createSource(tmpDir)
|
|
11
|
+
const dest = copyEscape({ root: tmpDir, sourceRoot, path: "components/Footer.tsx" })
|
|
12
|
+
expect(dest).toBe(join(tmpDir, ".livemark", "components", "Footer.tsx"))
|
|
13
|
+
expect(fs.readFileSync(dest, "utf8")).toBe("default\n")
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
it("should refuse to overwrite an existing override", () =>
|
|
17
|
+
temporaryDirectoryTask(async tmpDir => {
|
|
18
|
+
const sourceRoot = createSource(tmpDir)
|
|
19
|
+
copyEscape({ root: tmpDir, sourceRoot, path: "client.tsx" })
|
|
20
|
+
expect(() => copyEscape({ root: tmpDir, sourceRoot, path: "client.tsx" })).toThrow(
|
|
21
|
+
/already exists/,
|
|
22
|
+
)
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
it("should reject paths outside the escape contract", () =>
|
|
26
|
+
temporaryDirectoryTask(async tmpDir => {
|
|
27
|
+
const sourceRoot = createSource(tmpDir)
|
|
28
|
+
for (const path of ["index.ts", "components/../index.ts", "helpers/toc.ts"]) {
|
|
29
|
+
expect(() => copyEscape({ root: tmpDir, sourceRoot, path })).toThrow(
|
|
30
|
+
/not escapable/,
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
it("should reject an escapable path with no livemark file", () =>
|
|
36
|
+
temporaryDirectoryTask(async tmpDir => {
|
|
37
|
+
const sourceRoot = createSource(tmpDir)
|
|
38
|
+
expect(() =>
|
|
39
|
+
copyEscape({ root: tmpDir, sourceRoot, path: "components/Missing.tsx" }),
|
|
40
|
+
).toThrow(/not a livemark file/)
|
|
41
|
+
}))
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
function createSource(tmpDir: string) {
|
|
45
|
+
const sourceRoot = join(tmpDir, "source")
|
|
46
|
+
fs.mkdirSync(join(sourceRoot, "components"), { recursive: true })
|
|
47
|
+
fs.writeFileSync(join(sourceRoot, "components", "Footer.tsx"), "default\n")
|
|
48
|
+
fs.writeFileSync(join(sourceRoot, "client.tsx"), "default\n")
|
|
49
|
+
fs.writeFileSync(join(sourceRoot, "index.ts"), "default\n")
|
|
50
|
+
return sourceRoot
|
|
51
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { ESCAPABLE_FILES, ESCAPABLE_FOLDERS } from "../../settings.ts"
|
|
4
|
+
|
|
5
|
+
/** An escapable livemark file and whether the project already shadows it. */
|
|
6
|
+
export interface Escape {
|
|
7
|
+
path: string
|
|
8
|
+
escaped: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* List every file a project can shadow under `.livemark/`, sorted by path.
|
|
13
|
+
*/
|
|
14
|
+
export function listEscapes(props: { root: string; sourceRoot: string }) {
|
|
15
|
+
const paths: string[] = []
|
|
16
|
+
for (const folder of ESCAPABLE_FOLDERS) {
|
|
17
|
+
const dir = join(props.sourceRoot, folder)
|
|
18
|
+
if (!existsSync(dir)) continue
|
|
19
|
+
for (const entry of readdirSync(dir)) {
|
|
20
|
+
if (statSync(join(dir, entry)).isFile()) paths.push(`${folder}/${entry}`)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
for (const file of ESCAPABLE_FILES) {
|
|
24
|
+
if (existsSync(join(props.sourceRoot, file))) paths.push(file)
|
|
25
|
+
}
|
|
26
|
+
return paths.sort().map((path): Escape => ({
|
|
27
|
+
path,
|
|
28
|
+
escaped: existsSync(join(props.root, ".livemark", path)),
|
|
29
|
+
}))
|
|
30
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { temporaryDirectoryTask } from "tempy"
|
|
4
|
+
import { describe, expect, it } from "vite-plus/test"
|
|
5
|
+
import { listEscapes } from "./list.ts"
|
|
6
|
+
|
|
7
|
+
describe("listEscapes", () => {
|
|
8
|
+
it("should list folder files and top-level files, sorted", () =>
|
|
9
|
+
temporaryDirectoryTask(async tmpDir => {
|
|
10
|
+
const sourceRoot = join(tmpDir, "source")
|
|
11
|
+
writeFile(join(sourceRoot, "components", "Footer.tsx"))
|
|
12
|
+
writeFile(join(sourceRoot, "styles", "general.css"))
|
|
13
|
+
writeFile(join(sourceRoot, "client.tsx"))
|
|
14
|
+
writeFile(join(sourceRoot, "index.ts"))
|
|
15
|
+
fs.mkdirSync(join(sourceRoot, "elements", "nested"), { recursive: true })
|
|
16
|
+
|
|
17
|
+
const escapes = listEscapes({ root: tmpDir, sourceRoot })
|
|
18
|
+
expect(escapes.map(escape => escape.path)).toEqual([
|
|
19
|
+
"client.tsx",
|
|
20
|
+
"components/Footer.tsx",
|
|
21
|
+
"styles/general.css",
|
|
22
|
+
])
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
it("should mark files the project already shadows", () =>
|
|
26
|
+
temporaryDirectoryTask(async tmpDir => {
|
|
27
|
+
const sourceRoot = join(tmpDir, "source")
|
|
28
|
+
writeFile(join(sourceRoot, "components", "Footer.tsx"))
|
|
29
|
+
writeFile(join(sourceRoot, "components", "Header.tsx"))
|
|
30
|
+
writeFile(join(tmpDir, ".livemark", "components", "Footer.tsx"))
|
|
31
|
+
|
|
32
|
+
expect(listEscapes({ root: tmpDir, sourceRoot })).toEqual([
|
|
33
|
+
{ path: "components/Footer.tsx", escaped: true },
|
|
34
|
+
{ path: "components/Header.tsx", escaped: false },
|
|
35
|
+
])
|
|
36
|
+
}))
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
function writeFile(path: string) {
|
|
40
|
+
fs.mkdirSync(join(path, ".."), { recursive: true })
|
|
41
|
+
fs.writeFileSync(path, "default\n")
|
|
42
|
+
}
|
|
@@ -1,86 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readlinkSync, rmSync, symlinkSync } from "node:fs"
|
|
2
|
+
import { basename, dirname, join, relative, resolve } from "node:path"
|
|
3
|
+
import { resolveDependencyDir } from "../../helpers/dependency-dir.ts"
|
|
4
4
|
import { ESCAPABLE_FILES, ESCAPABLE_FOLDERS } from "../../settings.ts"
|
|
5
5
|
|
|
6
|
-
// This file ships at <livemark-pkg>/source/actions/target/build.ts AND
|
|
7
|
-
// gets copied into <livemark-pkg>/targets/<hash>/actions/target/build.ts
|
|
8
|
-
// as part of every per-consumer mount. content-collections loads the
|
|
9
|
-
// COPIED version, so `import.meta.dirname` can land inside `targets/`.
|
|
10
|
-
// Walk up to the first directory holding livemark's `package.json` so
|
|
11
|
-
// SOURCE_DIR / TARGETS_ROOT always anchor at the package, never at a
|
|
12
|
-
// per-consumer dir.
|
|
13
|
-
const LIVEMARK_PKG = findLivemarkPkg(import.meta.dirname)
|
|
14
|
-
const SOURCE_DIR = join(LIVEMARK_PKG, "source")
|
|
15
|
-
const TARGETS_ROOT = join(LIVEMARK_PKG, "targets")
|
|
16
|
-
|
|
17
|
-
function findLivemarkPkg(start: string) {
|
|
18
|
-
let cur = start
|
|
19
|
-
while (cur !== dirname(cur)) {
|
|
20
|
-
if (existsSync(join(cur, "package.json"))) return cur
|
|
21
|
-
cur = dirname(cur)
|
|
22
|
-
}
|
|
23
|
-
throw new Error(`Could not locate livemark package root from ${start}`)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
6
|
/**
|
|
27
|
-
* Synthesize
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* overlaying any matching files from the consumer's `.livemark/`. Every
|
|
32
|
-
* file lives at its target path as a real file — no symlinks — so
|
|
33
|
-
* Vite's relative-import and bare-specifier resolution all anchor
|
|
34
|
-
* inside the target dir, where node_modules walks reach livemark's pnpm
|
|
35
|
-
* subtree natively.
|
|
36
|
-
*
|
|
37
|
-
* Behavior:
|
|
38
|
-
* - First run (target dir missing): full source copy + overlay.
|
|
39
|
-
* - Subsequent runs: skip the source copy. Source is immutable in
|
|
40
|
-
* installed mode; in livemark's own dev the watcher keeps the target
|
|
41
|
-
* in sync. Only the `.livemark/` overlay is re-applied each time —
|
|
42
|
-
* cheap, and may have changed between runs.
|
|
43
|
-
* - With `--clear` in argv: wipe the entire target dir (including
|
|
44
|
-
* downstream caches like `.vite/`, `.content-collections/`,
|
|
45
|
-
* `cache/`, `routeTree.gen.ts`) and rebuild from scratch. Use this
|
|
46
|
-
* to recover from a stale or corrupted cache.
|
|
47
|
-
*
|
|
48
|
-
* Returns the absolute path to the target dir.
|
|
7
|
+
* Synthesize the project's Vite mount at `.livemark/.target/`: livemark's `source/` with
|
|
8
|
+
* the project's `.livemark/` escapes copied over it, plus a `node_modules` link to
|
|
9
|
+
* livemark's dependencies. Escapable folders are rebuilt on every run, so a removed escape
|
|
10
|
+
* never keeps rendering. Returns the target dir.
|
|
49
11
|
*/
|
|
50
|
-
export function buildTarget(configPath: string) {
|
|
51
|
-
const targetDir = targetDirFor(configPath)
|
|
52
|
-
const overridesRoot =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (clear) rmSync(targetDir, { recursive: true, force: true })
|
|
56
|
-
|
|
57
|
-
if (!existsSync(targetDir)) {
|
|
58
|
-
mkdirSync(targetDir, { recursive: true })
|
|
59
|
-
cpSync(SOURCE_DIR, targetDir, { recursive: true })
|
|
60
|
-
}
|
|
12
|
+
export function buildTarget(props: { configPath: string; sourceDir: string }) {
|
|
13
|
+
const targetDir = targetDirFor(props.configPath)
|
|
14
|
+
const overridesRoot = dirname(targetDir)
|
|
15
|
+
mkdirSync(targetDir, { recursive: true })
|
|
61
16
|
|
|
17
|
+
cpSync(props.sourceDir, targetDir, {
|
|
18
|
+
recursive: true,
|
|
19
|
+
filter: src => isPackageFile(src) && !isEscapableFolder(src, props.sourceDir),
|
|
20
|
+
})
|
|
62
21
|
for (const folder of ESCAPABLE_FOLDERS) {
|
|
63
|
-
const overlay = join(overridesRoot, folder)
|
|
64
|
-
if (!existsSync(overlay)) continue
|
|
65
22
|
const dest = join(targetDir, folder)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
23
|
+
rmSync(dest, { recursive: true, force: true })
|
|
24
|
+
const source = join(props.sourceDir, folder)
|
|
25
|
+
if (existsSync(source))
|
|
26
|
+
cpSync(source, dest, { recursive: true, filter: isPackageFile })
|
|
27
|
+
const overlay = join(overridesRoot, folder)
|
|
28
|
+
if (existsSync(overlay)) cpSync(overlay, dest, { recursive: true })
|
|
70
29
|
}
|
|
71
30
|
for (const file of ESCAPABLE_FILES) {
|
|
72
31
|
const overlay = join(overridesRoot, file)
|
|
73
|
-
if (
|
|
74
|
-
cpSync(overlay, join(targetDir, file), { force: true })
|
|
32
|
+
if (existsSync(overlay)) cpSync(overlay, join(targetDir, file))
|
|
75
33
|
}
|
|
76
34
|
|
|
35
|
+
linkDependencies(targetDir, resolveDependencyDir(props.sourceDir))
|
|
77
36
|
return targetDir
|
|
78
37
|
}
|
|
79
38
|
|
|
80
|
-
/**
|
|
81
|
-
* rebuilding it. Used by code that needs to write into the target
|
|
82
|
-
* (e.g. cache writers) outside of `buildTarget`'s setup phase. */
|
|
39
|
+
/** Locate the project's target dir without building it. */
|
|
83
40
|
export function targetDirFor(configPath: string) {
|
|
84
|
-
|
|
85
|
-
|
|
41
|
+
return join(dirname(configPath), ".livemark", ".target")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isPackageFile(src: string) {
|
|
45
|
+
return !/\.unit\.tsx?$/.test(src) && basename(src) !== "fixtures"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isEscapableFolder(src: string, sourceDir: string) {
|
|
49
|
+
return ESCAPABLE_FOLDERS.includes(relative(sourceDir, src))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function linkDependencies(targetDir: string, dependencyDir: string) {
|
|
53
|
+
const link = join(targetDir, "node_modules")
|
|
54
|
+
if (readLink(link) === resolve(dependencyDir)) return
|
|
55
|
+
// Not recursive: a stale link must go without touching the store it points into.
|
|
56
|
+
rmSync(link, { force: true })
|
|
57
|
+
symlinkSync(dependencyDir, link, "junction")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readLink(path: string) {
|
|
61
|
+
try {
|
|
62
|
+
return resolve(dirname(path), readlinkSync(path))
|
|
63
|
+
} catch {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
86
66
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readlinkSync, realpathSync } from "node:fs"
|
|
2
|
+
import { mkdir, rm, symlink, writeFile } from "node:fs/promises"
|
|
3
|
+
import { dirname, join } from "node:path"
|
|
4
|
+
import { temporaryDirectoryTask } from "tempy"
|
|
5
|
+
import { describe, expect, it } from "vite-plus/test"
|
|
6
|
+
import { buildTarget, targetDirFor } from "./build.ts"
|
|
7
|
+
|
|
8
|
+
describe("buildTarget", () => {
|
|
9
|
+
it("copies the source, overlays escapes and skips unit tests", () =>
|
|
10
|
+
temporaryDirectoryTask(async root => {
|
|
11
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
12
|
+
await put(join(root, "project/.livemark/components/Footer.tsx"), "escaped")
|
|
13
|
+
await put(join(root, "project/.livemark/head.ts"), "escaped head")
|
|
14
|
+
|
|
15
|
+
const targetDir = buildTarget({ configPath, sourceDir })
|
|
16
|
+
|
|
17
|
+
expect(targetDir).toBe(join(root, "project/.livemark/.target"))
|
|
18
|
+
expect(read(targetDir, "components/Footer.tsx")).toBe("escaped")
|
|
19
|
+
expect(read(targetDir, "components/Header.tsx")).toBe("default")
|
|
20
|
+
expect(read(targetDir, "head.ts")).toBe("escaped head")
|
|
21
|
+
expect(read(targetDir, "helpers/util.ts")).toBe("util")
|
|
22
|
+
expect(existsSync(join(targetDir, "helpers/util.unit.ts"))).toBe(false)
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
it("drops escapes removed since the last run", () =>
|
|
26
|
+
temporaryDirectoryTask(async root => {
|
|
27
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
28
|
+
const overrides = join(root, "project/.livemark")
|
|
29
|
+
await put(join(overrides, "components/Footer.tsx"), "escaped")
|
|
30
|
+
await put(join(overrides, "routes/about.tsx"), "about")
|
|
31
|
+
await put(join(overrides, "head.ts"), "escaped head")
|
|
32
|
+
buildTarget({ configPath, sourceDir })
|
|
33
|
+
|
|
34
|
+
await rm(join(overrides, "components"), { recursive: true })
|
|
35
|
+
await rm(join(overrides, "routes"), { recursive: true })
|
|
36
|
+
await rm(join(overrides, "head.ts"))
|
|
37
|
+
const targetDir = buildTarget({ configPath, sourceDir })
|
|
38
|
+
|
|
39
|
+
expect(read(targetDir, "components/Footer.tsx")).toBe("default")
|
|
40
|
+
expect(existsSync(join(targetDir, "routes/about.tsx"))).toBe(false)
|
|
41
|
+
expect(read(targetDir, "head.ts")).toBe("head")
|
|
42
|
+
}))
|
|
43
|
+
|
|
44
|
+
it("keeps generated entries between runs", () =>
|
|
45
|
+
temporaryDirectoryTask(async root => {
|
|
46
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
47
|
+
const targetDir = buildTarget({ configPath, sourceDir })
|
|
48
|
+
await put(join(targetDir, "cache/changelog/entry.md"), "cached")
|
|
49
|
+
|
|
50
|
+
buildTarget({ configPath, sourceDir })
|
|
51
|
+
|
|
52
|
+
expect(read(targetDir, "cache/changelog/entry.md")).toBe("cached")
|
|
53
|
+
}))
|
|
54
|
+
|
|
55
|
+
it("links node_modules to livemark's dependencies and repairs a stale link", () =>
|
|
56
|
+
temporaryDirectoryTask(async root => {
|
|
57
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
58
|
+
const link = join(targetDirFor(configPath), "node_modules")
|
|
59
|
+
const dependencyDir = realpathSync(join(root, "livemark/node_modules"))
|
|
60
|
+
|
|
61
|
+
buildTarget({ configPath, sourceDir })
|
|
62
|
+
expect(readlinkSync(link)).toBe(dependencyDir)
|
|
63
|
+
|
|
64
|
+
await rm(link)
|
|
65
|
+
await mkdir(join(root, "elsewhere"))
|
|
66
|
+
await symlink(join(root, "elsewhere"), link, "junction")
|
|
67
|
+
buildTarget({ configPath, sourceDir })
|
|
68
|
+
|
|
69
|
+
expect(readlinkSync(link)).toBe(dependencyDir)
|
|
70
|
+
expect(existsSync(join(root, "elsewhere"))).toBe(true)
|
|
71
|
+
}))
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
async function createFixture(root: string) {
|
|
75
|
+
const sourceDir = join(root, "livemark/source")
|
|
76
|
+
await put(join(sourceDir, "components/Footer.tsx"), "default")
|
|
77
|
+
await put(join(sourceDir, "components/Header.tsx"), "default")
|
|
78
|
+
await put(join(sourceDir, "routes/index.tsx"), "index")
|
|
79
|
+
await put(join(sourceDir, "helpers/util.ts"), "util")
|
|
80
|
+
await put(join(sourceDir, "helpers/util.unit.ts"), "test")
|
|
81
|
+
await put(join(sourceDir, "head.ts"), "head")
|
|
82
|
+
await mkdir(join(root, "livemark/node_modules"))
|
|
83
|
+
const configPath = join(root, "project/livemark.config.ts")
|
|
84
|
+
await put(configPath, "export default {}")
|
|
85
|
+
return { configPath, sourceDir }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function put(path: string, content: string) {
|
|
89
|
+
await mkdir(dirname(path), { recursive: true })
|
|
90
|
+
await writeFile(path, content)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function read(dir: string, path: string) {
|
|
94
|
+
return readFileSync(join(dir, path), "utf-8")
|
|
95
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { rmSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { targetDirFor } from "./build.ts"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wipe the project's target dir so the next run rebuilds it from scratch.
|
|
7
|
+
*/
|
|
8
|
+
export function clearTarget(configPath: string) {
|
|
9
|
+
const targetDir = targetDirFor(configPath)
|
|
10
|
+
// Unlink the dependency link first so the recursive remove cannot reach the store.
|
|
11
|
+
rmSync(join(targetDir, "node_modules"), { force: true })
|
|
12
|
+
rmSync(targetDir, { recursive: true, force: true })
|
|
13
|
+
return targetDir
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { mkdir, symlink, writeFile } from "node:fs/promises"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { temporaryDirectoryTask } from "tempy"
|
|
5
|
+
import { describe, expect, it } from "vite-plus/test"
|
|
6
|
+
import { targetDirFor } from "./build.ts"
|
|
7
|
+
import { clearTarget } from "./clear.ts"
|
|
8
|
+
|
|
9
|
+
describe("clearTarget", () => {
|
|
10
|
+
it("removes the target dir without following its dependency link", () =>
|
|
11
|
+
temporaryDirectoryTask(async root => {
|
|
12
|
+
const configPath = join(root, "livemark.config.ts")
|
|
13
|
+
const targetDir = targetDirFor(configPath)
|
|
14
|
+
await mkdir(join(targetDir, "cache"), { recursive: true })
|
|
15
|
+
await writeFile(join(targetDir, "cache", "entry.md"), "cached")
|
|
16
|
+
await mkdir(join(root, "deps"))
|
|
17
|
+
await writeFile(join(root, "deps", "kept.js"), "")
|
|
18
|
+
await symlink(join(root, "deps"), join(targetDir, "node_modules"), "junction")
|
|
19
|
+
|
|
20
|
+
expect(clearTarget(configPath)).toBe(targetDir)
|
|
21
|
+
expect(existsSync(targetDir)).toBe(false)
|
|
22
|
+
expect(existsSync(join(root, "deps", "kept.js"))).toBe(true)
|
|
23
|
+
expect(() => clearTarget(configPath)).not.toThrow()
|
|
24
|
+
}))
|
|
25
|
+
})
|
package/source/commands/build.ts
CHANGED
|
@@ -9,7 +9,6 @@ const configFile = join(import.meta.dirname, "..", "..", "source", "vite.config.
|
|
|
9
9
|
*/
|
|
10
10
|
export const build = new Command("build")
|
|
11
11
|
.description("Build the site for production")
|
|
12
|
-
.option("--clear", "Wipe and fully rebuild the runtime target dir")
|
|
13
12
|
.action(async () => {
|
|
14
13
|
const builder = await createBuilder({ configFile })
|
|
15
14
|
await builder.buildApp()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from "commander"
|
|
2
|
+
import pc from "picocolors"
|
|
3
|
+
import { loadConfig } from "../actions/config/load.ts"
|
|
4
|
+
import { clearTarget } from "../actions/target/clear.ts"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wipe the generated `.livemark/.target/` mount.
|
|
8
|
+
*/
|
|
9
|
+
export const clear = new Command("clear")
|
|
10
|
+
.description("Wipe the generated .livemark/.target/ mount")
|
|
11
|
+
.action(async () => {
|
|
12
|
+
const config = await loadConfig()
|
|
13
|
+
const targetDir = clearTarget(config.configPath)
|
|
14
|
+
console.log(`Cleared ${pc.dim(targetDir)}`)
|
|
15
|
+
})
|
|
@@ -1,79 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
statSync,
|
|
7
|
-
writeFileSync,
|
|
8
|
-
} from "node:fs"
|
|
9
|
-
import { dirname, join } from "node:path"
|
|
1
|
+
import { join } from "node:path"
|
|
2
|
+
import { autocompleteMultiselect, cancel, isCancel } from "@clack/prompts"
|
|
10
3
|
import { Command } from "commander"
|
|
11
4
|
import pc from "picocolors"
|
|
12
|
-
import {
|
|
5
|
+
import { copyEscape } from "../actions/escape/copy.ts"
|
|
6
|
+
import { listEscapes } from "../actions/escape/list.ts"
|
|
13
7
|
|
|
14
|
-
const
|
|
8
|
+
const sourceRoot = join(import.meta.dirname, "..", "..", "source")
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
|
-
*
|
|
11
|
+
* Pick, list or copy escapable livemark files into `.livemark/`.
|
|
18
12
|
*
|
|
19
|
-
* Without an argument,
|
|
20
|
-
*
|
|
13
|
+
* Without an argument, opens a searchable picker in a terminal and prints every
|
|
14
|
+
* file you can shadow otherwise (or with `--list`). With paths, copies each
|
|
15
|
+
* default from the livemark package into `.livemark/<path>`; fails if a
|
|
21
16
|
* destination already exists.
|
|
22
17
|
*/
|
|
23
18
|
export const escape = new Command("escape")
|
|
24
|
-
.description("
|
|
19
|
+
.description("Pick, list or copy escapable livemark files into .livemark/")
|
|
25
20
|
.argument(
|
|
26
|
-
"[
|
|
27
|
-
"
|
|
21
|
+
"[paths...]",
|
|
22
|
+
"paths relative to .livemark/ (e.g. components/Footer.tsx, client.tsx)",
|
|
28
23
|
)
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
.option("-l, --list", "print every escapable file instead of opening the picker")
|
|
25
|
+
.action(async (paths: string[], options: { list?: boolean }) => {
|
|
26
|
+
const root = process.cwd()
|
|
27
|
+
if (paths.length) return copyPaths(root, paths)
|
|
28
|
+
|
|
29
|
+
const escapes = listEscapes({ root, sourceRoot })
|
|
30
|
+
if (options.list || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
31
|
+
for (const escape of escapes) console.log(escape.path)
|
|
32
32
|
console.log(pc.dim("\nRun 'livemark escape <path>' to copy a file into .livemark/"))
|
|
33
33
|
return
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const src = join(defaultsRoot, relPath)
|
|
50
|
-
if (!existsSync(src) || !statSync(src).isFile()) {
|
|
51
|
-
console.error(pc.red(`Error: '${relPath}' is not a livemark file`))
|
|
52
|
-
process.exit(1)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const dest = join(process.cwd(), ".livemark", relPath)
|
|
56
|
-
if (existsSync(dest)) {
|
|
57
|
-
console.error(pc.red(`Error: .livemark/${relPath} already exists`))
|
|
58
|
-
process.exit(1)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
mkdirSync(dirname(dest), { recursive: true })
|
|
62
|
-
writeFileSync(dest, readFileSync(src))
|
|
63
|
-
console.log(`Copied ${pc.dim(relPath)} → ${pc.green(`.livemark/${relPath}`)}`)
|
|
36
|
+
const selected = await autocompleteMultiselect({
|
|
37
|
+
message: "Select files to escape into .livemark/",
|
|
38
|
+
placeholder: "Type to search...",
|
|
39
|
+
options: escapes.map(escape => ({
|
|
40
|
+
value: escape.path,
|
|
41
|
+
label: escape.path,
|
|
42
|
+
hint: escape.escaped ? "already escaped" : undefined,
|
|
43
|
+
disabled: escape.escaped,
|
|
44
|
+
})),
|
|
45
|
+
required: false,
|
|
46
|
+
})
|
|
47
|
+
if (isCancel(selected)) return cancel("Nothing copied")
|
|
48
|
+
copyPaths(root, selected)
|
|
64
49
|
})
|
|
65
50
|
|
|
66
|
-
function
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
51
|
+
function copyPaths(root: string, paths: string[]) {
|
|
52
|
+
for (const path of paths) {
|
|
53
|
+
try {
|
|
54
|
+
copyEscape({ root, sourceRoot, path })
|
|
55
|
+
console.log(`Copied ${pc.dim(path)} → ${pc.green(`.livemark/${path}`)}`)
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
58
|
+
console.error(pc.red(`Error: ${message}`))
|
|
59
|
+
process.exitCode = 1
|
|
73
60
|
}
|
|
74
61
|
}
|
|
75
|
-
for (const file of ESCAPABLE_FILES) {
|
|
76
|
-
if (existsSync(join(defaultsRoot, file))) files.push(file)
|
|
77
|
-
}
|
|
78
|
-
return files.sort()
|
|
79
62
|
}
|
|
@@ -9,7 +9,6 @@ const configFile = join(import.meta.dirname, "..", "..", "source", "vite.config.
|
|
|
9
9
|
*/
|
|
10
10
|
export const preview = new Command("preview")
|
|
11
11
|
.description("Preview the production build locally")
|
|
12
|
-
.option("--clear", "Wipe and fully rebuild the runtime target dir")
|
|
13
12
|
.action(async () => {
|
|
14
13
|
const server = await vitePreview({
|
|
15
14
|
configFile,
|
package/source/commands/start.ts
CHANGED
|
@@ -9,7 +9,6 @@ const configFile = join(import.meta.dirname, "..", "..", "source", "vite.config.
|
|
|
9
9
|
*/
|
|
10
10
|
export const start = new Command("start")
|
|
11
11
|
.description("Start a live server")
|
|
12
|
-
.option("--clear", "Wipe and fully rebuild the runtime target dir")
|
|
13
12
|
.action(async () => {
|
|
14
13
|
const server = await createServer({
|
|
15
14
|
configFile,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Renders pre-formatted HTML produced from ANSI escape sequences */
|
|
2
2
|
export function AnsiCode(props: { html: string }) {
|
|
3
3
|
return (
|
|
4
|
-
<figure className="shiki not-prose my-4 relative rounded-
|
|
4
|
+
<figure className="shiki not-prose my-4 relative rounded-card border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar">
|
|
5
5
|
<div className="text-[0.8125rem] py-3.5 overflow-auto">
|
|
6
6
|
<pre className="min-w-full w-max">
|
|
7
7
|
<code
|