@escape-game-over/atlas 0.1.88 → 0.1.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/routes/define.ts +6 -1
- package/src/routes/family.ts +116 -16
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -262,8 +262,10 @@ export {
|
|
|
262
262
|
enableRoutes,
|
|
263
263
|
type FamilyMember,
|
|
264
264
|
type FamilyMemberInput,
|
|
265
|
+
type FamilyMemberSlugs,
|
|
265
266
|
type FamilyOptions,
|
|
266
267
|
familyGuard,
|
|
268
|
+
familyMemberSlugs,
|
|
267
269
|
type RouteIdsOf,
|
|
268
270
|
} from "./routes/family.ts";
|
|
269
271
|
export type { RouteEntry } from "./routes/resolve.ts";
|
package/src/routes/define.ts
CHANGED
|
@@ -204,7 +204,12 @@ type InheritedDepthMismatch<
|
|
|
204
204
|
> = K extends keyof Base
|
|
205
205
|
? Base[K] extends { readonly slugByLocale?: infer M }
|
|
206
206
|
? {
|
|
207
|
-
|
|
207
|
+
// `-?`: the translations are a `Partial`, and a mapped type keeps
|
|
208
|
+
// its `?`, so every entry would read `never | undefined` and the
|
|
209
|
+
// lookup below `undefined` — not `never` — failing every `slug`
|
|
210
|
+
// override of a route whose translations are optional, family
|
|
211
|
+
// members among them.
|
|
212
|
+
[L in keyof M]-?: L extends OverriddenLocales<T, K>
|
|
208
213
|
? never
|
|
209
214
|
: M[L] extends infer V extends string
|
|
210
215
|
? SegmentCount<V> extends Depth
|
package/src/routes/family.ts
CHANGED
|
@@ -75,6 +75,113 @@ export interface FamilyOptions<L extends string> {
|
|
|
75
75
|
readonly sitemap?: RouteSitemap;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/** Per-locale translations, keyed by locale. */
|
|
79
|
+
type ByLocale = Readonly<Record<string, string>>;
|
|
80
|
+
|
|
81
|
+
/** One locale's path: its translated halves where it has them, else the defaults. */
|
|
82
|
+
type JoinedSlug<
|
|
83
|
+
Prefix extends string,
|
|
84
|
+
PrefixBy extends ByLocale,
|
|
85
|
+
Segment extends string,
|
|
86
|
+
SegmentBy extends ByLocale,
|
|
87
|
+
K,
|
|
88
|
+
> = `${K extends keyof PrefixBy ? PrefixBy[K] : Prefix}/${K extends keyof SegmentBy
|
|
89
|
+
? SegmentBy[K]
|
|
90
|
+
: Segment}`;
|
|
91
|
+
|
|
92
|
+
/** What {@link familyMemberSlugs} returns: literal paths, so depth checks still apply. */
|
|
93
|
+
export type FamilyMemberSlugs<
|
|
94
|
+
Prefix extends string,
|
|
95
|
+
Segment extends string,
|
|
96
|
+
PrefixBy extends ByLocale = Record<never, never>,
|
|
97
|
+
SegmentBy extends ByLocale = Record<never, never>,
|
|
98
|
+
> = { readonly slug: `${Prefix}/${Segment}` } & ([
|
|
99
|
+
keyof PrefixBy | keyof SegmentBy,
|
|
100
|
+
] extends [never]
|
|
101
|
+
? unknown
|
|
102
|
+
: {
|
|
103
|
+
readonly slugByLocale: {
|
|
104
|
+
readonly [K in keyof PrefixBy | keyof SegmentBy]: JoinedSlug<
|
|
105
|
+
Prefix,
|
|
106
|
+
PrefixBy,
|
|
107
|
+
Segment,
|
|
108
|
+
SegmentBy,
|
|
109
|
+
K
|
|
110
|
+
>;
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A family member's paths, in every locale, as {@link routeFamily} composes
|
|
116
|
+
* them — for a project that gives one member a segment of its own.
|
|
117
|
+
*
|
|
118
|
+
* ```ts
|
|
119
|
+
* overrideRoutes: {
|
|
120
|
+
* "rooms-illuminati": {
|
|
121
|
+
* enabled: true,
|
|
122
|
+
* ...familyMemberSlugs("rooms", "the-da-vinci-code", {
|
|
123
|
+
* prefixByLocale: ROOM_PREFIX_BY_LOCALE,
|
|
124
|
+
* }),
|
|
125
|
+
* },
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* Overriding only `slug` would move the default locale and leave every
|
|
130
|
+
* translated path at the family's id, at the same depth, so nothing would
|
|
131
|
+
* catch it. This restates them all from the same join the family used. Pass
|
|
132
|
+
* the family's `prefixByLocale`, so the member stays under its siblings in
|
|
133
|
+
* every locale.
|
|
134
|
+
*/
|
|
135
|
+
export function familyMemberSlugs<
|
|
136
|
+
const Prefix extends string,
|
|
137
|
+
const Segment extends string,
|
|
138
|
+
const PrefixBy extends ByLocale = Record<never, never>,
|
|
139
|
+
const SegmentBy extends ByLocale = Record<never, never>,
|
|
140
|
+
>(
|
|
141
|
+
prefix: Prefix,
|
|
142
|
+
segment: Segment,
|
|
143
|
+
translations: {
|
|
144
|
+
readonly prefixByLocale?: PrefixBy;
|
|
145
|
+
readonly segmentByLocale?: SegmentBy;
|
|
146
|
+
} = {}
|
|
147
|
+
): FamilyMemberSlugs<Prefix, Segment, PrefixBy, SegmentBy> {
|
|
148
|
+
const slugByLocale = joinByLocale(
|
|
149
|
+
prefix,
|
|
150
|
+
segment,
|
|
151
|
+
translations.prefixByLocale,
|
|
152
|
+
translations.segmentByLocale
|
|
153
|
+
);
|
|
154
|
+
return {
|
|
155
|
+
slug: `${prefix}/${segment}`,
|
|
156
|
+
...(slugByLocale === undefined ? {} : { slugByLocale }),
|
|
157
|
+
} as never;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The translated paths of one member, or `undefined` where neither half is
|
|
162
|
+
* translated. A locale gets one when either half is; the untranslated half
|
|
163
|
+
* falls back to the default, so every locale sits at the same depth by
|
|
164
|
+
* construction.
|
|
165
|
+
*/
|
|
166
|
+
function joinByLocale(
|
|
167
|
+
prefix: string,
|
|
168
|
+
segment: string,
|
|
169
|
+
prefixByLocale: Readonly<Partial<Record<string, string>>> | undefined,
|
|
170
|
+
segmentByLocale: Readonly<Partial<Record<string, string>>> | undefined
|
|
171
|
+
): Record<string, string> | undefined {
|
|
172
|
+
const locales = new Set([
|
|
173
|
+
...Object.keys(prefixByLocale ?? {}),
|
|
174
|
+
...Object.keys(segmentByLocale ?? {}),
|
|
175
|
+
]);
|
|
176
|
+
if (locales.size === 0) return undefined;
|
|
177
|
+
const slugByLocale: Record<string, string> = {};
|
|
178
|
+
for (const locale of locales) {
|
|
179
|
+
slugByLocale[locale] =
|
|
180
|
+
`${prefixByLocale?.[locale] ?? prefix}/${segmentByLocale?.[locale] ?? segment}`;
|
|
181
|
+
}
|
|
182
|
+
return slugByLocale;
|
|
183
|
+
}
|
|
184
|
+
|
|
78
185
|
/**
|
|
79
186
|
* Expands a family into ordinary route entries, ready to spread into
|
|
80
187
|
* `defineRoutes`.
|
|
@@ -127,26 +234,19 @@ export function routeFamily<
|
|
|
127
234
|
const segmentByLocale =
|
|
128
235
|
typeof member === "string" ? undefined : member.segmentByLocale;
|
|
129
236
|
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
for (const locale of locales) {
|
|
139
|
-
const localePrefix =
|
|
140
|
-
options?.prefixByLocale?.[locale as LocalesOf<C>] ?? prefix;
|
|
141
|
-
const localeSegment =
|
|
142
|
-
segmentByLocale?.[locale as LocalesOf<C>] ?? id;
|
|
143
|
-
slugByLocale[locale] = `${localePrefix}/${localeSegment}`;
|
|
144
|
-
}
|
|
237
|
+
// The same join `familyMemberSlugs` gives a project, so a member it
|
|
238
|
+
// moves sits exactly where the family would have put it.
|
|
239
|
+
const slugByLocale = joinByLocale(
|
|
240
|
+
prefix,
|
|
241
|
+
id,
|
|
242
|
+
options?.prefixByLocale,
|
|
243
|
+
segmentByLocale
|
|
244
|
+
);
|
|
145
245
|
|
|
146
246
|
routes[`${prefix.replaceAll("/", "-")}-${id}`] = {
|
|
147
247
|
slug: `${prefix}/${id}`,
|
|
148
248
|
enabled: options?.enabled ?? true,
|
|
149
|
-
...(
|
|
249
|
+
...(slugByLocale !== undefined
|
|
150
250
|
? {
|
|
151
251
|
slugByLocale: slugByLocale as Readonly<
|
|
152
252
|
Partial<Record<LocalesOf<C>, string>>
|