@escape-game-over/atlas 0.1.108 → 0.1.109
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/README.md +3 -0
- package/docs/checks.md +1 -0
- package/package.json +3 -3
- package/src/project.ts +57 -1
- package/src/site/api.ts +10 -0
- package/src/site/create.ts +14 -0
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ export const { project, redirects } = atlas.project(
|
|
|
72
72
|
|
|
73
73
|
enabledLocales: ["el-GR", "en-US"],
|
|
74
74
|
overrideRouting: { defaultLocale: "el-GR", prefixDefaultLocale: true },
|
|
75
|
+
clock: { "el-GR": "h23", "en-US": "h12" }, // how each language prints times
|
|
75
76
|
|
|
76
77
|
overrideMessages: {
|
|
77
78
|
// Greek only — the English copy still falls through to the default catalog.
|
|
@@ -243,6 +244,8 @@ regression-tested by an `@ts-expect-error` suite.
|
|
|
243
244
|
| A `robots` directive that is not a real one | error — the vocabulary is a union |
|
|
244
245
|
| A project `url` that is not `https://`, or ends in a slash | error |
|
|
245
246
|
| `defaultLocale` not among the project's `enabledLocales` | error |
|
|
247
|
+
| A `clock` leaving out a published locale, or naming an unpublished one | error |
|
|
248
|
+
| `site.clockFor` on a project that declares no clock | throws, naming the project |
|
|
246
249
|
| An unknown field in a project declaration | error — a typo cannot sit there unread |
|
|
247
250
|
| Two routes resolving to the same URL | build throws, naming both |
|
|
248
251
|
| A site icon that is not square, a multiple of 48px, and PNG | build throws |
|
package/docs/checks.md
CHANGED
|
@@ -42,6 +42,7 @@ sees.
|
|
|
42
42
|
| unknown message key, or an override dropping a `{placeholder}` | two routes resolving to the same URL |
|
|
43
43
|
| a message the project declared mandatory and never wrote (`CatalogCovers`) | |
|
|
44
44
|
| copy or a locale label written for a locale outside `enabledLocales` | |
|
|
45
|
+
| a `clock` leaving out a published locale, or naming one outside `enabledLocales` | the same, for a project that was not type-checked first |
|
|
45
46
|
| a slug whose translation sits at a different depth, in the registry or across the overlay | the same, when a slug is not a literal (computed, or from an env var) |
|
|
46
47
|
| unknown route id, or a locale the site does not ship | a locale published twice |
|
|
47
48
|
| a malformed language tag (`en_US`, `en-us`, `EN-US`, bare `en`) | an icon that is not square, a multiple of 48, and PNG |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escape-game-over/atlas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.109",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Typed, data-driven machinery for static multi-locale, multi-deployment Astro sites.",
|
|
6
6
|
"private": false,
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
"vite": ">= 8"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@biomejs/biome": "2.6.
|
|
66
|
+
"@biomejs/biome": "2.6.7",
|
|
67
67
|
"@types/node": "26.6.6",
|
|
68
68
|
"@vitest/coverage-istanbul": "5.1.0",
|
|
69
|
-
"astro": "7.4.
|
|
69
|
+
"astro": "7.4.11",
|
|
70
70
|
"typescript": "6.0.3",
|
|
71
71
|
"vitest": "5.1.0"
|
|
72
72
|
}
|
package/src/project.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
SiteConfigShape,
|
|
8
8
|
} from "./config.ts";
|
|
9
9
|
import type { Fonts } from "./fonts.ts";
|
|
10
|
+
import type { HourCycle } from "./hours.ts";
|
|
10
11
|
import type {
|
|
11
12
|
BaseCatalog,
|
|
12
13
|
OverrideCatalog,
|
|
@@ -105,6 +106,21 @@ export interface ProjectInput<L extends string, Routes> {
|
|
|
105
106
|
* declare is a compile error.
|
|
106
107
|
*/
|
|
107
108
|
readonly enabledLocales: readonly L[];
|
|
109
|
+
/**
|
|
110
|
+
* The clock each published language prints times on: `"h23"` for 18:00,
|
|
111
|
+
* `"h12"` for 6:00 PM. Read back with `site.clockFor(locale)`.
|
|
112
|
+
*
|
|
113
|
+
* One entry for every locale in `enabledLocales`, and none for any other,
|
|
114
|
+
* so what each page prints is written down beside the languages that print
|
|
115
|
+
* it: a bilingual Canadian site reads `{ "en-CA": "h12", "fr-CA": "h23" }`.
|
|
116
|
+
* Not taken from the locale's own default (`Intl`'s) on purpose: a site may
|
|
117
|
+
* print a 24-hour clock where its language's default is 12-hour, and that
|
|
118
|
+
* choice is the site's to state.
|
|
119
|
+
*
|
|
120
|
+
* Optional: a project that prints no times declares none, and
|
|
121
|
+
* `clockFor` refuses to answer for it.
|
|
122
|
+
*/
|
|
123
|
+
readonly clock?: Readonly<Partial<Record<L, HourCycle>>>;
|
|
108
124
|
/**
|
|
109
125
|
* Replaces parts of the site's `defaultRouting` for this project.
|
|
110
126
|
*
|
|
@@ -286,6 +302,17 @@ export type ProjectChecks<
|
|
|
286
302
|
? NoExcessKeys<T["overrideLocaleMeta"][K], keyof LocaleMetaOverride>
|
|
287
303
|
: LocaleNotEnabled<K>;
|
|
288
304
|
};
|
|
305
|
+
// The locales written must be ones this project publishes, as for
|
|
306
|
+
// `overrideLocaleMeta` above. That every one it publishes is there is
|
|
307
|
+
// checked in `ProjectOverrideChecks`: a conditional on `T` here would make
|
|
308
|
+
// the constraint circular.
|
|
309
|
+
readonly clock?: {
|
|
310
|
+
readonly [K in StringKeys<
|
|
311
|
+
T["clock"]
|
|
312
|
+
>]: K extends T["enabledLocales"][number]
|
|
313
|
+
? HourCycle
|
|
314
|
+
: LocaleNotEnabled<K>;
|
|
315
|
+
};
|
|
289
316
|
};
|
|
290
317
|
|
|
291
318
|
/** The same checks that map the whole override objects, in parameter position. */
|
|
@@ -312,7 +339,16 @@ export type ProjectOverrideChecks<
|
|
|
312
339
|
T["enabledLocales"][number]
|
|
313
340
|
>;
|
|
314
341
|
}
|
|
315
|
-
: unknown)
|
|
342
|
+
: unknown) &
|
|
343
|
+
(T extends { readonly clock: object }
|
|
344
|
+
? {
|
|
345
|
+
// A clock, once declared, is declared for every page: a locale
|
|
346
|
+
// left out would have no clock to print its times on.
|
|
347
|
+
readonly clock: {
|
|
348
|
+
readonly [K in T["enabledLocales"][number]]: HourCycle;
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
: unknown);
|
|
316
352
|
|
|
317
353
|
/** A project's own messages, as a catalog: none when it adds none. */
|
|
318
354
|
export type AddedCatalog<P> = P extends { readonly addMessages: infer A }
|
|
@@ -369,5 +405,25 @@ export function defineProject<
|
|
|
369
405
|
);
|
|
370
406
|
}
|
|
371
407
|
}
|
|
408
|
+
// The clock's rule, for the same reason.
|
|
409
|
+
if (project.clock !== undefined) {
|
|
410
|
+
const published: readonly string[] = project.enabledLocales;
|
|
411
|
+
const missing = published.filter(
|
|
412
|
+
(locale) => !(locale in (project.clock ?? {}))
|
|
413
|
+
);
|
|
414
|
+
if (missing.length > 0) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`Project "${project.siteName}": clock has no ${missing.join(", ")}. A project that declares a clock gives one for every locale it publishes.`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
const extra = Object.keys(project.clock).filter(
|
|
420
|
+
(locale) => !published.includes(locale)
|
|
421
|
+
);
|
|
422
|
+
if (extra.length > 0) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
`Project "${project.siteName}": clock names ${extra.join(", ")}, which this project does not publish.`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
372
428
|
return project;
|
|
373
429
|
}
|
package/src/site/api.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import type { GeneratedFile } from "../file.ts";
|
|
8
8
|
import type { PublicFilePath } from "../files.ts";
|
|
9
9
|
import type { Fonts } from "../fonts.ts";
|
|
10
|
+
import type { HourCycle } from "../hours.ts";
|
|
10
11
|
import type { TranslateFor } from "../i18n/translate.ts";
|
|
11
12
|
import type { LlmsSection } from "../llms.ts";
|
|
12
13
|
import type { ThemeColor } from "../meta/index.ts";
|
|
@@ -219,6 +220,15 @@ export interface Site<
|
|
|
219
220
|
bind?: { readonly values?: Readonly<Record<Values, string>> }
|
|
220
221
|
): PlainTextFor<Catalog, Values>;
|
|
221
222
|
|
|
223
|
+
/**
|
|
224
|
+
* The clock a locale's pages print times on, as the project's `clock`
|
|
225
|
+
* states it: hand it to `formatHours`, or to anything formatting a time.
|
|
226
|
+
*
|
|
227
|
+
* Throws for a project that declares no clock: one that prints times has
|
|
228
|
+
* to say how.
|
|
229
|
+
*/
|
|
230
|
+
clockFor(locale: L): HourCycle;
|
|
231
|
+
|
|
222
232
|
/** Absolute path of a route in one locale, e.g. `/el/sxetika-me-emas`. */
|
|
223
233
|
pathFor(id: RouteId, locale: L, options?: LinkOptions): UrlPath;
|
|
224
234
|
/** The same, prefixed with the site origin — for canonicals and sitemaps. */
|
package/src/site/create.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import type { GeneratedFile } from "../file.ts";
|
|
15
15
|
import type { PublicFilePath } from "../files.ts";
|
|
16
16
|
import { mergeFonts } from "../fonts.ts";
|
|
17
|
+
import type { HourCycle } from "../hours.ts";
|
|
17
18
|
import { type BaseCatalog, mergeCatalog } from "../i18n/define.ts";
|
|
18
19
|
import { createTranslateFactory } from "../i18n/translate.ts";
|
|
19
20
|
import type { ImageAsset } from "../image.ts";
|
|
@@ -192,6 +193,18 @@ export function createSite<
|
|
|
192
193
|
|
|
193
194
|
const resolved = mergeRoutes<L, Routes>(routes, project.overrideRoutes);
|
|
194
195
|
|
|
196
|
+
function clockFor(locale: L): HourCycle {
|
|
197
|
+
const cycle = project.clock?.[locale];
|
|
198
|
+
if (cycle === undefined) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
project.clock === undefined
|
|
201
|
+
? `Project "${project.siteName}" declares no clock, so it cannot print a time. Add \`clock\`, with an hour cycle for each locale it publishes.`
|
|
202
|
+
: `Project "${project.siteName}": clock has no ${locale}.`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
return cycle;
|
|
206
|
+
}
|
|
207
|
+
|
|
195
208
|
function pathFor(id: RouteId, locale: L, options?: LinkOptions): UrlPath {
|
|
196
209
|
const route = resolved[id];
|
|
197
210
|
if (route === undefined) {
|
|
@@ -867,6 +880,7 @@ export function createSite<
|
|
|
867
880
|
...bind,
|
|
868
881
|
}),
|
|
869
882
|
|
|
883
|
+
clockFor,
|
|
870
884
|
pathFor,
|
|
871
885
|
urlFor,
|
|
872
886
|
fileUrl,
|