Skip to content

Commit 316965a

Browse files
committed
refactor(react): extract @doc-kittens/react package
1 parent c8553db commit 316965a

102 files changed

Lines changed: 416 additions & 289 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/react-kitten-package.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@doc-kittens/react': major
3+
'@node-core/doc-kit': minor
4+
---
5+
6+
The React/JSX-based generators (`html` — previously `web` —, `jsx-ast`,
7+
`llms-txt`, `sitemap`, and `orama-db`) now live in the new
8+
`@doc-kittens/react` package and are loaded via import specifiers such as
9+
`@doc-kittens/react/html`. The `web` generator is renamed to `html`: the CLI
10+
shorthand `web` keeps working as a deprecated alias, but the configuration
11+
key is now `html` instead of `web`.

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export default defineConfig([
9696
{
9797
files: [
9898
'packages/core/src/generators/legacy-html/assets/*.js',
99-
'packages/core/src/generators/web/ui/**/*',
99+
'packages/react/src/html/ui/**/*',
100100
],
101101
languageOptions: {
102102
globals: {

package-lock.json

Lines changed: 36 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/package.json

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,12 @@
2323
"./ast": "./src/generators/ast/index.mjs",
2424
"./ast-js": "./src/generators/ast-js/index.mjs",
2525
"./json-simple": "./src/generators/json-simple/index.mjs",
26-
"./jsx-ast": "./src/generators/jsx-ast/index.mjs",
2726
"./legacy-html": "./src/generators/legacy-html/index.mjs",
2827
"./legacy-html-all": "./src/generators/legacy-html-all/index.mjs",
2928
"./legacy-json": "./src/generators/legacy-json/index.mjs",
3029
"./legacy-json-all": "./src/generators/legacy-json-all/index.mjs",
31-
"./llms-txt": "./src/generators/llms-txt/index.mjs",
3230
"./man-page": "./src/generators/man-page/index.mjs",
3331
"./metadata": "./src/generators/metadata/index.mjs",
34-
"./orama-db": "./src/generators/orama-db/index.mjs",
35-
"./sitemap": "./src/generators/sitemap/index.mjs",
36-
"./web": "./src/generators/web/index.mjs",
3732
"./package.json": "./package.json",
3833
"./shiki.config.mjs": "./shiki.config.mjs",
3934
"./src/*": "./src/*",
@@ -54,33 +49,18 @@
5449
],
5550
"dependencies": {
5651
"@actions/core": "^3.0.0",
57-
"@fontsource-variable/open-sans": "^5.3.0",
58-
"@fontsource/ibm-plex-mono": "^5.3.0",
59-
"@heroicons/react": "^2.2.0",
6052
"@node-core/rehype-shiki": "^1.4.2",
61-
"@node-core/ui-components": "^1.7.2",
62-
"@orama/orama": "^3.1.18",
63-
"@orama/ui": "^1.5.4",
6453
"@swc/html-wasm": "^1.15.43",
6554
"@swc/wasm": "^1.15.46",
6655
"acorn": "^8.17.0",
6756
"commander": "^15.0.0",
6857
"cosmiconfig": "^9.0.2",
6958
"dedent": "^1.7.2",
70-
"estree-util-to-js": "^2.0.0",
7159
"estree-util-visit": "^2.0.0",
7260
"github-slugger": "^2.0.0",
7361
"glob-parent": "^6.0.2",
74-
"hast-util-to-string": "^3.0.1",
7562
"hastscript": "^9.0.1",
76-
"mdast-util-slice-markdown": "^2.0.1",
7763
"piscina": "^5.2.0",
78-
"preact": "^10.29.2",
79-
"preact-render-to-string": "^6.7.0",
80-
"reading-time": "^1.5.0",
81-
"recma-jsx": "^1.0.1",
82-
"rehype-raw": "^7.0.0",
83-
"rehype-recma": "^1.0.0",
8464
"rehype-stringify": "^10.0.1",
8565
"remark-gfm": "^4.0.1",
8666
"remark-mdx": "^3.1.1",
@@ -97,7 +77,6 @@
9777
"unist-util-remove": "^4.0.0",
9878
"unist-util-select": "^5.1.0",
9979
"unist-util-visit": "^5.1.0",
100-
"vite": "~8.1.5",
10180
"yaml": "^2.9.0"
10281
}
10382
}

packages/core/src/generators/__tests__/index.test.mjs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
import assert from 'node:assert/strict';
22
import { describe, it } from 'node:test';
33

4-
import { allGenerators, publicGenerators } from '../index.mjs';
4+
import {
5+
allGenerators,
6+
deprecatedGenerators,
7+
publicGenerators,
8+
} from '../index.mjs';
59
import { loadGenerator, resolveGeneratorSpecifier } from '../loader.mjs';
610

711
const validDependencies = Object.values(allGenerators);
812

13+
// Deprecated aliases intentionally have keys that differ from the
14+
// generator's name, so they are excluded from the name-match assertions.
15+
const currentGenerators = Object.fromEntries(
16+
Object.entries(allGenerators).filter(
17+
([name]) => !(name in deprecatedGenerators)
18+
)
19+
);
20+
921
const loadedGenerators = await Promise.all(
10-
Object.entries(allGenerators).map(async ([name, specifier]) => [
22+
Object.entries(currentGenerators).map(async ([name, specifier]) => [
1123
name,
1224
specifier,
1325
await loadGenerator(specifier),
@@ -50,6 +62,15 @@ describe('All Generators', () => {
5062
});
5163
});
5264

65+
it('should resolve deprecated aliases to loadable generators', async () => {
66+
for (const [name, specifier] of Object.entries(deprecatedGenerators)) {
67+
assert.equal(resolveGeneratorSpecifier(name), specifier);
68+
69+
const generator = await loadGenerator(specifier);
70+
assert.ok(generator.name, `Deprecated alias "${name}" must load`);
71+
}
72+
});
73+
5374
it('should have ast generator as a top-level generator with no dependencies', async () => {
5475
const ast = await loadGenerator(allGenerators.ast);
5576
assert.ok(ast, 'ast generator should exist');

packages/core/src/generators/index.mjs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/**
44
* Maps the shorthand names accepted by the CLI and configuration files
5-
* (e.g. `--target web`) to the import specifiers they resolve to.
5+
* (e.g. `--target html`) to the import specifiers they resolve to.
66
*
77
* Generators are loaded dynamically by specifier (see `./loader.mjs`), so this
88
* module must not import any generator code — it is purely a lookup table.
@@ -18,10 +18,10 @@ export const publicGenerators = {
1818
'legacy-json-all': '@node-core/doc-kit/legacy-json-all',
1919
'addon-verify': '@node-core/doc-kit/addon-verify',
2020
'api-links': '@node-core/doc-kit/api-links',
21-
'orama-db': '@node-core/doc-kit/orama-db',
22-
'llms-txt': '@node-core/doc-kit/llms-txt',
23-
sitemap: '@node-core/doc-kit/sitemap',
24-
web: '@node-core/doc-kit/web',
21+
'orama-db': '@doc-kittens/react/orama-db',
22+
'llms-txt': '@doc-kittens/react/llms-txt',
23+
sitemap: '@doc-kittens/react/sitemap',
24+
html: '@doc-kittens/react/html',
2525
};
2626

2727
// These ones are special since they don't produce standard output,
@@ -30,11 +30,19 @@ export const publicGenerators = {
3030
const internalGenerators = {
3131
ast: '@node-core/doc-kit/ast',
3232
metadata: '@node-core/doc-kit/metadata',
33-
'jsx-ast': '@node-core/doc-kit/jsx-ast',
33+
'jsx-ast': '@doc-kittens/react/jsx-ast',
3434
'ast-js': '@node-core/doc-kit/ast-js',
3535
};
3636

37+
// Former names kept resolvable for existing invocations and config files.
38+
// Unlike the maps above, keys here intentionally differ from the generator's
39+
// `name` property.
40+
export const deprecatedGenerators = {
41+
web: '@doc-kittens/react/html',
42+
};
43+
3744
export const allGenerators = {
3845
...publicGenerators,
3946
...internalGenerators,
47+
...deprecatedGenerators,
4048
};

packages/core/src/generators/web/template.html

Lines changed: 0 additions & 22 deletions
This file was deleted.

packages/core/src/utils/remark.mjs

Lines changed: 14 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,30 @@
11
'use strict';
22

3-
import rehypeShikiji from '@node-core/rehype-shiki/plugin';
4-
import recmaJsx from 'recma-jsx';
5-
import recmaStringify from 'recma-stringify';
6-
import rehypeRaw from 'rehype-raw';
7-
import rehypeRecma from 'rehype-recma';
83
import rehypeStringify from 'rehype-stringify';
94
import remarkGfm from 'remark-gfm';
105
import remarkMdx from 'remark-mdx';
116
import remarkParse from 'remark-parse';
127
import remarkRehype from 'remark-rehype';
138
import remarkStringify from 'remark-stringify';
149
import { unified } from 'unified';
15-
import { visit } from 'unist-util-visit';
1610

17-
import syntaxHighlighter, { highlighter } from './highlighter.mjs';
11+
import syntaxHighlighter from './highlighter.mjs';
1812
import { lazy } from './misc.mjs';
19-
import {
20-
typeAnnotationToHast,
21-
typeAnnotationToHighlightedHast,
22-
} from './type-annotations/hast.mjs';
13+
import { typeAnnotationToHast } from './type-annotations/hast.mjs';
2314
import remarkTypeAnnotations from './type-annotations/remark.mjs';
24-
import { AST_NODE_TYPES } from '../generators/jsx-ast/constants.mjs';
25-
import transformAlerts from '../generators/jsx-ast/utils/plugins/alerts.mjs';
26-
import transformElements from '../generators/jsx-ast/utils/plugins/transformer.mjs';
2715

28-
const passThrough = ['element', ...Object.values(AST_NODE_TYPES.MDX)];
29-
const codeMetaProperty = 'codeMeta';
30-
31-
/**
32-
* Stores fenced code metadata on properties before rehypeRaw reparses the tree.
33-
*/
34-
const preserveCodeMeta = () => tree => {
35-
visit(tree, 'element', node => {
36-
const meta = node.data?.meta;
37-
38-
if (node.tagName === 'code' && typeof meta === 'string') {
39-
node.properties ||= {};
40-
node.properties[codeMetaProperty] = meta;
41-
}
42-
});
43-
};
44-
45-
/**
46-
* Restores fenced code metadata so the Shiki plugin can read displayName.
47-
*/
48-
const restoreCodeMeta = () => tree => {
49-
visit(tree, 'element', node => {
50-
const meta = node.properties?.[codeMetaProperty];
51-
52-
if (node.tagName === 'code' && typeof meta === 'string') {
53-
node.data = { ...node.data, meta };
54-
delete node.properties[codeMetaProperty];
55-
}
56-
});
57-
};
16+
// MDX node types that may appear in trees parsed by `getRemarkMdx`; the
17+
// rehype pipelines pass them through untouched.
18+
const passThrough = [
19+
'element',
20+
'mdxJsxTextElement',
21+
'mdxJsxFlowElement',
22+
'mdxJsxAttribute',
23+
'mdxJsxAttributeValueExpression',
24+
'mdxFlowExpression',
25+
'mdxTextExpression',
26+
'mdxjsEsm',
27+
];
5828

5929
/**
6030
* Retrieves an instance of Remark configured to parse GFM (GitHub Flavored Markdown)
@@ -125,34 +95,3 @@ export const getRemarkRehypeWithShiki = lazy(() =>
12595
// and we trust the sources of the Markdown files
12696
.use(rehypeStringify, { allowDangerousHtml: true })
12797
);
128-
129-
const singletonShiki = await rehypeShikiji({ highlighter });
130-
131-
/**
132-
* Retrieves an instance of Remark configured to output JSX code.
133-
* including parsing Code Boxes with syntax highlighting
134-
*/
135-
export const getRemarkRecma = lazy(() =>
136-
unified()
137-
.use(remarkParse)
138-
.use(transformAlerts)
139-
// We make Rehype ignore existing HTML nodes, and JSX nodes
140-
// as these are nodes we manually created during the generation process
141-
// We also allow dangerous HTML to be passed through, since we have HTML within our Markdown
142-
// and we trust the sources of the Markdown files
143-
.use(remarkRehype, {
144-
allowDangerousHtml: true,
145-
passThrough,
146-
// The web pipeline gets Shiki-highlighted types with embedded links
147-
handlers: { typeAnnotation: typeAnnotationToHighlightedHast },
148-
})
149-
.use(preserveCodeMeta)
150-
// Any `raw` HTML in the markdown must be converted to AST in order for Recma to understand it
151-
.use(rehypeRaw, { passThrough })
152-
.use(restoreCodeMeta)
153-
.use(() => singletonShiki)
154-
.use(transformElements)
155-
.use(rehypeRecma)
156-
.use(recmaJsx)
157-
.use(recmaStringify)
158-
);

0 commit comments

Comments
 (0)