@primer/brand-mcp 0.69.0 → 0.70.0-rc.11ae3568
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 +10 -9
- package/content/page-design.md +76 -0
- package/dist/brand/detect-asset-generator.d.ts +6 -0
- package/dist/brand/detect-asset-generator.js +21 -0
- package/dist/brand/detect-asset-generator.js.map +1 -0
- package/dist/brand/detect-framework.d.ts +3 -4
- package/dist/brand/detect-framework.js +18 -21
- package/dist/brand/detect-framework.js.map +1 -1
- package/dist/brand/docs-source.d.ts +8 -20
- package/dist/brand/docs-source.js +38 -41
- package/dist/brand/docs-source.js.map +1 -1
- package/dist/brand/resolve-assets.d.ts +4 -5
- package/dist/brand/resolve-assets.js +60 -68
- package/dist/brand/resolve-assets.js.map +1 -1
- package/dist/brand/resolve-install.d.ts +2 -4
- package/dist/brand/resolve-install.js +10 -10
- package/dist/brand/resolve-install.js.map +1 -1
- package/dist/catalog/load.js +0 -1
- package/dist/catalog/load.js.map +1 -1
- package/dist/catalog/types.d.ts +24 -14
- package/dist/catalog/types.js +2 -2
- package/dist/catalog/types.js.map +1 -1
- package/dist/catalog.json +105 -2
- package/dist/logger.d.ts +2 -2
- package/dist/review/rules.js +27 -0
- package/dist/review/rules.js.map +1 -1
- package/dist/review/types.d.ts +4 -4
- package/dist/server.js +13 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/primer-brand-asset/primer-brand-asset.js +12 -3
- package/dist/tools/primer-brand-asset/primer-brand-asset.js.map +1 -1
- package/dist/tools/primer-brand-examples/primer-brand-examples.js +23 -4
- package/dist/tools/primer-brand-examples/primer-brand-examples.js.map +1 -1
- package/dist/tools/primer-brand-page-design/index.d.ts +1 -0
- package/dist/tools/primer-brand-page-design/index.js +2 -0
- package/dist/tools/primer-brand-page-design/index.js.map +1 -0
- package/dist/tools/primer-brand-page-design/primer-brand-page-design.d.ts +6 -0
- package/dist/tools/primer-brand-page-design/primer-brand-page-design.js +19 -0
- package/dist/tools/primer-brand-page-design/primer-brand-page-design.js.map +1 -0
- package/dist/tools/primer-brand-review/primer-brand-review.js +23 -19
- package/dist/tools/primer-brand-review/primer-brand-review.js.map +1 -1
- package/dist/tools/primer-brand-setup/primer-brand-setup.js +1 -1
- package/dist/tools/primer-brand-setup/primer-brand-setup.js.map +1 -1
- package/dist/tools/primer-brand-tokens/primer-brand-tokens.js +38 -29
- package/dist/tools/primer-brand-tokens/primer-brand-tokens.js.map +1 -1
- package/dist/tools/register.js +3 -2
- package/dist/tools/register.js.map +1 -1
- package/dist/tools/types.d.ts +11 -12
- package/dist/util/text.d.ts +2 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,15 +6,16 @@ It is a version-aware, `stdio` (local) server which reads the docs and metadata
|
|
|
6
6
|
|
|
7
7
|
## Tools
|
|
8
8
|
|
|
9
|
-
| Tool
|
|
10
|
-
|
|
|
11
|
-
| `primer_brand_setup`
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
9
|
+
| Tool | What it does |
|
|
10
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `primer_brand_setup` | Configures Primer Brand first-time. Includes `ThemeProvider`, Mona Sans and RSC-directive configuration among other things |
|
|
12
|
+
| `primer_brand_page_design` | Page-design conventions (header/footer, hero media, gridline aesthetic, card grids, labels) and the current-brand reference templates to start from. |
|
|
13
|
+
| `primer_brand_component` | Get a component API information like props, allowed values and named sub-components. |
|
|
14
|
+
| `primer_brand_examples` | Reference examples taken from our Storybook. |
|
|
15
|
+
| `primer_brand_tokens` | Resolve design tokens by intent (color, space, typography) to CSS variables and values. |
|
|
16
|
+
| `primer_brand_asset` | Find Octicons (`@primer/octicons-react`) and Octovisuals (`@primer/octovisuals-react`) more easily |
|
|
17
|
+
| `primer_brand_docs` | Search and read Primer Brand guidance, including principles, accessibility and getting started. |
|
|
18
|
+
| `primer_brand_review` | Check generated JSX and CSS against the design system for unknown components, invalid props, hardcoded values |
|
|
18
19
|
|
|
19
20
|
## Getting started
|
|
20
21
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Page design patterns
|
|
2
|
+
|
|
3
|
+
These are the page design guidelines for GitHub marketing and landing pages, beyond what the component APIs or documentation guidelines recommend. Apply these by default unless the brief says otherwise. These are **design conventions** and the `primer_brand_review` tool does not flag them, so consult this before composing a page.
|
|
4
|
+
|
|
5
|
+
Learn individual component APIs and usage with `primer_brand_docs` and `primer_brand_component`. This guide is the step after: how to lay out an entire page and use those components alongside your custom ones correctly.
|
|
6
|
+
|
|
7
|
+
## Start here
|
|
8
|
+
|
|
9
|
+
The Flexsuite recipe templates are the current canonical source of truth for what GitHub web pages should look like today. Every component in them reflects the latest brand, unlike many older standalone examples. Use them as your
|
|
10
|
+
starting point and reference, and mirror their composition and section order:
|
|
11
|
+
|
|
12
|
+
- **Overview / landing page** — `FlexSuiteAIOverview` (hero with media, logo suite, gridline card
|
|
13
|
+
grids, rivers, pricing, FAQ, footer).
|
|
14
|
+
- **Category page** — `FlexSuiteSecurityCategory`.
|
|
15
|
+
- **Details page** — `FlexSuiteAIDetailsPlaylist`.
|
|
16
|
+
|
|
17
|
+
Pull adaptable component snippets with `primer_brand_examples`.
|
|
18
|
+
|
|
19
|
+
## Aesthetic: prefer the gridline look
|
|
20
|
+
|
|
21
|
+
`@primer/react-brand` spans multiple generations of GitHub's design language, and many components expose that choice as a variant or prop. **Gridline** is the current brand direction. It's a connected, ruled-line aesthetic, as seen throughout the Flexsuite templates. Bias toward it.
|
|
22
|
+
|
|
23
|
+
- **Do** — when a component offers a gridline-family option, choose it over the older or default look. "Expressive" gridline options are a more pronounced sub-variant of the same aesthetic, not a competing style; reach for them where a section wants extra emphasis.
|
|
24
|
+
- **Don't** — settle for a component's plainest or legacy variant, or borrow section layouts from an older brand generation, when a gridline equivalent exists.
|
|
25
|
+
|
|
26
|
+
Confirm the exact options with `primer_brand_component` rather than assuming which variants a component has — the specific names evolve over time.
|
|
27
|
+
|
|
28
|
+
## Page structure
|
|
29
|
+
|
|
30
|
+
### Frame every page with a header and footer
|
|
31
|
+
|
|
32
|
+
Every full page should be framed top and bottom, unless the user has requested a custom implementation.
|
|
33
|
+
|
|
34
|
+
- **Do** — use `SubdomainNavBar` at the top and `MinimalFooter` at the bottom (APIs via `primer_brand_component`).
|
|
35
|
+
- **Don't** — hand-roll a `<header>`, `<nav>`, or `<footer>` unless user has a separate plan for it.
|
|
36
|
+
|
|
37
|
+
### Spacing
|
|
38
|
+
|
|
39
|
+
Generous spacing is what lets a layout breathe; the Flexsuite recipes are a good reference for the rhythm to aim for.
|
|
40
|
+
|
|
41
|
+
- **Do** — use `Box` and `Stack` (both have responsive spacing props) to add consistent rhythm between sections.
|
|
42
|
+
- **Don't** — pack sections edge-to-edge or hand-roll ad-hoc margins.
|
|
43
|
+
|
|
44
|
+
## Component & element patterns
|
|
45
|
+
|
|
46
|
+
### Heroes carry media
|
|
47
|
+
|
|
48
|
+
A text-only hero reads as unfinished; heroes should carry a visual.
|
|
49
|
+
|
|
50
|
+
- **Do** — give `Hero` a `Hero.Image` (or `Hero.Video`); source imagery from `primer_brand_asset` (Octovisuals) or generate it with the Asset Generator MCP (see _Generated imagery_ below).
|
|
51
|
+
- **Don't** — ship a bare, text-only hero.
|
|
52
|
+
|
|
53
|
+
### Generated imagery adds color and interest
|
|
54
|
+
|
|
55
|
+
Bring on-brand color and life to the page with the Asset Generator MCP (when installed) instead of leaving large areas flat — most pages want at least one generated visual.
|
|
56
|
+
|
|
57
|
+
- **Do** — use **`create_product_landscape`** when the visual demos a product or feature (a hero or section showing the thing itself), and **`create_wallpaper`** for everything else, to add a colorful on-brand backdrop or accent. Browse the current set with `list_templates` / `list_themes` and pick a theme that fits the page's topic.
|
|
58
|
+
- **Don't** — reach for social media-specific templates like `create_social_square` or `create_landscape` when a product shot or wallpaper fits better.
|
|
59
|
+
|
|
60
|
+
### Group cards inside gridlines
|
|
61
|
+
|
|
62
|
+
A grid of items (features, pathways, plans) uses the connected **gridline** grid, not floating cards.
|
|
63
|
+
|
|
64
|
+
- **Do** — render a `Grid` with `columnGap="none" rowGap="none" enableGutters={false}` inside a bordered frame and let the frame draw the shared lines. Refer to the Flexsuite recipes for an example of this.
|
|
65
|
+
- **Don't** — output separate bordered `Card`s with gaps between them.
|
|
66
|
+
|
|
67
|
+
### Labels hug their content
|
|
68
|
+
|
|
69
|
+
Eyebrow/section labels are intrinsic width; stretching them reads as off-brand.
|
|
70
|
+
|
|
71
|
+
- **Do** — let `Hero.Label` / `SectionIntro.Label` / `EyebrowText` size to their content; alignment follows the section (start by default, or centered inside a centered `SectionIntro`).
|
|
72
|
+
- **Don't** — set a label full-width or give it a block/full-bleed background.
|
|
73
|
+
|
|
74
|
+
## General guidance
|
|
75
|
+
|
|
76
|
+
- If browser tooling (e.g. the Playwright MCP) is available, verify your work visually before finishing: serve the page locally, open it, and take screenshots across different breakpoints. Review the screenshots against this guidance and automatically fix obvious defects like clipped or overflowing content, cramped spacing, stretched/full-width labels that should preserve their intrinsic width, low-contrast text, a text-only hero, broken images, and off-brand tells (glows, purple gradients, pill buttons, glassmorphism, placeholder copy). Re-screenshot to confirm the fixes.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { delimiter, join } from 'node:path';
|
|
3
|
+
const BIN_NAME = 'asset-generator-mcp';
|
|
4
|
+
/**
|
|
5
|
+
* Returns true when the `asset-generator-mcp` CLI is resolvable on PATH
|
|
6
|
+
*/
|
|
7
|
+
export function detectAssetGenerator(env = process.env) {
|
|
8
|
+
const pathValue = env.PATH ?? env.Path ?? '';
|
|
9
|
+
if (!pathValue)
|
|
10
|
+
return { available: false };
|
|
11
|
+
const windowsExtensions = (env.PATHEXT ?? '').split(';').filter(Boolean);
|
|
12
|
+
const candidates = [BIN_NAME, ...windowsExtensions.map(extension => `${BIN_NAME}${extension.toLowerCase()}`)];
|
|
13
|
+
for (const directory of pathValue.split(delimiter)) {
|
|
14
|
+
if (!directory)
|
|
15
|
+
continue;
|
|
16
|
+
if (candidates.some(name => existsSync(join(directory, name))))
|
|
17
|
+
return { available: true };
|
|
18
|
+
}
|
|
19
|
+
return { available: false };
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=detect-asset-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-asset-generator.js","sourceRoot":"","sources":["../../src/brand/detect-asset-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,SAAS,CAAA;AAClC,OAAO,EAAC,SAAS,EAAE,IAAI,EAAC,MAAM,WAAW,CAAA;AAEzC,MAAM,QAAQ,GAAG,qBAAqB,CAAA;AAEtC;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAyB,OAAO,CAAC,GAAG;IAGvE,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAA;IAC5C,IAAI,CAAC,SAAS;QAAE,OAAO,EAAC,SAAS,EAAE,KAAK,EAAC,CAAA;IAEzC,MAAM,iBAAiB,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACxE,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,GAAG,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAA;IAE7G,KAAK,MAAM,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,SAAS;YAAE,SAAQ;QACxB,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,EAAC,SAAS,EAAE,IAAI,EAAC,CAAA;IAC1F,CAAC;IACD,OAAO,EAAC,SAAS,EAAE,KAAK,EAAC,CAAA;AAC3B,CAAC"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
export type FrameworkId = 'next-app' | 'next-pages' | 'vite' | 'astro' | 'remix' | 'unknown';
|
|
2
|
-
export
|
|
2
|
+
export type FrameworkInfo = {
|
|
3
3
|
id: FrameworkId;
|
|
4
4
|
label: string;
|
|
5
5
|
/** Uses React Server Components, so providers need a `'use client'` boundary. */
|
|
6
6
|
rsc: boolean;
|
|
7
7
|
projectDir: string | null;
|
|
8
|
-
}
|
|
8
|
+
};
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* (where providers go, whether a `'use client'` boundary is required, which style import to use).
|
|
10
|
+
* A best-effort detection of the consumer's UI framework so setup guidance can be tailored
|
|
12
11
|
*/
|
|
13
12
|
export declare function detectFramework(fromDir?: string): FrameworkInfo;
|
|
@@ -1,35 +1,32 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, sep } from 'node:path';
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
/**
|
|
4
|
+
* A best-effort detection of the consumer's UI framework so setup guidance can be tailored
|
|
5
|
+
*/
|
|
6
|
+
export function detectFramework(fromDir = process.cwd()) {
|
|
7
|
+
// Walk up from `fromDir` to the nearest package.json, ignoring any inside node_modules.
|
|
8
|
+
let projectDir = null;
|
|
9
|
+
for (let dir = fromDir;;) {
|
|
10
|
+
if (!dir.split(sep).includes('node_modules') && existsSync(join(dir, 'package.json'))) {
|
|
11
|
+
projectDir = dir;
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
9
14
|
const parent = dirname(dir);
|
|
10
15
|
if (parent === dir)
|
|
11
|
-
|
|
16
|
+
break;
|
|
12
17
|
dir = parent;
|
|
13
18
|
}
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
if (!projectDir)
|
|
20
|
+
return { id: 'unknown', label: 'a React project', rsc: false, projectDir: null };
|
|
21
|
+
// Merge direct + dev dependencies; an unreadable or invalid package.json yields no deps.
|
|
22
|
+
let deps = {};
|
|
16
23
|
try {
|
|
17
24
|
const parsed = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf8'));
|
|
18
|
-
|
|
25
|
+
deps = { ...parsed.dependencies, ...parsed.devDependencies };
|
|
19
26
|
}
|
|
20
27
|
catch {
|
|
21
|
-
|
|
28
|
+
// ignore — treat as no deps
|
|
22
29
|
}
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Best-effort detection of the consumer's framework so setup guidance can be tailored
|
|
26
|
-
* (where providers go, whether a `'use client'` boundary is required, which style import to use).
|
|
27
|
-
*/
|
|
28
|
-
export function detectFramework(fromDir = process.cwd()) {
|
|
29
|
-
const projectDir = findProjectDir(fromDir);
|
|
30
|
-
if (!projectDir)
|
|
31
|
-
return { id: 'unknown', label: 'a React project', rsc: false, projectDir: null };
|
|
32
|
-
const deps = readDependencies(projectDir);
|
|
33
30
|
if (deps.next) {
|
|
34
31
|
const appRouter = existsSync(join(projectDir, 'app')) || existsSync(join(projectDir, 'src', 'app'));
|
|
35
32
|
return appRouter
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-framework.js","sourceRoot":"","sources":["../../src/brand/detect-framework.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAChD,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAY5C
|
|
1
|
+
{"version":3,"file":"detect-framework.js","sourceRoot":"","sources":["../../src/brand/detect-framework.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAChD,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAY5C;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IAC7D,wFAAwF;IACxF,IAAI,UAAU,GAAkB,IAAI,CAAA;IACpC,KAAK,IAAI,GAAG,GAAG,OAAO,IAAM,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;YACtF,UAAU,GAAG,GAAG,CAAA;YAChB,MAAK;QACP,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,MAAK;QACzB,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;IACD,IAAI,CAAC,UAAU;QAAE,OAAO,EAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAC,CAAA;IAE/F,yFAAyF;IACzF,IAAI,IAAI,GAA2B,EAAE,CAAA;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAG/E,CAAA;QACD,IAAI,GAAG,EAAC,GAAG,MAAM,CAAC,YAAY,EAAE,GAAG,MAAM,CAAC,eAAe,EAAC,CAAA;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;IAC9B,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;QACnG,OAAO,SAAS;YACd,CAAC,CAAC,EAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,sBAAsB,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAC;YACxE,CAAC,CAAC,EAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IACjF,CAAC;IACD,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACxD,OAAO,EAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IAC9D,CAAC;IACD,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,EAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IAC5E,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,EAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IACjF,OAAO,EAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;AAC1E,CAAC"}
|
|
@@ -3,38 +3,26 @@ import type { BrandInstall } from './resolve-install.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* One entry in the documentation table of contents (parsed from `llms.txt`).
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
6
|
+
export type DocEntry = {
|
|
7
7
|
label: string;
|
|
8
8
|
/** Path relative to the docs root, e.g. `docs/components/Hero/index.md`. */
|
|
9
9
|
path: string;
|
|
10
10
|
description?: string;
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
12
|
export type DocOrigin = 'installed' | 'live' | 'none';
|
|
13
|
-
export
|
|
13
|
+
export type DocResult = {
|
|
14
14
|
text: string;
|
|
15
15
|
origin: DocOrigin;
|
|
16
16
|
version: string | null;
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* Resolution order, by design:
|
|
22
|
-
* 1. The bundled, version-pinned docs of the installed `@primer/react-brand` (authoritative,
|
|
23
|
-
* offline, matches the consumer's version).
|
|
24
|
-
* 2. The live site's `llms.txt` / pages — best-effort enrichment for guidance that is not
|
|
25
|
-
* bundled, bounded by a short timeout and cached.
|
|
26
|
-
*
|
|
27
|
-
* Version-sensitive facts never depend on the network; this only covers prose guidance.
|
|
19
|
+
* Docs source is either user-installed (bundled with the installed `@primer/react-brand`), or live from the site. The
|
|
20
|
+
* MCP client will always try the installed docs first, and fall back to the live site if not found.
|
|
28
21
|
*/
|
|
29
|
-
export
|
|
22
|
+
export type DocsSource = {
|
|
30
23
|
origin: DocOrigin;
|
|
31
24
|
version: string | null;
|
|
32
25
|
index(): Promise<DocEntry[]>;
|
|
33
26
|
read(path: string): Promise<DocResult | null>;
|
|
34
|
-
}
|
|
27
|
+
};
|
|
35
28
|
export declare function createDocsSource(brand: BrandInstall, logger: Logger): DocsSource;
|
|
36
|
-
/**
|
|
37
|
-
* Parse the `llms.txt` markdown table of contents into structured entries.
|
|
38
|
-
* Lines look like: `- [Hero](docs/components/Hero/index.md): A prominent banner...`
|
|
39
|
-
*/
|
|
40
|
-
export declare function parseToc(markdown: string): DocEntry[];
|
|
@@ -1,17 +1,44 @@
|
|
|
1
1
|
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, normalize, resolve, sep } from 'node:path';
|
|
3
|
-
const
|
|
4
|
-
const FETCH_TIMEOUT_MS = 2500;
|
|
3
|
+
const prodURL = 'https://primer.style/brand';
|
|
5
4
|
export function createDocsSource(brand, logger) {
|
|
6
5
|
const cache = new Map();
|
|
7
6
|
const hasLocalDocs = Boolean(brand.llmsPath && brand.docsDir);
|
|
8
7
|
const docsRoot = brand.docsDir ? dirname(brand.docsDir) : null;
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Prevents path traversal (e.g. `../../etc/passwd`) reaching the filesystem.
|
|
10
|
+
*/
|
|
11
|
+
const safeJoin = (root, path) => {
|
|
12
|
+
const target = resolve(join(root, normalize(path)));
|
|
13
|
+
const realRoot = realpathSync(root);
|
|
14
|
+
const prefix = realRoot.endsWith(sep) ? realRoot : `${realRoot}${sep}`;
|
|
15
|
+
if (target !== realRoot && !target.startsWith(prefix))
|
|
16
|
+
return null;
|
|
17
|
+
return target;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Parse the `llms.txt` markdown table of contents into structured entries.
|
|
21
|
+
*/
|
|
22
|
+
const parseToc = (markdown) => {
|
|
23
|
+
const entries = [];
|
|
24
|
+
const linkLine = /^- \[(.+?)\]\((.+?)\)(?::\s*(.*))?$/;
|
|
25
|
+
for (const raw of markdown.split('\n')) {
|
|
26
|
+
const match = linkLine.exec(raw.trim());
|
|
27
|
+
if (!match)
|
|
28
|
+
continue;
|
|
29
|
+
const [, label, path, description] = match;
|
|
30
|
+
if (!label || !path)
|
|
31
|
+
continue;
|
|
32
|
+
entries.push({ label, path, description: description?.trim() || undefined });
|
|
33
|
+
}
|
|
34
|
+
return entries;
|
|
35
|
+
};
|
|
36
|
+
const fetchText = async (url) => {
|
|
10
37
|
const cached = cache.get(url);
|
|
11
38
|
if (cached !== undefined)
|
|
12
39
|
return cached;
|
|
13
40
|
try {
|
|
14
|
-
const response = await fetch(url, { signal: AbortSignal.timeout(
|
|
41
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(2500) });
|
|
15
42
|
if (!response.ok)
|
|
16
43
|
return null;
|
|
17
44
|
const text = await response.text();
|
|
@@ -22,26 +49,26 @@ export function createDocsSource(brand, logger) {
|
|
|
22
49
|
logger.debug(`live fetch failed for ${url}: ${error.message}`);
|
|
23
50
|
return null;
|
|
24
51
|
}
|
|
25
|
-
}
|
|
26
|
-
async
|
|
52
|
+
};
|
|
53
|
+
const index = async () => {
|
|
27
54
|
if (hasLocalDocs && brand.llmsPath) {
|
|
28
55
|
return parseToc(readFileSync(brand.llmsPath, 'utf8'));
|
|
29
56
|
}
|
|
30
|
-
const live = await fetchText(`${
|
|
57
|
+
const live = await fetchText(`${prodURL}/llms.txt`);
|
|
31
58
|
return live ? parseToc(live) : [];
|
|
32
|
-
}
|
|
33
|
-
async
|
|
59
|
+
};
|
|
60
|
+
const read = async (path) => {
|
|
34
61
|
if (hasLocalDocs && docsRoot) {
|
|
35
62
|
const safe = safeJoin(docsRoot, path);
|
|
36
63
|
if (safe && existsSync(safe)) {
|
|
37
64
|
return { text: readFileSync(safe, 'utf8'), origin: 'installed', version: brand.version };
|
|
38
65
|
}
|
|
39
66
|
}
|
|
40
|
-
const live = await fetchText(`${
|
|
67
|
+
const live = await fetchText(`${prodURL}/${path.replace(/^docs\//, '').replace(/\.md$/, '')}`);
|
|
41
68
|
if (live)
|
|
42
69
|
return { text: live, origin: 'live', version: null };
|
|
43
70
|
return null;
|
|
44
|
-
}
|
|
71
|
+
};
|
|
45
72
|
return {
|
|
46
73
|
origin: hasLocalDocs ? 'installed' : 'live',
|
|
47
74
|
version: brand.version,
|
|
@@ -49,34 +76,4 @@ export function createDocsSource(brand, logger) {
|
|
|
49
76
|
read,
|
|
50
77
|
};
|
|
51
78
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Parse the `llms.txt` markdown table of contents into structured entries.
|
|
54
|
-
* Lines look like: `- [Hero](docs/components/Hero/index.md): A prominent banner...`
|
|
55
|
-
*/
|
|
56
|
-
export function parseToc(markdown) {
|
|
57
|
-
const entries = [];
|
|
58
|
-
const linkLine = /^- \[(.+?)\]\((.+?)\)(?::\s*(.*))?$/;
|
|
59
|
-
for (const raw of markdown.split('\n')) {
|
|
60
|
-
const match = linkLine.exec(raw.trim());
|
|
61
|
-
if (!match)
|
|
62
|
-
continue;
|
|
63
|
-
const [, label, path, description] = match;
|
|
64
|
-
if (!label || !path)
|
|
65
|
-
continue;
|
|
66
|
-
entries.push({ label, path, description: description?.trim() || undefined });
|
|
67
|
-
}
|
|
68
|
-
return entries;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Join a user-supplied doc path onto the docs root, refusing anything that escapes it.
|
|
72
|
-
* Prevents path traversal (e.g. `../../etc/passwd`) reaching the filesystem.
|
|
73
|
-
*/
|
|
74
|
-
function safeJoin(root, path) {
|
|
75
|
-
const target = resolve(join(root, normalize(path)));
|
|
76
|
-
const realRoot = realpathSync(root);
|
|
77
|
-
const prefix = realRoot.endsWith(sep) ? realRoot : `${realRoot}${sep}`;
|
|
78
|
-
if (target !== realRoot && !target.startsWith(prefix))
|
|
79
|
-
return null;
|
|
80
|
-
return target;
|
|
81
|
-
}
|
|
82
79
|
//# sourceMappingURL=docs-source.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs-source.js","sourceRoot":"","sources":["../../src/brand/docs-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAuBhE,MAAM,
|
|
1
|
+
{"version":3,"file":"docs-source.js","sourceRoot":"","sources":["../../src/brand/docs-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAuBhE,MAAM,OAAO,GAAG,4BAA4B,CAAA;AAa5C,MAAM,UAAU,gBAAgB,CAAC,KAAmB,EAAE,MAAc;IAClE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEvC,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;IAC7D,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAE9D;;OAEG;IACH,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,IAAY,EAAiB,EAAE;QAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACnD,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;QACnC,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,GAAG,EAAE,CAAA;QACtE,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAA;QAClE,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IAED;;OAEG;IACH,MAAM,QAAQ,GAAG,CAAC,QAAgB,EAAc,EAAE;QAChD,MAAM,OAAO,GAAe,EAAE,CAAA;QAC9B,MAAM,QAAQ,GAAG,qCAAqC,CAAA;QACtD,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;YACvC,IAAI,CAAC,KAAK;gBAAE,SAAQ;YACpB,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,KAAK,CAAA;YAC1C,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI;gBAAE,SAAQ;YAC7B,OAAO,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,SAAS,EAAC,CAAC,CAAA;QAC5E,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;IAED,MAAM,SAAS,GAAG,KAAK,EAAE,GAAW,EAA0B,EAAE;QAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC7B,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAA;QACvC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC,CAAC,CAAA;YACtE,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAA;YAC7B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAClC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpB,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAA;YACzE,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,IAAyB,EAAE;QAC5C,IAAI,YAAY,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnC,OAAO,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAA;QACvD,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,OAAO,WAAW,CAAC,CAAA;QACnD,OAAO,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACnC,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,KAAK,EAAE,IAAY,EAA6B,EAAE;QAC7D,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YACrC,IAAI,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,OAAO,EAAC,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAC,CAAA;YACxF,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;QAC9F,IAAI,IAAI;YAAE,OAAO,EAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAC,CAAA;QAC5D,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,OAAO;QACL,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM;QAC3C,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK;QACL,IAAI;KACL,CAAA;AACH,CAAC"}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import type { CatalogAsset } from '../catalog/types.js';
|
|
2
|
-
export
|
|
2
|
+
export type InstalledAssets = {
|
|
3
3
|
assets: CatalogAsset[];
|
|
4
4
|
sources: Array<{
|
|
5
5
|
module: string;
|
|
6
6
|
version: string | null;
|
|
7
7
|
}>;
|
|
8
|
-
}
|
|
8
|
+
};
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* exported names. This makes `primer_brand_asset` version-accurate
|
|
12
|
-
* the project actually has, rather than the snapshot baked into the catalog at build time.
|
|
10
|
+
* Resolves the icon/illustration packages installed in the consumer's project and extract their
|
|
11
|
+
* exported names. This makes `primer_brand_asset` version-accurate.
|
|
13
12
|
*/
|
|
14
13
|
export declare function resolveInstalledAssets(fromDir?: string): InstalledAssets;
|
|
@@ -1,92 +1,84 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
|
-
const
|
|
3
|
+
const iconPackages = [
|
|
4
4
|
{ module: '@primer/octicons-react', kind: 'icon' },
|
|
5
5
|
{ module: '@primer/octovisuals-react', kind: 'illustration' },
|
|
6
6
|
];
|
|
7
|
-
/** Walk up from `fromDir` to find an installed package directory. */
|
|
8
|
-
function findPackageDir(packageName, fromDir) {
|
|
9
|
-
let dir = fromDir;
|
|
10
|
-
for (;;) {
|
|
11
|
-
const candidate = join(dir, 'node_modules', ...packageName.split('/'));
|
|
12
|
-
if (existsSync(join(candidate, 'package.json')))
|
|
13
|
-
return candidate;
|
|
14
|
-
const parent = dirname(dir);
|
|
15
|
-
if (parent === dir)
|
|
16
|
-
return null;
|
|
17
|
-
dir = parent;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function readVersion(packageDir) {
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8')).version ?? null;
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
7
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* in the package's output directory are scanned, not just `index.d.ts`.
|
|
8
|
+
* Resolves the icon/illustration packages installed in the consumer's project and extract their
|
|
9
|
+
* exported names. This makes `primer_brand_asset` version-accurate.
|
|
32
10
|
*/
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
let
|
|
11
|
+
export function resolveInstalledAssets(fromDir = process.cwd()) {
|
|
12
|
+
/** Walk up from `fromDir` to find an installed package directory. */
|
|
13
|
+
const findPackageDir = (packageName) => {
|
|
14
|
+
let dir = fromDir;
|
|
15
|
+
for (;;) {
|
|
16
|
+
const candidate = join(dir, 'node_modules', ...packageName.split('/'));
|
|
17
|
+
if (existsSync(join(candidate, 'package.json')))
|
|
18
|
+
return candidate;
|
|
19
|
+
const parent = dirname(dir);
|
|
20
|
+
if (parent === dir)
|
|
21
|
+
return null;
|
|
22
|
+
dir = parent;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const readVersion = (packageDir) => {
|
|
37
26
|
try {
|
|
38
|
-
|
|
27
|
+
return JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8')).version ?? null;
|
|
39
28
|
}
|
|
40
29
|
catch {
|
|
41
|
-
|
|
30
|
+
return null;
|
|
42
31
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
};
|
|
33
|
+
/** Collects exported PascalCase const names from a package's `.d.ts` type declarations. */
|
|
34
|
+
const exportedNames = (packageDir) => {
|
|
35
|
+
const names = new Set();
|
|
36
|
+
for (const subDir of ['dist', 'lib', '.']) {
|
|
37
|
+
let entries;
|
|
47
38
|
try {
|
|
48
|
-
|
|
39
|
+
entries = readdirSync(join(packageDir, subDir));
|
|
49
40
|
}
|
|
50
41
|
catch {
|
|
51
42
|
continue;
|
|
52
43
|
}
|
|
53
|
-
for (const
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
names.add(
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
if (!entry.endsWith('.d.ts'))
|
|
46
|
+
continue;
|
|
47
|
+
let source;
|
|
48
|
+
try {
|
|
49
|
+
source = readFileSync(join(packageDir, subDir, entry), 'utf8');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
for (const match of source.matchAll(/(?:export\s+)?declare\s+const\s+([A-Z]\w+)/g)) {
|
|
55
|
+
if (match[1])
|
|
56
|
+
names.add(match[1]);
|
|
57
|
+
}
|
|
58
|
+
for (const match of source.matchAll(/export\s+const\s+([A-Z]\w+)/g)) {
|
|
59
|
+
if (match[1])
|
|
60
|
+
names.add(match[1]);
|
|
61
|
+
}
|
|
62
|
+
for (const match of source.matchAll(/export\s*\{([^}]*)\}/g)) {
|
|
63
|
+
for (const part of (match[1] ?? '').split(',')) {
|
|
64
|
+
const ident = part
|
|
65
|
+
.trim()
|
|
66
|
+
.split(/\s+as\s+/)
|
|
67
|
+
.pop()
|
|
68
|
+
?.trim();
|
|
69
|
+
if (ident && /^[A-Z]\w+$/.test(ident))
|
|
70
|
+
names.add(ident);
|
|
71
|
+
}
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
function keepName(name, kind) {
|
|
77
|
-
// Octovisuals components are also named `...Icon`, so they are kept by package, not suffix.
|
|
78
|
-
return kind === 'icon' ? name.endsWith('Icon') : /^[A-Z]/.test(name) && !/(Props|Type|Metadata)$/.test(name);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Resolve the icon/illustration packages installed in the consumer's project and extract their
|
|
82
|
-
* exported names. This makes `primer_brand_asset` version-accurate: it reflects the Octicons/Octovisuals
|
|
83
|
-
* the project actually has, rather than the snapshot baked into the catalog at build time.
|
|
84
|
-
*/
|
|
85
|
-
export function resolveInstalledAssets(fromDir = process.cwd()) {
|
|
75
|
+
return [...names];
|
|
76
|
+
};
|
|
77
|
+
const keepName = (name, kind) => kind === 'icon' ? name.endsWith('Icon') : /^[A-Z]/.test(name) && !/(Props|Type|Metadata)$/.test(name);
|
|
86
78
|
const assets = [];
|
|
87
79
|
const sources = [];
|
|
88
|
-
for (const { module, kind } of
|
|
89
|
-
const packageDir = findPackageDir(module
|
|
80
|
+
for (const { module, kind } of iconPackages) {
|
|
81
|
+
const packageDir = findPackageDir(module);
|
|
90
82
|
if (!packageDir)
|
|
91
83
|
continue;
|
|
92
84
|
const names = exportedNames(packageDir).filter(name => keepName(name, kind));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-assets.js","sourceRoot":"","sources":["../../src/brand/resolve-assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,WAAW,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,WAAW,CAAA;AASvC,MAAM,
|
|
1
|
+
{"version":3,"file":"resolve-assets.js","sourceRoot":"","sources":["../../src/brand/resolve-assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
* This is what makes the server version-aware: tools prefer the docs and version of the
|
|
4
4
|
* package the project actually depends on.
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
6
|
+
export type BrandInstall = {
|
|
7
7
|
found: boolean;
|
|
8
8
|
version: string | null;
|
|
9
9
|
packageDir: string | null;
|
|
10
|
-
/** Bundled version-pinned docs directory, when the installed version ships one. */
|
|
11
10
|
docsDir: string | null;
|
|
12
|
-
/** Bundled `llms.txt` table of contents, when present. */
|
|
13
11
|
llmsPath: string | null;
|
|
14
|
-
}
|
|
12
|
+
};
|
|
15
13
|
/**
|
|
16
14
|
* Walk up from `fromDir` looking for `node_modules/@primer/react-brand`. We resolve from the
|
|
17
15
|
* working directory (not from this server's own location) so that, when launched inside a
|