Skip to content

Commit a7f3e0e

Browse files
tanemclaude
andcommitted
Export the option and return types
`src/index.tsx` re-exported only `NProgress` and `useNProgress`, so the option shape and the hook's return shape reached the emitted declarations as local interfaces with no exported names. Anyone typing a wrapper around either entry point had to reach for `Parameters<typeof useNProgress>[0]` and `ReturnType<typeof useNProgress>`. `Options` is renamed to `NProgressOptions`, since `Options` is too generic an identifier to put in a consumer's import namespace. The return shape, previously an inline object type on `useNProgress` and spelled `ReturnType<typeof useNProgress>` in the render-prop signature, is now `NProgressState`. No MIGRATION.md entry: neither name was reachable from the package entry point before this commit. Both are type-only exports, so the runtime bundles are byte-identical: ESM 997 B, CJS 1.06 kB gzipped, unchanged. `test/bundles.spec.ts` already asserts facts about what the build emits, so the public export surface is checked there, over the declaration files it finds in `dist` rather than a hard-coded list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent cda7ae5 commit a7f3e0e

6 files changed

Lines changed: 51 additions & 13 deletions

File tree

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ const Progress = ({ isAnimating }) => (
7272

7373
## API
7474

75-
The package exports one hook and one component. Both take the same [options](#options) and produce the same [values](#return-value), so the choice between them is a matter of which pattern suits the calling code.
75+
The package exports one hook and one component. Both take the same [options](#options) and produce the same [values](#return-value), so the choice between them is a matter of which pattern suits the calling code. Both shapes are exported as types, for typing code that wraps either entry point:
76+
77+
```ts
78+
import type { NProgressOptions, NProgressState } from '@tanem/react-nprogress'
79+
```
7680

7781
### `useNProgress`
7882

@@ -106,7 +110,7 @@ Takes the options as props and calls `children` with the values the hook returns
106110

107111
### Options
108112

109-
All four options are optional.
113+
All four options are optional. The type is `NProgressOptions`.
110114

111115
| Option | Type | Default |
112116
| ----------------------------------------- | --------- | ------- |
@@ -133,7 +137,7 @@ Lower bound for `progress`, between `0` and `1`. The first increment starts from
133137

134138
### Return Value
135139

136-
`useNProgress` returns these values, and `NProgress` passes the same object to `children`.
140+
`useNProgress` returns these values, and `NProgress` passes the same object to `children`. The type is `NProgressState`.
137141

138142
| Value | Type | Description |
139143
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |

src/NProgress.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { FC, ReactElement } from 'react'
22

3-
import type { Options } from './types'
3+
import type { NProgressOptions, NProgressState } from './types'
44
import { useNProgress } from './useNProgress'
55

6-
type Props = Options & {
7-
children: (renderProps: ReturnType<typeof useNProgress>) => ReactElement
6+
type Props = NProgressOptions & {
7+
children: (renderProps: NProgressState) => ReactElement
88
}
99

1010
export const NProgress: FC<Props> = ({ children, ...restProps }: Props) => {

src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './NProgress'
2+
export type { NProgressOptions, NProgressState } from './types'
23
export * from './useNProgress'

src/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
export interface Options {
1+
export interface NProgressOptions {
22
animationDuration?: number
33
incrementDuration?: number
44
isAnimating?: boolean
55
minimum?: number
66
}
7+
8+
export interface NProgressState {
9+
animationDuration: number
10+
isFinished: boolean
11+
progress: number
12+
}

src/useNProgress.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useEffect, useReducer } from 'react'
33
import { clamp } from './clamp'
44
import { createTimeout } from './createTimeout'
55
import { increment } from './increment'
6-
import type { Options } from './types'
6+
import type { NProgressOptions, NProgressState } from './types'
77

88
// A four-phase state machine. `idle` and `finished` both report
99
// `isFinished: true` and differ only in the progress they hold, so the phase,
@@ -67,11 +67,7 @@ export const useNProgress = ({
6767
incrementDuration = 200,
6868
isAnimating = false,
6969
minimum = 0.08,
70-
}: Options = {}): {
71-
animationDuration: number
72-
isFinished: boolean
73-
progress: number
74-
} => {
70+
}: NProgressOptions = {}): NProgressState => {
7571
const [{ phase, progress }, dispatch] = useReducer(reducer, initialState)
7672

7773
useEffect(() => {

test/bundles.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@ const publishedModules = fs
1616
.readdirSync(distDir)
1717
.filter((file) => /\.[cm]?js$/.test(file))
1818

19+
// Derived the same way, and for the same reason, as `publishedModules`.
20+
const declarationFiles = fs
21+
.readdirSync(distDir)
22+
.filter((file) => /\.d\.[cm]?ts$/.test(file))
23+
24+
// Consumers writing a typed wrapper around the hook or the render-props
25+
// component need names for the option and return shapes, so both are part of
26+
// the public API rather than internal declarations the bundler happened to
27+
// inline.
28+
const publicNames = [
29+
'NProgress',
30+
'NProgressOptions',
31+
'NProgressState',
32+
'useNProgress',
33+
]
34+
1935
describe('published modules', () => {
2036
it('should be present', () => {
2137
expect(publishedModules.length).toBeGreaterThan(0)
@@ -29,3 +45,18 @@ describe('published modules', () => {
2945
},
3046
)
3147
})
48+
49+
describe('declaration files', () => {
50+
it('should be present', () => {
51+
expect(declarationFiles.length).toBeGreaterThan(0)
52+
})
53+
54+
it.each(declarationFiles)('%s should export the public API', (file) => {
55+
const contents = fs.readFileSync(path.join(distDir, file), 'utf8')
56+
const exported = (contents.match(/export\s*\{[^}]*\}/g) ?? []).join(' ')
57+
58+
for (const name of publicNames) {
59+
expect(exported).toMatch(new RegExp(`\\b${name}\\b`))
60+
}
61+
})
62+
})

0 commit comments

Comments
 (0)