@jxsuite/compiler 1.5.0 → 2.0.0

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.
@@ -7,6 +7,8 @@
7
7
 
8
8
  import { dereference } from "@apidevtools/json-schema-ref-parser";
9
9
  import { collectServerEntries } from "../shared.ts";
10
+ import { localeNegotiationMiddleware } from "../site/locale-negotiation.ts";
11
+ import type { ResolvedI18n } from "../site/i18n.ts";
10
12
  import type { JxElement } from "@jxsuite/schema/types";
11
13
 
12
14
  /**
@@ -97,6 +99,7 @@ export interface SiteConnectorSpec {
97
99
  * `"cloudflare-pages"`, `"node"`, `"bun"`
98
100
  * @param {SiteMountSpec[]} [opts.mounts] - Extension server mounts, already sorted by order
99
101
  * @param {SiteConnectorSpec[]} [opts.connectors] - Connector classes used by the mounts
102
+ * @param {ResolvedI18n | null} [opts.i18n] - Locale routing; drives the `/` negotiation middleware
100
103
  * @returns {string | null} Complete worker/server source string, or null
101
104
  */
102
105
  export function compileSiteServer(
@@ -106,9 +109,16 @@ export function compileSiteServer(
106
109
  adapter?: string | null;
107
110
  mounts?: SiteMountSpec[];
108
111
  connectors?: SiteConnectorSpec[];
112
+ i18n?: ResolvedI18n | null;
109
113
  } = {},
110
114
  ) {
111
- const { baseUrl = "/_jx/server", adapter = null, mounts = [], connectors = [] } = opts;
115
+ const {
116
+ baseUrl = "/_jx/server",
117
+ adapter = null,
118
+ mounts = [],
119
+ connectors = [],
120
+ i18n = null,
121
+ } = opts;
112
122
  if (entries.length === 0 && mounts.length === 0 && !adapter) {
113
123
  return null;
114
124
  }
@@ -128,12 +138,19 @@ export function compileSiteServer(
128
138
  ? "\napp.all('*', (c) => c.env.ASSETS.fetch(c.req.raw))\n"
129
139
  : "\n";
130
140
 
141
+ /*
142
+ * Negotiation is middleware and it is registered FIRST, before any route and before the ASSETS
143
+ * fallthrough. It has to be: on `/` it either redirects or hands the request straight back to
144
+ * whatever would have served it, and it cannot do the second from behind a terminal handler.
145
+ */
146
+ const localeBlock = localeNegotiationMiddleware(i18n);
147
+
131
148
  return `// Generated by @jxsuite/compiler — do not edit manually
132
149
  import { Hono } from 'hono'
133
150
  ${imports}${mountBlock.imports}
134
151
 
135
152
  const app = new Hono()
136
- ${mountBlock.routes}${routes}
153
+ ${localeBlock}${mountBlock.routes}${routes}
137
154
  ${adapterBlock}
138
155
  export default app
139
156
  `;
@@ -15,6 +15,7 @@ import {
15
15
  isNodeDynamic,
16
16
  PREFORMATTED_TAGS,
17
17
  pureSchemeOf,
18
+ resolveStaticTagName,
18
19
  resolveStaticValue,
19
20
  SELF_CLOSING,
20
21
  } from "../shared.ts";
@@ -160,7 +161,9 @@ function compileNode(
160
161
  return `<${tagName}></${tagName}>`;
161
162
  }
162
163
 
163
- const tag = def.tagName ?? "div";
164
+ // Resolved against the same scope the attributes are, so the prerendered element and its
165
+ // Attributes come from one decision rather than two.
166
+ const tag = resolveStaticTagName(def.tagName, nextContext.scope);
164
167
  const attrs = buildAttrs(def, nextContext.scope);
165
168
 
166
169
  // Void elements (br, hr, img, …) have no closing tag.
package/src/types.ts CHANGED
@@ -6,6 +6,8 @@ export interface HeadMergeContext {
6
6
  url?: string;
7
7
  siteUrl?: string;
8
8
  pageUrl?: string;
9
+ /** `rel="alternate"` links for this page's translations (site-architecture.md §13.5). */
10
+ alternates?: readonly { hreflang: string; href: string }[];
9
11
  }
10
12
 
11
13
  export interface SiteRoute {