import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { beforeAll, describe, expect, it } from 'vitest' import ts from 'typescript' const TEMPLATES_INDEX = resolve(dirname(fileURLToPath(import.meta.url)), '../../templates/index.ts') const COMPONENTS_INDEX = resolve(dirname(fileURLToPath(import.meta.url)), '../../components/index.ts') const COMPOSABLES_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '../../composables') // Composables removed in MINT 1.4 with the templates and sample grouping. const REMOVED_COMPOSABLE_FILES = [ 'useBioTemplateComponents.ts', 'useBioTemplateControls.ts', 'useBioTemplatePackWorkspace.ts', 'useBioTemplatePresetWorkspace.ts', 'useBioTemplateWorkspace.ts', 'useTemplateCollection.ts', 'useExperimentSave.ts', 'useGroupAssignment.ts', ].map(file => resolve(COMPOSABLES_DIR, file)) // One program for every file: building one per file took longer than the test timeout on CI. let program: ts.Program let checker: ts.TypeChecker beforeAll(() => { program = ts.createProgram([TEMPLATES_INDEX, COMPONENTS_INDEX, ...REMOVED_COMPOSABLE_FILES], { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext, moduleResolution: ts.ModuleResolutionKind.Bundler, noEmit: true, }) checker = program.getTypeChecker() }, 60_000) function exportsOf(file: string): ts.Symbol[] { return checker.getExportsOfModule(checker.getSymbolAtLocation(program.getSourceFile(file)!)!) } function isDeprecated(symbol: ts.Symbol): boolean { return symbol.getJsDocTags(checker).some(tag => tag.name === 'deprecated') } // TypeScript ignores @deprecated on `export { ... } from` blocks; an import // site only shows the deprecation when the original declaration carries it. function undeprecatedValueExports(file: string): string[] { return exportsOf(file) .map(symbol => (symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol)) .filter(symbol => symbol.flags & ts.SymbolFlags.Value) .filter(symbol => !isDeprecated(symbol)) .map(symbol => symbol.getName()) } describe('templates deprecation', () => { it('should expose deprecated component tags to TypeScript consumers', () => { const names = ['BioTemplateRenderer', 'SmartGroupModal', 'DoseDesignWorkspaceView'] const tagged = exportsOf(COMPONENTS_INDEX).filter(isDeprecated).map(symbol => symbol.getName()) expect(names.filter(name => !tagged.includes(name))).toEqual([]) }) it('should not tag components that stay public in MINT 1.4', () => { const names = ['SampleSelector', 'SequenceProgressBar', 'InstrumentStateBadge'] const tagged = exportsOf(COMPONENTS_INDEX).filter(isDeprecated).map(symbol => symbol.getName()) expect(names.filter(name => tagged.includes(name))).toEqual([]) }) it('should tag every template value export when the templates move out in MINT 1.4', () => { expect(undeprecatedValueExports(TEMPLATES_INDEX)).toEqual([]) }) it('should tag every value export of the template and grouping composables when they are removed in MINT 1.4', () => { const untagged = REMOVED_COMPOSABLE_FILES.flatMap(undeprecatedValueExports) expect(untagged).toEqual([]) }) })