@human-synthesis/norns-core 0.0.9 → 0.0.10
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 +1 -1
- package/src/preprocess.js +105 -1
package/package.json
CHANGED
package/src/preprocess.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sveltePreprocess } from 'svelte-preprocess';
|
|
2
2
|
import { compile as compileCivet } from '@danielx/civet';
|
|
3
3
|
|
|
4
|
-
export { transformIfChains, transformSnippets, rewritePugClasses };
|
|
4
|
+
export { transformIfChains, transformSnippets, rewritePugClasses, extractPugClasses };
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
const SCRIPT_TAG = /<script\b([^>]*)>/i;
|
|
@@ -205,6 +205,110 @@ function rewritePugLine(line) {
|
|
|
205
205
|
return `${indent}${tag}${safe.join('')}${newAttrs}${rest}`;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Walk `content` and return the set of Pug class-shorthand names found in
|
|
210
|
+
* element class chains (`.foo.bar-baz.hover:bg-red`) plus the value of any
|
|
211
|
+
* `class="..."` attribute on the same lines.
|
|
212
|
+
*
|
|
213
|
+
* Tailwind v4's content scanner extracts utility candidates from string
|
|
214
|
+
* contexts (`class="…"`, JS strings) but doesn't recognise Pug's chained
|
|
215
|
+
* shorthand — the dotted chain looks like one token. Pages render with the
|
|
216
|
+
* class names present in the markup but no matching CSS, which is silent
|
|
217
|
+
* and hard to spot. The companion `nornsTailwindPlugin()` Vite plugin in
|
|
218
|
+
* `@human-synthesis/norns` calls this and feeds the union into Tailwind via
|
|
219
|
+
* an injected `@source inline(...)` directive.
|
|
220
|
+
*
|
|
221
|
+
* Skips lines inside `<script>` / `<style>` blocks. Skips lines that begin
|
|
222
|
+
* with `|`, `<`, `+`, `:`, or `//` (Pug text emits, raw HTML, mixin calls,
|
|
223
|
+
* pug filters, and comments). For each remaining line, reads an optional
|
|
224
|
+
* tag, then chained `.<class>` segments (handling `:`, `/`, and fractional
|
|
225
|
+
* `.\d+` continuations), then collects the value of any `class="…"` or
|
|
226
|
+
* `class!="…"` attribute that follows.
|
|
227
|
+
*
|
|
228
|
+
* Pure function — does not mutate `content`. Returns a `Set<string>` so
|
|
229
|
+
* callers can union across many files without dedup work.
|
|
230
|
+
*
|
|
231
|
+
* @param {string} content
|
|
232
|
+
* @returns {Set<string>}
|
|
233
|
+
*/
|
|
234
|
+
function extractPugClasses(content) {
|
|
235
|
+
const out = new Set();
|
|
236
|
+
if (typeof content !== 'string' || content.length === 0) return out;
|
|
237
|
+
|
|
238
|
+
const blockRanges = [];
|
|
239
|
+
const blockRe = /<(script|style)\b[^>]*>[\s\S]*?<\/\1>/gi;
|
|
240
|
+
let m;
|
|
241
|
+
while ((m = blockRe.exec(content)) !== null) {
|
|
242
|
+
blockRanges.push([m.index, m.index + m[0].length]);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const lines = content.split('\n');
|
|
246
|
+
let offset = 0;
|
|
247
|
+
for (const line of lines) {
|
|
248
|
+
const lineEnd = offset + line.length;
|
|
249
|
+
const inBlock = blockRanges.some(([s, e]) => offset < e && lineEnd > s);
|
|
250
|
+
if (!inBlock) collectFromPugLine(line, out);
|
|
251
|
+
offset = lineEnd + 1; // +1 for the newline
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function collectFromPugLine(line, into) {
|
|
257
|
+
const trimmed = line.trimStart();
|
|
258
|
+
if (!trimmed) return;
|
|
259
|
+
const first = trimmed[0];
|
|
260
|
+
if (first === '|' || first === '<') return;
|
|
261
|
+
if (trimmed.startsWith('//')) return;
|
|
262
|
+
if (first === '+' || first === ':') return;
|
|
263
|
+
|
|
264
|
+
let i = 0;
|
|
265
|
+
while (i < line.length && /\s/.test(line[i])) i++;
|
|
266
|
+
|
|
267
|
+
// Optional element tag.
|
|
268
|
+
if (i < line.length && /[a-zA-Z]/.test(line[i])) {
|
|
269
|
+
let j = i;
|
|
270
|
+
while (j < line.length && /[\w-]/.test(line[j])) j++;
|
|
271
|
+
i = j;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// `.class` / `#id` segments. Mirrors `rewritePugLine` so the two stay
|
|
275
|
+
// in sync — both must accept the same chained-shorthand grammar.
|
|
276
|
+
while (i < line.length && (line[i] === '.' || line[i] === '#')) {
|
|
277
|
+
const sep = line[i];
|
|
278
|
+
let j = i + 1;
|
|
279
|
+
if (sep === '#') {
|
|
280
|
+
while (j < line.length && /[\w-]/.test(line[j])) j++;
|
|
281
|
+
} else {
|
|
282
|
+
while (j < line.length && /[\w/:-]/.test(line[j])) j++;
|
|
283
|
+
while (j < line.length && line[j] === '.' && /\d/.test(line[j + 1] || '')) {
|
|
284
|
+
j++;
|
|
285
|
+
while (j < line.length && /\d/.test(line[j])) j++;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (j === i + 1) break;
|
|
289
|
+
if (sep === '.') into.add(line.slice(i + 1, j));
|
|
290
|
+
i = j;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// `(attrs)` block — pull class="..." and class!="..." values too. Pug
|
|
294
|
+
// chained shorthand often coexists with a `(class="...")` attribute on
|
|
295
|
+
// the same line (especially after `rewritePugClasses` routes special
|
|
296
|
+
// chars there). Capturing both lets a single pass cover the full set.
|
|
297
|
+
if (line[i] === '(') {
|
|
298
|
+
const close = findMatchingParen(line, i);
|
|
299
|
+
if (close !== -1) {
|
|
300
|
+
const attrs = line.slice(i + 1, close);
|
|
301
|
+
const re = /(?:^|\s)class\s*!?=\s*"([^"]*)"/g;
|
|
302
|
+
let am;
|
|
303
|
+
while ((am = re.exec(attrs)) !== null) {
|
|
304
|
+
for (const tok of am[1].split(/\s+/)) {
|
|
305
|
+
if (tok) into.add(tok);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
208
312
|
function mergeClassIntoAttrs(attrsStr, classesToAdd) {
|
|
209
313
|
const classStr = classesToAdd.join(' ');
|
|
210
314
|
if (!attrsStr) return `(class="${classStr}")`;
|