wiki-formant 0.52.0 → 0.52.1

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 CHANGED
@@ -268,7 +268,7 @@ Four configs had restated these by hand on the grounds that a config could not i
268
268
  `withWikiDefaults(config, { csp })` wraps the whole config: the headers on every route with `csp` enforced in production, AVIF before WebP from the image optimiser, Turbopack's build cache off (Vercel restored a stale one and shipped a stylesheet two commits old), and `distDir` from `STUDIO_DIST_DIR` for radix-studio's second dev server. What the app sets wins, and its own `headers()` follow these.
269
269
 
270
270
  ```ts
271
- export default withWikiDefaults({ async redirects() { /* … */ } }, { csp });
271
+ export default withWikiDefaults<NextConfig>({ async redirects() { /* … */ } }, { csp });
272
272
  ```
273
273
 
274
274
  `contentSecurityPolicy` starts from the policy all four origins shared line for line, and each directive passed replaces its default rather than appending, so the call site reads as the whole list. An empty list drops a directive.
@@ -66,22 +66,28 @@ for (const f of cssFiles) {
66
66
  }
67
67
  }
68
68
 
69
- // Classes this project defines itself. Everything else the build emitted is a
70
- // Tailwind utility, which is what makes the composition count derivable rather
71
- // than a maintained prefix list. Read from rule preludes only, so a decimal in a
72
- // value or a class named in a comment is not taken for a selector.
73
- const globalsFile = walk(SRC_DIR, '.css').find(f => f.endsWith('globals.css'));
74
- const named = new Set();
75
- if (globalsFile) {
76
- const css = fs.readFileSync(globalsFile, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '');
69
+ // Classes defined by this project's stylesheets or by wiki-formant's base.css.
70
+ // Everything else the build emitted is a Tailwind utility, which is what makes
71
+ // the composition count derivable rather than a maintained prefix list. Read
72
+ // from rule preludes only, so a decimal in a value or a class named in a
73
+ // comment is not taken for a selector.
74
+ const classesIn = file => {
75
+ const out = new Set();
76
+ const css = fs.readFileSync(file, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '');
77
77
  for (const [, prelude] of css.matchAll(/([^{};]*)\{/g)) {
78
78
  if (prelude.trim().startsWith('@')) continue;
79
79
  for (const m of prelude.matchAll(/\.((?:\\.|[-\w])+)/g)) {
80
80
  const name = m[1].replace(/\\/g, '');
81
- if (/^[A-Za-z_-]/.test(name)) named.add(name);
81
+ if (/^[A-Za-z_-]/.test(name)) out.add(name);
82
82
  }
83
83
  }
84
- }
84
+ return out;
85
+ };
86
+ const ownCss = walk(SRC_DIR, '.css');
87
+ const globalsFile = ownCss.find(f => f.endsWith('globals.css'));
88
+ const own = new Set(ownCss.flatMap(f => [...classesIn(f)]));
89
+ const baseCss = new URL('../dist/base.css', import.meta.url);
90
+ const named = new Set([...own, ...(fs.existsSync(baseCss) ? classesIn(baseCss) : [])]);
85
91
 
86
92
  // Only bare literals: anything interpolated is beyond a static check.
87
93
  const dead = new Map();
@@ -120,20 +126,22 @@ if (compositions.length) {
120
126
  }
121
127
 
122
128
  // Every token in the JavaScript the build ships, server and client. A selector
123
- // is referenced when its name appears whole, or when a prefix of it ending in `-`
124
- // or `_` does, which is how `editorial-banner-${variant}` is written.
129
+ // is referenced when its name appears whole, when a prefix of it ending in `-`
130
+ // or `_` does, which is how `editorial-banner-${variant}` is written, or when it
131
+ // splits at a `-` or `_` into two shipped tokens, which is how `RailShell`'s
132
+ // `${prefix}--open` is written.
125
133
  const shipped = new Set();
126
134
  for (const f of [...walk(STATIC_DIR, '.js'), ...walk('.next/server', '.js')]) {
127
135
  for (const [tok] of fs.readFileSync(f, 'utf8').matchAll(/[\w-]+/g)) shipped.add(tok);
128
136
  }
129
137
  const referenced = name =>
130
138
  shipped.has(name) ||
131
- [...name].some((c, i) => i >= 3 && (c === '-' || c === '_') && shipped.has(name.slice(0, i + 1)));
132
- const unused = [...named].filter(n => !referenced(n)).sort();
139
+ [...name].some((c, i) => i >= 3 && (c === '-' || c === '_') && (shipped.has(name.slice(0, i + 1)) || (shipped.has(name.slice(0, i)) && shipped.has(name.slice(i)))));
140
+ const unused = [...own].filter(n => !referenced(n)).sort();
133
141
 
134
142
  if (unused.length) {
135
143
  const verb = CHECK_UNUSED ? 'must be deleted' : 'advisory';
136
- console.error(`check-classes: ${unused.length} selector(s) in ${globalsFile} referenced by nothing the build ships (${verb}):\n`);
144
+ console.error(`check-classes: ${unused.length} selector(s) in ${ownCss.join(', ')} referenced by nothing the build ships (${verb}):\n`);
137
145
  console.error(` ${unused.join(', ')}\n`);
138
146
  console.error(' Class names inside HTML stored in a database do not count, so check stored content before deleting one.\n');
139
147
  }
package/dist/headers.d.ts CHANGED
@@ -30,7 +30,7 @@ interface WikiNextConfig {
30
30
  distDir?: string;
31
31
  experimental?: object;
32
32
  images?: object;
33
- headers?: () => Promise<unknown[]>;
33
+ headers?: () => Promise<unknown[]> | unknown[];
34
34
  }
35
35
  /**
36
36
  * An app's Next config with the settings every app here wrote out the same way:
@@ -46,7 +46,8 @@ interface WikiNextConfig {
46
46
  * Next's dev runtime is inline and `eval`. An origin that sets its policy
47
47
  * per request (a nonce) passes none.
48
48
  *
49
- * Anything the app sets wins, and its own `headers()` follow these.
49
+ * Anything the app sets wins, and its own `headers()` follow these. Pass
50
+ * `NextConfig` as `T` so the object is typed as Next's own config.
50
51
  */
51
52
  export declare function withWikiDefaults<T extends WikiNextConfig>(config: T, { csp }?: {
52
53
  csp?: string;
package/dist/headers.js CHANGED
@@ -93,7 +93,8 @@ export function securityHeaders(csp) {
93
93
  * Next's dev runtime is inline and `eval`. An origin that sets its policy
94
94
  * per request (a nonce) passes none.
95
95
  *
96
- * Anything the app sets wins, and its own `headers()` follow these.
96
+ * Anything the app sets wins, and its own `headers()` follow these. Pass
97
+ * `NextConfig` as `T` so the object is typed as Next's own config.
97
98
  */
98
99
  export function withWikiDefaults(config, { csp } = {}) {
99
100
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-formant",
3
- "version": "0.52.0",
3
+ "version": "0.52.1",
4
4
  "description": "The portable half of a wiki: derived taxonomy and facet controls, a version-negotiating MCP transport, markdown twins, block rendering, a rich-text editor engine, conditional-GET plumbing, and the headers and CSP every origin sends.",
5
5
  "type": "module",
6
6
  "license": "MIT",