blume 1.5.2 → 1.6.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/CHANGELOG.md +85 -0
- package/dist/cli/index.js +3639 -1377
- package/dist/cli/index.js.map +103 -91
- package/dist/types/ai/component-markdown.d.ts +79 -0
- package/dist/types/components/layout/nav-utils.d.ts +60 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +206 -4
- package/dist/types/core/config.d.ts +6 -4
- package/dist/types/core/data.d.ts +23 -1
- package/dist/types/core/github.d.ts +35 -0
- package/dist/types/core/i18n-ui.d.ts +8 -0
- package/dist/types/core/navigation.d.ts +69 -0
- package/dist/types/core/schema.d.ts +117 -1
- package/dist/types/core/sources/types.d.ts +31 -6
- package/dist/types/core/types.d.ts +23 -2
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/openapi/references.d.ts +21 -1
- package/dist/types/seo/jsonld.d.ts +105 -0
- package/dist/types/theme/fonts.d.ts +34 -4
- package/docs/_snippets/include-demo.mdx +7 -0
- package/docs/advanced/api-reference.mdx +3 -3
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/graphql.mdx +84 -0
- package/docs/advanced/meta.ts +8 -1
- package/docs/configuration/ai.mdx +21 -3
- package/docs/configuration/index.mdx +24 -0
- package/docs/configuration/search.mdx +13 -1
- package/docs/configuration/seo.mdx +27 -0
- package/docs/configuration/theming.mdx +17 -0
- package/docs/content/components.mdx +7 -0
- package/docs/content/includes.mdx +68 -0
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +25 -0
- package/docs/content/sources.mdx +42 -1
- package/docs/content/syntax.mdx +69 -1
- package/docs/content/versioning.mdx +15 -9
- package/docs/reference/cli.mdx +2 -1
- package/package.json +23 -14
- package/skills/blume-migrate/SKILL.md +16 -7
- package/skills/blume-migrate/references/docusaurus.md +5 -3
- package/skills/blume-migrate/references/fumadocs.md +10 -2
- package/skills/blume-migrate/references/mintlify.md +3 -2
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +1 -1
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/ask-data.ts +2 -1
- package/src/ai/component-markdown.ts +199 -36
- package/src/ai/llms.ts +93 -6
- package/src/ai/markdown.ts +2 -2
- package/src/ai/mcp/discovery.ts +10 -2
- package/src/ai/mcp/server.ts +74 -2
- package/src/astro/generate.ts +183 -116
- package/src/astro/include-hmr.ts +81 -0
- package/src/astro/include-refresh.ts +0 -0
- package/src/astro/index.ts +3 -5
- package/src/astro/templates.ts +125 -76
- package/src/cli/commands/build.ts +84 -15
- package/src/cli/init/questions.ts +1 -0
- package/src/cli/init/scaffold.ts +27 -4
- package/src/components/colors.ts +142 -0
- package/src/components/content/Badge.astro +5 -12
- package/src/components/content/Callout.astro +19 -36
- package/src/components/content/Card.astro +15 -21
- package/src/components/content/Component.astro +10 -1
- package/src/components/content/GithubInfo.astro +28 -9
- package/src/components/content/Tabs.astro +27 -5
- package/src/components/content/github-info.ts +20 -5
- package/src/components/dropdown-dismiss.ts +122 -0
- package/src/components/layout/Fonts.astro +15 -8
- package/src/components/layout/Header.astro +44 -0
- package/src/components/layout/LanguageSwitcher.astro +9 -1
- package/src/components/layout/NavSelector.astro +12 -3
- package/src/components/layout/NavTree.astro +6 -18
- package/src/components/layout/PageActions.astro +29 -8
- package/src/components/layout/PageLayout.astro +10 -1
- package/src/components/layout/ReferenceLayout.astro +6 -1
- package/src/components/layout/RootLayout.astro +46 -15
- package/src/components/layout/Search.astro +36 -4
- package/src/components/layout/TableOfContents.astro +8 -2
- package/src/components/layout/head-scripts.ts +53 -1
- package/src/components/openapi/ApiOverview.astro +13 -3
- package/src/components/openapi/AsyncApiOperation.astro +7 -14
- package/src/components/openapi/GraphqlChip.astro +33 -0
- package/src/components/openapi/GraphqlFieldsTable.astro +111 -0
- package/src/components/openapi/GraphqlOperation.astro +186 -0
- package/src/components/openapi/GraphqlType.astro +154 -0
- package/src/components/openapi/MethodBadge.astro +3 -14
- package/src/components/openapi/Operation.astro +12 -5
- package/src/components/openapi/OperationPanel.astro +43 -0
- package/src/components/openapi/RequestPanel.astro +5 -10
- package/src/components/openapi/Responses.astro +1 -16
- package/src/components/openapi/graphql-helpers.ts +466 -0
- package/src/components/openapi/playground-client.ts +15 -0
- package/src/components/openapi/sample-panels.ts +45 -0
- package/src/components/openapi/snippets.ts +13 -35
- package/src/core/base-path.ts +11 -0
- package/src/core/config-input.ts +209 -2
- package/src/core/config.ts +6 -4
- package/src/core/content-assets.ts +15 -4
- package/src/core/data.ts +18 -2
- package/src/core/diagnostics.ts +8 -0
- package/src/core/frontmatter.ts +20 -8
- package/src/core/github.ts +71 -0
- package/src/core/graph.ts +22 -8
- package/src/core/heading-markers.ts +96 -0
- package/src/core/i18n-ui.ts +11 -0
- package/src/core/includes.ts +632 -0
- package/src/core/last-modified.ts +36 -11
- package/src/core/links.ts +79 -13
- package/src/core/meta.ts +2 -1
- package/src/core/nav-diagnostics.ts +11 -2
- package/src/core/navigation.ts +27 -6
- package/src/core/project-graph.ts +61 -9
- package/src/core/schema.ts +226 -35
- package/src/core/server-features.ts +5 -9
- package/src/core/sources/github-releases.ts +2 -2
- package/src/core/sources/normalize.ts +502 -115
- package/src/core/sources/notion.ts +43 -8
- package/src/core/sources/obsidian.ts +1038 -0
- package/src/core/sources/read.ts +36 -1
- package/src/core/sources/resolve.ts +34 -1
- package/src/core/sources/types.ts +28 -6
- package/src/core/sources/watch.ts +12 -8
- package/src/core/tsconfig-aliases.ts +48 -35
- package/src/core/types.ts +25 -2
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +2 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +2 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +2 -0
- package/src/core/ui-packs/el.ts +2 -0
- package/src/core/ui-packs/es.ts +2 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +2 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +2 -0
- package/src/core/ui-packs/hu.ts +2 -0
- package/src/core/ui-packs/id.ts +2 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +2 -0
- package/src/core/ui-packs/ko.ts +2 -0
- package/src/core/ui-packs/nl.ts +2 -0
- package/src/core/ui-packs/no.ts +2 -0
- package/src/core/ui-packs/pl.ts +2 -0
- package/src/core/ui-packs/pt-br.ts +2 -0
- package/src/core/ui-packs/pt.ts +2 -0
- package/src/core/ui-packs/ro.ts +2 -0
- package/src/core/ui-packs/ru.ts +2 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +2 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +2 -0
- package/src/core/ui-packs/uk.ts +2 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/core/version-cut.ts +21 -3
- package/src/core/yaml.ts +26 -0
- package/src/deploy/function-bundle.ts +251 -0
- package/src/eval/schema.ts +3 -1
- package/src/markdown/code-title.ts +22 -16
- package/src/markdown/features.ts +21 -0
- package/src/markdown/fence-meta.ts +50 -0
- package/src/markdown/heading-anchors.ts +198 -37
- package/src/markdown/include.ts +247 -0
- package/src/markdown/index.ts +43 -34
- package/src/markdown/language-icon.ts +2 -2
- package/src/markdown/mdast.ts +7 -3
- package/src/markdown/ts2js.ts +264 -0
- package/src/openapi/asyncapi.ts +4 -1
- package/src/openapi/graphql-build.ts +293 -0
- package/src/openapi/graphql.ts +212 -0
- package/src/openapi/model.ts +38 -5
- package/src/openapi/parse.ts +34 -0
- package/src/openapi/proxy.ts +30 -5
- package/src/openapi/references.ts +89 -13
- package/src/openapi/render-mdx.ts +48 -8
- package/src/openapi/scalar.ts +5 -12
- package/src/openapi/source.ts +91 -23
- package/src/registry/eject.ts +11 -0
- package/src/search/documents.ts +229 -37
- package/src/search/orama-index.ts +9 -5
- package/src/seo/jsonld.ts +293 -51
- package/src/theme/code-block-padding.ts +16 -0
- package/src/theme/entry.ts +65 -11
- package/src/theme/fonts.ts +189 -16
- package/src/translate/prompts.ts +2 -0
- package/src/translate/run.ts +7 -0
- package/src/translate/work-list.ts +0 -0
package/src/core/sources/read.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
|
|
3
|
+
import { expandIncludes, hasIncludeStatements } from "../includes.ts";
|
|
3
4
|
import type { PageRecord } from "../types.ts";
|
|
4
5
|
import type { ContentSource } from "./types.ts";
|
|
5
6
|
|
|
@@ -12,7 +13,7 @@ export interface EntryReadContext {
|
|
|
12
13
|
/**
|
|
13
14
|
* Read an entry's raw body text without assuming a filesystem path. Prefers the
|
|
14
15
|
* body captured at scan time (staged sources), then the owning source's lazy
|
|
15
|
-
* `read()`, then the back-compat `sourcePath` (
|
|
16
|
+
* `read()`, then the back-compat `sourcePath` (any local file). Returns `""` when an
|
|
16
17
|
* entry can't be resolved, so callers degrade gracefully.
|
|
17
18
|
*/
|
|
18
19
|
export const readEntryText = async (
|
|
@@ -34,3 +35,37 @@ export const readEntryText = async (
|
|
|
34
35
|
}
|
|
35
36
|
return "";
|
|
36
37
|
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Read an entry's raw body with `<include>` statements expanded — what search
|
|
41
|
+
* indexing, the `.md`/`.mdx` mirrors, and llms-full.txt should see, matching
|
|
42
|
+
* the spliced content the rendered page shows. Filesystem entries only
|
|
43
|
+
* (includes resolve as filesystem paths); everything else passes through
|
|
44
|
+
* verbatim. Expansion errors were already surfaced as scan diagnostics, so
|
|
45
|
+
* unresolvable statements simply stay verbatim here.
|
|
46
|
+
*/
|
|
47
|
+
export const readExpandedEntryText = async (
|
|
48
|
+
ctx: EntryReadContext,
|
|
49
|
+
page: PageRecord
|
|
50
|
+
): Promise<string> => {
|
|
51
|
+
const raw = await readEntryText(ctx, page);
|
|
52
|
+
if (!(page.sourcePath && hasIncludeStatements(raw))) {
|
|
53
|
+
return raw;
|
|
54
|
+
}
|
|
55
|
+
const source = ctx.sources?.find(
|
|
56
|
+
(candidate) => candidate.name === page.source.name
|
|
57
|
+
);
|
|
58
|
+
// Mirror the scan's expansion gate exactly (`!staged && contentRoot`): a
|
|
59
|
+
// staged/custom source's entries were never expanded at scan time, so no
|
|
60
|
+
// headings/links/diagnostics exist for the spliced content — expanding only
|
|
61
|
+
// here would splice text the rest of the pipeline never validated, with no
|
|
62
|
+
// content-root bound on resolution.
|
|
63
|
+
if (!source || source.staged || !source.contentRoot) {
|
|
64
|
+
return raw;
|
|
65
|
+
}
|
|
66
|
+
const expansion = await expandIncludes(raw, {
|
|
67
|
+
contentRoot: source.contentRoot,
|
|
68
|
+
sourcePath: page.sourcePath,
|
|
69
|
+
});
|
|
70
|
+
return expansion.text;
|
|
71
|
+
};
|
|
@@ -8,6 +8,7 @@ import { filesystemSource } from "./filesystem.ts";
|
|
|
8
8
|
import { githubReleasesSource } from "./github-releases.ts";
|
|
9
9
|
import { mdxRemoteSource } from "./mdx-remote.ts";
|
|
10
10
|
import { notionSource } from "./notion.ts";
|
|
11
|
+
import { obsidianSource } from "./obsidian.ts";
|
|
11
12
|
import { sanitySource } from "./sanity.ts";
|
|
12
13
|
import type { ContentSource, SourceContext } from "./types.ts";
|
|
13
14
|
|
|
@@ -47,9 +48,25 @@ const sourceContext = (
|
|
|
47
48
|
refresh: runtime.refresh ?? runtime.mode === "build",
|
|
48
49
|
});
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* The frontmatter keys each content type declares (`content.types[*].frontmatter`),
|
|
53
|
+
* keyed by type — so a source that drops unknown keys keeps, per note, exactly
|
|
54
|
+
* the ones `normalizeEntry` will accept for that note's type.
|
|
55
|
+
*/
|
|
56
|
+
const typeFrontmatterKeys = (
|
|
57
|
+
config: ResolvedConfig
|
|
58
|
+
): Record<string, string[]> =>
|
|
59
|
+
Object.fromEntries(
|
|
60
|
+
Object.entries(config.content.types).map(([type, def]) => [
|
|
61
|
+
type,
|
|
62
|
+
Object.keys(def.frontmatter),
|
|
63
|
+
])
|
|
64
|
+
);
|
|
65
|
+
|
|
50
66
|
const buildSource = (
|
|
51
67
|
def: ContentSourceConfig,
|
|
52
68
|
name: string,
|
|
69
|
+
config: ResolvedConfig,
|
|
53
70
|
context: ProjectContext,
|
|
54
71
|
runtime: SourceRuntime
|
|
55
72
|
): ContentSource => {
|
|
@@ -97,6 +114,22 @@ const buildSource = (
|
|
|
97
114
|
sourceContext(context, name, runtime)
|
|
98
115
|
);
|
|
99
116
|
}
|
|
117
|
+
if (def.type === "obsidian") {
|
|
118
|
+
return obsidianSource(
|
|
119
|
+
{
|
|
120
|
+
defaultType: config.content.defaultType,
|
|
121
|
+
exclude: def.exclude,
|
|
122
|
+
frontmatterKeys: Object.keys(config.frontmatter.extend),
|
|
123
|
+
i18n: config.i18n,
|
|
124
|
+
name,
|
|
125
|
+
prefix: def.prefix,
|
|
126
|
+
typeFrontmatterKeys: typeFrontmatterKeys(config),
|
|
127
|
+
vault: def.vault,
|
|
128
|
+
versions: config.versions,
|
|
129
|
+
},
|
|
130
|
+
sourceContext(context, name, runtime)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
100
133
|
if (def.type === "github-releases") {
|
|
101
134
|
return githubReleasesSource(
|
|
102
135
|
{
|
|
@@ -198,7 +231,7 @@ const contentSources = (
|
|
|
198
231
|
|
|
199
232
|
const nameFor = uniqueNamer();
|
|
200
233
|
return defs.map((def) =>
|
|
201
|
-
buildSource(def, nameFor(baseName(def)), context, runtime)
|
|
234
|
+
buildSource(def, nameFor(baseName(def)), config, context, runtime)
|
|
202
235
|
);
|
|
203
236
|
};
|
|
204
237
|
|
|
@@ -28,9 +28,15 @@ export interface SourceEntry {
|
|
|
28
28
|
*/
|
|
29
29
|
raw?: string;
|
|
30
30
|
/**
|
|
31
|
-
* Absolute filesystem path when the entry originates from disk.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Absolute filesystem path when the entry originates from disk. Set by any
|
|
32
|
+
* local-file adapter — the filesystem source, and staged local sources such
|
|
33
|
+
* as Obsidian, whose bodies are rewritten but whose notes are still real
|
|
34
|
+
* files. It gives diagnostics a path the author recognizes and lets relative
|
|
35
|
+
* image checks resolve next to the file. Omitted by remote/CMS adapters.
|
|
36
|
+
*
|
|
37
|
+
* It does *not* imply the entry renders through the docs glob collection —
|
|
38
|
+
* read `source.staged` for that — and git last-modified additionally needs
|
|
39
|
+
* the owning source to expose a `contentRoot` to bound the log's pathspec.
|
|
34
40
|
*/
|
|
35
41
|
sourcePath?: string;
|
|
36
42
|
/** Optional provenance for "edit this page". */
|
|
@@ -39,6 +45,19 @@ export interface SourceEntry {
|
|
|
39
45
|
lastModified?: string;
|
|
40
46
|
/** Content hash for cache invalidation / HMR; adapter-computed when cheap. */
|
|
41
47
|
hash?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The body with `<include>` statements expanded, set by the scan's expansion
|
|
50
|
+
* pass (filesystem entries whose body contains a statement). `normalizeEntry`
|
|
51
|
+
* extracts headings/links/components from this text so a partial's content
|
|
52
|
+
* counts toward the including page, with `origins` mapping each expanded
|
|
53
|
+
* line back to the file and raw line it came from for diagnostics.
|
|
54
|
+
*/
|
|
55
|
+
expanded?: {
|
|
56
|
+
text: string;
|
|
57
|
+
origins: { file: string; line: number }[];
|
|
58
|
+
/** Absolute paths of every file included, transitively. */
|
|
59
|
+
includes: string[];
|
|
60
|
+
};
|
|
42
61
|
}
|
|
43
62
|
|
|
44
63
|
/** The result of a single `ContentSource.load()` call. */
|
|
@@ -99,9 +118,12 @@ export interface ContentSource {
|
|
|
99
118
|
/** Optional route prefix; the source's routes namespace under `/<prefix>/`. */
|
|
100
119
|
readonly prefix?: string;
|
|
101
120
|
/**
|
|
102
|
-
* Resolved on-disk root, set by
|
|
103
|
-
* folder-meta discovery (scan under this root)
|
|
104
|
-
*
|
|
121
|
+
* Resolved on-disk root, set by sources whose entries live on disk. For
|
|
122
|
+
* filesystem sources it drives folder-meta discovery (scan under this root)
|
|
123
|
+
* and the docs-collection base; for staged local sources (an Obsidian
|
|
124
|
+
* vault) it only bounds the git last-modified pathspec — folder-meta
|
|
125
|
+
* discovery is guarded on `staged`. Omitted by remote/CMS sources that have
|
|
126
|
+
* no local tree.
|
|
105
127
|
*/
|
|
106
128
|
readonly contentRoot?: string;
|
|
107
129
|
/** Pull every entry. Called once per scan. */
|
|
@@ -17,9 +17,8 @@ import type { WatchListener } from "node:fs";
|
|
|
17
17
|
* render (e.g. `.blume/.astro/data-store.json`). Left unfiltered, each such write
|
|
18
18
|
* re-triggers a rescan + runtime regeneration whose writes land back under
|
|
19
19
|
* `.blume/` and fire the watcher again: a self-sustaining loop that stalls page
|
|
20
|
-
* renders and floods the console
|
|
21
|
-
*
|
|
22
|
-
* option, so we filter by the changed path in the callback.
|
|
20
|
+
* renders and floods the console. `fs.watch` has no ignore option, so we filter
|
|
21
|
+
* by the changed path in the callback.
|
|
23
22
|
*/
|
|
24
23
|
export const BLUME_IGNORE_DIRS = [
|
|
25
24
|
".blume",
|
|
@@ -60,19 +59,24 @@ export const excludeDirSegments = (patterns: readonly string[]): string[] =>
|
|
|
60
59
|
|
|
61
60
|
/**
|
|
62
61
|
* Build a recursive-watch listener that fires `onChange` for content changes but
|
|
63
|
-
* ignores events whose path crosses an ignored directory segment
|
|
64
|
-
* `
|
|
65
|
-
*
|
|
62
|
+
* ignores events whose path crosses an ignored directory segment — one in
|
|
63
|
+
* `ignoreDirs`, or one `ignoreSegment` rejects, for a scan whose skip rule is
|
|
64
|
+
* a shape (every dot-name) rather than a list. A missing `filename` — rare;
|
|
65
|
+
* the platform couldn't name the changed path — falls through to `onChange`
|
|
66
|
+
* rather than silently dropping a real edit. Exported for testing.
|
|
66
67
|
*/
|
|
67
68
|
export const ignoringWatchListener = (
|
|
68
69
|
onChange: () => void,
|
|
69
|
-
ignoreDirs: Iterable<string> = BLUME_WATCH_IGNORE_DIRS
|
|
70
|
+
ignoreDirs: Iterable<string> = BLUME_WATCH_IGNORE_DIRS,
|
|
71
|
+
ignoreSegment: (segment: string) => boolean = () => false
|
|
70
72
|
): WatchListener<string> => {
|
|
71
73
|
const ignore = new Set(ignoreDirs);
|
|
72
74
|
return (_event, filename) => {
|
|
73
75
|
if (
|
|
74
76
|
filename !== null &&
|
|
75
|
-
filename
|
|
77
|
+
filename
|
|
78
|
+
.split(/[/\\]/u)
|
|
79
|
+
.some((segment) => ignore.has(segment) || ignoreSegment(segment))
|
|
76
80
|
) {
|
|
77
81
|
return;
|
|
78
82
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { parseTsconfig } from "get-tsconfig";
|
|
4
3
|
import { dirname, join, resolve } from "pathe";
|
|
4
|
+
import ts from "typescript";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Read the project's TypeScript path aliases (`compilerOptions.paths`) and turn
|
|
@@ -14,21 +14,32 @@ import { dirname, join, resolve } from "pathe";
|
|
|
14
14
|
* import would have to be rewritten to a relative path. Reading the aliases here
|
|
15
15
|
* lets those components port over unchanged.
|
|
16
16
|
*
|
|
17
|
-
* Parsing is
|
|
18
|
-
* paths,
|
|
19
|
-
*
|
|
20
|
-
* non-fatal: anything
|
|
17
|
+
* Parsing is the TypeScript compiler's own job — JSONC, the full `extends`
|
|
18
|
+
* chain (relative paths, package specifiers, TS 5.0 arrays), `${configDir}`
|
|
19
|
+
* substitution, and where relative `paths` anchor all follow tsc exactly
|
|
20
|
+
* because tsc does the work. Best-effort and non-fatal: anything unreadable
|
|
21
|
+
* yields no aliases.
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* tsc records the directory of the config that declared `paths` on the
|
|
26
|
+
* resolved options (that is what relative targets anchor to when there is no
|
|
27
|
+
* `baseUrl`), but leaves the field out of its public typings.
|
|
28
|
+
*/
|
|
29
|
+
interface PathsBaseOptions {
|
|
30
|
+
pathsBasePath?: string;
|
|
31
|
+
}
|
|
26
32
|
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Parse-only host: `include` globbing would walk the whole project for a file
|
|
35
|
+
* list nobody reads, so directories always enumerate as empty.
|
|
36
|
+
*/
|
|
37
|
+
const PARSE_HOST: ts.ParseConfigHost = {
|
|
38
|
+
fileExists: ts.sys.fileExists,
|
|
39
|
+
readDirectory: () => [],
|
|
40
|
+
readFile: ts.sys.readFile,
|
|
41
|
+
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
42
|
+
};
|
|
32
43
|
|
|
33
44
|
/** Whether a `paths` fallback entry is a usable target (raw JSONC may lie). */
|
|
34
45
|
const isPathTarget = (target: string | undefined): target is string =>
|
|
@@ -37,12 +48,11 @@ const isPathTarget = (target: string | undefined): target is string =>
|
|
|
37
48
|
/** Convert one tsconfig `paths` mapping to a Vite alias, or null to skip. */
|
|
38
49
|
const toAlias = (
|
|
39
50
|
key: string,
|
|
40
|
-
value: string
|
|
41
|
-
baseDir: string
|
|
42
|
-
configDir: string
|
|
51
|
+
value: string[],
|
|
52
|
+
baseDir: string
|
|
43
53
|
): { find: string; replacement: string } | null => {
|
|
44
54
|
// tsconfig allows a fallback array; Vite aliases are 1:1, so take the first.
|
|
45
|
-
const first =
|
|
55
|
+
const [first] = value;
|
|
46
56
|
if (!isPathTarget(first)) {
|
|
47
57
|
return null;
|
|
48
58
|
}
|
|
@@ -52,10 +62,7 @@ const toAlias = (
|
|
|
52
62
|
if (find === "" || find === "*") {
|
|
53
63
|
return null;
|
|
54
64
|
}
|
|
55
|
-
return {
|
|
56
|
-
find,
|
|
57
|
-
replacement: resolve(baseDir, substituteConfigDir(target, configDir)),
|
|
58
|
-
};
|
|
65
|
+
return { find, replacement: resolve(baseDir, target) };
|
|
59
66
|
};
|
|
60
67
|
|
|
61
68
|
/**
|
|
@@ -72,27 +79,33 @@ export const resolveTsconfigAliases = (
|
|
|
72
79
|
if (!entry) {
|
|
73
80
|
return {};
|
|
74
81
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} catch {
|
|
79
|
-
// Unparseable config or unresolvable extends: no aliases, as before.
|
|
80
|
-
return {};
|
|
81
|
-
}
|
|
82
|
-
const paths = options?.paths;
|
|
83
|
-
if (!paths) {
|
|
82
|
+
const { config, error } = ts.readConfigFile(entry, ts.sys.readFile);
|
|
83
|
+
if (error) {
|
|
84
|
+
// Unreadable or malformed JSONC: no aliases, as before.
|
|
84
85
|
return {};
|
|
85
86
|
}
|
|
86
87
|
const configDir = dirname(entry);
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
const
|
|
88
|
+
// Diagnostics (an unresolvable `extends`, no input files) are tsc's to
|
|
89
|
+
// report; like tsc, keep whatever options still resolved.
|
|
90
|
+
const { options } = ts.parseJsonConfigFileContent(
|
|
91
|
+
config,
|
|
92
|
+
PARSE_HOST,
|
|
90
93
|
configDir,
|
|
91
|
-
|
|
94
|
+
undefined,
|
|
95
|
+
entry
|
|
92
96
|
);
|
|
97
|
+
const { paths } = options;
|
|
98
|
+
if (!paths) {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
// SAFETY: the assertion only exposes `pathsBasePath`, which tsc populates on
|
|
102
|
+
// the resolved options but omits from `CompilerOptions`.
|
|
103
|
+
const { pathsBasePath } = options as PathsBaseOptions;
|
|
104
|
+
// tsc already made `baseUrl` absolute and expanded `${configDir}`.
|
|
105
|
+
const baseDir = options.baseUrl ?? pathsBasePath ?? configDir;
|
|
93
106
|
const entries: [string, string][] = [];
|
|
94
107
|
for (const [key, value] of Object.entries(paths)) {
|
|
95
|
-
const alias = toAlias(key, value, baseDir
|
|
108
|
+
const alias = toAlias(key, value, baseDir);
|
|
96
109
|
if (alias) {
|
|
97
110
|
entries.push([alias.find, alias.replacement]);
|
|
98
111
|
}
|
package/src/core/types.ts
CHANGED
|
@@ -66,6 +66,10 @@ export interface PageLink {
|
|
|
66
66
|
line: number;
|
|
67
67
|
/** 1-based column of the target within the line. */
|
|
68
68
|
column: number;
|
|
69
|
+
/** Absolute path of the file the link was written in, when that isn't the
|
|
70
|
+
* page's own source — a link inside an included partial. Diagnostics point
|
|
71
|
+
* here so authors fix the partial, not the page that spliced it. */
|
|
72
|
+
file?: string;
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
/**
|
|
@@ -105,7 +109,7 @@ export interface PageRecord {
|
|
|
105
109
|
id: string;
|
|
106
110
|
/** Provenance: the owning source's name and its source-local ref. */
|
|
107
111
|
source: { name: string; ref: string };
|
|
108
|
-
/** Absolute source path. Populated by
|
|
112
|
+
/** Absolute source path. Populated by any local-file adapter (back-compat). */
|
|
109
113
|
sourcePath?: string;
|
|
110
114
|
/**
|
|
111
115
|
* Renderable body captured at scan time. Set for staged (non-filesystem)
|
|
@@ -173,6 +177,12 @@ export interface PageRecord {
|
|
|
173
177
|
// oxlint-disable-next-line anti-slop/no-unsafe-dictionary-type -- values are outputs of arbitrary user-supplied zod schemas (transforms included), so no narrower type exists
|
|
174
178
|
custom?: Record<string, unknown>;
|
|
175
179
|
headings: Heading[];
|
|
180
|
+
/**
|
|
181
|
+
* Raw HTML element ids (`<a id="…">`) outside headings, deduplicated —
|
|
182
|
+
* fragment-link targets `blume validate` accepts alongside heading slugs.
|
|
183
|
+
*/
|
|
184
|
+
anchors: string[];
|
|
185
|
+
|
|
176
186
|
/** Whether the file is `.md`/`.mdx`. */
|
|
177
187
|
format: "md" | "mdx";
|
|
178
188
|
/** Internal/asset links discovered in the page (for validation). */
|
|
@@ -181,6 +191,9 @@ export interface PageRecord {
|
|
|
181
191
|
componentsUsed?: string[];
|
|
182
192
|
/** Resolved "last updated" ISO date, when the feature is enabled. */
|
|
183
193
|
lastModified?: string;
|
|
194
|
+
/** Absolute paths of files this page `<include>`s, transitively. Drives the
|
|
195
|
+
* dev-server invalidation edge from a partial to the pages that splice it. */
|
|
196
|
+
includes?: string[];
|
|
184
197
|
}
|
|
185
198
|
|
|
186
199
|
/** A node in the generated navigation tree. */
|
|
@@ -266,6 +279,12 @@ export interface FeaturedLink {
|
|
|
266
279
|
icon?: string;
|
|
267
280
|
}
|
|
268
281
|
|
|
282
|
+
/** A plain header link, or the header's single call to action. */
|
|
283
|
+
export interface HeaderAction {
|
|
284
|
+
href: string;
|
|
285
|
+
label: string;
|
|
286
|
+
}
|
|
287
|
+
|
|
269
288
|
/** The complete navigation model derived from the content graph. */
|
|
270
289
|
export interface Navigation {
|
|
271
290
|
tabs: NavTab[];
|
|
@@ -281,6 +300,10 @@ export interface Navigation {
|
|
|
281
300
|
* graphs; treat as `/`.
|
|
282
301
|
*/
|
|
283
302
|
root?: string;
|
|
303
|
+
/** Plain links in the header, left of the icon buttons. */
|
|
304
|
+
actions?: HeaderAction[];
|
|
305
|
+
/** The single primary call to action in the header. */
|
|
306
|
+
cta?: HeaderAction | null;
|
|
284
307
|
/** Pinned links shown above the sidebar sections, unscoped by tab. */
|
|
285
308
|
featured: FeaturedLink[];
|
|
286
309
|
/** Repo URL for the header link, or null when hidden (`navigation.repo`). */
|
|
@@ -340,7 +363,7 @@ export interface RouteManifestEntry {
|
|
|
340
363
|
/** Astro collection-relative entry id, passed to `getEntry`. */
|
|
341
364
|
entryId: string;
|
|
342
365
|
path: string;
|
|
343
|
-
/** Absolute source path; populated for
|
|
366
|
+
/** Absolute source path; populated for local-file entries only (back-compat). */
|
|
344
367
|
sourcePath?: string;
|
|
345
368
|
/** Adapter-supplied "edit this page" URL (non-filesystem sources). */
|
|
346
369
|
editUrl?: string;
|
package/src/core/ui-packs/ar.ts
CHANGED
package/src/core/ui-packs/bg.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const bg: UIStringsOverride = {
|
|
|
20
20
|
generating: "Генериране…",
|
|
21
21
|
openIn: "Отвори в {name}",
|
|
22
22
|
openInChat: "Отвори в чата",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Прочетете {url}, за да мога да задавам въпроси за тази страница.",
|
|
23
25
|
scrollToTop: "Нагоре",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/bn.ts
CHANGED
package/src/core/ui-packs/ca.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const ca: UIStringsOverride = {
|
|
|
20
20
|
generating: "S'està generant…",
|
|
21
21
|
openIn: "Obre a {name}",
|
|
22
22
|
openInChat: "Obre al xat",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Llegeix {url} perquè pugui fer-te preguntes sobre aquesta pàgina.",
|
|
23
25
|
scrollToTop: "Torna a dalt",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/cs.ts
CHANGED
package/src/core/ui-packs/da.ts
CHANGED
package/src/core/ui-packs/de.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const de: UIStringsOverride = {
|
|
|
20
20
|
generating: "Wird generiert…",
|
|
21
21
|
openIn: "In {name} öffnen",
|
|
22
22
|
openInChat: "Im Chat öffnen",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Lies {url}, damit ich dir Fragen zu dieser Seite stellen kann.",
|
|
23
25
|
scrollToTop: "Nach oben scrollen",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/el.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const el: UIStringsOverride = {
|
|
|
20
20
|
generating: "Δημιουργία…",
|
|
21
21
|
openIn: "Άνοιγμα στο {name}",
|
|
22
22
|
openInChat: "Άνοιγμα στη συνομιλία",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Διαβάστε το {url} για να μπορώ να κάνω ερωτήσεις για αυτή τη σελίδα.",
|
|
23
25
|
scrollToTop: "Επιστροφή στην κορυφή",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/es.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const es: UIStringsOverride = {
|
|
|
20
20
|
generating: "Generando…",
|
|
21
21
|
openIn: "Abrir en {name}",
|
|
22
22
|
openInChat: "Abrir en el chat",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Lee {url} para que pueda hacerte preguntas sobre esta página.",
|
|
23
25
|
scrollToTop: "Volver arriba",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/fa.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const fa: UIStringsOverride = {
|
|
|
20
20
|
generating: "در حال تولید…",
|
|
21
21
|
openIn: "باز کردن در {name}",
|
|
22
22
|
openInChat: "باز کردن در گفتگو",
|
|
23
|
+
openInChatPrompt: "{url} را بخوانید تا بتوانم درباره این صفحه سؤال بپرسم.",
|
|
23
24
|
scrollToTop: "بازگشت به بالا",
|
|
24
25
|
},
|
|
25
26
|
ask: {
|
package/src/core/ui-packs/fi.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const fi: UIStringsOverride = {
|
|
|
20
20
|
generating: "Luodaan…",
|
|
21
21
|
openIn: "Avaa sovelluksessa {name}",
|
|
22
22
|
openInChat: "Avaa keskustelussa",
|
|
23
|
+
openInChatPrompt: "Lue {url}, jotta voin kysyä kysymyksiä tästä sivusta.",
|
|
23
24
|
scrollToTop: "Takaisin ylös",
|
|
24
25
|
},
|
|
25
26
|
ask: {
|
package/src/core/ui-packs/fr.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const fr: UIStringsOverride = {
|
|
|
20
20
|
generating: "Génération…",
|
|
21
21
|
openIn: "Ouvrir dans {name}",
|
|
22
22
|
openInChat: "Ouvrir dans le chat",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Lisez {url} pour que je puisse vous poser des questions sur cette page.",
|
|
23
25
|
scrollToTop: "Revenir en haut",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/he.ts
CHANGED
package/src/core/ui-packs/hi.ts
CHANGED
package/src/core/ui-packs/hr.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const hr: UIStringsOverride = {
|
|
|
20
20
|
generating: "Generiranje…",
|
|
21
21
|
openIn: "Otvori u {name}",
|
|
22
22
|
openInChat: "Otvori u chatu",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Pročitajte {url} — postavljat ću pitanja o ovoj stranici.",
|
|
23
25
|
scrollToTop: "Natrag na vrh",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/hu.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const hu: UIStringsOverride = {
|
|
|
20
20
|
generating: "Generálás…",
|
|
21
21
|
openIn: "Megnyitás itt: {name}",
|
|
22
22
|
openInChat: "Megnyitás a csevegésben",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Olvasd el a következőt: {url}, hogy kérdéseket tehessek fel erről az oldalról.",
|
|
23
25
|
scrollToTop: "Vissza a tetejére",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/id.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const id: UIStringsOverride = {
|
|
|
20
20
|
generating: "Menghasilkan…",
|
|
21
21
|
openIn: "Buka di {name}",
|
|
22
22
|
openInChat: "Buka di obrolan",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Baca {url} agar saya bisa mengajukan pertanyaan tentang halaman ini.",
|
|
23
25
|
scrollToTop: "Kembali ke atas",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/it.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const it: UIStringsOverride = {
|
|
|
20
20
|
generating: "Generazione in corso…",
|
|
21
21
|
openIn: "Apri in {name}",
|
|
22
22
|
openInChat: "Apri nella chat",
|
|
23
|
+
openInChatPrompt: "Leggi {url} così posso farti domande su questa pagina.",
|
|
23
24
|
scrollToTop: "Torna su",
|
|
24
25
|
},
|
|
25
26
|
ask: {
|
package/src/core/ui-packs/ja.ts
CHANGED
package/src/core/ui-packs/ko.ts
CHANGED
package/src/core/ui-packs/nl.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const nl: UIStringsOverride = {
|
|
|
20
20
|
generating: "Genereren…",
|
|
21
21
|
openIn: "Openen in {name}",
|
|
22
22
|
openInChat: "Openen in chat",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Lees {url}, zodat ik je vragen kan stellen over deze pagina.",
|
|
23
25
|
scrollToTop: "Naar boven scrollen",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
package/src/core/ui-packs/no.ts
CHANGED
package/src/core/ui-packs/pl.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const pl: UIStringsOverride = {
|
|
|
20
20
|
generating: "Generowanie…",
|
|
21
21
|
openIn: "Otwórz w {name}",
|
|
22
22
|
openInChat: "Otwórz w czacie",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Przeczytaj {url}, aby móc odpowiadać na pytania dotyczące tej strony.",
|
|
23
25
|
scrollToTop: "Wróć na górę",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|
|
@@ -20,6 +20,8 @@ export const ptBR: UIStringsOverride = {
|
|
|
20
20
|
generating: "Gerando…",
|
|
21
21
|
openIn: "Abrir em {name}",
|
|
22
22
|
openInChat: "Abrir no chat",
|
|
23
|
+
openInChatPrompt:
|
|
24
|
+
"Leia {url} para que eu possa fazer perguntas sobre esta página.",
|
|
23
25
|
scrollToTop: "Voltar ao topo",
|
|
24
26
|
},
|
|
25
27
|
ask: {
|