|
| 1 | +/** |
| 2 | + * Module which handles sorting reflections according to a user specified strategy. |
| 3 | + * @module |
| 4 | + */ |
| 5 | + |
| 6 | +import { DeclarationReflection, ReflectionKind } from "../models"; |
| 7 | + |
| 8 | +export const SORT_STRATEGIES = [ |
| 9 | + "source-order", |
| 10 | + "alphabetical", |
| 11 | + "enum-value-ascending", |
| 12 | + "enum-value-descending", |
| 13 | + "static-first", |
| 14 | + "instance-first", |
| 15 | + "visibility", |
| 16 | + "required-first", |
| 17 | + "kind", |
| 18 | +] as const; |
| 19 | + |
| 20 | +export type SortStrategy = typeof SORT_STRATEGIES[number]; |
| 21 | + |
| 22 | +// Return true if a < b |
| 23 | +const sorts: Record< |
| 24 | + SortStrategy, |
| 25 | + (a: DeclarationReflection, b: DeclarationReflection) => boolean |
| 26 | +> = { |
| 27 | + "source-order"(a, b) { |
| 28 | + const aSymbol = a.project.getSymbolFromReflection(a); |
| 29 | + const bSymbol = b.project.getSymbolFromReflection(b); |
| 30 | + |
| 31 | + // This is going to be somewhat ambiguous. No way around that. Treat the first |
| 32 | + // declaration of a symbol as its ordering declaration. |
| 33 | + const aDecl = aSymbol?.getDeclarations()?.[0]; |
| 34 | + const bDecl = bSymbol?.getDeclarations()?.[0]; |
| 35 | + |
| 36 | + if (aDecl && bDecl) { |
| 37 | + const aFile = aDecl.getSourceFile().fileName; |
| 38 | + const bFile = bDecl.getSourceFile().fileName; |
| 39 | + if (aFile < bFile) { |
| 40 | + return true; |
| 41 | + } |
| 42 | + if (aFile == bFile && aDecl.pos < bDecl.pos) { |
| 43 | + return true; |
| 44 | + } |
| 45 | + |
| 46 | + return false; |
| 47 | + } |
| 48 | + |
| 49 | + // Someone is doing something weird. Fail to re-order. This *might* be a bug in TD |
| 50 | + // but it could also be TS having some exported symbol without a declaration. |
| 51 | + return false; |
| 52 | + }, |
| 53 | + alphabetical(a, b) { |
| 54 | + return a.name < b.name; |
| 55 | + }, |
| 56 | + "enum-value-ascending"(a, b) { |
| 57 | + if ( |
| 58 | + a.kind == ReflectionKind.EnumMember && |
| 59 | + b.kind == ReflectionKind.EnumMember |
| 60 | + ) { |
| 61 | + return ( |
| 62 | + parseFloat(a.defaultValue ?? "0") < |
| 63 | + parseFloat(b.defaultValue ?? "0") |
| 64 | + ); |
| 65 | + } |
| 66 | + return false; |
| 67 | + }, |
| 68 | + "enum-value-descending"(a, b) { |
| 69 | + if ( |
| 70 | + a.kind == ReflectionKind.EnumMember && |
| 71 | + b.kind == ReflectionKind.EnumMember |
| 72 | + ) { |
| 73 | + return ( |
| 74 | + parseFloat(b.defaultValue ?? "0") < |
| 75 | + parseFloat(a.defaultValue ?? "0") |
| 76 | + ); |
| 77 | + } |
| 78 | + return false; |
| 79 | + }, |
| 80 | + "static-first"(a, b) { |
| 81 | + return a.flags.isStatic && !b.flags.isStatic; |
| 82 | + }, |
| 83 | + "instance-first"(a, b) { |
| 84 | + return !a.flags.isStatic && b.flags.isStatic; |
| 85 | + }, |
| 86 | + visibility(a, b) { |
| 87 | + // Note: flags.isPublic may not be set on public members. It will only be set |
| 88 | + // if the user explicitly marks members as public. Therefore, we can't use it |
| 89 | + // here to get a reliable sort order. |
| 90 | + if (a.flags.isPrivate) { |
| 91 | + return false; // Not sorted before anything |
| 92 | + } |
| 93 | + if (a.flags.isProtected) { |
| 94 | + return b.flags.isPrivate; // Sorted before privates |
| 95 | + } |
| 96 | + if (b.flags.isPrivate || b.flags.isProtected) { |
| 97 | + return true; // We are public, sort before b if b is less visible |
| 98 | + } |
| 99 | + return false; |
| 100 | + }, |
| 101 | + "required-first"(a, b) { |
| 102 | + return !a.flags.isOptional && b.flags.isOptional; |
| 103 | + }, |
| 104 | + kind(a, b) { |
| 105 | + const weights = [ |
| 106 | + ReflectionKind.Reference, |
| 107 | + ReflectionKind.Project, |
| 108 | + ReflectionKind.Module, |
| 109 | + ReflectionKind.Namespace, |
| 110 | + ReflectionKind.Enum, |
| 111 | + ReflectionKind.EnumMember, |
| 112 | + ReflectionKind.Class, |
| 113 | + ReflectionKind.Interface, |
| 114 | + ReflectionKind.TypeAlias, |
| 115 | + |
| 116 | + ReflectionKind.Constructor, |
| 117 | + ReflectionKind.Event, |
| 118 | + ReflectionKind.Property, |
| 119 | + ReflectionKind.Variable, |
| 120 | + ReflectionKind.Function, |
| 121 | + ReflectionKind.Accessor, |
| 122 | + ReflectionKind.Method, |
| 123 | + ReflectionKind.ObjectLiteral, |
| 124 | + |
| 125 | + ReflectionKind.Parameter, |
| 126 | + ReflectionKind.TypeParameter, |
| 127 | + ReflectionKind.TypeLiteral, |
| 128 | + ReflectionKind.CallSignature, |
| 129 | + ReflectionKind.ConstructorSignature, |
| 130 | + ReflectionKind.IndexSignature, |
| 131 | + ReflectionKind.GetSignature, |
| 132 | + ReflectionKind.SetSignature, |
| 133 | + ] as const; |
| 134 | + |
| 135 | + return weights.indexOf(a.kind) < weights.indexOf(b.kind); |
| 136 | + }, |
| 137 | +}; |
| 138 | + |
| 139 | +export function sortReflections( |
| 140 | + strategies: DeclarationReflection[], |
| 141 | + strats: readonly SortStrategy[] |
| 142 | +) { |
| 143 | + strategies.sort((a, b) => { |
| 144 | + for (const s of strats) { |
| 145 | + if (sorts[s](a, b)) { |
| 146 | + return -1; |
| 147 | + } |
| 148 | + if (sorts[s](b, a)) { |
| 149 | + return 1; |
| 150 | + } |
| 151 | + } |
| 152 | + return 0; |
| 153 | + }); |
| 154 | +} |
0 commit comments