@escape-game-over/atlas 0.1.24 → 0.1.26

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.
Files changed (50) hide show
  1. package/README.md +27 -44
  2. package/bin/use-project.mjs +18 -13
  3. package/docs/NOT-BUILT.md +1 -1
  4. package/docs/client-scripts.md +96 -193
  5. package/docs/rich-text.md +18 -20
  6. package/package.json +9 -15
  7. package/src/analytics/google.ts +6 -6
  8. package/src/analytics/index.ts +4 -3
  9. package/src/analytics/tags.ts +13 -58
  10. package/src/analytics/umami.ts +8 -8
  11. package/src/astro/AtlasElement.astro +26 -0
  12. package/src/astro/ConsentBanner.astro +25 -0
  13. package/src/astro/ConsentElement.astro +67 -0
  14. package/src/astro/Document.astro +44 -0
  15. package/src/astro/Image.astro +101 -0
  16. package/src/astro/MetaTags.astro +30 -40
  17. package/src/astro/RichText.astro +88 -0
  18. package/src/astro/Zoom.astro +61 -0
  19. package/src/astro/client.ts +18 -9
  20. package/src/astro/consent.ts +20 -0
  21. package/src/astro/dev-log.ts +8 -14
  22. package/src/astro/element.ts +76 -112
  23. package/src/astro/filters-view.ts +48 -64
  24. package/src/astro/filters.ts +42 -35
  25. package/src/astro/index.ts +2 -9
  26. package/src/astro/ref.ts +46 -0
  27. package/src/astro/site-routes.ts +9 -15
  28. package/src/config.ts +23 -36
  29. package/src/content/index.ts +1 -1
  30. package/src/content/marks.ts +13 -13
  31. package/src/content/rich.ts +26 -42
  32. package/src/hours.ts +48 -11
  33. package/src/i18n/define.ts +14 -74
  34. package/src/index.ts +40 -57
  35. package/src/meta/index.ts +7 -13
  36. package/src/meta/share-image.ts +2 -26
  37. package/src/meta/tag.ts +1 -45
  38. package/src/money.ts +161 -6
  39. package/src/project.ts +84 -73
  40. package/src/routes/define.ts +8 -44
  41. package/src/routes/resolve.ts +1 -1
  42. package/src/site/api.ts +7 -33
  43. package/src/site/create.ts +6 -10
  44. package/src/site/define.ts +120 -0
  45. package/src/site/index.ts +2 -5
  46. package/src/site/page.ts +4 -2
  47. package/src/sitemap.ts +2 -35
  48. package/src/warn.ts +16 -17
  49. package/src/astro/dom.ts +0 -35
  50. package/src/astro/markup.ts +0 -656
package/README.md CHANGED
@@ -61,7 +61,7 @@ Linking to a page the active deployment switched off is a compile error, not a
61
61
  One file per deployment, saying only what differs:
62
62
 
63
63
  ```ts
64
- export default defineProject(config, defaultMessages, defaultRoutes, {
64
+ export default atlas.project(defaultMessages, defaultRoutes, {
65
65
  url: "https://acme.example",
66
66
  siteName: "Acme Rome",
67
67
  icon,
@@ -92,22 +92,22 @@ never writes a type argument:
92
92
 
93
93
  ```ts
94
94
  // 1. which languages exist, how URLs are shaped
95
- export default defineSiteConfig({ locales: {...}, defaultRouting: {...} })
95
+ export const atlas = defineSite({ locales: {...}, defaultRouting: {...} })
96
96
 
97
97
  // 2. the base copy and routes
98
- export const baseMessages = defineMessages(config, {...})
99
- export const baseRoutes = defineRoutes(config, {...})
98
+ export const baseMessages = atlas.messages({...})
99
+ export const baseRoutes = atlas.routes({...})
100
100
 
101
101
  // 3. one deployment
102
- export default defineProject(config, baseMessages, baseRoutes, {...})
102
+ export default atlas.project(baseMessages, baseRoutes, {...})
103
103
 
104
104
  // 4. the API the pages use
105
- export const site = createSite(config, baseMessages, baseRoutes, project)
105
+ export const site = atlas.site(baseMessages, baseRoutes, project)
106
106
  ```
107
107
 
108
108
  Step 4 returns `t()`, `rich()`, `plain()`, `pathFor()`, `urlFor()`, `fileUrl()`,
109
- `alternatesFor()`, `localeLinksFor()`, `metaFor()`, `breadcrumbFor()`,
110
- `staticPaths()`, `routes`, `entries`, `sitemap()`, `robots()`, `llms()` and
109
+ `localeLinksFor()`, `metaFor()`, `breadcrumbFor()`,
110
+ `getStaticPaths()`, `routes`, `sitemap()`, `robots()`, `llms()` and
111
111
  `redirects()` already wired.
112
112
 
113
113
  `t()` is bound to a locale and knows each message's `{placeholders}` from its
@@ -233,7 +233,7 @@ worded.
233
233
  The same question answers it every time: *would this differ between two pages of
234
234
  the same site?*
235
235
 
236
- | Per page — passed to `metaFor()` | Site-level — declared in `defineProject()` |
236
+ | Per page — passed to `metaFor()` | Site-level — declared in `atlas.project()` |
237
237
  | -------------------------------- | ------------------------------------------ |
238
238
  | `title`, `description` | `siteName`, `twitterSite` |
239
239
  | `image` (asset **and** its alt) | `icon`, `themeColor`, `colorScheme` |
@@ -251,16 +251,9 @@ actually renders, and why `twitter:card` is pinned.
251
251
 
252
252
  A project switches routes off. Anything defined *alongside* a route then has two
253
253
  ways to rot: the page is built and its config is missing, or the page is gone and
254
- its config lingers. Two types, differing only in how many routes they speak for:
254
+ its config lingers. `PerRoute<typeof site, T>` rejects both.
255
255
 
256
- | | `PerRoute<typeof site, T>` | `WhenEnabled<typeof site, "careers", T>` |
257
- | --------------------- | ------------------------------------ | ------------------------------------------------ |
258
- | Shape | a table keyed by route id | a single value |
259
- | Says | "every built route has one of these" | "this value belongs to *that* page" |
260
- | When the route is off | that key is rejected | the type is `never` — the field cannot be filled |
261
- | Use with | `satisfies` | a normal annotation |
262
-
263
- `PerRoute` must be used with `satisfies`, not as an annotation: excess-property
256
+ It must be used with `satisfies`, not as an annotation: excess-property
264
257
  checking is what catches the stale half, and only an object literal checked
265
258
  against a known target gets it.
266
259
 
@@ -269,8 +262,6 @@ const heroes = {
269
262
  home: { variant: "wide" },
270
263
  about: { variant: "tall" },
271
264
  } satisfies PerRoute<typeof site, Hero>; // ✗ if `about` is off, ✗ if `contact` is on
272
-
273
- const careers: WhenEnabled<typeof site, "careers", Careers> = { ats: "…" }; // ✗ if off
274
265
  ```
275
266
 
276
267
  ## The rules this package keeps
@@ -299,16 +290,8 @@ const careers: WhenEnabled<typeof site, "careers", Careers> = { ats: "…" }; /
299
290
  ".": "./src/index.ts",
300
291
  "./astro": "./src/astro/index.ts",
301
292
  "./astro/images": "./src/astro/images.ts",
302
- "./astro/background-video": "./src/astro/background-video.ts",
303
- "./astro/carousel": "./src/astro/carousel.ts",
304
- "./astro/consent": "./src/astro/consent.ts",
305
- "./astro/dev-log": "./src/astro/dev-log.ts",
306
- "./astro/dom": "./src/astro/dom.ts",
307
- "./astro/element": "./src/astro/element.ts",
308
- "./astro/filters": "./src/astro/filters.ts",
309
- "./astro/filters-view": "./src/astro/filters-view.ts",
310
- "./astro/meta-tags": "./src/astro/MetaTags.astro",
311
- "./astro/youtube": "./src/astro/youtube.ts"
293
+ "./astro/document": "./src/astro/Document.astro",
294
+ "./client": "./src/astro/client.ts"
312
295
  }
313
296
  ```
314
297
 
@@ -324,27 +307,27 @@ may use `node:` built-ins but not `astro:assets`. `./astro/images` is the
324
307
  opposite: it runs inside the build, from a page, and is unusable from a config.
325
308
  Merging them breaks whichever caller loads first.
326
309
 
327
- The rest of `astro/` is the browser half — `carousel`, `filters`, `filters-view`,
328
- `youtube`, `background-video`, `consent`, `element`, `dom`, `dev-log` — which
329
- runs in a reader's browser rather
330
- than in the build, and is separate again for the same reason: none of it can be
331
- reached from a config, and none of it draws. See
310
+ `./client` is the browser half — `carousel`, `filters`, `filters-view`,
311
+ `youtube`, `background-video`, `consent`, `element` — one path
312
+ for all of it. See
332
313
  [`docs/client-scripts.md`](docs/client-scripts.md).
333
314
 
334
315
  **The package ships TypeScript source, and there is no build step.**
335
- `MetaTags.astro` could not go through `tsc` anyway, and Astro's own
316
+ `Document.astro` could not go through `tsc` anyway, and Astro's own
336
317
  `tsconfigs/base.json` already sets `allowImportingTsExtensions`, so every
337
318
  consumer gets it for free.
338
319
 
339
320
  ## The `atlas` command
340
321
 
341
322
  ```bash
342
- atlas use <project> # copies config/projects/<name> -> config/project
323
+ atlas use <project> # links config/project -> config/projects/<name>
343
324
  atlas use --fallback rome # only when nothing else named one
344
325
  ```
345
326
 
346
- The whole directory is copied, so extra files and nested folders come along
347
- without touching the tool — the contract is just that it contains `project.ts`.
327
+ A link, not a copy: an edit made through `config/project` lands in the real
328
+ file. A directory junction on Windows, which needs no admin rights. Ignore it with
329
+ `**/config/project` — no trailing slash, since git sees a link as a file. The
330
+ contract is just that the project contains `project.ts`.
348
331
  It resolves paths from the working directory, so a workspace's own
349
332
  `package.json` passes nothing. A deployment pipeline can skip it and write its
350
333
  own `config/project`.
@@ -358,7 +341,7 @@ command whose output gets deployed has to say what it is building.
358
341
  ```txt
359
342
  src/ the package — see the export map above for what is reachable
360
343
  index.ts the public API
361
- site/ createSite() and the Site it returns
344
+ site/ defineSite(), createSite() and the Site it returns
362
345
  meta/ head tags: canonical, hreflang, Open Graph, Twitter, robots
363
346
  jsonld/ the @graph — one file per node, each linked to Google's docs
364
347
  i18n/ defineMessages(), t(), placeholder extraction
@@ -400,9 +383,9 @@ proves the package is genuinely independent of anything consuming it.
400
383
 
401
384
  The `astro check` pass covers `src/astro/`, the one directory that cannot meet
402
385
  that bar. It needs `astro/client` for `astro:assets` and `ImageMetadata`, and
403
- `MetaTags.astro` needs a checker that can parse `.astro` at all — `tsc` reads
404
- zero `.astro` files no matter how they are globbed. Without this pass the one
405
- component the package ships is checked by nothing: `astro check` in
386
+ the `.astro` components need a checker that can parse `.astro` at all — `tsc` reads
387
+ zero `.astro` files no matter how they are globbed. Without this pass the
388
+ components the package ships are checked by nothing: `astro check` in
406
389
  `test:examples` only ever walks an example's own `src/`, so it never reaches it,
407
390
  and a type error there passes the entire gate.
408
391
 
@@ -412,7 +395,7 @@ practical reason: `astro check` writes a `.astro/` types directory and a
412
395
  publish both. The `tsconfig.json` there adds nothing — it extends
413
396
  `src/astro/tsconfig.json` and only widens the glob, so the rules stay in one
414
397
  place. `src/astro/tsconfig.json` is what an editor finds; without it, opening
415
- `MetaTags.astro` resolves against the root config, which supplies no ambient
398
+ `Document.astro` resolves against the root config, which supplies no ambient
416
399
  types, and every prop degrades to `any`.
417
400
 
418
401
  `test:examples` **builds every deployment**, not just type-checks one, and both
@@ -1,16 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Copies a project overlay into `config/project`, the folder the whole build
3
+ * Links `config/project` to one project overlay, the folder the whole build
4
4
  * reads from. Run automatically before `dev`, `build` and `check`.
5
5
  *
6
+ * A link rather than a copy, so an edit made through `config/project` — where
7
+ * an editor's go-to-definition lands — is an edit to the real file. A
8
+ * junction on Windows, which needs no admin rights; a symlink elsewhere.
9
+ *
6
10
  * atlas use acme
7
11
  * PROJECT=acme npm run build
8
12
  *
9
- * The entire project directory is copied, so extra files and nested folders come
10
- * along automatically. A deployment pipeline can skip this command and write its
11
- * own `config/project` — the contract is just that it contains `project.ts`.
13
+ * A deployment pipeline can skip this command and write its own
14
+ * `config/project` — the contract is just that it contains `project.ts`.
12
15
  */
13
- import { cp, readdir, rm, stat, writeFile } from "node:fs/promises";
16
+ import { lstat, readdir, rm, stat, symlink, unlink } from "node:fs/promises";
14
17
  import { join } from "node:path";
15
18
 
16
19
  /**
@@ -26,10 +29,7 @@ import { join } from "node:path";
26
29
  const ROOT = process.cwd();
27
30
  const PROJECTS_DIR = join(ROOT, "config", "projects");
28
31
  const TARGET_DIR = join(ROOT, "config", "project");
29
- /**
30
- * Only an entry-point check — the whole directory is copied recursively, so a
31
- * project can add files and folders freely without touching this script.
32
- */
32
+ /** Only an entry-point check: a project can add files and folders freely. */
33
33
  const ENTRY_FILE = "project.ts";
34
34
 
35
35
  /**
@@ -122,10 +122,15 @@ if (!(await exists(join(sourceDir, ENTRY_FILE)))) {
122
122
  process.exit(1);
123
123
  }
124
124
 
125
- await rm(TARGET_DIR, { recursive: true, force: true });
126
- await cp(sourceDir, TARGET_DIR, { recursive: true });
127
- await writeFile(join(TARGET_DIR, ".project"), `${name}\n`, "utf8");
125
+ // Only the link goes, never what it points at. A real directory is a copy left
126
+ // by an older version of this command.
127
+ const existing = await lstat(TARGET_DIR).catch(() => undefined);
128
+ if (existing?.isSymbolicLink()) await unlink(TARGET_DIR);
129
+ else if (existing !== undefined) await rm(TARGET_DIR, { recursive: true });
130
+
131
+ // "junction" is Windows' no-admin directory link, and ignored everywhere else.
132
+ await symlink(sourceDir, TARGET_DIR, "junction");
128
133
 
129
134
  console.log(
130
- `Using project "${name}" (config/projects/${name} -> config/project)`
135
+ `Using project "${name}" (config/project -> config/projects/${name})`
131
136
  );
package/docs/NOT-BUILT.md CHANGED
@@ -230,7 +230,7 @@ type could — a property that is not valid for the type it sits on.
230
230
  ### Should a paragraph be authored as an array of translation keys?
231
231
 
232
232
  **No. One sentence is one message, and the styling lives in the copy as
233
- `[marks]`. `content/` carries the parser, the runs and `plain()`; nothing
233
+ `[marks]`. `content/` carries the parser, the runs and `site.plain()`; nothing
234
234
  carries a `ContentItem[]`.**
235
235
 
236
236
  This is the shape the sibling B2C template uses, and it is the one thing from