@unterberg/nivel 0.0.3 → 0.0.4
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 +69 -66
- package/dist/{chunk-UDOIFPCZ.js → chunk-2EDJWL3U.js} +5 -5
- package/dist/chunk-67GE3PJ6.js +473 -0
- package/dist/chunk-67GE3PJ6.js.map +1 -0
- package/dist/{chunk-K5ZYRA3G.js → chunk-AURANIZE.js} +2 -2
- package/dist/{chunk-FARXFRHG.js → chunk-GHQDRDZL.js} +1 -1
- package/dist/chunk-JSZZPQEP.js +9 -0
- package/dist/chunk-JSZZPQEP.js.map +1 -0
- package/dist/cli.js +44 -5
- package/dist/cli.js.map +1 -1
- package/dist/client.js +2 -2
- package/dist/config.d.ts +6 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -1
- package/dist/mdx.js +2 -1
- package/dist/mdx.js.map +1 -1
- package/dist/runtime/client.js +2 -2
- package/dist/runtime/node.d.ts +16 -2
- package/dist/runtime/node.js +6 -4
- package/dist/vike.d.ts +5 -1
- package/dist/vike.js +25 -4
- package/dist/vike.js.map +1 -1
- package/package.json +7 -3
- package/dist/chunk-DNCQR5NH.js +0 -231
- package/dist/chunk-DNCQR5NH.js.map +0 -1
- /package/dist/{chunk-UDOIFPCZ.js.map → chunk-2EDJWL3U.js.map} +0 -0
- /package/dist/{chunk-K5ZYRA3G.js.map → chunk-AURANIZE.js.map} +0 -0
- /package/dist/{chunk-FARXFRHG.js.map → chunk-GHQDRDZL.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,70 +1,33 @@
|
|
|
1
1
|
# nivel engine
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@unterberg/nivel` is the docs runtime for this repo's Vike-based documentation sites.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- `packages/engine`: the reusable `@unterberg/nivel` package
|
|
8
|
-
- `packages/consumer-test`: the reference consumer used to exercise the engine against real docs content, currently based on the [Telefunc docs](https://telefunc.com)
|
|
9
|
-
|
|
10
|
-
## Alpha Status
|
|
11
|
-
|
|
12
|
-
This project is an early proof of concept and should be expected to have rough edges. The main goal is to validate the core engine ideas and architecture, not to provide a polished general-purpose doc builder right now. Some specific things to keep in mind:
|
|
13
|
-
|
|
14
|
-
- Expect breaking changes.
|
|
15
|
-
- The public API is not stable yet. (blackbox)
|
|
16
|
-
- The supported stack fixed currently to Vike + Vite + React.
|
|
17
|
-
- `basePath` is currently fixed to `/docs`.
|
|
18
|
-
- The consumer app in this repo is still the main integration example.
|
|
19
|
-
|
|
20
|
-
If you need a polished general-purpose docpress replacement today, this is not that yet.
|
|
21
|
-
|
|
22
|
-
## What It Does
|
|
23
|
-
|
|
24
|
-
The engine currently owns the core docs runtime:
|
|
5
|
+
It currently owns:
|
|
25
6
|
|
|
26
7
|
- docs graph validation and resolution
|
|
27
8
|
- generated Vike routes from MDX content
|
|
28
|
-
-
|
|
9
|
+
- docs layout primitives such as navbar, sidebar, table of contents, pagination, and meta head wiring
|
|
29
10
|
- MDX setup with built-in docs components and code-block transforms
|
|
30
|
-
-
|
|
11
|
+
- optional Algolia search wiring
|
|
12
|
+
- engine-owned fonts and shared assets
|
|
31
13
|
|
|
32
|
-
The
|
|
14
|
+
The supported stack is currently Vike + Vite + React. Expect breaking changes while the package is still alpha.
|
|
33
15
|
|
|
34
|
-
|
|
35
|
-
- the consumer owns docs content, `docs/docs.graph.ts`, `pages/+docs.ts`, and brand/theme assets
|
|
36
|
-
|
|
37
|
-
## Minimal Shape
|
|
38
|
-
|
|
39
|
-
At the moment, a consumer looks roughly like this:
|
|
16
|
+
## Public Helpers
|
|
40
17
|
|
|
41
18
|
```ts
|
|
42
|
-
|
|
43
|
-
import { defineDocsConfig } from '@unterberg/nivel'
|
|
44
|
-
import { docsGraph } from '../docs/docs.graph'
|
|
45
|
-
|
|
46
|
-
export default defineDocsConfig({
|
|
47
|
-
siteTitle: 'My Docs',
|
|
48
|
-
basePath: '/docs',
|
|
49
|
-
graph: docsGraph,
|
|
50
|
-
algolia: {
|
|
51
|
-
appId: 'YOUR_APP_ID',
|
|
52
|
-
apiKey: 'YOUR_SEARCH_ONLY_API_KEY',
|
|
53
|
-
indexName: 'YOUR_INDEX_NAME',
|
|
54
|
-
},
|
|
55
|
-
})
|
|
19
|
+
import { defineDocsConfig, defineDocsGraph } from '@unterberg/nivel/config'
|
|
56
20
|
```
|
|
57
21
|
|
|
58
|
-
|
|
59
|
-
// docs/docs.graph.ts
|
|
60
|
-
import { defineDocsGraph } from '@unterberg/nivel'
|
|
22
|
+
Both helpers are identity functions. They are also re-exported from `@unterberg/nivel`, but the dedicated config entry keeps `pages/+docs.ts` and `docs/docs.graph.ts` on a lean config-time import path.
|
|
61
23
|
|
|
62
|
-
|
|
24
|
+
```ts
|
|
25
|
+
const docsGraph = defineDocsGraph({
|
|
63
26
|
items: [
|
|
64
27
|
{
|
|
65
28
|
kind: 'section',
|
|
66
29
|
id: 'docs',
|
|
67
|
-
title: '
|
|
30
|
+
title: 'Docs',
|
|
68
31
|
items: [
|
|
69
32
|
{
|
|
70
33
|
kind: 'page',
|
|
@@ -77,31 +40,71 @@ export const docsGraph = defineDocsGraph({
|
|
|
77
40
|
},
|
|
78
41
|
],
|
|
79
42
|
})
|
|
43
|
+
|
|
44
|
+
export default defineDocsConfig({
|
|
45
|
+
siteTitle: 'My Docs',
|
|
46
|
+
siteDescription: 'Documentation site powered by @unterberg/nivel.',
|
|
47
|
+
basePath: '/docs',
|
|
48
|
+
graph: docsGraph,
|
|
49
|
+
})
|
|
80
50
|
```
|
|
81
51
|
|
|
82
|
-
|
|
52
|
+
## Recommended Consumer Files
|
|
53
|
+
|
|
54
|
+
A consumer should keep these files explicit and local:
|
|
83
55
|
|
|
84
|
-
-
|
|
85
|
-
- `
|
|
86
|
-
- `AppLayout` in global `+Layout`
|
|
87
|
-
- `nivel prepare` before dev/build/typecheck
|
|
88
|
-
- `@import '@unterberg/nivel/tailwind-sources.css'` in the consumer Tailwind entry
|
|
56
|
+
- hand-authored: `pages/+docs.ts`, `docs/docs.graph.ts`, docs content, brand assets, and consumer CSS/Tailwind/theme files
|
|
57
|
+
- scaffolded once but still editable: `pages/+config.ts`, `pages/+Head.tsx`, `pages/+Layout.tsx`, `pages/+onCreateGlobalContext.ts`, `pages/+Wrapper.tsx`, and `global.d.ts`
|
|
89
58
|
|
|
90
|
-
|
|
59
|
+
Only `(nivel-generated)` stays engine-generated.
|
|
91
60
|
|
|
92
|
-
##
|
|
61
|
+
## Standard Vike Config
|
|
93
62
|
|
|
94
|
-
|
|
95
|
-
- The supported stack is currently narrow: Vike + Vite + React.
|
|
96
|
-
- `basePath` is currently fixed to `/docs`.
|
|
97
|
-
- The package is still validated mainly through one real consumer, not a broad set of independent adopters.
|
|
98
|
-
- The setup and examples are still catching up with the implementation, so the integration story is not fully polished yet.
|
|
63
|
+
Use the helper from `@unterberg/nivel/vike` for the standard Vike setup:
|
|
99
64
|
|
|
100
|
-
|
|
65
|
+
```ts
|
|
66
|
+
// pages/+docs.ts
|
|
67
|
+
import { defineDocsConfig } from '@unterberg/nivel/config'
|
|
68
|
+
import { docsGraph } from '../docs/docs.graph'
|
|
69
|
+
|
|
70
|
+
export default defineDocsConfig({
|
|
71
|
+
siteTitle: 'My Docs',
|
|
72
|
+
siteDescription: 'Documentation site powered by @unterberg/nivel.',
|
|
73
|
+
basePath: '/docs',
|
|
74
|
+
graph: docsGraph,
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// pages/+config.ts
|
|
80
|
+
import { createNivelVikeConfig } from '@unterberg/nivel/vike'
|
|
81
|
+
import docsConfig from './+docs'
|
|
82
|
+
|
|
83
|
+
export { config }
|
|
84
|
+
|
|
85
|
+
const config = createNivelVikeConfig(docsConfig)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`createNivelVikeConfig()` owns the standard `title`, `description`, Vike React extension, default `data-theme`, `passToClient`, and `prerender` wiring. Consumers can still spread or override it locally if they need extra Vike config.
|
|
89
|
+
|
|
90
|
+
## CLI
|
|
91
|
+
|
|
92
|
+
`nivel prepare` generates docs pages from `pages/+docs.ts`.
|
|
93
|
+
|
|
94
|
+
`nivel init` scaffolds the visible consumer shell files and updates the standard docs scripts in `package.json`.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
nivel init
|
|
98
|
+
nivel prepare
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`nivel init` does not create or overwrite consumer CSS/Tailwind/theme files. Keep those hand-authored. Add the engine Tailwind helper manually in your stylesheet:
|
|
102
|
+
|
|
103
|
+
```css
|
|
104
|
+
@import '@unterberg/nivel/tailwind-sources.css';
|
|
105
|
+
```
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
- Improve the package-level docs and examples so setup is easier to understand without reading the consumer app in detail.
|
|
104
|
-
- Reduce hard-coded assumptions where it makes sense, starting with the areas that currently make the engine feel too tied to its first consumer.
|
|
107
|
+
If you use Algolia, `apiKey` must be a search-only public key because requests are made from the browser.
|
|
105
108
|
|
|
106
109
|
## Commands
|
|
107
110
|
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
UniversalMdxProvider,
|
|
3
|
-
renderInlineMarkdown
|
|
4
|
-
} from "./chunk-L6ZVB6XH.js";
|
|
5
1
|
import {
|
|
6
2
|
createHeadingSlugger,
|
|
7
3
|
getActiveSectionByPathname,
|
|
@@ -10,6 +6,10 @@ import {
|
|
|
10
6
|
isSamePagePathname,
|
|
11
7
|
normalizeHeadingTitle
|
|
12
8
|
} from "./chunk-D7IAGT53.js";
|
|
9
|
+
import {
|
|
10
|
+
UniversalMdxProvider,
|
|
11
|
+
renderInlineMarkdown
|
|
12
|
+
} from "./chunk-L6ZVB6XH.js";
|
|
13
13
|
import {
|
|
14
14
|
withSiteBaseUrl
|
|
15
15
|
} from "./chunk-PYYPYIBD.js";
|
|
@@ -1535,4 +1535,4 @@ export {
|
|
|
1535
1535
|
ProseContainer,
|
|
1536
1536
|
DocsPage
|
|
1537
1537
|
};
|
|
1538
|
-
//# sourceMappingURL=chunk-
|
|
1538
|
+
//# sourceMappingURL=chunk-2EDJWL3U.js.map
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractDocHeadings,
|
|
3
|
+
getResolvedPageById,
|
|
4
|
+
resolveDocsConfig
|
|
5
|
+
} from "./chunk-D7IAGT53.js";
|
|
6
|
+
|
|
7
|
+
// src/runtime/node/codegen.ts
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
var GENERATED_DIRNAME = "(nivel-generated)";
|
|
11
|
+
var writeFileIfChanged = (filePath, source) => {
|
|
12
|
+
const current = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : null;
|
|
13
|
+
if (current === source) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
17
|
+
fs.writeFileSync(filePath, source);
|
|
18
|
+
};
|
|
19
|
+
var toPosix = (value) => value.split(path.sep).join(path.posix.sep);
|
|
20
|
+
var getRelativeImportPath = (fromDirectory, toFile) => {
|
|
21
|
+
const relativePath = toPosix(path.relative(fromDirectory, toFile));
|
|
22
|
+
if (relativePath.startsWith(".")) {
|
|
23
|
+
return relativePath;
|
|
24
|
+
}
|
|
25
|
+
return `./${relativePath}`;
|
|
26
|
+
};
|
|
27
|
+
var serializeData = (data) => JSON.stringify(data, null, 2);
|
|
28
|
+
var collectFiles = (directoryPath) => {
|
|
29
|
+
if (!fs.existsSync(directoryPath)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
33
|
+
return entries.flatMap((entry) => {
|
|
34
|
+
const entryPath = path.join(directoryPath, entry.name);
|
|
35
|
+
return entry.isDirectory() ? collectFiles(entryPath) : [entryPath];
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
var removeEmptyDirectories = (directoryPath, rootPath) => {
|
|
39
|
+
if (!fs.existsSync(directoryPath)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const entry of fs.readdirSync(directoryPath, { withFileTypes: true })) {
|
|
43
|
+
if (!entry.isDirectory()) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
removeEmptyDirectories(path.join(directoryPath, entry.name), rootPath);
|
|
47
|
+
}
|
|
48
|
+
if (directoryPath === rootPath) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (fs.readdirSync(directoryPath).length === 0) {
|
|
52
|
+
fs.rmdirSync(directoryPath);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var getGeneratedPageSource = (contentImportPath) => {
|
|
56
|
+
return [
|
|
57
|
+
"import { DocsPage } from '@unterberg/nivel/client'",
|
|
58
|
+
`import Content from ${JSON.stringify(contentImportPath)}`,
|
|
59
|
+
"",
|
|
60
|
+
"const Page = () => {",
|
|
61
|
+
" return <DocsPage Content={Content} />",
|
|
62
|
+
"}",
|
|
63
|
+
"",
|
|
64
|
+
"export default Page",
|
|
65
|
+
""
|
|
66
|
+
].join("\n");
|
|
67
|
+
};
|
|
68
|
+
var getGeneratedDataSource = (data) => {
|
|
69
|
+
return [
|
|
70
|
+
"import type { DocPageData } from '@unterberg/nivel'",
|
|
71
|
+
"",
|
|
72
|
+
`const data: DocPageData = ${serializeData(data)}`,
|
|
73
|
+
"",
|
|
74
|
+
"const pageData = () => {",
|
|
75
|
+
" return data",
|
|
76
|
+
"}",
|
|
77
|
+
"",
|
|
78
|
+
"export default pageData",
|
|
79
|
+
""
|
|
80
|
+
].join("\n");
|
|
81
|
+
};
|
|
82
|
+
var getGeneratedGlobalContextSource = (data) => {
|
|
83
|
+
return [
|
|
84
|
+
"import type { DocsGlobalContextData } from '@unterberg/nivel'",
|
|
85
|
+
"",
|
|
86
|
+
`const docsGlobalContextData: DocsGlobalContextData = ${serializeData(data)}`,
|
|
87
|
+
"",
|
|
88
|
+
"export { docsGlobalContextData }",
|
|
89
|
+
""
|
|
90
|
+
].join("\n");
|
|
91
|
+
};
|
|
92
|
+
var getRouteString = (href) => {
|
|
93
|
+
if (href === "/") {
|
|
94
|
+
return href;
|
|
95
|
+
}
|
|
96
|
+
return href.replace(/\/+$/g, "");
|
|
97
|
+
};
|
|
98
|
+
var getGeneratedRouteSource = (href) => {
|
|
99
|
+
return [`export default ${JSON.stringify(getRouteString(href))}`, ""].join("\n");
|
|
100
|
+
};
|
|
101
|
+
var getGeneratedTextExport = (value) => {
|
|
102
|
+
return [`export default ${JSON.stringify(value)}`, ""].join("\n");
|
|
103
|
+
};
|
|
104
|
+
var toDocPageLinkData = (page) => {
|
|
105
|
+
if (!page) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
id: page.id,
|
|
110
|
+
title: page.title,
|
|
111
|
+
href: page.href,
|
|
112
|
+
documentTitle: page.documentTitle
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
var getGeneratedPagesRoot = (rootDir) => path.join(rootDir, "pages", GENERATED_DIRNAME);
|
|
116
|
+
var syncGeneratedDocsPages = (options) => {
|
|
117
|
+
const { rootDir, docsConfig } = options;
|
|
118
|
+
const resolved = resolveDocsConfig(docsConfig);
|
|
119
|
+
const generatedPagesRoot = getGeneratedPagesRoot(rootDir);
|
|
120
|
+
const docsRoot = path.join(rootDir, "docs");
|
|
121
|
+
const expectedFiles = /* @__PURE__ */ new Set();
|
|
122
|
+
const globalContextFilePath = path.join(generatedPagesRoot, "_docsGlobalContext.ts");
|
|
123
|
+
fs.mkdirSync(generatedPagesRoot, { recursive: true });
|
|
124
|
+
const globalContextData = {
|
|
125
|
+
siteTitle: resolved.siteTitle,
|
|
126
|
+
basePath: resolved.basePath,
|
|
127
|
+
theme: resolved.theme,
|
|
128
|
+
footer: resolved.footer,
|
|
129
|
+
brand: resolved.brand,
|
|
130
|
+
head: resolved.head,
|
|
131
|
+
partners: resolved.partners,
|
|
132
|
+
algolia: resolved.algolia,
|
|
133
|
+
pages: resolved.pages,
|
|
134
|
+
navbarItems: resolved.navbarItems,
|
|
135
|
+
sidebarSections: resolved.sections
|
|
136
|
+
};
|
|
137
|
+
writeFileIfChanged(globalContextFilePath, getGeneratedGlobalContextSource(globalContextData));
|
|
138
|
+
expectedFiles.add(globalContextFilePath);
|
|
139
|
+
for (const [pageIndex, page] of resolved.pages.entries()) {
|
|
140
|
+
const contentFilePath = path.join(docsRoot, page.source);
|
|
141
|
+
if (!fs.existsSync(contentFilePath)) {
|
|
142
|
+
throw new Error(`Docs page "${page.id}" points to missing source file: ${contentFilePath}`);
|
|
143
|
+
}
|
|
144
|
+
const pageSource = fs.readFileSync(contentFilePath, "utf8");
|
|
145
|
+
const data = {
|
|
146
|
+
page: getResolvedPageById(resolved, page.id),
|
|
147
|
+
headings: extractDocHeadings(pageSource),
|
|
148
|
+
previousPage: toDocPageLinkData(resolved.pages[pageIndex - 1]),
|
|
149
|
+
nextPage: toDocPageLinkData(resolved.pages[pageIndex + 1])
|
|
150
|
+
};
|
|
151
|
+
for (const routeHref of [page.href, ...page.aliasHrefs]) {
|
|
152
|
+
const routeSlug = routeHref.replace(/^\/docs\//, "").replace(/\/+$/g, "");
|
|
153
|
+
const pageDir = path.join(generatedPagesRoot, ...routeSlug.split("/"));
|
|
154
|
+
const contentImportPath = getRelativeImportPath(pageDir, contentFilePath);
|
|
155
|
+
const pageFilePath = path.join(pageDir, "+Page.tsx");
|
|
156
|
+
const dataFilePath = path.join(pageDir, "+data.ts");
|
|
157
|
+
const routeFilePath = path.join(pageDir, "+route.ts");
|
|
158
|
+
const titleFilePath = path.join(pageDir, "+title.ts");
|
|
159
|
+
writeFileIfChanged(pageFilePath, getGeneratedPageSource(contentImportPath));
|
|
160
|
+
writeFileIfChanged(dataFilePath, getGeneratedDataSource(data));
|
|
161
|
+
writeFileIfChanged(routeFilePath, getGeneratedRouteSource(routeHref));
|
|
162
|
+
writeFileIfChanged(titleFilePath, getGeneratedTextExport(page.documentTitle));
|
|
163
|
+
expectedFiles.add(pageFilePath);
|
|
164
|
+
expectedFiles.add(dataFilePath);
|
|
165
|
+
expectedFiles.add(routeFilePath);
|
|
166
|
+
expectedFiles.add(titleFilePath);
|
|
167
|
+
if (page.description) {
|
|
168
|
+
const descriptionFilePath = path.join(pageDir, "+description.ts");
|
|
169
|
+
writeFileIfChanged(descriptionFilePath, getGeneratedTextExport(page.description));
|
|
170
|
+
expectedFiles.add(descriptionFilePath);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const filePath of collectFiles(generatedPagesRoot)) {
|
|
175
|
+
if (expectedFiles.has(filePath)) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
fs.rmSync(filePath, { force: true });
|
|
179
|
+
}
|
|
180
|
+
removeEmptyDirectories(generatedPagesRoot, generatedPagesRoot);
|
|
181
|
+
};
|
|
182
|
+
var isDocsSourcePath = (filePath, rootDir) => {
|
|
183
|
+
const normalized = toPosix(filePath);
|
|
184
|
+
const docsRoot = toPosix(path.join(rootDir, "docs"));
|
|
185
|
+
const docsConfigPath = toPosix(path.join(rootDir, "pages", "+docs.ts"));
|
|
186
|
+
const generatedRoot = toPosix(getGeneratedPagesRoot(rootDir));
|
|
187
|
+
if (normalized.startsWith(generatedRoot)) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return normalized === docsConfigPath || normalized.startsWith(`${docsRoot}/`);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/runtime/node/loadDocsConfig.ts
|
|
194
|
+
import path2 from "path";
|
|
195
|
+
var getDocsConfigModulePath = (rootDir) => {
|
|
196
|
+
return path2.join(rootDir, "pages", "+docs.ts");
|
|
197
|
+
};
|
|
198
|
+
var getDocsConfigFromLoadedModule = (loaded, modulePath) => {
|
|
199
|
+
const docsConfig = loaded.default;
|
|
200
|
+
if (!docsConfig) {
|
|
201
|
+
throw new Error(`Expected default export from ${modulePath}`);
|
|
202
|
+
}
|
|
203
|
+
return docsConfig;
|
|
204
|
+
};
|
|
205
|
+
var loadDocsConfig = async (options) => {
|
|
206
|
+
const modulePath = getDocsConfigModulePath(options.rootDir);
|
|
207
|
+
const loaded = await options.loadModule(modulePath);
|
|
208
|
+
return getDocsConfigFromLoadedModule(loaded, modulePath);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/runtime/node/scaffold.ts
|
|
212
|
+
import fs2 from "fs";
|
|
213
|
+
import path3 from "path";
|
|
214
|
+
var MANAGED_SCRIPT_NAMES = ["generate:docs", "predev", "prebuild", "pretypecheck"];
|
|
215
|
+
var REQUIRED_DEPENDENCIES = ["@unterberg/nivel", "react", "react-dom", "vike", "vike-react"];
|
|
216
|
+
var REQUIRED_DEV_DEPENDENCIES = ["vite", "typescript", "@types/react", "@types/react-dom"];
|
|
217
|
+
var getDocsConfigTemplate = () => {
|
|
218
|
+
return [
|
|
219
|
+
"import { defineDocsConfig } from '@unterberg/nivel/config'",
|
|
220
|
+
"import { docsGraph } from '../docs/docs.graph'",
|
|
221
|
+
"",
|
|
222
|
+
"const docsConfig = defineDocsConfig({",
|
|
223
|
+
" graph: docsGraph,",
|
|
224
|
+
" siteTitle: 'My Docs',",
|
|
225
|
+
" siteDescription: 'Documentation site powered by @unterberg/nivel.',",
|
|
226
|
+
" basePath: '/docs',",
|
|
227
|
+
"})",
|
|
228
|
+
"",
|
|
229
|
+
"export default docsConfig",
|
|
230
|
+
""
|
|
231
|
+
].join("\n");
|
|
232
|
+
};
|
|
233
|
+
var getDocsGraphTemplate = () => {
|
|
234
|
+
return [
|
|
235
|
+
"import { defineDocsGraph } from '@unterberg/nivel/config'",
|
|
236
|
+
"",
|
|
237
|
+
"export const docsGraph = defineDocsGraph({",
|
|
238
|
+
" items: [",
|
|
239
|
+
" {",
|
|
240
|
+
" kind: 'section',",
|
|
241
|
+
" id: 'docs',",
|
|
242
|
+
" title: 'Docs',",
|
|
243
|
+
" items: [",
|
|
244
|
+
" {",
|
|
245
|
+
" kind: 'page',",
|
|
246
|
+
" id: 'gettingStarted',",
|
|
247
|
+
" title: 'Getting Started',",
|
|
248
|
+
" slug: 'getting-started',",
|
|
249
|
+
" source: 'content/getting-started/content.mdx',",
|
|
250
|
+
" description: 'Getting started with @unterberg/nivel.',",
|
|
251
|
+
" },",
|
|
252
|
+
" ],",
|
|
253
|
+
" },",
|
|
254
|
+
" ],",
|
|
255
|
+
"})",
|
|
256
|
+
""
|
|
257
|
+
].join("\n");
|
|
258
|
+
};
|
|
259
|
+
var getConfigTemplate = () => {
|
|
260
|
+
return [
|
|
261
|
+
"import { createNivelVikeConfig } from '@unterberg/nivel/vike'",
|
|
262
|
+
"import docsConfig from './+docs'",
|
|
263
|
+
"",
|
|
264
|
+
"export { config }",
|
|
265
|
+
"",
|
|
266
|
+
"const config = createNivelVikeConfig(docsConfig)",
|
|
267
|
+
""
|
|
268
|
+
].join("\n");
|
|
269
|
+
};
|
|
270
|
+
var getHeadTemplate = () => {
|
|
271
|
+
return [
|
|
272
|
+
"import { MetaHead } from '@unterberg/nivel/client'",
|
|
273
|
+
"",
|
|
274
|
+
"export const Head = () => {",
|
|
275
|
+
" return <MetaHead />",
|
|
276
|
+
"}",
|
|
277
|
+
""
|
|
278
|
+
].join("\n");
|
|
279
|
+
};
|
|
280
|
+
var getLayoutTemplate = () => {
|
|
281
|
+
return [
|
|
282
|
+
"import { AppLayout } from '@unterberg/nivel/client'",
|
|
283
|
+
"import type { ReactNode } from 'react'",
|
|
284
|
+
"",
|
|
285
|
+
"const Layout = ({ children }: { children: ReactNode }) => {",
|
|
286
|
+
" return <AppLayout>{children}</AppLayout>",
|
|
287
|
+
"}",
|
|
288
|
+
"",
|
|
289
|
+
"export default Layout",
|
|
290
|
+
""
|
|
291
|
+
].join("\n");
|
|
292
|
+
};
|
|
293
|
+
var getGlobalContextTemplate = () => {
|
|
294
|
+
return [
|
|
295
|
+
"import { docsGlobalContextData } from './(nivel-generated)/_docsGlobalContext'",
|
|
296
|
+
"",
|
|
297
|
+
"export const onCreateGlobalContext = (globalContext: { docs?: typeof docsGlobalContextData }) => {",
|
|
298
|
+
" globalContext.docs = docsGlobalContextData",
|
|
299
|
+
"}",
|
|
300
|
+
""
|
|
301
|
+
].join("\n");
|
|
302
|
+
};
|
|
303
|
+
var getWrapperTemplate = () => {
|
|
304
|
+
return [
|
|
305
|
+
"import type { ReactNode } from 'react'",
|
|
306
|
+
"",
|
|
307
|
+
"const Wrapper = ({ children }: { children: ReactNode }) => {",
|
|
308
|
+
" return <>{children}</>",
|
|
309
|
+
"}",
|
|
310
|
+
"",
|
|
311
|
+
"export default Wrapper",
|
|
312
|
+
""
|
|
313
|
+
].join("\n");
|
|
314
|
+
};
|
|
315
|
+
var getGlobalTypesTemplate = () => {
|
|
316
|
+
return [
|
|
317
|
+
"declare module '*.mdx' {",
|
|
318
|
+
" import type { ComponentType } from 'react'",
|
|
319
|
+
"",
|
|
320
|
+
" const MdxComponent: ComponentType",
|
|
321
|
+
" export default MdxComponent",
|
|
322
|
+
"}",
|
|
323
|
+
"",
|
|
324
|
+
"declare module '*.css'",
|
|
325
|
+
"",
|
|
326
|
+
"declare global {",
|
|
327
|
+
" namespace Vike {",
|
|
328
|
+
" interface GlobalContext {",
|
|
329
|
+
" docs: import('@unterberg/nivel').DocsGlobalContextData",
|
|
330
|
+
" }",
|
|
331
|
+
" }",
|
|
332
|
+
"}",
|
|
333
|
+
""
|
|
334
|
+
].join("\n");
|
|
335
|
+
};
|
|
336
|
+
var getManagedFileEntries = () => {
|
|
337
|
+
return [
|
|
338
|
+
["pages/+docs.ts", getDocsConfigTemplate()],
|
|
339
|
+
["docs/docs.graph.ts", getDocsGraphTemplate()],
|
|
340
|
+
["pages/+config.ts", getConfigTemplate()],
|
|
341
|
+
["pages/+Head.tsx", getHeadTemplate()],
|
|
342
|
+
["pages/+Layout.tsx", getLayoutTemplate()],
|
|
343
|
+
["pages/+onCreateGlobalContext.ts", getGlobalContextTemplate()],
|
|
344
|
+
["pages/+Wrapper.tsx", getWrapperTemplate()],
|
|
345
|
+
["global.d.ts", getGlobalTypesTemplate()]
|
|
346
|
+
];
|
|
347
|
+
};
|
|
348
|
+
var getGenerateDocsRunner = (packageJson) => {
|
|
349
|
+
const packageManager = packageJson.packageManager?.trim() ?? "";
|
|
350
|
+
if (packageManager.startsWith("pnpm@")) {
|
|
351
|
+
return "pnpm generate:docs";
|
|
352
|
+
}
|
|
353
|
+
if (packageManager.startsWith("npm@")) {
|
|
354
|
+
return "npm run generate:docs";
|
|
355
|
+
}
|
|
356
|
+
return "npm run generate:docs";
|
|
357
|
+
};
|
|
358
|
+
var getManagedScripts = (packageJson) => {
|
|
359
|
+
const generateDocsRunner = getGenerateDocsRunner(packageJson);
|
|
360
|
+
return {
|
|
361
|
+
"generate:docs": "nivel prepare",
|
|
362
|
+
predev: generateDocsRunner,
|
|
363
|
+
prebuild: generateDocsRunner,
|
|
364
|
+
pretypecheck: generateDocsRunner
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
var writeFile = (filePath, source) => {
|
|
368
|
+
fs2.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
369
|
+
fs2.writeFileSync(filePath, source);
|
|
370
|
+
};
|
|
371
|
+
var writeManagedFile = (rootDir, relativeFilePath, source, force, result) => {
|
|
372
|
+
const filePath = path3.join(rootDir, relativeFilePath);
|
|
373
|
+
const exists = fs2.existsSync(filePath);
|
|
374
|
+
if (exists && !force) {
|
|
375
|
+
result.skippedFiles.push(relativeFilePath);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
writeFile(filePath, source);
|
|
379
|
+
if (exists) {
|
|
380
|
+
result.overwrittenFiles.push(relativeFilePath);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
result.createdFiles.push(relativeFilePath);
|
|
384
|
+
};
|
|
385
|
+
var readPackageJson = (rootDir) => {
|
|
386
|
+
const packageJsonPath = path3.join(rootDir, "package.json");
|
|
387
|
+
if (!fs2.existsSync(packageJsonPath)) {
|
|
388
|
+
throw new Error(`Expected package.json in ${rootDir}`);
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
packageJson: JSON.parse(fs2.readFileSync(packageJsonPath, "utf8")),
|
|
392
|
+
packageJsonPath
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
var patchPackageScripts = (packageJson, packageJsonPath, result) => {
|
|
396
|
+
const scripts = { ...packageJson.scripts ?? {} };
|
|
397
|
+
const managedScripts = getManagedScripts(packageJson);
|
|
398
|
+
for (const scriptName of MANAGED_SCRIPT_NAMES) {
|
|
399
|
+
if (scripts[scriptName] === managedScripts[scriptName]) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
scripts[scriptName] = managedScripts[scriptName];
|
|
403
|
+
result.updatedScripts.push(scriptName);
|
|
404
|
+
}
|
|
405
|
+
const nextPackageJson = {
|
|
406
|
+
...packageJson,
|
|
407
|
+
scripts
|
|
408
|
+
};
|
|
409
|
+
fs2.writeFileSync(packageJsonPath, `${JSON.stringify(nextPackageJson, null, 2)}
|
|
410
|
+
`);
|
|
411
|
+
};
|
|
412
|
+
var getMissingDependencies = (packageJson) => {
|
|
413
|
+
const installed = /* @__PURE__ */ new Set([
|
|
414
|
+
...Object.keys(packageJson.dependencies ?? {}),
|
|
415
|
+
...Object.keys(packageJson.devDependencies ?? {})
|
|
416
|
+
]);
|
|
417
|
+
return [...REQUIRED_DEPENDENCIES, ...REQUIRED_DEV_DEPENDENCIES].filter((packageName) => !installed.has(packageName));
|
|
418
|
+
};
|
|
419
|
+
var getInitSummary = (result) => {
|
|
420
|
+
const lines = ["Initialized nivel consumer scaffolding."];
|
|
421
|
+
if (result.createdFiles.length > 0) {
|
|
422
|
+
lines.push(`Created files: ${result.createdFiles.join(", ")}`);
|
|
423
|
+
}
|
|
424
|
+
if (result.overwrittenFiles.length > 0) {
|
|
425
|
+
lines.push(`Overwritten files: ${result.overwrittenFiles.join(", ")}`);
|
|
426
|
+
}
|
|
427
|
+
if (result.skippedFiles.length > 0) {
|
|
428
|
+
lines.push(`Skipped existing files: ${result.skippedFiles.join(", ")}`);
|
|
429
|
+
}
|
|
430
|
+
if (result.updatedScripts.length > 0) {
|
|
431
|
+
lines.push(`Updated package.json scripts: ${result.updatedScripts.join(", ")}`);
|
|
432
|
+
}
|
|
433
|
+
if (result.missingDependencies.length > 0) {
|
|
434
|
+
lines.push(`Missing dependencies: ${result.missingDependencies.join(", ")}`);
|
|
435
|
+
lines.push(
|
|
436
|
+
"Consumer CSS stays hand-authored. Add your stylesheet import manually, for example in pages/+Wrapper.tsx."
|
|
437
|
+
);
|
|
438
|
+
} else if (!result.allDependenciesPresent) {
|
|
439
|
+
lines.push("Dependency validation completed with warnings.");
|
|
440
|
+
} else {
|
|
441
|
+
lines.push("All required dependencies are already present.");
|
|
442
|
+
}
|
|
443
|
+
return `${lines.join("\n")}
|
|
444
|
+
`;
|
|
445
|
+
};
|
|
446
|
+
var initConsumer = (options) => {
|
|
447
|
+
const result = {
|
|
448
|
+
allDependenciesPresent: true,
|
|
449
|
+
createdFiles: [],
|
|
450
|
+
missingDependencies: [],
|
|
451
|
+
overwrittenFiles: [],
|
|
452
|
+
skippedFiles: [],
|
|
453
|
+
updatedScripts: []
|
|
454
|
+
};
|
|
455
|
+
const { packageJson, packageJsonPath } = readPackageJson(options.rootDir);
|
|
456
|
+
for (const [relativeFilePath, source] of getManagedFileEntries()) {
|
|
457
|
+
writeManagedFile(options.rootDir, relativeFilePath, source, options.force, result);
|
|
458
|
+
}
|
|
459
|
+
patchPackageScripts(packageJson, packageJsonPath, result);
|
|
460
|
+
result.missingDependencies = getMissingDependencies(packageJson);
|
|
461
|
+
result.allDependenciesPresent = result.missingDependencies.length === 0;
|
|
462
|
+
return result;
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
export {
|
|
466
|
+
getGeneratedPagesRoot,
|
|
467
|
+
syncGeneratedDocsPages,
|
|
468
|
+
isDocsSourcePath,
|
|
469
|
+
loadDocsConfig,
|
|
470
|
+
getInitSummary,
|
|
471
|
+
initConsumer
|
|
472
|
+
};
|
|
473
|
+
//# sourceMappingURL=chunk-67GE3PJ6.js.map
|