@techninja/clearstack 0.4.16 → 0.4.17

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.
@@ -24,14 +24,14 @@ Both are driven by the same `clearstack.routes.json` config and data sources.
24
24
  "/trait/:slug": {
25
25
  "title": "{slug.name}",
26
26
  "description": "{slug.description}",
27
- "image": "{slug.cover_image.url}",
28
- "data": "src/data/trait_manifest.json:traits",
27
+ "image": "{slug.image}",
28
+ "data": "data/traits-og.json",
29
29
  "ogTemplate": "trait"
30
30
  },
31
31
  "/shop/product/:sku": {
32
32
  "title": "{sku.name} | {store.name}",
33
33
  "description": "{sku.description}",
34
- "image": "{store.url}{sku.images.0}",
34
+ "image": "{sku.images.0}",
35
35
  "data": "src/data/products.json",
36
36
  "ogTemplate": "product"
37
37
  }
@@ -111,6 +111,53 @@ clearstack build all --url=https://mysite.com --site=MySite --out=dist
111
111
  | `--logo` | Logo path or URL |
112
112
  | `--slug` | Single item slug (fast iteration) |
113
113
 
114
+ ## Cloudflare Pages Routing
115
+
116
+ Cloudflare Pages evaluates `_redirects` **before** checking the filesystem.
117
+ The standard SPA catch-all `/* /index.html 200` will intercept all pre-rendered
118
+ pages before they can be served. Since CF Pages serves `.html` files natively
119
+ for extensionless URLs when a matching file exists on the filesystem, no extra
120
+ rewrite rules are needed — just ensure the catch-all is the **only** rule and
121
+ that it comes after any explicit redirects:
122
+
123
+ ```
124
+ /old-path /new-path 301
125
+ /* /index.html 200
126
+ ```
127
+
128
+ The pre-rendered files (`shop/product/SM-TEE.html`) will be served directly by
129
+ CF Pages' static file layer before the `_redirects` rules are evaluated.
130
+
131
+ ## Canonical Tags
132
+
133
+ Inject `<link rel="canonical">` into every pre-rendered page so Google
134
+ deduplicates the static HTML shell from the SPA view at the same URL. Do this
135
+ as a post-processing step in `build.js` after `buildOG` runs:
136
+
137
+ ```js
138
+ import { readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
139
+ import { resolve } from 'node:path';
140
+
141
+ function injectCanonicals(distDir, baseUrl) {
142
+ const prefixes = ['shop/product', 'trait', 'gene'];
143
+ for (const prefix of prefixes) {
144
+ const dir = resolve(distDir, prefix);
145
+ if (!existsSync(dir)) continue;
146
+ for (const file of readdirSync(dir)) {
147
+ if (!file.endsWith('.html')) continue;
148
+ const slug = file.slice(0, -5);
149
+ const url = `${baseUrl}/${prefix}/${slug}`;
150
+ const filePath = resolve(dir, file);
151
+ let html = readFileSync(filePath, 'utf-8');
152
+ if (!html.includes('rel="canonical"')) {
153
+ html = html.replace('</head>', ` <link rel="canonical" href="${url}" />\n </head>`);
154
+ writeFileSync(filePath, html);
155
+ }
156
+ }
157
+ }
158
+ }
159
+ ```
160
+
114
161
  ## Requirements
115
162
 
116
163
  - Zero config for basic cases (route title + description → OG tags)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techninja/clearstack",
3
- "version": "0.4.16",
3
+ "version": "0.4.17",
4
4
  "type": "module",
5
5
  "description": "A no-build web component framework specification — scaffold, validate, and evolve spec-compliant projects",
6
6
  "bin": {
@@ -24,14 +24,14 @@ Both are driven by the same `clearstack.routes.json` config and data sources.
24
24
  "/trait/:slug": {
25
25
  "title": "{slug.name}",
26
26
  "description": "{slug.description}",
27
- "image": "{slug.cover_image.url}",
28
- "data": "src/data/trait_manifest.json:traits",
27
+ "image": "{slug.image}",
28
+ "data": "data/traits-og.json",
29
29
  "ogTemplate": "trait"
30
30
  },
31
31
  "/shop/product/:sku": {
32
32
  "title": "{sku.name} | {store.name}",
33
33
  "description": "{sku.description}",
34
- "image": "{store.url}{sku.images.0}",
34
+ "image": "{sku.images.0}",
35
35
  "data": "src/data/products.json",
36
36
  "ogTemplate": "product"
37
37
  }
@@ -111,6 +111,53 @@ clearstack build all --url=https://mysite.com --site=MySite --out=dist
111
111
  | `--logo` | Logo path or URL |
112
112
  | `--slug` | Single item slug (fast iteration) |
113
113
 
114
+ ## Cloudflare Pages Routing
115
+
116
+ Cloudflare Pages evaluates `_redirects` **before** checking the filesystem.
117
+ The standard SPA catch-all `/* /index.html 200` will intercept all pre-rendered
118
+ pages before they can be served. Since CF Pages serves `.html` files natively
119
+ for extensionless URLs when a matching file exists on the filesystem, no extra
120
+ rewrite rules are needed — just ensure the catch-all is the **only** rule and
121
+ that it comes after any explicit redirects:
122
+
123
+ ```
124
+ /old-path /new-path 301
125
+ /* /index.html 200
126
+ ```
127
+
128
+ The pre-rendered files (`shop/product/SM-TEE.html`) will be served directly by
129
+ CF Pages' static file layer before the `_redirects` rules are evaluated.
130
+
131
+ ## Canonical Tags
132
+
133
+ Inject `<link rel="canonical">` into every pre-rendered page so Google
134
+ deduplicates the static HTML shell from the SPA view at the same URL. Do this
135
+ as a post-processing step in `build.js` after `buildOG` runs:
136
+
137
+ ```js
138
+ import { readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
139
+ import { resolve } from 'node:path';
140
+
141
+ function injectCanonicals(distDir, baseUrl) {
142
+ const prefixes = ['shop/product', 'trait', 'gene'];
143
+ for (const prefix of prefixes) {
144
+ const dir = resolve(distDir, prefix);
145
+ if (!existsSync(dir)) continue;
146
+ for (const file of readdirSync(dir)) {
147
+ if (!file.endsWith('.html')) continue;
148
+ const slug = file.slice(0, -5);
149
+ const url = `${baseUrl}/${prefix}/${slug}`;
150
+ const filePath = resolve(dir, file);
151
+ let html = readFileSync(filePath, 'utf-8');
152
+ if (!html.includes('rel="canonical"')) {
153
+ html = html.replace('</head>', ` <link rel="canonical" href="${url}" />\n </head>`);
154
+ writeFileSync(filePath, html);
155
+ }
156
+ }
157
+ }
158
+ }
159
+ ```
160
+
114
161
  ## Requirements
115
162
 
116
163
  - Zero config for basic cases (route title + description → OG tags)