|
| 1 | +/** |
| 2 | + * Type-level pins for useViewModelInstance's overload resolution. |
| 3 | + * |
| 4 | + * This file is a lint/compile target, not a runtime suite: eslint runs it |
| 5 | + * with `reportUnusedDisableDirectives: 'error'` (see eslint.config.mjs), so |
| 6 | + * - calls that MUST resolve to a @deprecated overload carry a |
| 7 | + * `eslint-disable-next-line @typescript-eslint/no-deprecated` — if the |
| 8 | + * resolution ever changes, the directive becomes unused and lint fails; |
| 9 | + * - calls that MUST stay non-deprecated are written bare — if they ever |
| 10 | + * resolve to a deprecated overload, `no-deprecated` fails directly; |
| 11 | + * - `@ts-expect-error` pins invalid combinations at the compiler level. |
| 12 | + */ |
| 13 | +import { |
| 14 | + useViewModelInstance, |
| 15 | + type UseViewModelInstanceFileParams, |
| 16 | + type UseViewModelInstanceRequiredResult, |
| 17 | + type UseViewModelInstanceResult, |
| 18 | +} from '../useViewModelInstance'; |
| 19 | +import type { RiveFile } from '../../specs/RiveFile.nitro'; |
| 20 | +import type { ViewModel } from '../../specs/ViewModel.nitro'; |
| 21 | +import type { RiveViewRef } from '../../index'; |
| 22 | + |
| 23 | +export function useDeprecationResolutionPins( |
| 24 | + file: RiveFile, |
| 25 | + vm: ViewModel, |
| 26 | + ref: RiveViewRef |
| 27 | +) { |
| 28 | + // ── must be DEPRECATED (directive required; unused directive = lint error) |
| 29 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 30 | + const bare = useViewModelInstance(file); |
| 31 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 32 | + const noAsync = useViewModelInstance(file, { instanceName: 'X' }); |
| 33 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 34 | + const asyncFalse = useViewModelInstance(file, { async: false }); |
| 35 | + |
| 36 | + const widened = { async: true }; // widens to { async: boolean } |
| 37 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 38 | + const fromWidened = useViewModelInstance(file, widened); |
| 39 | + |
| 40 | + const viaExportedType: UseViewModelInstanceFileParams = { async: true }; |
| 41 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 42 | + const fromExportedType = useViewModelInstance(file, viaExportedType); |
| 43 | + |
| 44 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 45 | + const vmSource = useViewModelInstance(vm, { name: 'X' }); |
| 46 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 47 | + const refSource = useViewModelInstance(ref); |
| 48 | + |
| 49 | + // ── must stay NON-deprecated (bare calls; no-deprecated fails on regress) |
| 50 | + const asyncFile = useViewModelInstance(file, { async: true }); |
| 51 | + const asyncArtboard = useViewModelInstance(file, { |
| 52 | + async: true, |
| 53 | + artboardName: 'Main', |
| 54 | + }); |
| 55 | + const asyncVm = useViewModelInstance(vm, { async: true, useNew: true }); |
| 56 | + const asyncRef = useViewModelInstance(ref, { async: true }); |
| 57 | + // The documented fix for widened params re-pins the literal at the call: |
| 58 | + const repinned = useViewModelInstance(file, { |
| 59 | + ...viaExportedType, |
| 60 | + async: true, |
| 61 | + }); |
| 62 | + |
| 63 | + // ── required narrowing only with a literal async on a non-null source |
| 64 | + const req: UseViewModelInstanceRequiredResult = useViewModelInstance(file, { |
| 65 | + async: true, |
| 66 | + required: true, |
| 67 | + }); |
| 68 | + // Widened params lose the narrowing (resolve to the plain deprecated |
| 69 | + // overload) but must still compile: |
| 70 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 71 | + const reqWidened: UseViewModelInstanceResult = useViewModelInstance( |
| 72 | + file, |
| 73 | + viaExportedType |
| 74 | + ); |
| 75 | + |
| 76 | + // ── invalid combinations must not compile |
| 77 | + // @ts-expect-error artboardName and viewModelName are mutually exclusive |
| 78 | + const invalid = useViewModelInstance(file, { |
| 79 | + async: true, |
| 80 | + artboardName: 'A', |
| 81 | + viewModelName: 'B', |
| 82 | + }); |
| 83 | + |
| 84 | + return { |
| 85 | + bare, |
| 86 | + noAsync, |
| 87 | + asyncFalse, |
| 88 | + fromWidened, |
| 89 | + fromExportedType, |
| 90 | + vmSource, |
| 91 | + refSource, |
| 92 | + asyncFile, |
| 93 | + asyncArtboard, |
| 94 | + asyncVm, |
| 95 | + asyncRef, |
| 96 | + repinned, |
| 97 | + req, |
| 98 | + reqWidened, |
| 99 | + invalid, |
| 100 | + }; |
| 101 | +} |
| 102 | + |
| 103 | +// jest picks up every file under __tests__ — the real gates for this file |
| 104 | +// are `yarn tsc` and `yarn lint` (see header). |
| 105 | +describe('useViewModelInstance deprecation type pins', () => { |
| 106 | + it('is enforced by tsc and eslint, not at runtime', () => { |
| 107 | + expect(typeof useDeprecationResolutionPins).toBe('function'); |
| 108 | + }); |
| 109 | +}); |
0 commit comments