@proveanything/smartlinks 2.0.15 → 2.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "2.0.15",
3
+ "version": "2.0.17",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,8 @@
23
23
  },
24
24
  "./baseline.css": "./dist/baseline.css",
25
25
  "./baseline.classes.json": "./dist/baseline.classes.json",
26
+ "./theme.css": "./dist/theme.css",
27
+ "./theme-boot.js": "./dist/theme-boot.js",
26
28
  "./package.json": "./package.json"
27
29
  },
28
30
  "bin": {
@@ -46,6 +48,8 @@
46
48
  "docs": "typedoc",
47
49
  "docs:summary": "node generate-api-summary.cjs",
48
50
  "docs:openapi": "node generate-openapi.cjs",
51
+ "docs:openapi:lint": "npx --yes @redocly/cli@latest lint openapi.yaml",
52
+ "docs:openapi:check": "node generate-openapi.cjs && npx --yes @redocly/cli@latest lint openapi.yaml",
49
53
  "build:docs": "tsc build-docs.ts --outDir dist && node dist/build-docs.js",
50
54
  "prepublishOnly": "npm run build"
51
55
  },
@@ -16,7 +16,7 @@
16
16
  // Exit code: 0 = clean, 1 = violations (CI-friendly).
17
17
  // =============================================================================
18
18
 
19
- import { readFileSync, existsSync } from 'node:fs';
19
+ import { readFileSync, existsSync, readdirSync } from 'node:fs';
20
20
  import { resolve, dirname, join } from 'node:path';
21
21
  import { fileURLToPath, pathToFileURL } from 'node:url';
22
22
  import { bareImportsOf } from './lib/bare-imports.mjs';
@@ -184,6 +184,54 @@ if (meta.cssBaseline) {
184
184
  }
185
185
  }
186
186
 
187
+ // ---- Host theming (theme tokens) -------------------------------------------
188
+ // If the app declares meta.respectsHostTheme, warn on hardcoded Tailwind PALETTE utilities in
189
+ // component SOURCE (e.g. bg-blue-600, text-zinc-900) — those pin a colour instead of following the
190
+ // host brand via the semantic tokens (bg-primary, text-foreground, border-border). Scans source,
191
+ // not compiled bundles (bundles are full of legitimate hex). Heuristic + WARN-only. See
192
+ // docs/theme-tokens.md.
193
+ if (meta.respectsHostTheme) {
194
+ const THEME_TOKENS_VERSION = 'v1';
195
+ if (!meta.themeTokens) {
196
+ warnings.push(`meta.respectsHostTheme is true but meta.themeTokens is not declared — set it to "${THEME_TOKENS_VERSION}".`);
197
+ } else if (meta.themeTokens !== THEME_TOKENS_VERSION) {
198
+ warnings.push(`meta.themeTokens is "${meta.themeTokens}" but this SDK ships theme tokens "${THEME_TOKENS_VERSION}".`);
199
+ }
200
+
201
+ const PALETTE = 'slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose';
202
+ const UTIL = 'bg|text|border|ring|divide|from|via|to|fill|stroke|outline|decoration|shadow|accent|caret';
203
+ const paletteRe = new RegExp(`\\b(?:${UTIL})-(?:${PALETTE})-(?:50|100|200|300|400|500|600|700|800|900|950)\\b`, 'g');
204
+
205
+ const SRC_EXT = /\.(tsx|ts|jsx|js|vue|html|svelte)$/;
206
+ const SKIP_DIR = new Set(['node_modules', 'dist', '.nuxt', '.output', '.git', 'public']);
207
+ function walk(dir, out = []) {
208
+ let entries = [];
209
+ try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return out; }
210
+ for (const e of entries) {
211
+ if (e.isDirectory()) { if (!SKIP_DIR.has(e.name)) walk(join(dir, e.name), out); }
212
+ else if (SRC_EXT.test(e.name)) out.push(join(dir, e.name));
213
+ }
214
+ return out;
215
+ }
216
+
217
+ const srcRoot = existsSync(join(appDir, 'src')) ? join(appDir, 'src') : appDir;
218
+ const offenders = [];
219
+ let totalHits = 0;
220
+ for (const f of walk(srcRoot)) {
221
+ const hits = [...new Set((readFileSync(f, 'utf8').match(paletteRe) || []))];
222
+ if (hits.length) { offenders.push({ file: f.replace(appDir + '/', '').replace(appDir + '\\', ''), hits }); totalHits += hits.length; }
223
+ }
224
+
225
+ if (offenders.length === 0) {
226
+ console.log(`${GREEN}✓${RESET} theme ${DIM}(respectsHostTheme)${RESET} — no hardcoded palette utilities in source`);
227
+ } else {
228
+ console.log(`${YELLOW}⚠${RESET} theme ${DIM}(respectsHostTheme)${RESET} — hardcoded palette utilities in ${offenders.length} file${offenders.length === 1 ? '' : 's'} (use bg-primary / text-foreground / border-border instead):`);
229
+ for (const o of offenders.slice(0, 15)) console.log(` ${DIM}${o.file}${RESET} ${YELLOW}${o.hits.slice(0, 6).join(' ')}${o.hits.length > 6 ? ' …' : ''}${RESET}`);
230
+ if (offenders.length > 15) console.log(` ${DIM}…and ${offenders.length - 15} more file(s)${RESET}`);
231
+ warnings.push(`respectsHostTheme is true but ${totalHits} hardcoded palette utilit${totalHits === 1 ? 'y' : 'ies'} found in source — replace with semantic tokens, or drop respectsHostTheme if the app intentionally brings its own look.`);
232
+ }
233
+ }
234
+
187
235
  console.log('');
188
236
  for (const w of warnings) console.log(`${YELLOW}⚠ ${w}${RESET}`);
189
237