Skip to content

Commit efad8b9

Browse files
AVGVSTVS96claude
andcommitted
refactor: remove type workarounds and add TODO.md
- Remove _typeCheck assertions from entry points (unnecessary noise) - Replace `as any` casts with proper LooseHighlighter type - Add TODO.md documenting remaining architecture improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bde2144 commit efad8b9

File tree

5 files changed

+100
-24
lines changed

5 files changed

+100
-24
lines changed

TODO.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Architecture Improvements
2+
3+
Tracked improvements for react-shiki codebase.
4+
5+
## High Priority
6+
7+
### Consolidate Bundle Factories
8+
`bundles/full.ts` and `bundles/web.ts` are nearly identical - only the import differs. Extract shared logic into a factory that accepts bundle configuration.
9+
10+
```typescript
11+
// Before: Two nearly identical files
12+
// bundles/full.ts imports from 'shiki'
13+
// bundles/web.ts imports from 'shiki/bundle/web'
14+
15+
// After: Single factory with config
16+
const createBundleHighlighter = (getSingletonHighlighter, createEngine) =>
17+
async (langs, themes, engine) => { ... }
18+
```
19+
20+
### Consolidate Entry Points
21+
`index.ts`, `web.ts`, and `core.ts` repeat the same wrapper pattern. Create a shared entry point factory.
22+
23+
```typescript
24+
// Current: Same pattern repeated 3 times
25+
export const useShikiHighlighter = <F extends OutputFormat = 'react'>(...) => {
26+
return useBaseHook(..., createXxxHighlighter);
27+
};
28+
export const ShikiHighlighter = createShikiHighlighterComponent(useShikiHighlighter);
29+
30+
// Proposed: Entry point factory
31+
const { useShikiHighlighter, ShikiHighlighter } = createEntryPoint(createFullHighlighter);
32+
```
33+
34+
## Medium Priority
35+
36+
### Type-Safe Transformer Registry
37+
The output transformer registry loses type information on lookup:
38+
```typescript
39+
return outputTransformers[format](context) as OutputFormatMap[F];
40+
```
41+
42+
Could use conditional types or function overloads for full type safety.
43+
44+
### Centralize Constants
45+
`DEFAULT_THEMES` is defined in `options.ts`. Consider a `constants.ts` for all defaults:
46+
- Default themes
47+
- Default line number start
48+
- Default output format
49+
50+
### Split Types File
51+
`types.ts` mixes public API types with internal types. Consider:
52+
- `types.ts` - Public API types (Language, Theme, HighlighterOptions)
53+
- `internal-types.ts` - Internal types (TimeoutState, TransformContext)
54+
55+
## Low Priority
56+
57+
### Surface Errors to Consumers
58+
Currently errors are logged but not surfaced:
59+
```typescript
60+
highlight().catch(console.error);
61+
```
62+
63+
Consider an `onError` callback option or returning error state from hook.
64+
65+
### Component Output Type
66+
The component casts the hook return:
67+
```typescript
68+
) as ReactNode | string | null;
69+
```
70+
71+
This is because the component restricts `outputFormat` to `'react' | 'html'` but still uses the generic hook. Could create a component-specific hook variant.
72+
73+
### Empty String Fallbacks
74+
Token output uses empty strings as fallbacks:
75+
```typescript
76+
fg: theme?.fg ?? '',
77+
bg: theme?.bg ?? '',
78+
```
79+
80+
Consider `undefined` for missing values, letting consumers decide on fallbacks.

package/src/core.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ export const useShikiHighlighter = <F extends OutputFormat = 'react'>(
9393
);
9494
};
9595

96-
// Type assertion to satisfy UseShikiHighlighter contract
97-
const _typeCheck: UseShikiHighlighter = useShikiHighlighter;
98-
void _typeCheck;
99-
10096
/**
10197
* ShikiHighlighter component using a custom highlighter.
10298
* Requires a highlighter to be provided.

package/src/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ export const useShikiHighlighter = <F extends OutputFormat = 'react'>(
8383
);
8484
};
8585

86-
// Type assertion to satisfy UseShikiHighlighter contract
87-
const _typeCheck: UseShikiHighlighter = useShikiHighlighter;
88-
void _typeCheck;
89-
9086
/**
9187
* ShikiHighlighter component using the full bundle.
9288
* Includes all languages and themes for maximum compatibility.

package/src/lib/output.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/**
22
* Output transformers for converting Shiki highlighter output to different formats.
3-
* Each transformer is a pure function that takes highlighter output and returns the desired format.
43
*/
54

65
import type { ReactNode } from 'react';
@@ -11,14 +10,27 @@ import type {
1110
Highlighter,
1211
HighlighterCore,
1312
CodeToHastOptions,
13+
CodeToTokensOptions,
14+
CodeToTokensBaseOptions,
1415
TokensResult,
1516
ThemedToken,
1617
} from 'shiki';
1718

1819
import type { OutputFormat, OutputFormatMap } from './types';
1920

21+
/**
22+
* Highlighter with loosened method signatures for dynamic language/theme usage.
23+
*/
24+
type LooseHighlighter = (Highlighter | HighlighterCore) & {
25+
codeToTokens(code: string, options: CodeToTokensOptions): TokensResult;
26+
codeToTokensBase(
27+
code: string,
28+
options: CodeToTokensBaseOptions
29+
): ThemedToken[][];
30+
};
31+
2032
type TransformContext = {
21-
highlighter: Highlighter | HighlighterCore;
33+
highlighter: LooseHighlighter;
2234
code: string;
2335
options: CodeToHastOptions;
2436
isMultiTheme: boolean;
@@ -57,18 +69,15 @@ const transformToTokens = ({
5769
isMultiTheme,
5870
}: TransformContext): TokensResult => {
5971
if (isMultiTheme) {
60-
return (highlighter as any).codeToTokens(
61-
code,
62-
options
63-
) as TokensResult;
72+
return highlighter.codeToTokens(code, options as CodeToTokensOptions);
6473
}
6574

66-
const tokens = (highlighter as any).codeToTokensBase(
75+
const tokens = highlighter.codeToTokensBase(
6776
code,
68-
options
69-
) as ThemedToken[][];
77+
options as CodeToTokensBaseOptions
78+
);
7079

71-
const themeId = (options as { theme?: string }).theme;
80+
const themeId = (options as CodeToTokensBaseOptions).theme;
7281
const theme = themeId ? highlighter.getTheme(themeId) : undefined;
7382

7483
return {
@@ -93,7 +102,6 @@ const outputTransformers = {
93102

94103
/**
95104
* Transform highlighter output to the specified format.
96-
* Uses the transformer registry for clean dispatch without conditionals.
97105
*/
98106
export const transformOutput = <F extends OutputFormat>(
99107
format: F,
@@ -103,7 +111,7 @@ export const transformOutput = <F extends OutputFormat>(
103111
isMultiTheme: boolean
104112
): OutputFormatMap[F] => {
105113
const context: TransformContext = {
106-
highlighter,
114+
highlighter: highlighter as LooseHighlighter,
107115
code,
108116
options,
109117
isMultiTheme,

package/src/web.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ export const useShikiHighlighter = <F extends OutputFormat = 'react'>(
8383
);
8484
};
8585

86-
// Type assertion to satisfy UseShikiHighlighter contract
87-
const _typeCheck: UseShikiHighlighter = useShikiHighlighter;
88-
void _typeCheck;
89-
9086
/**
9187
* ShikiHighlighter component using the web bundle.
9288
* Includes web-focused languages for balanced size and functionality.

0 commit comments

Comments
 (0)