@clarify-labs/templates 0.5.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.
Files changed (34) hide show
  1. package/LICENSE +1 -0
  2. package/complete/clarify.ts +94 -0
  3. package/complete/index.html +11 -0
  4. package/complete/public/favicon.svg +11 -0
  5. package/complete/public/logo.svg +11 -0
  6. package/complete/source/en-US/api/embedding.mdx +46 -0
  7. package/complete/source/en-US/api.openapi.json +319 -0
  8. package/complete/source/en-US/changelog.mdx +20 -0
  9. package/complete/source/en-US/guides/navigation.mdx +78 -0
  10. package/complete/source/en-US/guides/writing-content.mdx +102 -0
  11. package/complete/source/en-US/index.mdx +77 -0
  12. package/complete/source/en-US/reference/components.mdx +65 -0
  13. package/complete/source/en-US/reference/configuration.mdx +76 -0
  14. package/complete/source/zh-CN/guides/writing-content.mdx +30 -0
  15. package/complete/source/zh-CN/index.mdx +39 -0
  16. package/minimal/clarify.ts +9 -0
  17. package/minimal/index.html +11 -0
  18. package/minimal/public/favicon.svg +11 -0
  19. package/minimal/public/logo.svg +11 -0
  20. package/minimal/source/index.mdx +7 -0
  21. package/output/index.cjs +87 -0
  22. package/output/index.cjs.map +1 -0
  23. package/output/index.d.ts +22 -0
  24. package/output/index.js +80 -0
  25. package/output/index.js.map +1 -0
  26. package/package.json +42 -0
  27. package/standard/clarify.ts +57 -0
  28. package/standard/index.html +11 -0
  29. package/standard/public/favicon.svg +11 -0
  30. package/standard/public/logo.svg +11 -0
  31. package/standard/source/changelog.mdx +20 -0
  32. package/standard/source/guides/navigation.mdx +70 -0
  33. package/standard/source/guides/writing-content.mdx +102 -0
  34. package/standard/source/index.mdx +77 -0
@@ -0,0 +1,102 @@
1
+ ---
2
+ title: Writing content
3
+ description: Learn the Clarify MDX patterns included in the starter template.
4
+ ---
5
+
6
+ # Writing content
7
+
8
+ Clarify pages are MDX files. You can start with plain Markdown and add components when a page needs richer structure.
9
+
10
+ ## Frontmatter
11
+
12
+ Use frontmatter to control page metadata.
13
+
14
+ ```mdx title="page.mdx"
15
+ ---
16
+ title: Writing content
17
+ description: Explain how authors write documentation.
18
+ keywords:
19
+ - mdx
20
+ - docs
21
+ ---
22
+
23
+ # Writing content
24
+ ```
25
+
26
+ ## Callouts
27
+
28
+ <Callout title="Use callouts for important notes">
29
+ Callouts are useful for setup requirements, warnings, migration notes, and product constraints.
30
+ </Callout>
31
+
32
+ <Note>
33
+ `Note` is a lightweight MDX primitive for contextual information inside a section.
34
+ </Note>
35
+
36
+ ## Cards
37
+
38
+ <CardGroup cols={3}>
39
+ <Card title="Concept" icon="Lightbulb">
40
+ Explain the idea behind a feature.
41
+ </Card>
42
+ <Card title="Guide" icon="Route">
43
+ Walk users through a workflow step by step.
44
+ </Card>
45
+ <Card title="Reference" icon="Library">
46
+ Document exact options, props, commands, or API shapes.
47
+ </Card>
48
+ </CardGroup>
49
+
50
+ ## Code groups
51
+
52
+ Use code groups when users need package-manager or framework variants.
53
+
54
+ <CodeGroup title="Install dependencies">
55
+
56
+ ```bash title="pnpm"
57
+ pnpm add @clarify-labs/cli
58
+ ```
59
+
60
+ ```bash title="npm"
61
+ npm install @clarify-labs/cli
62
+ ```
63
+
64
+ ```bash title="bun"
65
+ bun add @clarify-labs/cli
66
+ ```
67
+
68
+ </CodeGroup>
69
+
70
+ ## Properties
71
+
72
+ Use properties for configuration, component props, environment variables, or response fields.
73
+
74
+ <Properties>
75
+ <Property name="title" type="string">
76
+ Site title used in the header and SEO metadata.
77
+ </Property>
78
+ <Property name="description" type="string">
79
+ Default description used for pages without a custom description.
80
+ </Property>
81
+ <Property name="tabs" type="ClarifyTabsConfig">
82
+ Top-level navigation areas. Each tab owns its sidebar groups and pages.
83
+ </Property>
84
+ </Properties>
85
+
86
+ ## Two-column layout
87
+
88
+ <Row>
89
+ <Col>
90
+ ### Explain the workflow
91
+
92
+ Put prose, diagrams, and decisions in the left column. This keeps the guide readable.
93
+ </Col>
94
+ <Col sticky>
95
+ ```ts title="example.ts"
96
+ export const docs = {
97
+ format: 'MDX',
98
+ api: 'OpenAPI',
99
+ }
100
+ ```
101
+ </Col>
102
+ </Row>
@@ -0,0 +1,77 @@
1
+ ---
2
+ title: Overview
3
+ description: A complete starter showing the core Clarify features you can edit.
4
+ keywords:
5
+ - documentation
6
+ - mdx
7
+ - openapi
8
+ ---
9
+
10
+ # Build documentation with Clarify
11
+
12
+ This starter includes a working site configuration, navigation, MDX pages, built-in components, and an OpenAPI reference. Use it as a checklist: replace the sample content with your product docs and keep the pieces you need.
13
+
14
+ <Callout title="Starter tip">
15
+ All editable content lives in `{{contentDir}}`. Start with `clarify.ts` for site structure, then edit the MDX and OpenAPI files.
16
+ </Callout>
17
+
18
+ ## What is included
19
+
20
+ <CardGroup cols={2}>
21
+ <Card title="Site configuration" icon="Settings2" href="/reference/configuration">
22
+ Navbar links, footer links, banner, theme tokens, tabs, sidebar groups, and OpenAPI navigation.
23
+ </Card>
24
+ <Card title="MDX authoring" icon="PenLine" href="/guides/writing-content">
25
+ Frontmatter, headings, tables, cards, callouts, code groups, properties, and layout primitives.
26
+ </Card>
27
+ <Card title="OpenAPI reference" icon="Webhook" href="/api">
28
+ A complete `.openapi.json` file that renders a full API reference page automatically.
29
+ </Card>
30
+ <Card title="Embedded endpoints" icon="CodeXml" href="/api/embedding">
31
+ Use `OpenApiOperation` inside MDX when an API operation belongs next to a guide step.
32
+ </Card>
33
+ </CardGroup>
34
+
35
+ ## Quick edits
36
+
37
+ 1. Update `clarify.ts` with your product name, logo, theme, navbar, footer, tabs, and sidebar pages.
38
+ 2. Replace pages under `{{contentDir}}/guides` and `{{contentDir}}/reference` with your real documentation.
39
+ 3. Replace `{{contentDir}}/api.openapi.json` with your API description.
40
+ 4. Run `pnpm dev` to preview and `pnpm build` to create the static site.
41
+
42
+ ## Example workflow
43
+
44
+ <Row>
45
+ <Col>
46
+ ### Write docs in MDX
47
+
48
+ Use Markdown for normal documentation and MDX components for richer product explanations.
49
+
50
+ <Properties>
51
+ <Property name="Frontmatter" type="YAML">
52
+ Page metadata such as `title`, `description`, and `keywords`.
53
+ </Property>
54
+ <Property name="Navigation" type="clarify.ts">
55
+ Tabs, groups, page links, icons, and OpenAPI entries.
56
+ </Property>
57
+ <Property name="API specs" type="OpenAPI 3.x">
58
+ `.openapi.json`, `.openapi.yaml`, and `.openapi.yml` files become API reference pages.
59
+ </Property>
60
+ </Properties>
61
+ </Col>
62
+ <Col sticky>
63
+ ```ts title="clarify.ts"
64
+ import { defineConfig } from '@clarify-labs/cli'
65
+
66
+ export default defineConfig({
67
+ title: 'Your Docs',
68
+ tabs: [
69
+ {
70
+ tab: 'Docs',
71
+ pages: 'FileTree',
72
+ },
73
+ ],
74
+ })
75
+ ```
76
+ </Col>
77
+ </Row>
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: MDX components
3
+ description: Examples of built-in MDX components available in Clarify pages.
4
+ ---
5
+
6
+ # MDX components
7
+
8
+ This page shows the built-in components available in MDX pages.
9
+
10
+ ## Cards
11
+
12
+ <CardGroup cols={2}>
13
+ <Card title="Linked card" icon="ArrowRight" href="/guides/writing-content">
14
+ Cards can link to internal or external destinations.
15
+ </Card>
16
+ <Card title="Static card" icon="Box">
17
+ Cards can also be used as static feature summaries.
18
+ </Card>
19
+ </CardGroup>
20
+
21
+ ## Callout and note
22
+
23
+ <Callout title="Callout">
24
+ Use this for important guidance that should stand out from normal prose.
25
+ </Callout>
26
+
27
+ <Note>
28
+ Use notes for lightweight contextual tips.
29
+ </Note>
30
+
31
+ ## Properties
32
+
33
+ <Properties>
34
+ <Property name="children" type="ReactNode">
35
+ The content rendered inside the component.
36
+ </Property>
37
+ <Property name="title" type="string">
38
+ A short label used by cards, callouts, or headings.
39
+ </Property>
40
+ <Property name="icon" type="string">
41
+ Icon names are resolved from Lucide icons.
42
+ </Property>
43
+ </Properties>
44
+
45
+ ## Code
46
+
47
+ ```ts title="typed-example.ts"
48
+ type Feature = {
49
+ name: string
50
+ stable: boolean
51
+ }
52
+
53
+ export const feature: Feature = {
54
+ name: 'Clarify starter',
55
+ stable: true,
56
+ }
57
+ ```
58
+
59
+ ## OpenAPI endpoints
60
+
61
+ Use `OpenApiOperation` to embed a single operation from the site’s OpenAPI spec:
62
+
63
+ <OpenApiOperation specPath="/api" path="/v1/projects" method="get" />
64
+
65
+ <OpenApiOperation specPath="/api" path="/v1/projects" method="post" />
@@ -0,0 +1,76 @@
1
+ ---
2
+ title: Configuration
3
+ description: A practical overview of the Clarify configuration fields used by this starter.
4
+ ---
5
+
6
+ # Configuration
7
+
8
+ The `clarify.ts` file controls the site shell, theme, navigation, and OpenAPI entries.
9
+
10
+ <Properties>
11
+ <Property name="title" type="string">
12
+ Product or documentation site name.
13
+ </Property>
14
+ <Property name="description" type="string">
15
+ Default SEO description for the site.
16
+ </Property>
17
+ <Property name="logo" type="string | { light?: string; dark?: string }">
18
+ Path to a logo in `public` or an absolute URL.
19
+ </Property>
20
+ <Property name="favicon" type="string | { light?: string; dark?: string }">
21
+ Browser favicon path.
22
+ </Property>
23
+ <Property name="banner" type="{ content: string; dismissible?: boolean }">
24
+ Optional announcement banner shown above the site.
25
+ </Property>
26
+ <Property name="theme" type="ClarifyThemeConfig">
27
+ Preset, color tokens, radius tokens, and layout settings.
28
+ </Property>
29
+ <Property name="navbar" type="{ links?: ClarifyNavbarLink[] }">
30
+ Top navigation links. Use `external: true` for off-site links.
31
+ </Property>
32
+ <Property name="footer" type="ClarifyFooterConfig">
33
+ Footer links, social links, and copyright text.
34
+ </Property>
35
+ <Property name="tabs" type="ClarifyTabsConfig">
36
+ Top-level navigation tabs and sidebar pages.
37
+ </Property>
38
+ <Property name="i18n" type="ClarifyI18nConfig">
39
+ Optional locale configuration for multilingual sites.
40
+ </Property>
41
+ </Properties>
42
+
43
+ ## Theme example
44
+
45
+ ```ts title="clarify.ts"
46
+ theme: {
47
+ preset: 'default',
48
+ tokens: {
49
+ colors: {
50
+ primary: '#2563eb',
51
+ accent: '#7c3aed',
52
+ background: '#ffffff',
53
+ foreground: '#111827',
54
+ },
55
+ radius: {
56
+ lg: '0.75rem',
57
+ xl: '1rem',
58
+ },
59
+ },
60
+ }
61
+ ```
62
+
63
+ ## i18n example
64
+
65
+ ```ts title="clarify.ts"
66
+ i18n: {
67
+ defaultLocale: 'en-US',
68
+ missing: 'fallback',
69
+ locales: [
70
+ { code: 'en-US', label: 'English', dir: 'ltr' },
71
+ { code: 'zh-CN', label: '简体中文', dir: 'ltr' },
72
+ ],
73
+ }
74
+ ```
75
+
76
+ When i18n is enabled, place content under locale folders such as `{{contentDir}}/en-US/index.mdx` and `{{contentDir}}/zh-CN/index.mdx`.
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: 编写内容
3
+ description: 学习如何在 Clarify 模板中组织多语言 MDX 页面。
4
+ ---
5
+
6
+ # 编写内容
7
+
8
+ Clarify 会根据 `clarify.ts` 里的 `i18n.locales` 扫描语言目录。默认语言 `en-US` 直接渲染在根路径,其他语言会带上语言前缀,例如 `/zh-CN/guides/writing-content`。
9
+
10
+ ## 翻译一个页面
11
+
12
+ 如果英文页面位于:
13
+
14
+ ```txt
15
+ {{contentDir}}/en-US/guides/writing-content.mdx
16
+ ```
17
+
18
+ 对应中文页面应放在:
19
+
20
+ ```txt
21
+ {{contentDir}}/zh-CN/guides/writing-content.mdx
22
+ ```
23
+
24
+ ## 渐进式翻译
25
+
26
+ 本模板设置了 `missing: 'fallback'`。当某个页面还没有中文版本时,Clarify 会临时使用默认语言页面,方便你逐步完成翻译。
27
+
28
+ <Note>
29
+ 页面 frontmatter 也可以按语言分别维护,例如中文页面使用中文 `title` 和 `description`。
30
+ </Note>
@@ -0,0 +1,39 @@
1
+ ---
2
+ title: 概览
3
+ description: 一个展示 Clarify i18n 工作流的中文示例页面。
4
+ keywords:
5
+ - 文档
6
+ - i18n
7
+ - mdx
8
+ ---
9
+
10
+ # 使用 Clarify 构建多语言文档
11
+
12
+ 这个页面演示了 Clarify 的原生 i18n 目录约定。默认语言内容位于 `{{contentDir}}/en-US`,中文内容位于 `{{contentDir}}/zh-CN`。
13
+
14
+ <Callout title="i18n 提示">
15
+ 当前模板只翻译了首页和写作指南,其他页面会根据 `missing: 'fallback'` 自动回退到英文内容。
16
+ </Callout>
17
+
18
+ ## 目录结构
19
+
20
+ ```txt
21
+ {{contentDir}}/
22
+ en-US/
23
+ index.mdx
24
+ guides/writing-content.mdx
25
+ zh-CN/
26
+ index.mdx
27
+ guides/writing-content.mdx
28
+ ```
29
+
30
+ ## 下一步
31
+
32
+ <CardGroup cols={2}>
33
+ <Card title="编辑中文内容" icon="Languages" href="/zh-CN/guides/writing-content">
34
+ 在 `{{contentDir}}/zh-CN` 中添加或更新中文页面。
35
+ </Card>
36
+ <Card title="保留回退内容" icon="GitBranch" href="/reference/configuration">
37
+ 未翻译页面会使用默认语言内容,适合渐进式翻译。
38
+ </Card>
39
+ </CardGroup>
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from '@clarify-labs/cli'
2
+
3
+ export default defineConfig({
4
+ title: 'Clarify Docs',
5
+ description: 'Documentation powered by Clarify',
6
+ logo: '/logo.svg',
7
+ favicon: '/favicon.svg',
8
+ theme: { preset: 'default' },
9
+ })
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Clarify Docs</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="279px" height="200px" viewBox="0 0 279 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
+ <title>clarify</title>
4
+ <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5
+ <g id="clarify" transform="translate(-0, 0.0054)">
6
+ <path d="M44.867217,0.0054376148 C130.715579,1.00591567 200,70.9091172 200,156.994562 L199.986321,155.127494 C199.384888,180.009127 179.026368,199.994562 154,199.994562 L46,199.994562 C20.5949015,199.994562 0,179.399661 0,153.994562 L0,45.9945624 C0,21.0975659 19.7793434,0.820282524 44.4815926,0.0191499894 Z" fill="#00D492"></path>
7
+ <path d="M123.867217,0.0054376148 C209.715579,1.00591567 279,70.9091172 279,156.994562 L278.986321,155.127494 C278.384888,180.009127 258.026368,199.994562 233,199.994562 L125,199.994562 C99.5949015,199.994562 79,179.399661 79,153.994562 L79,45.9945624 C79,21.0975659 98.7793434,0.820282524 123.481593,0.0191499894 Z" id="clarify-right" fill-opacity="0.6" fill="#00F6C9" transform="translate(179, 100) rotate(-90) translate(-179, -100)"></path>
8
+ <path d="M199.979267,154.416906 L199.984438,155.157125 L199.962366,155.872459 C198.977251,180.406837 178.776104,199.994562 154,199.994562 L124.372438,199.994125 L124.249053,199.994081 C99.6939678,199.603921 79.8122712,179.973634 79.01915,155.518407 L79.0054376,155.132783 C79.5830823,105.566626 103.129768,61.5221348 139.498462,33.1463428 C175.69419,61.3871018 199.186123,105.143863 199.979267,154.416906 Z" id="形状结合" fill="#00F692"></path>
9
+ </g>
10
+ </g>
11
+ </svg>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="279px" height="200px" viewBox="0 0 279 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
+ <title>clarify</title>
4
+ <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5
+ <g id="clarify" transform="translate(-0, 0.0054)">
6
+ <path d="M44.867217,0.0054376148 C130.715579,1.00591567 200,70.9091172 200,156.994562 L199.986321,155.127494 C199.384888,180.009127 179.026368,199.994562 154,199.994562 L46,199.994562 C20.5949015,199.994562 0,179.399661 0,153.994562 L0,45.9945624 C0,21.0975659 19.7793434,0.820282524 44.4815926,0.0191499894 Z" fill="#00D492"></path>
7
+ <path d="M123.867217,0.0054376148 C209.715579,1.00591567 279,70.9091172 279,156.994562 L278.986321,155.127494 C278.384888,180.009127 258.026368,199.994562 233,199.994562 L125,199.994562 C99.5949015,199.994562 79,179.399661 79,153.994562 L79,45.9945624 C79,21.0975659 98.7793434,0.820282524 123.481593,0.0191499894 Z" id="clarify-right" fill-opacity="0.6" fill="#00F6C9" transform="translate(179, 100) rotate(-90) translate(-179, -100)"></path>
8
+ <path d="M199.979267,154.416906 L199.984438,155.157125 L199.962366,155.872459 C198.977251,180.406837 178.776104,199.994562 154,199.994562 L124.372438,199.994125 L124.249053,199.994081 C99.6939678,199.603921 79.8122712,179.973634 79.01915,155.518407 L79.0054376,155.132783 C79.5830823,105.566626 103.129768,61.5221348 139.498462,33.1463428 C175.69419,61.3871018 199.186123,105.143863 199.979267,154.416906 Z" id="形状结合" fill="#00F692"></path>
9
+ </g>
10
+ </g>
11
+ </svg>
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: Welcome
3
+ ---
4
+
5
+ # Welcome to Clarify
6
+
7
+ Start writing your documentation in `{{contentDir}}`.
@@ -0,0 +1,87 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let node_fs = require("node:fs");
3
+ let node_path = require("node:path");
4
+ //#region index.ts
5
+ var supportedTemplates = [
6
+ "minimal",
7
+ "standard",
8
+ "complete"
9
+ ];
10
+ var TemplateConflictError = class extends Error {
11
+ conflicts;
12
+ constructor(conflicts) {
13
+ super(`[clarify] Init target has conflicting files:\n${conflicts.map((file) => ` - ${file}`).join("\n")}\nUse --force to overwrite them.`);
14
+ this.conflicts = conflicts;
15
+ this.name = "TemplateConflictError";
16
+ }
17
+ };
18
+ function resolveTemplate(template) {
19
+ const selectedTemplate = template ?? "standard";
20
+ if (supportedTemplates.includes(selectedTemplate)) return selectedTemplate;
21
+ throw new Error(`[clarify] Unknown template "${selectedTemplate}". Available templates: ${supportedTemplates.join(", ")}.`);
22
+ }
23
+ function getTemplateDirectory(templatesDirectory, template) {
24
+ return (0, node_path.resolve)(templatesDirectory, template);
25
+ }
26
+ function renderTemplate(content, variables) {
27
+ return content.replaceAll("{{contentDir}}", variables.contentDir);
28
+ }
29
+ function toTemplateTargetPath(relativePath, contentDir) {
30
+ return relativePath.replace(/^source(?=\/|$)/, contentDir);
31
+ }
32
+ function copyTemplateDirectory(templateDirectory, targetDirectory, options) {
33
+ const created = [];
34
+ const variables = {
35
+ contentDir: options.contentDir,
36
+ ...options.variables
37
+ };
38
+ if (options.force !== true) {
39
+ const conflicts = findTemplateConflicts(templateDirectory, targetDirectory, options.contentDir);
40
+ if (conflicts.length > 0) throw new TemplateConflictError(conflicts);
41
+ }
42
+ copyDirectory(templateDirectory, templateDirectory, targetDirectory, options.contentDir, variables, created);
43
+ return { created };
44
+ }
45
+ function findTemplateConflicts(templateDirectory, targetDirectory, contentDir) {
46
+ const conflicts = [];
47
+ collectTemplateConflicts(templateDirectory, templateDirectory, targetDirectory, contentDir, conflicts);
48
+ return conflicts;
49
+ }
50
+ function collectTemplateConflicts(templateDirectory, sourceDirectory, targetDirectory, contentDir, conflicts) {
51
+ for (const entry of (0, node_fs.readdirSync)(sourceDirectory, { withFileTypes: true })) {
52
+ const sourcePath = (0, node_path.resolve)(sourceDirectory, entry.name);
53
+ const targetRelativePath = toTemplateTargetPath((0, node_path.relative)(templateDirectory, sourcePath), contentDir);
54
+ const targetPath = (0, node_path.resolve)(targetDirectory, targetRelativePath);
55
+ if (entry.isDirectory()) {
56
+ collectTemplateConflicts(templateDirectory, sourcePath, targetDirectory, contentDir, conflicts);
57
+ continue;
58
+ }
59
+ if (entry.isFile() && (0, node_fs.existsSync)(targetPath)) conflicts.push(targetRelativePath);
60
+ }
61
+ }
62
+ function copyDirectory(templateDirectory, sourceDirectory, targetDirectory, contentDir, variables, created) {
63
+ for (const entry of (0, node_fs.readdirSync)(sourceDirectory, { withFileTypes: true })) {
64
+ const sourcePath = (0, node_path.resolve)(sourceDirectory, entry.name);
65
+ const targetRelativePath = toTemplateTargetPath((0, node_path.relative)(templateDirectory, sourcePath), contentDir);
66
+ const targetPath = (0, node_path.resolve)(targetDirectory, targetRelativePath);
67
+ if (entry.isDirectory()) {
68
+ copyDirectory(templateDirectory, sourcePath, targetDirectory, contentDir, variables, created);
69
+ continue;
70
+ }
71
+ if (!entry.isFile()) continue;
72
+ const content = renderTemplate((0, node_fs.readFileSync)(sourcePath, "utf-8"), variables);
73
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(targetPath), { recursive: true });
74
+ (0, node_fs.writeFileSync)(targetPath, content, "utf-8");
75
+ created.push(targetRelativePath);
76
+ }
77
+ }
78
+ //#endregion
79
+ exports.TemplateConflictError = TemplateConflictError;
80
+ exports.copyTemplateDirectory = copyTemplateDirectory;
81
+ exports.getTemplateDirectory = getTemplateDirectory;
82
+ exports.renderTemplate = renderTemplate;
83
+ exports.resolveTemplate = resolveTemplate;
84
+ exports.supportedTemplates = supportedTemplates;
85
+ exports.toTemplateTargetPath = toTemplateTargetPath;
86
+
87
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../index.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, relative, resolve } from 'node:path'\n\nexport const supportedTemplates = ['minimal', 'standard', 'complete'] as const\n\nexport type ClarifyTemplate = typeof supportedTemplates[number]\n\nexport type TemplateVariables = {\n contentDir: string\n}\n\nexport type TemplateCopyOptions = {\n contentDir: string\n force?: boolean\n variables?: Partial<TemplateVariables>\n}\n\nexport type TemplateCopyResult = {\n created: string[]\n}\n\nexport class TemplateConflictError extends Error {\n constructor(public readonly conflicts: string[]) {\n super(`[clarify] Init target has conflicting files:\\n${conflicts.map(file => ` - ${file}`).join('\\n')}\\nUse --force to overwrite them.`)\n this.name = 'TemplateConflictError'\n }\n}\n\nexport function resolveTemplate(template?: string): ClarifyTemplate {\n const selectedTemplate = template ?? 'standard'\n if (supportedTemplates.includes(selectedTemplate as ClarifyTemplate)) return selectedTemplate as ClarifyTemplate\n throw new Error(`[clarify] Unknown template \"${selectedTemplate}\". Available templates: ${supportedTemplates.join(', ')}.`)\n}\n\nexport function getTemplateDirectory(templatesDirectory: string, template: ClarifyTemplate): string {\n return resolve(templatesDirectory, template)\n}\n\nexport function renderTemplate(content: string, variables: TemplateVariables): string {\n return content.replaceAll('{{contentDir}}', variables.contentDir)\n}\n\nexport function toTemplateTargetPath(relativePath: string, contentDir: string): string {\n return relativePath.replace(/^source(?=\\/|$)/, contentDir)\n}\n\nexport function copyTemplateDirectory(templateDirectory: string, targetDirectory: string, options: TemplateCopyOptions): TemplateCopyResult {\n const created: string[] = []\n const variables = {\n contentDir: options.contentDir,\n ...options.variables,\n }\n\n if (options.force !== true) {\n const conflicts = findTemplateConflicts(templateDirectory, targetDirectory, options.contentDir)\n if (conflicts.length > 0) throw new TemplateConflictError(conflicts)\n }\n\n copyDirectory(templateDirectory, templateDirectory, targetDirectory, options.contentDir, variables, created)\n\n return { created }\n}\n\nfunction findTemplateConflicts(templateDirectory: string, targetDirectory: string, contentDir: string): string[] {\n const conflicts: string[] = []\n collectTemplateConflicts(templateDirectory, templateDirectory, targetDirectory, contentDir, conflicts)\n return conflicts\n}\n\nfunction collectTemplateConflicts(templateDirectory: string, sourceDirectory: string, targetDirectory: string, contentDir: string, conflicts: string[]): void {\n for (const entry of readdirSync(sourceDirectory, { withFileTypes: true })) {\n const sourcePath = resolve(sourceDirectory, entry.name)\n const sourceRelativePath = relative(templateDirectory, sourcePath)\n const targetRelativePath = toTemplateTargetPath(sourceRelativePath, contentDir)\n const targetPath = resolve(targetDirectory, targetRelativePath)\n\n if (entry.isDirectory()) {\n collectTemplateConflicts(templateDirectory, sourcePath, targetDirectory, contentDir, conflicts)\n continue\n }\n\n if (entry.isFile() && existsSync(targetPath)) conflicts.push(targetRelativePath)\n }\n}\n\nfunction copyDirectory(templateDirectory: string, sourceDirectory: string, targetDirectory: string, contentDir: string, variables: TemplateVariables, created: string[]): void {\n for (const entry of readdirSync(sourceDirectory, { withFileTypes: true })) {\n const sourcePath = resolve(sourceDirectory, entry.name)\n const sourceRelativePath = relative(templateDirectory, sourcePath)\n const targetRelativePath = toTemplateTargetPath(sourceRelativePath, contentDir)\n const targetPath = resolve(targetDirectory, targetRelativePath)\n\n if (entry.isDirectory()) {\n copyDirectory(templateDirectory, sourcePath, targetDirectory, contentDir, variables, created)\n continue\n }\n\n if (!entry.isFile()) continue\n\n const content = renderTemplate(readFileSync(sourcePath, 'utf-8'), variables)\n mkdirSync(dirname(targetPath), { recursive: true })\n writeFileSync(targetPath, content, 'utf-8')\n created.push(targetRelativePath)\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export declare const supportedTemplates: readonly ["minimal", "standard", "complete"];
2
+ export type ClarifyTemplate = typeof supportedTemplates[number];
3
+ export type TemplateVariables = {
4
+ contentDir: string;
5
+ };
6
+ export type TemplateCopyOptions = {
7
+ contentDir: string;
8
+ force?: boolean;
9
+ variables?: Partial<TemplateVariables>;
10
+ };
11
+ export type TemplateCopyResult = {
12
+ created: string[];
13
+ };
14
+ export declare class TemplateConflictError extends Error {
15
+ readonly conflicts: string[];
16
+ constructor(conflicts: string[]);
17
+ }
18
+ export declare function resolveTemplate(template?: string): ClarifyTemplate;
19
+ export declare function getTemplateDirectory(templatesDirectory: string, template: ClarifyTemplate): string;
20
+ export declare function renderTemplate(content: string, variables: TemplateVariables): string;
21
+ export declare function toTemplateTargetPath(relativePath: string, contentDir: string): string;
22
+ export declare function copyTemplateDirectory(templateDirectory: string, targetDirectory: string, options: TemplateCopyOptions): TemplateCopyResult;
@@ -0,0 +1,80 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, relative, resolve } from "node:path";
3
+ //#region index.ts
4
+ var supportedTemplates = [
5
+ "minimal",
6
+ "standard",
7
+ "complete"
8
+ ];
9
+ var TemplateConflictError = class extends Error {
10
+ conflicts;
11
+ constructor(conflicts) {
12
+ super(`[clarify] Init target has conflicting files:\n${conflicts.map((file) => ` - ${file}`).join("\n")}\nUse --force to overwrite them.`);
13
+ this.conflicts = conflicts;
14
+ this.name = "TemplateConflictError";
15
+ }
16
+ };
17
+ function resolveTemplate(template) {
18
+ const selectedTemplate = template ?? "standard";
19
+ if (supportedTemplates.includes(selectedTemplate)) return selectedTemplate;
20
+ throw new Error(`[clarify] Unknown template "${selectedTemplate}". Available templates: ${supportedTemplates.join(", ")}.`);
21
+ }
22
+ function getTemplateDirectory(templatesDirectory, template) {
23
+ return resolve(templatesDirectory, template);
24
+ }
25
+ function renderTemplate(content, variables) {
26
+ return content.replaceAll("{{contentDir}}", variables.contentDir);
27
+ }
28
+ function toTemplateTargetPath(relativePath, contentDir) {
29
+ return relativePath.replace(/^source(?=\/|$)/, contentDir);
30
+ }
31
+ function copyTemplateDirectory(templateDirectory, targetDirectory, options) {
32
+ const created = [];
33
+ const variables = {
34
+ contentDir: options.contentDir,
35
+ ...options.variables
36
+ };
37
+ if (options.force !== true) {
38
+ const conflicts = findTemplateConflicts(templateDirectory, targetDirectory, options.contentDir);
39
+ if (conflicts.length > 0) throw new TemplateConflictError(conflicts);
40
+ }
41
+ copyDirectory(templateDirectory, templateDirectory, targetDirectory, options.contentDir, variables, created);
42
+ return { created };
43
+ }
44
+ function findTemplateConflicts(templateDirectory, targetDirectory, contentDir) {
45
+ const conflicts = [];
46
+ collectTemplateConflicts(templateDirectory, templateDirectory, targetDirectory, contentDir, conflicts);
47
+ return conflicts;
48
+ }
49
+ function collectTemplateConflicts(templateDirectory, sourceDirectory, targetDirectory, contentDir, conflicts) {
50
+ for (const entry of readdirSync(sourceDirectory, { withFileTypes: true })) {
51
+ const sourcePath = resolve(sourceDirectory, entry.name);
52
+ const targetRelativePath = toTemplateTargetPath(relative(templateDirectory, sourcePath), contentDir);
53
+ const targetPath = resolve(targetDirectory, targetRelativePath);
54
+ if (entry.isDirectory()) {
55
+ collectTemplateConflicts(templateDirectory, sourcePath, targetDirectory, contentDir, conflicts);
56
+ continue;
57
+ }
58
+ if (entry.isFile() && existsSync(targetPath)) conflicts.push(targetRelativePath);
59
+ }
60
+ }
61
+ function copyDirectory(templateDirectory, sourceDirectory, targetDirectory, contentDir, variables, created) {
62
+ for (const entry of readdirSync(sourceDirectory, { withFileTypes: true })) {
63
+ const sourcePath = resolve(sourceDirectory, entry.name);
64
+ const targetRelativePath = toTemplateTargetPath(relative(templateDirectory, sourcePath), contentDir);
65
+ const targetPath = resolve(targetDirectory, targetRelativePath);
66
+ if (entry.isDirectory()) {
67
+ copyDirectory(templateDirectory, sourcePath, targetDirectory, contentDir, variables, created);
68
+ continue;
69
+ }
70
+ if (!entry.isFile()) continue;
71
+ const content = renderTemplate(readFileSync(sourcePath, "utf-8"), variables);
72
+ mkdirSync(dirname(targetPath), { recursive: true });
73
+ writeFileSync(targetPath, content, "utf-8");
74
+ created.push(targetRelativePath);
75
+ }
76
+ }
77
+ //#endregion
78
+ export { TemplateConflictError, copyTemplateDirectory, getTemplateDirectory, renderTemplate, resolveTemplate, supportedTemplates, toTemplateTargetPath };
79
+
80
+ //# sourceMappingURL=index.js.map