specra-cli 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.MD +33 -0
- package/README.md +246 -0
- package/dist/api-client-VHQARPDT.js +15 -0
- package/dist/api-client-VHQARPDT.js.map +1 -0
- package/dist/chunk-5765WX4D.js +192 -0
- package/dist/chunk-5765WX4D.js.map +1 -0
- package/dist/chunk-72RDEJR2.js +94 -0
- package/dist/chunk-72RDEJR2.js.map +1 -0
- package/dist/chunk-SQ2MMFUZ.js +102 -0
- package/dist/chunk-SQ2MMFUZ.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +242 -0
- package/dist/cli.js.map +1 -0
- package/dist/deploy-V4JO2D6B.js +179 -0
- package/dist/deploy-V4JO2D6B.js.map +1 -0
- package/dist/doctor-ICALAJ4N.js +309 -0
- package/dist/doctor-ICALAJ4N.js.map +1 -0
- package/dist/login-UG3WU7DY.js +92 -0
- package/dist/login-UG3WU7DY.js.map +1 -0
- package/dist/logout-WJKHJZT6.js +24 -0
- package/dist/logout-WJKHJZT6.js.map +1 -0
- package/dist/logs-BLUJPWNO.js +77 -0
- package/dist/logs-BLUJPWNO.js.map +1 -0
- package/dist/projects-LJ57GK3D.js +49 -0
- package/dist/projects-LJ57GK3D.js.map +1 -0
- package/package.json +50 -0
- package/templates/book-docs/.env.sample +1 -0
- package/templates/book-docs/docs/v1.0.0/concepts.mdx +89 -0
- package/templates/book-docs/docs/v1.0.0/content/_category_.json +7 -0
- package/templates/book-docs/docs/v1.0.0/content/formatting.mdx +128 -0
- package/templates/book-docs/docs/v1.0.0/content/reusable-content.mdx +116 -0
- package/templates/book-docs/docs/v1.0.0/content/structure.mdx +92 -0
- package/templates/book-docs/docs/v1.0.0/customization/_category_.json +7 -0
- package/templates/book-docs/docs/v1.0.0/customization/branding.mdx +115 -0
- package/templates/book-docs/docs/v1.0.0/customization/themes.mdx +81 -0
- package/templates/book-docs/docs/v1.0.0/introduction.mdx +38 -0
- package/templates/book-docs/docs/v1.0.0/quickstart.mdx +112 -0
- package/templates/book-docs/docs/v2.0.0/concepts.mdx +89 -0
- package/templates/book-docs/docs/v2.0.0/content/_category_.json +7 -0
- package/templates/book-docs/docs/v2.0.0/content/formatting.mdx +128 -0
- package/templates/book-docs/docs/v2.0.0/content/reusable-content.mdx +116 -0
- package/templates/book-docs/docs/v2.0.0/content/structure.mdx +92 -0
- package/templates/book-docs/docs/v2.0.0/customization/_category_.json +7 -0
- package/templates/book-docs/docs/v2.0.0/customization/branding.mdx +115 -0
- package/templates/book-docs/docs/v2.0.0/customization/themes.mdx +81 -0
- package/templates/book-docs/docs/v2.0.0/introduction.mdx +39 -0
- package/templates/book-docs/docs/v2.0.0/quickstart.mdx +112 -0
- package/templates/book-docs/gitignore +7 -0
- package/templates/book-docs/package.json +28 -0
- package/templates/book-docs/postcss.config.mjs +8 -0
- package/templates/book-docs/public/api-specs/openapi-example.json +259 -0
- package/templates/book-docs/public/api-specs/postman-example.json +205 -0
- package/templates/book-docs/public/api-specs/test-api.json +256 -0
- package/templates/book-docs/public/api-specs/users-api.json +264 -0
- package/templates/book-docs/specra.config.json +77 -0
- package/templates/book-docs/src/app.css +86 -0
- package/templates/book-docs/src/app.html +17 -0
- package/templates/book-docs/src/params/product.ts +7 -0
- package/templates/book-docs/src/routes/+layout.server.ts +14 -0
- package/templates/book-docs/src/routes/+layout.svelte +21 -0
- package/templates/book-docs/src/routes/+page.server.ts +9 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
- package/templates/book-docs/src/routes/docs/[version]/+layout.server.ts +42 -0
- package/templates/book-docs/src/routes/docs/[version]/+page.server.ts +27 -0
- package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
- package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
- package/templates/book-docs/static/favicon.svg +4 -0
- package/templates/book-docs/svelte.config.js +13 -0
- package/templates/book-docs/tsconfig.json +12 -0
- package/templates/book-docs/vite.config.ts +6 -0
- package/templates/jbrains-docs/.env.sample +1 -0
- package/templates/jbrains-docs/docs/v1.0.0/advanced/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v1.0.0/advanced/async.mdx +95 -0
- package/templates/jbrains-docs/docs/v1.0.0/advanced/generics.mdx +126 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/control-flow.mdx +106 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/syntax.mdx +129 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/types.mdx +135 -0
- package/templates/jbrains-docs/docs/v1.0.0/getting-started.mdx +111 -0
- package/templates/jbrains-docs/docs/v1.0.0/home.mdx +37 -0
- package/templates/jbrains-docs/docs/v1.0.0/tools/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v1.0.0/tools/build-tools.mdx +165 -0
- package/templates/jbrains-docs/docs/v1.0.0/tools/testing.mdx +112 -0
- package/templates/jbrains-docs/docs/v2.0.0/advanced/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v2.0.0/advanced/async.mdx +95 -0
- package/templates/jbrains-docs/docs/v2.0.0/advanced/generics.mdx +126 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/control-flow.mdx +106 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/syntax.mdx +129 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/types.mdx +135 -0
- package/templates/jbrains-docs/docs/v2.0.0/getting-started.mdx +111 -0
- package/templates/jbrains-docs/docs/v2.0.0/home.mdx +37 -0
- package/templates/jbrains-docs/docs/v2.0.0/tools/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v2.0.0/tools/build-tools.mdx +165 -0
- package/templates/jbrains-docs/docs/v2.0.0/tools/testing.mdx +112 -0
- package/templates/jbrains-docs/gitignore +7 -0
- package/templates/jbrains-docs/package.json +28 -0
- package/templates/jbrains-docs/postcss.config.mjs +8 -0
- package/templates/jbrains-docs/public/api-specs/openapi-example.json +259 -0
- package/templates/jbrains-docs/public/api-specs/postman-example.json +205 -0
- package/templates/jbrains-docs/public/api-specs/test-api.json +256 -0
- package/templates/jbrains-docs/public/api-specs/users-api.json +264 -0
- package/templates/jbrains-docs/specra.config.json +80 -0
- package/templates/jbrains-docs/src/app.css +86 -0
- package/templates/jbrains-docs/src/app.html +17 -0
- package/templates/jbrains-docs/src/params/product.ts +7 -0
- package/templates/jbrains-docs/src/routes/+layout.server.ts +14 -0
- package/templates/jbrains-docs/src/routes/+layout.svelte +21 -0
- package/templates/jbrains-docs/src/routes/+page.server.ts +9 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/+layout.server.ts +42 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/+page.server.ts +27 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
- package/templates/jbrains-docs/static/favicon.svg +4 -0
- package/templates/jbrains-docs/svelte.config.js +13 -0
- package/templates/jbrains-docs/tsconfig.json +12 -0
- package/templates/jbrains-docs/vite.config.ts +6 -0
- package/templates/minimal/.env.sample +1 -0
- package/templates/minimal/docs/v1.0.0/about.mdx +57 -0
- package/templates/minimal/docs/v1.0.0/components/_category_.json +8 -0
- package/templates/minimal/docs/v1.0.0/components/callout.mdx +83 -0
- package/templates/minimal/docs/v1.0.0/components/code-block.mdx +103 -0
- package/templates/minimal/docs/v1.0.0/components/index.mdx +8 -0
- package/templates/minimal/docs/v1.0.0/components/tabs.mdx +92 -0
- package/templates/minimal/docs/v1.0.0/configuration.mdx +322 -0
- package/templates/minimal/docs/v1.0.0/features.mdx +197 -0
- package/templates/minimal/docs/v1.0.0/getting-started.mdx +183 -0
- package/templates/minimal/docs/v2.0.0/about.mdx +57 -0
- package/templates/minimal/docs/v2.0.0/components/_category_.json +8 -0
- package/templates/minimal/docs/v2.0.0/components/callout.mdx +83 -0
- package/templates/minimal/docs/v2.0.0/components/code-block.mdx +103 -0
- package/templates/minimal/docs/v2.0.0/components/index.mdx +8 -0
- package/templates/minimal/docs/v2.0.0/components/tabs.mdx +92 -0
- package/templates/minimal/docs/v2.0.0/configuration.mdx +322 -0
- package/templates/minimal/docs/v2.0.0/features.mdx +197 -0
- package/templates/minimal/docs/v2.0.0/getting-started.mdx +183 -0
- package/templates/minimal/gitignore +7 -0
- package/templates/minimal/package.json +29 -0
- package/templates/minimal/postcss.config.mjs +8 -0
- package/templates/minimal/specra.config.json +91 -0
- package/templates/minimal/src/app.css +86 -0
- package/templates/minimal/src/app.html +17 -0
- package/templates/minimal/src/hooks.server.ts +8 -0
- package/templates/minimal/src/params/product.ts +7 -0
- package/templates/minimal/src/routes/+error.svelte +10 -0
- package/templates/minimal/src/routes/+layout.server.ts +14 -0
- package/templates/minimal/src/routes/+layout.svelte +21 -0
- package/templates/minimal/src/routes/+page.server.ts +9 -0
- package/templates/minimal/src/routes/+page.svelte +149 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
- package/templates/minimal/src/routes/docs/[version]/+layout.server.ts +42 -0
- package/templates/minimal/src/routes/docs/[version]/+page.server.ts +27 -0
- package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
- package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
- package/templates/minimal/static/api-specs/openapi-example.json +259 -0
- package/templates/minimal/static/api-specs/postman-example.json +205 -0
- package/templates/minimal/static/api-specs/test-api.json +256 -0
- package/templates/minimal/static/api-specs/users-api.json +264 -0
- package/templates/minimal/static/favicon.svg +4 -0
- package/templates/minimal/svelte.config.js +13 -0
- package/templates/minimal/tsconfig.json +12 -0
- package/templates/minimal/vite.config.ts +6 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%sveltekit.assets%/favicon.svg" type="image/svg+xml" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap"
|
|
11
|
+
rel="stylesheet">
|
|
12
|
+
%sveltekit.head%
|
|
13
|
+
</head>
|
|
14
|
+
<body data-sveltekit-preload-data="hover" class="font-sans antialiased">
|
|
15
|
+
<div style="display: contents">%sveltekit.body%</div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getConfig, initConfig } from 'specra';
|
|
2
|
+
import specraConfig from '../../specra.config.json';
|
|
3
|
+
import type { LayoutServerLoad } from './$types';
|
|
4
|
+
import type { SpecraConfig } from 'specra';
|
|
5
|
+
|
|
6
|
+
initConfig(specraConfig as unknown as Partial<SpecraConfig>);
|
|
7
|
+
|
|
8
|
+
export const prerender = true;
|
|
9
|
+
export const trailingSlash = 'always';
|
|
10
|
+
|
|
11
|
+
export const load: LayoutServerLoad = async () => {
|
|
12
|
+
const config = getConfig();
|
|
13
|
+
return { config };
|
|
14
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import '../app.css';
|
|
3
|
+
import { LayoutProviders } from 'specra/components';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
import type { LayoutData } from './$types';
|
|
6
|
+
|
|
7
|
+
let { data, children }: { data: LayoutData; children: Snippet } = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<svelte:head>
|
|
11
|
+
<title>{data?.config?.site?.title || 'Documentation'}</title>
|
|
12
|
+
<meta name="description" content={data?.config?.site?.description || 'Modern documentation platform'} />
|
|
13
|
+
</svelte:head>
|
|
14
|
+
|
|
15
|
+
{#if data?.config}
|
|
16
|
+
<LayoutProviders config={data.config}>
|
|
17
|
+
{@render children?.()}
|
|
18
|
+
</LayoutProviders>
|
|
19
|
+
{:else}
|
|
20
|
+
{@render children?.()}
|
|
21
|
+
{/if}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { redirect } from '@sveltejs/kit';
|
|
2
|
+
import { getConfig } from 'specra';
|
|
3
|
+
import type { PageServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: PageServerLoad = async () => {
|
|
6
|
+
const config = getConfig();
|
|
7
|
+
const activeVersion = config.site?.activeVersion || 'v1.0.0';
|
|
8
|
+
redirect(302, `/docs/${activeVersion}/home`);
|
|
9
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getCachedVersions, getCachedAllDocs, getEffectiveConfig, getI18nConfig, getVersionsMeta, getProducts, loadVersionConfig } from 'specra';
|
|
2
|
+
import { redirect } from '@sveltejs/kit';
|
|
3
|
+
import type { LayoutServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: LayoutServerLoad = async ({ params }) => {
|
|
6
|
+
const { product, version } = params;
|
|
7
|
+
|
|
8
|
+
// Verify this is a valid product — if not, fall through to 404
|
|
9
|
+
const products = getProducts();
|
|
10
|
+
const matchedProduct = products.find(p => p.slug === product);
|
|
11
|
+
if (!matchedProduct) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const i18nConfig = getI18nConfig();
|
|
16
|
+
const defaultLocale = i18nConfig?.defaultLocale || 'en';
|
|
17
|
+
|
|
18
|
+
// Block access to hidden versions — redirect to product's active version
|
|
19
|
+
const currentVersionConfig = loadVersionConfig(version, product);
|
|
20
|
+
if (currentVersionConfig?.hidden) {
|
|
21
|
+
const config = getEffectiveConfig(version, product);
|
|
22
|
+
const activeVersion = matchedProduct.config.activeVersion || config.site?.activeVersion || 'v1.0.0';
|
|
23
|
+
throw redirect(302, `/docs/${product}/${activeVersion}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const allDocs = await getCachedAllDocs(version, defaultLocale, product);
|
|
27
|
+
const versions = getCachedVersions(product);
|
|
28
|
+
const config = getEffectiveConfig(version, product);
|
|
29
|
+
const versionsMeta = getVersionsMeta(versions, product);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
allDocs,
|
|
33
|
+
versions,
|
|
34
|
+
versionsMeta,
|
|
35
|
+
config,
|
|
36
|
+
product,
|
|
37
|
+
products,
|
|
38
|
+
versionBanner: currentVersionConfig?.banner,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { redirect } from '@sveltejs/kit';
|
|
2
|
+
import { getCachedAllDocs, getProducts } from 'specra';
|
|
3
|
+
import type { PageServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
6
|
+
const { product, version } = params;
|
|
7
|
+
|
|
8
|
+
// Verify product exists
|
|
9
|
+
const products = getProducts();
|
|
10
|
+
const matchedProduct = products.find(p => p.slug === product);
|
|
11
|
+
if (!matchedProduct) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const docs = await getCachedAllDocs(version, undefined, product);
|
|
16
|
+
|
|
17
|
+
if (docs.length === 0) {
|
|
18
|
+
const activeVersion = matchedProduct.config.activeVersion || 'v1.0.0';
|
|
19
|
+
redirect(302, `/docs/${product}/${activeVersion}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Redirect to first doc in this product's version
|
|
23
|
+
redirect(302, `/docs/${product}/${version}/${docs[0].slug}`);
|
|
24
|
+
};
|
package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractTableOfContents,
|
|
3
|
+
getAdjacentDocs,
|
|
4
|
+
isCategoryPage,
|
|
5
|
+
getCachedAllDocs,
|
|
6
|
+
getCachedDocBySlug,
|
|
7
|
+
getI18nConfig,
|
|
8
|
+
getProducts,
|
|
9
|
+
} from 'specra';
|
|
10
|
+
import type { PageServerLoad } from './$types';
|
|
11
|
+
|
|
12
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
13
|
+
const { product, version, slug: slugArray } = params;
|
|
14
|
+
const slug = slugArray.replace(/\/$/, '');
|
|
15
|
+
|
|
16
|
+
// Verify product exists
|
|
17
|
+
const products = getProducts();
|
|
18
|
+
const matchedProduct = products.find(p => p.slug === product);
|
|
19
|
+
if (!matchedProduct) {
|
|
20
|
+
return {
|
|
21
|
+
version,
|
|
22
|
+
slug,
|
|
23
|
+
product,
|
|
24
|
+
isCategory: false,
|
|
25
|
+
isNotFound: true,
|
|
26
|
+
doc: null,
|
|
27
|
+
categoryTitle: null,
|
|
28
|
+
categoryDescription: null,
|
|
29
|
+
categoryTabGroup: undefined,
|
|
30
|
+
toc: [],
|
|
31
|
+
previous: null,
|
|
32
|
+
next: null,
|
|
33
|
+
title: 'Page Not Found',
|
|
34
|
+
description: 'The requested documentation page could not be found.',
|
|
35
|
+
ogUrl: `/docs/${product}/${version}/${slug}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const i18nConfig = getI18nConfig();
|
|
40
|
+
const slugParts = slug.split('/');
|
|
41
|
+
let locale: string | undefined;
|
|
42
|
+
if (i18nConfig && i18nConfig.locales.includes(slugParts[0])) {
|
|
43
|
+
locale = slugParts[0];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const allDocs = await getCachedAllDocs(version, locale, product);
|
|
47
|
+
const isCategory = isCategoryPage(slug, allDocs);
|
|
48
|
+
const doc = await getCachedDocBySlug(slug, version, product);
|
|
49
|
+
const urlPrefix = `/docs/${product}/${version}`;
|
|
50
|
+
|
|
51
|
+
let title = 'Page Not Found';
|
|
52
|
+
let description = 'The requested documentation page could not be found.';
|
|
53
|
+
let ogUrl = `${urlPrefix}/${slug}`;
|
|
54
|
+
|
|
55
|
+
if (doc) {
|
|
56
|
+
title = doc.meta.title || doc.title;
|
|
57
|
+
description = doc.meta.description || `Documentation for ${title}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!doc && isCategory) {
|
|
61
|
+
const categoryDoc = allDocs.find((d) => d.slug.startsWith(slug + '/'));
|
|
62
|
+
const categoryTabGroup = categoryDoc?.meta?.tab_group || categoryDoc?.categoryTabGroup;
|
|
63
|
+
const categoryTitle = slug
|
|
64
|
+
.split('/')
|
|
65
|
+
.pop()
|
|
66
|
+
?.replace(/-/g, ' ')
|
|
67
|
+
.replace(/\b\w/g, (l) => l.toUpperCase()) || 'Category';
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
version,
|
|
71
|
+
slug,
|
|
72
|
+
product,
|
|
73
|
+
isCategory: true,
|
|
74
|
+
isNotFound: false,
|
|
75
|
+
doc: null,
|
|
76
|
+
categoryTitle,
|
|
77
|
+
categoryDescription: 'Browse the documentation in this section.',
|
|
78
|
+
categoryTabGroup,
|
|
79
|
+
toc: [],
|
|
80
|
+
previous: null,
|
|
81
|
+
next: null,
|
|
82
|
+
title,
|
|
83
|
+
description,
|
|
84
|
+
ogUrl,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!doc) {
|
|
89
|
+
return {
|
|
90
|
+
version,
|
|
91
|
+
slug,
|
|
92
|
+
product,
|
|
93
|
+
isCategory: false,
|
|
94
|
+
isNotFound: true,
|
|
95
|
+
doc: null,
|
|
96
|
+
categoryTitle: null,
|
|
97
|
+
categoryDescription: null,
|
|
98
|
+
categoryTabGroup: undefined,
|
|
99
|
+
toc: [],
|
|
100
|
+
previous: null,
|
|
101
|
+
next: null,
|
|
102
|
+
title,
|
|
103
|
+
description,
|
|
104
|
+
ogUrl,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const toc = extractTableOfContents(doc.meta.content || doc.content);
|
|
109
|
+
const { previous, next } = getAdjacentDocs(slug, allDocs);
|
|
110
|
+
const showCategoryIndex = isCategory && !!doc;
|
|
111
|
+
const matchingDoc = allDocs.find((d) => d.slug === slug);
|
|
112
|
+
const currentPageTabGroup = doc.meta?.tab_group || matchingDoc?.categoryTabGroup;
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
version,
|
|
116
|
+
slug,
|
|
117
|
+
product,
|
|
118
|
+
isCategory: showCategoryIndex,
|
|
119
|
+
isNotFound: false,
|
|
120
|
+
doc,
|
|
121
|
+
categoryTitle: null,
|
|
122
|
+
categoryDescription: null,
|
|
123
|
+
categoryTabGroup: currentPageTabGroup,
|
|
124
|
+
toc,
|
|
125
|
+
previous: previous ? { title: previous.meta.title, slug: previous.slug } : null,
|
|
126
|
+
next: next ? { title: next.meta.title, slug: next.slug } : null,
|
|
127
|
+
title,
|
|
128
|
+
description,
|
|
129
|
+
ogUrl,
|
|
130
|
+
};
|
|
131
|
+
};
|
package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
TableOfContents,
|
|
4
|
+
Header,
|
|
5
|
+
TabGroups,
|
|
6
|
+
DocLayout,
|
|
7
|
+
CategoryIndex,
|
|
8
|
+
HotReloadIndicator,
|
|
9
|
+
DevModeBadge,
|
|
10
|
+
MdxHotReload,
|
|
11
|
+
MdxContent,
|
|
12
|
+
NotFoundContent,
|
|
13
|
+
SearchHighlight,
|
|
14
|
+
MobileDocLayout,
|
|
15
|
+
mdxComponents,
|
|
16
|
+
} from 'specra/components';
|
|
17
|
+
import type { PageData } from './$types';
|
|
18
|
+
|
|
19
|
+
let { data }: { data: PageData } = $props();
|
|
20
|
+
|
|
21
|
+
let allDocsCompat: any[] = $derived(data.allDocs);
|
|
22
|
+
let previousDoc = $derived(data.previous ?? undefined);
|
|
23
|
+
let nextDoc = $derived(data.next ?? undefined);
|
|
24
|
+
let categoryTitle = $derived(data.categoryTitle ?? undefined);
|
|
25
|
+
let categoryDescription = $derived(data.categoryDescription ?? undefined);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<svelte:head>
|
|
29
|
+
<title>{data.title}</title>
|
|
30
|
+
<meta name="description" content={data.description} />
|
|
31
|
+
<meta property="og:title" content={data.title} />
|
|
32
|
+
<meta property="og:description" content={data.description} />
|
|
33
|
+
<meta property="og:url" content={data.ogUrl} />
|
|
34
|
+
<meta property="og:site_name" content="Documentation Platform" />
|
|
35
|
+
<meta property="og:type" content="article" />
|
|
36
|
+
<meta property="og:locale" content="en_US" />
|
|
37
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
38
|
+
<meta name="twitter:title" content={data.title} />
|
|
39
|
+
<meta name="twitter:description" content={data.description} />
|
|
40
|
+
<link rel="canonical" href={data.ogUrl} />
|
|
41
|
+
</svelte:head>
|
|
42
|
+
|
|
43
|
+
{#if !data.doc && data.isCategory}
|
|
44
|
+
<!-- Category page without doc content -->
|
|
45
|
+
<MobileDocLayout
|
|
46
|
+
docs={allDocsCompat}
|
|
47
|
+
version={data.version}
|
|
48
|
+
product={data.product}
|
|
49
|
+
config={data.config}
|
|
50
|
+
activeTabGroup={data.categoryTabGroup}
|
|
51
|
+
>
|
|
52
|
+
{#snippet header()}
|
|
53
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config} products={data.products} currentProduct={data.product}>
|
|
54
|
+
{#snippet subheader()}
|
|
55
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
56
|
+
<TabGroups
|
|
57
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
58
|
+
activeTabId={data.categoryTabGroup}
|
|
59
|
+
docs={allDocsCompat}
|
|
60
|
+
version={data.version}
|
|
61
|
+
product={data.product}
|
|
62
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
63
|
+
/>
|
|
64
|
+
{/if}
|
|
65
|
+
{/snippet}
|
|
66
|
+
</Header>
|
|
67
|
+
{/snippet}
|
|
68
|
+
<CategoryIndex
|
|
69
|
+
categoryPath={data.slug}
|
|
70
|
+
version={data.version}
|
|
71
|
+
product={data.product}
|
|
72
|
+
allDocs={allDocsCompat}
|
|
73
|
+
title={categoryTitle}
|
|
74
|
+
description={categoryDescription}
|
|
75
|
+
config={data.config}
|
|
76
|
+
/>
|
|
77
|
+
</MobileDocLayout>
|
|
78
|
+
<MdxHotReload />
|
|
79
|
+
<HotReloadIndicator />
|
|
80
|
+
<DevModeBadge />
|
|
81
|
+
{:else if data.isNotFound}
|
|
82
|
+
<!-- Not found -->
|
|
83
|
+
<MobileDocLayout
|
|
84
|
+
docs={allDocsCompat}
|
|
85
|
+
version={data.version}
|
|
86
|
+
product={data.product}
|
|
87
|
+
config={data.config}
|
|
88
|
+
>
|
|
89
|
+
{#snippet header()}
|
|
90
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config} products={data.products} currentProduct={data.product}>
|
|
91
|
+
{#snippet subheader()}
|
|
92
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
93
|
+
<TabGroups
|
|
94
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
95
|
+
activeTabId={data.categoryTabGroup}
|
|
96
|
+
docs={allDocsCompat}
|
|
97
|
+
version={data.version}
|
|
98
|
+
product={data.product}
|
|
99
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
100
|
+
/>
|
|
101
|
+
{/if}
|
|
102
|
+
{/snippet}
|
|
103
|
+
</Header>
|
|
104
|
+
{/snippet}
|
|
105
|
+
<NotFoundContent version={data.version} />
|
|
106
|
+
</MobileDocLayout>
|
|
107
|
+
<MdxHotReload />
|
|
108
|
+
<HotReloadIndicator />
|
|
109
|
+
<DevModeBadge />
|
|
110
|
+
{:else if data.doc}
|
|
111
|
+
<!-- Normal doc or category with doc content -->
|
|
112
|
+
<MobileDocLayout
|
|
113
|
+
docs={allDocsCompat}
|
|
114
|
+
version={data.version}
|
|
115
|
+
product={data.product}
|
|
116
|
+
config={data.config}
|
|
117
|
+
activeTabGroup={data.categoryTabGroup}
|
|
118
|
+
>
|
|
119
|
+
{#snippet header()}
|
|
120
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config} products={data.products} currentProduct={data.product}>
|
|
121
|
+
{#snippet subheader()}
|
|
122
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
123
|
+
<TabGroups
|
|
124
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
125
|
+
activeTabId={data.categoryTabGroup}
|
|
126
|
+
docs={allDocsCompat}
|
|
127
|
+
version={data.version}
|
|
128
|
+
product={data.product}
|
|
129
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
130
|
+
/>
|
|
131
|
+
{/if}
|
|
132
|
+
{/snippet}
|
|
133
|
+
</Header>
|
|
134
|
+
{/snippet}
|
|
135
|
+
{#snippet toc()}
|
|
136
|
+
{#if !data.isCategory}
|
|
137
|
+
<TableOfContents items={data.toc} config={data.config} />
|
|
138
|
+
{/if}
|
|
139
|
+
{/snippet}
|
|
140
|
+
|
|
141
|
+
{#if data.isCategory}
|
|
142
|
+
{#snippet categoryContent()}
|
|
143
|
+
{#if data.doc?.contentNodes}
|
|
144
|
+
<MdxContent nodes={data.doc.contentNodes} components={mdxComponents} />
|
|
145
|
+
{:else if data.doc?.content}
|
|
146
|
+
{@html data.doc.content}
|
|
147
|
+
{/if}
|
|
148
|
+
{/snippet}
|
|
149
|
+
<CategoryIndex
|
|
150
|
+
categoryPath={data.slug}
|
|
151
|
+
version={data.version}
|
|
152
|
+
allDocs={allDocsCompat}
|
|
153
|
+
title={data.doc.meta.title}
|
|
154
|
+
description={data.doc.meta.description}
|
|
155
|
+
content={categoryContent}
|
|
156
|
+
config={data.config}
|
|
157
|
+
/>
|
|
158
|
+
{:else}
|
|
159
|
+
<SearchHighlight />
|
|
160
|
+
<DocLayout
|
|
161
|
+
meta={data.doc.meta}
|
|
162
|
+
previousDoc={previousDoc}
|
|
163
|
+
nextDoc={nextDoc}
|
|
164
|
+
version={data.version}
|
|
165
|
+
slug={data.slug}
|
|
166
|
+
product={data.product}
|
|
167
|
+
config={data.config}
|
|
168
|
+
>
|
|
169
|
+
{#if data.doc.contentNodes}
|
|
170
|
+
<MdxContent nodes={data.doc.contentNodes} components={mdxComponents} />
|
|
171
|
+
{:else}
|
|
172
|
+
{@html data.doc.content}
|
|
173
|
+
{/if}
|
|
174
|
+
</DocLayout>
|
|
175
|
+
{/if}
|
|
176
|
+
</MobileDocLayout>
|
|
177
|
+
<MdxHotReload />
|
|
178
|
+
<HotReloadIndicator />
|
|
179
|
+
<DevModeBadge />
|
|
180
|
+
{/if}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getCachedVersions, getCachedAllDocs, getEffectiveConfig, getI18nConfig, getVersionsMeta, getProducts, loadVersionConfig } from 'specra';
|
|
2
|
+
import { redirect } from '@sveltejs/kit';
|
|
3
|
+
import type { LayoutServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: LayoutServerLoad = async ({ params }) => {
|
|
6
|
+
const { version } = params;
|
|
7
|
+
|
|
8
|
+
// Route disambiguation: if this "version" is actually a product slug,
|
|
9
|
+
// the +page.server.ts will handle the redirect. The layout still needs
|
|
10
|
+
// to return data for the version case.
|
|
11
|
+
const products = getProducts();
|
|
12
|
+
const isProduct = products.some(p => p.slug === version);
|
|
13
|
+
if (isProduct) {
|
|
14
|
+
// Return minimal data — the page will redirect before rendering
|
|
15
|
+
return { allDocs: [], versions: [], versionsMeta: [], config: getEffectiveConfig(''), products };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const i18nConfig = getI18nConfig();
|
|
19
|
+
const defaultLocale = i18nConfig?.defaultLocale || 'en';
|
|
20
|
+
|
|
21
|
+
// Block access to hidden versions — redirect to active version
|
|
22
|
+
const currentVersionConfig = loadVersionConfig(version);
|
|
23
|
+
if (currentVersionConfig?.hidden) {
|
|
24
|
+
const config = getEffectiveConfig(version);
|
|
25
|
+
const activeVersion = config.site?.activeVersion || 'v1.0.0';
|
|
26
|
+
throw redirect(302, `/docs/${activeVersion}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const allDocs = await getCachedAllDocs(version, defaultLocale);
|
|
30
|
+
const versions = getCachedVersions();
|
|
31
|
+
const config = getEffectiveConfig(version);
|
|
32
|
+
const versionsMeta = getVersionsMeta(versions);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
allDocs,
|
|
36
|
+
versions,
|
|
37
|
+
versionsMeta,
|
|
38
|
+
config,
|
|
39
|
+
products,
|
|
40
|
+
versionBanner: currentVersionConfig?.banner,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { redirect } from '@sveltejs/kit';
|
|
2
|
+
import { getCachedVersions, getCachedAllDocs, getProducts, getEffectiveConfig } from 'specra';
|
|
3
|
+
import type { PageServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
6
|
+
const { version } = params;
|
|
7
|
+
|
|
8
|
+
// Route disambiguation: check if this "version" is actually a product slug
|
|
9
|
+
const products = getProducts();
|
|
10
|
+
const matchedProduct = products.find(p => p.slug === version);
|
|
11
|
+
if (matchedProduct) {
|
|
12
|
+
// This is /docs/{product} — redirect to the product's active version
|
|
13
|
+
const config = getEffectiveConfig('', version);
|
|
14
|
+
const activeVersion = matchedProduct.config.activeVersion || config.site?.activeVersion || 'v1.0.0';
|
|
15
|
+
redirect(302, `/docs/${version}/${activeVersion}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Standard version route
|
|
19
|
+
const docs = await getCachedAllDocs(version);
|
|
20
|
+
|
|
21
|
+
if (docs.length === 0) {
|
|
22
|
+
redirect(302, '/docs/v1.0.0');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Redirect to first doc
|
|
26
|
+
redirect(302, `/docs/${version}/${docs[0].slug}`);
|
|
27
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractTableOfContents,
|
|
3
|
+
getAdjacentDocs,
|
|
4
|
+
isCategoryPage,
|
|
5
|
+
getCachedAllDocs,
|
|
6
|
+
getCachedDocBySlug,
|
|
7
|
+
getI18nConfig,
|
|
8
|
+
} from 'specra';
|
|
9
|
+
import type { PageServerLoad } from './$types';
|
|
10
|
+
|
|
11
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
12
|
+
const { version, slug: slugArray } = params;
|
|
13
|
+
const slug = slugArray.replace(/\/$/, '');
|
|
14
|
+
|
|
15
|
+
const i18nConfig = getI18nConfig();
|
|
16
|
+
const slugParts = slug.split('/');
|
|
17
|
+
let locale: string | undefined;
|
|
18
|
+
if (i18nConfig && i18nConfig.locales.includes(slugParts[0])) {
|
|
19
|
+
locale = slugParts[0];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const allDocs = await getCachedAllDocs(version, locale);
|
|
23
|
+
const isCategory = isCategoryPage(slug, allDocs);
|
|
24
|
+
const doc = await getCachedDocBySlug(slug, version);
|
|
25
|
+
|
|
26
|
+
let title = 'Page Not Found';
|
|
27
|
+
let description = 'The requested documentation page could not be found.';
|
|
28
|
+
let ogUrl = `/docs/${version}/${slug}`;
|
|
29
|
+
|
|
30
|
+
if (doc) {
|
|
31
|
+
title = doc.meta.title || doc.title;
|
|
32
|
+
description = doc.meta.description || `Documentation for ${title}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Category page without doc content
|
|
36
|
+
if (!doc && isCategory) {
|
|
37
|
+
const categoryDoc = allDocs.find((d) => d.slug.startsWith(slug + '/'));
|
|
38
|
+
const categoryTabGroup = categoryDoc?.meta?.tab_group || categoryDoc?.categoryTabGroup;
|
|
39
|
+
const categoryTitle = slug
|
|
40
|
+
.split('/')
|
|
41
|
+
.pop()
|
|
42
|
+
?.replace(/-/g, ' ')
|
|
43
|
+
.replace(/\b\w/g, (l) => l.toUpperCase()) || 'Category';
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
version,
|
|
47
|
+
slug,
|
|
48
|
+
isCategory: true,
|
|
49
|
+
isNotFound: false,
|
|
50
|
+
doc: null,
|
|
51
|
+
categoryTitle,
|
|
52
|
+
categoryDescription: 'Browse the documentation in this section.',
|
|
53
|
+
categoryTabGroup,
|
|
54
|
+
toc: [],
|
|
55
|
+
previous: null,
|
|
56
|
+
next: null,
|
|
57
|
+
title,
|
|
58
|
+
description,
|
|
59
|
+
ogUrl,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Not found
|
|
64
|
+
if (!doc) {
|
|
65
|
+
return {
|
|
66
|
+
version,
|
|
67
|
+
slug,
|
|
68
|
+
isCategory: false,
|
|
69
|
+
isNotFound: true,
|
|
70
|
+
doc: null,
|
|
71
|
+
categoryTitle: null,
|
|
72
|
+
categoryDescription: null,
|
|
73
|
+
categoryTabGroup: undefined,
|
|
74
|
+
toc: [],
|
|
75
|
+
previous: null,
|
|
76
|
+
next: null,
|
|
77
|
+
title,
|
|
78
|
+
description,
|
|
79
|
+
ogUrl,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Normal doc page
|
|
84
|
+
const toc = extractTableOfContents(doc.meta.content || doc.content);
|
|
85
|
+
const { previous, next } = getAdjacentDocs(slug, allDocs);
|
|
86
|
+
const showCategoryIndex = isCategory && !!doc;
|
|
87
|
+
const matchingDoc = allDocs.find((d) => d.slug === slug);
|
|
88
|
+
const currentPageTabGroup = doc.meta?.tab_group || matchingDoc?.categoryTabGroup;
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
version,
|
|
92
|
+
slug,
|
|
93
|
+
isCategory: showCategoryIndex,
|
|
94
|
+
isNotFound: false,
|
|
95
|
+
doc,
|
|
96
|
+
categoryTitle: null,
|
|
97
|
+
categoryDescription: null,
|
|
98
|
+
categoryTabGroup: currentPageTabGroup,
|
|
99
|
+
toc,
|
|
100
|
+
previous: previous ? { title: previous.meta.title, slug: previous.slug } : null,
|
|
101
|
+
next: next ? { title: next.meta.title, slug: next.slug } : null,
|
|
102
|
+
title,
|
|
103
|
+
description,
|
|
104
|
+
ogUrl,
|
|
105
|
+
};
|
|
106
|
+
};
|