upcontent 0.1.3 → 0.1.5
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/.upcontent/config.json +1 -1
- package/customization/config-json.md +1 -1
- package/customization/navigation.md +1 -1
- package/index.md +126 -4
- package/package.json +1 -1
- package/src/content.config.ts +1 -0
- package/src/lib/sidebar.test.ts +7 -7
- package/src/lib/sidebar.ts +8 -5
package/.upcontent/config.json
CHANGED
|
@@ -68,7 +68,7 @@ The optional fields below cover navigation, Starlight presentation, and content
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"navigation": {
|
|
71
|
-
"roots": ["
|
|
71
|
+
"roots": ["index.md", "guides"],
|
|
72
72
|
"labelOverrides": { "api": "API reference" },
|
|
73
73
|
"blocklist": {
|
|
74
74
|
"exact": ["notes.md"],
|
|
@@ -7,7 +7,7 @@ sidebar:
|
|
|
7
7
|
|
|
8
8
|
The sidebar is generated from the consumer file structure. Use configuration when the default filesystem order is not the right experience for readers.
|
|
9
9
|
|
|
10
|
-
If the repository has a root `index.md`, it becomes the website homepage and
|
|
10
|
+
If the repository has a root `index.md`, it becomes the website homepage and the root `README.md` remains GitHub-only. Repositories without `index.md` keep the backwards-compatible behavior where `README.md` is the homepage.
|
|
11
11
|
|
|
12
12
|
## Select top-level roots
|
|
13
13
|
|
package/index.md
CHANGED
|
@@ -5,15 +5,137 @@ sidebar:
|
|
|
5
5
|
order: 1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Upcontent turns
|
|
8
|
+
Upcontent turns the documentation repository you already have into a fast, searchable, customizable portal.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
[](https://github.com/lumamontes/upcontent/actions/workflows/ci.yml)
|
|
11
|
+
[](https://www.npmjs.com/package/upcontent)
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## Explore Upcontent
|
|
13
14
|
|
|
15
|
+
- [See the live portal](https://lumamontes.github.io/upcontent/)
|
|
16
|
+
- [Explore the capability showcase](showcase/)
|
|
14
17
|
- [Set up a consumer repository](getting-started/consumer-repository/)
|
|
15
18
|
- [Run your first build](getting-started/first-build/)
|
|
19
|
+
- [Configure content](customization/content/)
|
|
20
|
+
- [Configure navigation](customization/navigation/)
|
|
16
21
|
- [Customize the portal](customization/)
|
|
17
22
|
- [Validate before publishing](guides/validate-your-site/)
|
|
23
|
+
- [Publish to GitHub Pages](deployment/github-pages/)
|
|
24
|
+
- [Publish to another static host](deployment/static-hosts/)
|
|
25
|
+
- [Publish releases through npm](deployment/npm/)
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
## See the result
|
|
28
|
+
|
|
29
|
+
This is the real Upcontent portal generated from this repository and deployed to GitHub Pages.
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<a href="https://lumamontes.github.io/upcontent/"><img src="assets/readme/portal-home.png" alt="Upcontent documentation portal home page" width="900"></a>
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
The same build includes a capability showcase for technical content, structured data, callouts, diagrams, navigation, and search.
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<a href="showcase/"><img src="assets/readme/portal-showcase.png" alt="Upcontent capability showcase page" width="900"></a>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
## From repository to portal
|
|
42
|
+
|
|
43
|
+
Upcontent keeps your source of truth in Git and adds the publishing layer: clear navigation, full-text search, technical content rendering, consumer-owned branding, and a repeatable static build.
|
|
44
|
+
|
|
45
|
+
If your Markdown already lives in a GitHub repository, the flow is:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
pnpm dlx upcontent init
|
|
49
|
+
pnpm dlx upcontent dev
|
|
50
|
+
pnpm dlx upcontent check
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The first command creates `.upcontent/` and a GitHub Pages workflow. The second starts a local portal using the repository you are already in. The third builds the consumer portal and validates the generated site. You do not need to clone the Upcontent renderer into your documentation repository.
|
|
54
|
+
|
|
55
|
+
Push the generated workflow and your documentation is published as a static site. The source repository remains the source of truth; Upcontent does not require a database, a companion server, or a new authoring system.
|
|
56
|
+
|
|
57
|
+
## What you get
|
|
58
|
+
|
|
59
|
+
- A responsive [Starlight](https://starlight.astro.build/) portal with navigation, search, themes, and table of contents.
|
|
60
|
+
- A `.upcontent/config.json` file for identity, navigation, rendering, and content boundaries.
|
|
61
|
+
- Consumer-owned logos, favicon, CSS, site identity, and navigation.
|
|
62
|
+
- Rendered callouts, Mermaid diagrams, JSON, YAML, CSV, and wiki links.
|
|
63
|
+
- A static `dist/` directory deployable to GitHub Pages or any static host.
|
|
64
|
+
- Build checks that catch broken wiki links and excluded content before publication.
|
|
65
|
+
|
|
66
|
+
## Is it a good fit?
|
|
67
|
+
|
|
68
|
+
Upcontent is a good fit when:
|
|
69
|
+
|
|
70
|
+
- Your documentation already lives in Markdown or MDX.
|
|
71
|
+
- Your team wants to keep writing in Git.
|
|
72
|
+
- You want the portal to be owned and deployed by the documentation repository.
|
|
73
|
+
- You need more than raw Markdown, but do not need a dynamic application.
|
|
74
|
+
- You want branding and navigation without maintaining a custom docs frontend.
|
|
75
|
+
|
|
76
|
+
It is not the right fit when your site needs runtime authentication, server-rendered personalization, or review comments inside the published portal.
|
|
77
|
+
|
|
78
|
+
## Explore this repository locally
|
|
79
|
+
|
|
80
|
+
Clone this repository, then start the included golden consumer:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
git clone https://github.com/lumamontes/upcontent.git
|
|
84
|
+
cd upcontent
|
|
85
|
+
pnpm install
|
|
86
|
+
make dev CONTENT_PATH=.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then open the local URL printed by Astro. The [capability showcase](showcase/) is the fastest way to see the complete rendering and customization surface.
|
|
90
|
+
|
|
91
|
+
## Configure your portal
|
|
92
|
+
|
|
93
|
+
The consumer configuration is deliberately small:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"site": {
|
|
98
|
+
"title": "Engineering Docs",
|
|
99
|
+
"description": "Documentation for the engineering team.",
|
|
100
|
+
"socialImage": "https://docs.example.com/social-card.png",
|
|
101
|
+
"locale": "en-US",
|
|
102
|
+
"logo": {
|
|
103
|
+
"src": ".upcontent/logo.svg",
|
|
104
|
+
"alt": "Engineering Docs"
|
|
105
|
+
},
|
|
106
|
+
"favicon": ".upcontent/favicon.svg"
|
|
107
|
+
},
|
|
108
|
+
"seo": {
|
|
109
|
+
"enabled": true
|
|
110
|
+
},
|
|
111
|
+
"repo": {
|
|
112
|
+
"url": "https://github.com/acme/engineering-docs"
|
|
113
|
+
},
|
|
114
|
+
"theme": {
|
|
115
|
+
"customCss": [".upcontent/theme.css"]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Follow [Set up a consumer repository](getting-started/consumer-repository/) for the complete setup, then use [Customization](customization/) to shape the portal.
|
|
121
|
+
|
|
122
|
+
## Build for production
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
make build CONTENT_PATH=/path/to/your-consumer-repo
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The generated site is written to `dist/` and includes the Pagefind search index. Before publishing, run:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
pnpm test
|
|
132
|
+
pnpm check
|
|
133
|
+
make build CONTENT_PATH=.
|
|
134
|
+
make check-external
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Read the [deployment guide](deployment/) for GitHub Pages and other static hosts.
|
|
138
|
+
|
|
139
|
+
## Built on Astro and Starlight
|
|
140
|
+
|
|
141
|
+
Upcontent uses the official [Astro](https://astro.build/) framework and [Starlight](https://starlight.astro.build/) documentation theme as its rendering foundation. Upcontent adds the consumer-repository contract, content integrity checks, configuration surface, and deployment workflow around them.
|
package/package.json
CHANGED
package/src/content.config.ts
CHANGED
|
@@ -86,6 +86,7 @@ function portalDocsLoader(): Loader {
|
|
|
86
86
|
const homepage = hasRootIndex(docsBasePath) ? 'index' : 'readme'
|
|
87
87
|
const patterns = [
|
|
88
88
|
'**/[^_]*.{markdown,mdown,mkdn,mkd,mdwn,md,mdx}',
|
|
89
|
+
...(homepage === 'index' ? ['![rR][eE][aA][dD][mM][eE].{markdown,mdown,mkdn,mkd,mdwn,md,mdx}'] : []),
|
|
89
90
|
...getBlocklist().map(blocked => `!${toCaseInsensitiveGlob(blocked)}${blocked.endsWith('/') ? '**' : ''}`),
|
|
90
91
|
]
|
|
91
92
|
const wrappedContext: LoaderContext = {
|
package/src/lib/sidebar.test.ts
CHANGED
|
@@ -98,7 +98,7 @@ describe('buildSidebar', () => {
|
|
|
98
98
|
mountFs(ROOT, { 'index.md': null, guides: { 'guide.md': null } })
|
|
99
99
|
|
|
100
100
|
const sidebar = buildSidebar(ROOT)
|
|
101
|
-
expect(sidebar[0]).toEqual({ slug: 'index', label: '
|
|
101
|
+
expect(sidebar[0]).toEqual({ slug: 'index', label: 'Getting Started' })
|
|
102
102
|
})
|
|
103
103
|
|
|
104
104
|
it('ignora dotfiles e dot-directories', () => {
|
|
@@ -110,13 +110,13 @@ describe('buildSidebar', () => {
|
|
|
110
110
|
it('ignora arquivos bloqueados (floor hardcoded)', () => {
|
|
111
111
|
mountFs(ROOT, { 'CLAUDE.md': null, 'README.md': null })
|
|
112
112
|
const sidebar = buildSidebar(ROOT)
|
|
113
|
-
expect(sidebar).toEqual([{ slug: 'index', label: '
|
|
113
|
+
expect(sidebar).toEqual([{ slug: 'index', label: 'Getting Started' }])
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
it('fixa o README em primeiro, relabelado como
|
|
116
|
+
it('fixa o README em primeiro, relabelado como Getting Started, na frente de tudo', () => {
|
|
117
117
|
mountFs(ROOT, { 'README.md': null, domains: { historico: { 'a.md': null } } })
|
|
118
118
|
const sidebar = buildSidebar(ROOT) as { label: string }[]
|
|
119
|
-
expect(sidebar[0]).toEqual({ slug: 'index', label: '
|
|
119
|
+
expect(sidebar[0]).toEqual({ slug: 'index', label: 'Getting Started' })
|
|
120
120
|
expect(sidebar[1].label).toBe('Historico')
|
|
121
121
|
})
|
|
122
122
|
|
|
@@ -130,11 +130,11 @@ describe('buildSidebar', () => {
|
|
|
130
130
|
expect(sidebar[0]).toEqual({ slug: 'index', label: 'Docs' })
|
|
131
131
|
})
|
|
132
132
|
|
|
133
|
-
it('
|
|
133
|
+
it('não publica README quando index.md é a homepage', () => {
|
|
134
134
|
mountFs(ROOT, { 'README.md': null, 'index.markdown': null, domains: { historico: { 'a.md': null } } })
|
|
135
135
|
const sidebar = buildSidebar(ROOT) as { slug?: string; label?: string }[]
|
|
136
|
-
expect(sidebar[0]).toEqual({ slug: 'index', label: '
|
|
137
|
-
expect(sidebar).toContainEqual({ slug: 'readme', label: 'Readme' })
|
|
136
|
+
expect(sidebar[0]).toEqual({ slug: 'index', label: 'Getting Started' })
|
|
137
|
+
expect(sidebar).not.toContainEqual({ slug: 'readme', label: 'Readme' })
|
|
138
138
|
})
|
|
139
139
|
|
|
140
140
|
it('aplica labelOverrides do .upcontent/config.json em cima do Title Case', () => {
|
package/src/lib/sidebar.ts
CHANGED
|
@@ -98,8 +98,11 @@ export function buildSidebar(docsRoot: string): SidebarEntry[] {
|
|
|
98
98
|
const homepage = hasRootIndex(docsRoot) ? 'index' : 'readme'
|
|
99
99
|
const configuredRoots = getPortalConfig().navigation?.roots
|
|
100
100
|
const visibleTopLevel = configuredRoots
|
|
101
|
-
? topLevel.filter(({ name, isDir }) =>
|
|
102
|
-
|
|
101
|
+
? topLevel.filter(({ name, isDir }) =>
|
|
102
|
+
!(homepage === 'index' && !isDir && toSidebarSlug(name, homepage) === 'readme')
|
|
103
|
+
&& (configuredRoots.includes(name) || (!isDir && ['index', 'readme'].includes(toSidebarSlug(name, homepage))))
|
|
104
|
+
)
|
|
105
|
+
: topLevel.filter(({ name, isDir }) => !(homepage === 'index' && !isDir && toSidebarSlug(name, homepage) === 'readme'))
|
|
103
106
|
|
|
104
107
|
const entries: SidebarEntry[] = []
|
|
105
108
|
for (const { name, isDir } of visibleTopLevel) {
|
|
@@ -113,10 +116,10 @@ export function buildSidebar(docsRoot: string): SidebarEntry[] {
|
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
|
|
116
|
-
// A homepage fica fixa em primeiro, com o label configurável
|
|
117
|
-
//
|
|
119
|
+
// A homepage fica fixa em primeiro, com o label configurável, sem competir
|
|
120
|
+
// alfabeticamente com as outras páginas.
|
|
118
121
|
const homepageIndex = entries.findIndex(e => !isSidebarGroup(e) && e.slug === 'index')
|
|
119
122
|
const homepageEntry = homepageIndex >= 0 ? entries.splice(homepageIndex, 1)[0] : undefined
|
|
120
123
|
const sorted = sortEntries(entries)
|
|
121
|
-
return homepageEntry ? [{ slug: 'index', label: resolveLabel(homepage, '
|
|
124
|
+
return homepageEntry ? [{ slug: 'index', label: resolveLabel(homepage, 'Getting Started') }, ...sorted] : sorted
|
|
122
125
|
}
|