@chassis-ui/docs 0.5.0 → 0.5.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ozgur Gunes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Shared Astro layouts, components, and utilities for Chassis documentation sites.
4
4
 
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-blue.svg)](https://www.npmjs.com/package/@chassis-ui/docs) [![npm](https://img.shields.io/npm/v/@chassis-ui/docs.svg)](https://www.npmjs.com/package/@chassis-ui/docs)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Version: 0.5.1](https://img.shields.io/badge/Version-0.5.1-blue.svg)](https://www.npmjs.com/package/@chassis-ui/docs) [![npm](https://img.shields.io/npm/v/@chassis-ui/docs.svg)](https://www.npmjs.com/package/@chassis-ui/docs)
6
6
 
7
7
  ## Overview
8
8
 
@@ -27,7 +27,7 @@ npm install @chassis-ui/docs
27
27
  ```json
28
28
  {
29
29
  "@astrojs/markdown-remark": "^7.0.0",
30
- "@chassis-ui/css": ">=0.3.2",
30
+ "@chassis-ui/css": "^0.5.0-0",
31
31
  "@pagefind/component-ui": "^1.0.0",
32
32
  "@shikijs/transformers": "^4.3.1",
33
33
  "astro": "^7.0.0",
@@ -51,7 +51,7 @@ import SingleLayout from '@chassis-ui/docs/layouts/SingleLayout.astro'
51
51
  import TableOfContents from '@chassis-ui/docs/components/TableOfContents.astro'
52
52
  import ThemeToggler from '@chassis-ui/docs/components/ThemeToggler.astro'
53
53
 
54
- import { generateTOC, processImage } from '@chassis-ui/docs'
54
+ import { generateToc, getStaticImageSize } from '@chassis-ui/docs'
55
55
  ---
56
56
 
57
57
  <SingleLayout title="About" description="…">
@@ -59,6 +59,53 @@ import { generateTOC, processImage } from '@chassis-ui/docs'
59
59
  </SingleLayout>
60
60
  ```
61
61
 
62
+ ## What a site must provide
63
+
64
+ The layouts and components read from the consuming site. A site must provide the
65
+ following, or the build fails.
66
+
67
+ ### Modules behind the `@libs/*` path alias
68
+
69
+ The site maps `@libs/*` to its own `src/libs/*` in `tsconfig.json`, and each module
70
+ exports:
71
+
72
+ | Module | Exports | Read by |
73
+ | ----------------- | ------------------------------------------------------------------------------- | -------------------------------------- |
74
+ | `@libs/config` | `getConfig()`, returning the parsed `config.yml` | most layouts and components |
75
+ | `@libs/content` | `docsPages`, the `docs` collection. `getCalloutByName(name)` and `CalloutName`. | `DocsLayout`, `DocsSidebar`, `Callout` |
76
+ | `@libs/data` | `getData('sidebar')`, and the types `SidebarItem` and `SidebarSubItem` | `DocsSidebar` |
77
+ | `@libs/path` | `getChassisDocsPath(path)` and `getDocsPublicFsPath()` | `DocsLayout`, `Social` |
78
+ | `@libs/clipboard` | `initCopyButtons(selector, getText)` | `Code` |
79
+
80
+ The site also defines a `docs` content collection. Its entry type is used by
81
+ `BaseLayout` and `DocsLayout`.
82
+
83
+ ### Keys in `config.yml`
84
+
85
+ `getConfig()` must return these keys:
86
+
87
+ - `title`, `subtitle`, `description`, `authors`
88
+ - `repo`, `current_version`, `docsPath`
89
+ - `github_org`, `figma_handle`, `x_username`
90
+ - `anchors.min`, `anchors.max`
91
+ - `analytics.google_id`
92
+
93
+ These keys are optional. They say where files live when the site is not built from the
94
+ root of its repository:
95
+
96
+ | Key | Meaning | Example |
97
+ | ------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------- |
98
+ | `sourceDir` | Directory that the `file` props of the source-file shortcodes are relative to, from the build's working directory | `"../css"` |
99
+ | `sourcePath` | The same directory, from the root of the repository | `"packages/css"` |
100
+ | `sitePath` | The site's root, from the root of the repository | `"packages/site"` |
101
+ | `siteBranch` | The branch that "View on GitHub" links to. Defaults to `main`. | `"main"` |
102
+
103
+ The source-file shortcodes are `<ScssDocs>`, `<ScssDocsSimple>`, `<JsDocs>` and
104
+ `<Code filePath>`. They link to their files at the tag `v<current_version>`.
105
+
106
+ A site that validates `config.yml` with `z.object` must add the optional keys to its
107
+ schema. Otherwise they are dropped silently.
108
+
62
109
  ## Exports
63
110
 
64
111
  | Subpath | Contents |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chassis-ui/docs",
3
3
  "description": "Shared Astro components and layouts for Chassis documentation sites.",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -25,7 +25,8 @@
25
25
  "files": [
26
26
  "src",
27
27
  "index.ts",
28
- "README.md"
28
+ "README.md",
29
+ "LICENSE"
29
30
  ],
30
31
  "main": "index.ts",
31
32
  "exports": {
@@ -40,6 +41,9 @@
40
41
  "publishConfig": {
41
42
  "access": "public"
42
43
  },
44
+ "engines": {
45
+ "node": ">=22.12.0"
46
+ },
43
47
  "scripts": {
44
48
  "astro": "astro"
45
49
  },
@@ -56,7 +60,7 @@
56
60
  },
57
61
  "dependencies": {
58
62
  "github-slugger": "^2.0.0",
59
- "image-size": "^2.0.2",
63
+ "image-size": "^2.0.4",
60
64
  "mdast-util-from-markdown": "^2.0.3",
61
65
  "mdast-util-to-string": "^4.0.0",
62
66
  "medium-zoom": "^1.1.0",
@@ -65,8 +69,8 @@
65
69
  "unist-util-visit": "^5.1.0"
66
70
  },
67
71
  "devDependencies": {
68
- "@types/hast": "^3.0.4",
69
- "astro": "^7.0.6",
72
+ "@types/hast": "^3.0.5",
73
+ "astro": "^7.3.5",
70
74
  "unified": "^11.0.5"
71
75
  }
72
76
  }
@@ -26,9 +26,8 @@ const { layout } = Astro.props
26
26
  data-cx-placement="bottom-end"
27
27
  >
28
28
  <Icon name="circle-half-solid" class="theme-icon-active" />
29
- <span
30
- class=`${layout === 'examples' ? 'visually-hidden' : 'lg:d-none ms-xs'}`
31
- id="cxd-theme-text">Toggle theme</span
29
+ <span class=`${layout === 'examples' ? 'visually-hidden' : 'lg:d-none ms-xs'}` id="cxd-theme-text"
30
+ >Toggle theme</span
32
31
  >
33
32
  </button>
34
33
  <ul class="menu" role="menu" aria-label="Theme options">
@@ -15,8 +15,6 @@ const { tooltip = 'This feature is only available in CSS and not in Figma compon
15
15
  Astro.props
16
16
  ---
17
17
 
18
- <span
19
- class="badge lg info smooth mb-md rounded-md"
20
- data-cx-toggle="tooltip"
21
- title={tooltip}>CSS Only</span
18
+ <span class="badge lg info smooth mb-md rounded-md" data-cx-toggle="tooltip" title={tooltip}
19
+ >CSS Only</span
22
20
  >
@@ -1,9 +1,8 @@
1
1
  ---
2
2
  import fs from 'node:fs'
3
- import path from 'node:path'
4
3
  import { transformerNotationDiff, transformerNotationHighlight } from '@shikijs/transformers'
5
- import { getConfig } from '@libs/config'
6
4
  import { highlightCode } from '../../libs/highlight'
5
+ import { getSourceFsPath, getSourceUrl } from '../../libs/source'
7
6
  import Icon from './Icon.astro'
8
7
 
9
8
  interface Props {
@@ -41,7 +40,8 @@ interface Props {
41
40
  */
42
41
  fileMatch?: string
43
42
  /**
44
- * A path to the file containing the code to highlight relative to the root of the repository.
43
+ * A path to the file containing the code to highlight, relative to the `sourceDir` of the
44
+ * site's `config.yml` (the working directory of the build when it is not set).
45
45
  * This takes precedence over the `code` prop.
46
46
  */
47
47
  filePath?: string
@@ -87,9 +87,7 @@ const {
87
87
  hasPreview = false
88
88
  } = Astro.props
89
89
 
90
- const fileUrl = file
91
- ? `${getConfig().repo}/blob/v${getConfig().current_version}/${file}`.replaceAll('\\', '/')
92
- : null
90
+ const fileUrl = file ? getSourceUrl(file) : null
93
91
 
94
92
  // Extract language from multiple possible sources (for markdown code blocks)
95
93
  // Priority: lang prop > data-language attribute > className pattern
@@ -121,7 +119,7 @@ const displayLang = detectedLang
121
119
  : ''
122
120
 
123
121
  let codeToDisplay = filePath
124
- ? fs.readFileSync(path.join(process.cwd(), filePath), 'utf8')
122
+ ? fs.readFileSync(getSourceFsPath(filePath), 'utf8')
125
123
  : Array.isArray(code)
126
124
  ? code.join('\n')
127
125
  : code
@@ -19,8 +19,6 @@ const tooltipText =
19
19
  `This variant${plural ? 's' : ''} exist${plural ? '' : 's'} in the Figma component library with bound design tokens.`
20
20
  ---
21
21
 
22
- <span
23
- class="badge lg primary smooth mb-md rounded-md"
24
- data-cx-toggle="tooltip"
25
- title={tooltipText}>In Figma</span
22
+ <span class="badge lg primary smooth mb-md rounded-md" data-cx-toggle="tooltip" title={tooltipText}
23
+ >In Figma</span
26
24
  >
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import fs from 'node:fs'
3
+ import { getSourceFsPath } from '../../libs/source'
3
4
  import Code from './Code.astro'
4
5
 
5
6
  // Prints everything between `// js-docs-start "name"` and `// js-docs-end "name"`
@@ -11,7 +12,8 @@ interface Props {
11
12
  */
12
13
  name: string
13
14
  /**
14
- * File path that contains the content to display relative to the root of the repository.
15
+ * File path that contains the content to display, relative to the `sourceDir` of the site's
16
+ * `config.yml` (the working directory of the build when it is not set).
15
17
  */
16
18
  file: string
17
19
  }
@@ -27,7 +29,7 @@ if (!name || !file) {
27
29
  let content: string
28
30
 
29
31
  try {
30
- const fileContent = fs.readFileSync(file, 'utf8')
32
+ const fileContent = fs.readFileSync(getSourceFsPath(file), 'utf8')
31
33
 
32
34
  const matches = fileContent.match(
33
35
  new RegExp(`// js-docs-start ${name}\n((?:.|\n)*)// js-docs-end ${name}`, 'm')
@@ -2,6 +2,7 @@
2
2
  import fs from 'node:fs'
3
3
  import path from 'node:path'
4
4
  import * as sass from 'sass'
5
+ import { getSourceFsPath } from '../../libs/source'
5
6
  import Code from './Code.astro'
6
7
 
7
8
  // Prints everything between `// scss-docs-start "name"` and `// scss-docs-end "name"`
@@ -14,7 +15,8 @@ interface Props {
14
15
  */
15
16
  name: string
16
17
  /**
17
- * File path that contains the content to display relative to the root of the repository.
18
+ * File path that contains the content to display, relative to the `sourceDir` of the site's
19
+ * `config.yml` (the working directory of the build when it is not set).
18
20
  */
19
21
  file: string
20
22
  /**
@@ -74,7 +76,7 @@ let content: string
74
76
  let lang: string = 'scss'
75
77
 
76
78
  try {
77
- const fileContent = fs.readFileSync(file, 'utf8')
79
+ const fileContent = fs.readFileSync(getSourceFsPath(file), 'utf8')
78
80
 
79
81
  const matches = fileContent.match(
80
82
  new RegExp(`// scss-docs-start ${name}\n((?:.|\n)*)// scss-docs-end ${name}`, 'm')
@@ -90,7 +92,7 @@ try {
90
92
 
91
93
  if (compile) {
92
94
  // Derive the SCSS load path from the file argument (e.g. "scss/_foo.scss" → "scss/")
93
- const scssLoadPath = path.resolve(process.cwd(), file.split('/')[0])
95
+ const scssLoadPath = getSourceFsPath(file.split('/')[0])
94
96
 
95
97
  const scssInput = [
96
98
  '@use "config" as *;',
@@ -107,7 +109,7 @@ try {
107
109
  const result = await sass.compileStringAsync(scssInput, {
108
110
  loadPaths: [
109
111
  scssLoadPath,
110
- path.resolve(process.cwd(), 'scss/vendor'),
112
+ getSourceFsPath('scss/vendor'),
111
113
  // Vite's own Sass importer resolves bare npm specifiers (e.g. `@chassis-ui/tokens/...`)
112
114
  // through its module resolver, independent of `loadPaths`. This raw Sass JS API call
113
115
  // bypasses Vite, so `node_modules` must be added here explicitly.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import fs from 'node:fs'
3
+ import { getSourceFsPath } from '../../libs/source'
3
4
  import Code from './Code.astro'
4
5
 
5
6
  // Prints everything between `// scss-docs-start "name"` and `// scss-docs-end "name"`
@@ -11,7 +12,8 @@ interface Props {
11
12
  */
12
13
  name: string
13
14
  /**
14
- * File path that contains the content to display relative to the root of the repository.
15
+ * File path that contains the content to display, relative to the `sourceDir` of the site's
16
+ * `config.yml` (the working directory of the build when it is not set).
15
17
  */
16
18
  file: string
17
19
  }
@@ -29,7 +31,7 @@ if (!name || !file) {
29
31
  let content: string
30
32
 
31
33
  try {
32
- const fileContent = fs.readFileSync(file, 'utf8')
34
+ const fileContent = fs.readFileSync(getSourceFsPath(file), 'utf8')
33
35
 
34
36
  const matches = fileContent.match(
35
37
  new RegExp(`// scss-docs-start ${name}\n((?:.|\n)*)// scss-docs-end ${name}`, 'm')
@@ -8,8 +8,10 @@
8
8
  import type { MarkdownHeading } from 'astro'
9
9
  import type { CollectionEntry } from 'astro:content'
10
10
  import { getConfig } from '@libs/config'
11
+ import { docsPages } from '@libs/content'
11
12
  import { getChassisDocsPath } from '@libs/path'
12
13
  import type { LayoutOverridesHTMLAttributes } from '../libs/layout'
14
+ import { getSiteFileUrl } from '../libs/source'
13
15
  import { getSlug, processMarkdownToHtml } from '../libs/utils'
14
16
  import BaseLayout from './BaseLayout.astro'
15
17
  import DocsSidebar from '../components/DocsSidebar.astro'
@@ -23,10 +25,20 @@ interface Props {
23
25
  headings?: MarkdownHeading[]
24
26
  /** Document ID from the docs collection */
25
27
  id: CollectionEntry<'docs'>['id']
28
+ /**
29
+ * Path of the page's source file from the site's root. Looked up from the docs collection
30
+ * by `id` when omitted.
31
+ */
32
+ filePath?: string
26
33
  }
27
34
 
28
35
  const { frontmatter, headings, id } = Astro.props
29
36
 
37
+ // "View on GitHub" links to the page's own source file. The old link, which assumed the
38
+ // file sat at `<id>.mdx` in the repository root, is kept only when no file path is known.
39
+ const filePath = Astro.props.filePath ?? docsPages.find((page) => page.id === id)?.filePath
40
+ const githubUrl = filePath ? getSiteFileUrl(filePath) : `${getConfig().repo}/blob/${id}.mdx`
41
+
30
42
  type DocSection = NonNullable<typeof frontmatter.sections>[number]
31
43
 
32
44
  // Extract the directory/section from the ID (format: "directory/filename.mdx")
@@ -71,9 +83,7 @@ if (frontmatter.toc) {
71
83
  <main class="cxd-main">
72
84
  <div class="cxd-intro">
73
85
  <slot name="intro">
74
- <div
75
- class="md:d-flex md:flex-row-reverse justify-content-between align-items-start"
76
- >
86
+ <div class="md:d-flex md:flex-row-reverse justify-content-between align-items-start">
77
87
  <div class="mb-md md:mb-0 d-flex text-nowrap">
78
88
  {
79
89
  // This is needed because we want to show the badge if show_badge isn't present or is set to false
@@ -88,7 +98,7 @@ if (frontmatter.toc) {
88
98
  }
89
99
  <a
90
100
  class="button default sm"
91
- href={`${getConfig().repo}/blob/${id}.mdx`}
101
+ href={githubUrl}
92
102
  title="View and edit this file on GitHub"
93
103
  target="_blank"
94
104
  rel="noopener"
@@ -0,0 +1,62 @@
1
+ import path from 'node:path'
2
+ import { getConfig } from '@libs/config'
3
+
4
+ /**
5
+ * The part of the site's `config.yml` that says where files live, on disk and on GitHub.
6
+ *
7
+ * All four location keys are optional. A site that sets none of them is assumed to be built
8
+ * from the root of its repository, with its source files in that root.
9
+ */
10
+ interface SourceConfig {
11
+ /** Repository URL (e.g. `"https://github.com/chassis-ui/css"`). */
12
+ repo: string
13
+ /** Version of the release that links to source files name. */
14
+ current_version: string
15
+ /**
16
+ * Directory the `file` props of `<ScssDocs>`, `<ScssDocsSimple>`, `<JsDocs>` and `<Code>` are
17
+ * relative to, itself relative to the working directory of the build (e.g. `"../css"` for a
18
+ * site in `packages/site`).
19
+ */
20
+ sourceDir?: string
21
+ /** The same directory from the root of the repository (e.g. `"packages/css"`). */
22
+ sourcePath?: string
23
+ /** The site's root from the root of the repository (e.g. `"packages/site"`). */
24
+ sitePath?: string
25
+ /** The branch that "View on GitHub" links to. Defaults to `"main"`. */
26
+ siteBranch?: string
27
+ }
28
+
29
+ function getSourceConfig(): SourceConfig {
30
+ return getConfig() as SourceConfig
31
+ }
32
+
33
+ /** Joins URL segments, skipping empty ones, and resolves any `..` in them. */
34
+ function toUrl(segments: (string | undefined)[]): string {
35
+ const url = segments.filter(Boolean).join('/').replaceAll('\\', '/')
36
+
37
+ return new URL(url).href
38
+ }
39
+
40
+ /** Absolute path of a file or directory that a `file` prop names. */
41
+ export function getSourceFsPath(file = ''): string {
42
+ return path.resolve(process.cwd(), getSourceConfig().sourceDir ?? '.', file)
43
+ }
44
+
45
+ /** URL of a source file on GitHub, in the tag of the current version. */
46
+ export function getSourceUrl(file: string): string {
47
+ const { repo, current_version: version, sourcePath } = getSourceConfig()
48
+
49
+ return toUrl([repo, 'blob', `v${version}`, sourcePath, file])
50
+ }
51
+
52
+ /**
53
+ * URL of a file of the site on GitHub, on the site's branch.
54
+ *
55
+ * @param filePath Path of the file from the site's root, as Astro stores it in a content
56
+ * entry's `filePath` (e.g. `"content/docs/getting-started/introduction.mdx"`).
57
+ */
58
+ export function getSiteFileUrl(filePath: string): string {
59
+ const { repo, sitePath, siteBranch = 'main' } = getSourceConfig()
60
+
61
+ return toUrl([repo, 'blob', siteBranch, sitePath, filePath])
62
+ }