strata-css 1.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.
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Strata Scanner — Optimised v2
3
+ * Cached glob results + file content cache + single pass regex
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const fs = require('fs')
9
+ const path = require('path')
10
+ const glob = require('glob')
11
+
12
+ const ALLOWED_EXTENSIONS = new Set([
13
+ '.html','.jsx','.tsx','.vue','.astro','.svelte','.js','.ts'
14
+ ])
15
+
16
+ const CLASS_PATTERN = /class(?:Name)?=(?:["'`]([^"'`]+)["'`]|\{["'`]([^"'`]+)["'`]\})/g
17
+
18
+ // File content cache — keyed by path, stores { mtime, classes }
19
+ const fileCache = new Map()
20
+
21
+ // Glob result cache — keyed by pattern, stores { mtime, files }
22
+ const globCache = new Map()
23
+
24
+ function getFiles(contentGlobs) {
25
+ const allFiles = []
26
+ for (let g = 0; g < contentGlobs.length; g++) {
27
+ const pattern = contentGlobs[g]
28
+ const cached = globCache.get(pattern)
29
+
30
+ // Glob results rarely change — cache for 500ms
31
+ if (cached && (Date.now() - cached.time) < 500) {
32
+ cached.files.forEach(f => allFiles.push(f))
33
+ continue
34
+ }
35
+
36
+ const files = glob.sync(pattern, { nodir: true })
37
+ .filter(f => path.extname(f).toLowerCase() !== '.css')
38
+
39
+ globCache.set(pattern, { files, time: Date.now() })
40
+ files.forEach(f => allFiles.push(f))
41
+ }
42
+ return allFiles
43
+ }
44
+
45
+ function extractClassesFromFile(filePath) {
46
+ const ext = path.extname(filePath).toLowerCase()
47
+ if (!ALLOWED_EXTENSIONS.has(ext)) return null
48
+
49
+ let mtime
50
+ try { mtime = fs.statSync(filePath).mtimeMs }
51
+ catch { return null }
52
+
53
+ const cached = fileCache.get(filePath)
54
+ if (cached && cached.mtime === mtime) return cached.classes
55
+
56
+ let content
57
+ try { content = fs.readFileSync(filePath, 'utf8') }
58
+ catch { return null }
59
+
60
+ const classes = new Set()
61
+ CLASS_PATTERN.lastIndex = 0
62
+ let match
63
+
64
+ while ((match = CLASS_PATTERN.exec(content)) !== null) {
65
+ const str = match[1] || match[2]
66
+ if (!str) continue
67
+ const parts = str.split(/\s+/)
68
+ for (let i = 0; i < parts.length; i++) {
69
+ const t = parts[i].trim()
70
+ if (t) classes.add(t)
71
+ }
72
+ }
73
+
74
+ fileCache.set(filePath, { mtime, classes })
75
+ return classes
76
+ }
77
+
78
+ function scanFiles(contentGlobs) {
79
+ const allClasses = new Set()
80
+ const files = getFiles(contentGlobs)
81
+
82
+ for (let i = 0; i < files.length; i++) {
83
+ const classes = extractClassesFromFile(files[i])
84
+ if (classes) classes.forEach(cls => allClasses.add(cls))
85
+ }
86
+
87
+ return allClasses
88
+ }
89
+
90
+ function getWatchFiles(contentGlobs) {
91
+ return getFiles(contentGlobs)
92
+ }
93
+
94
+ function clearFileCache(filePath) {
95
+ if (filePath) {
96
+ fileCache.delete(filePath)
97
+ globCache.clear() // a specific file change may mean add/delete — re-glob
98
+ } else {
99
+ fileCache.clear()
100
+ // Full invalidate only clears content cache, not glob results.
101
+ // Which files exist hasn't changed — only their content did.
102
+ // Preserving globCache avoids an expensive filesystem scan every build.
103
+ }
104
+ }
105
+
106
+ module.exports = { scanFiles, extractClassesFromFile, getWatchFiles, clearFileCache }
package/strata.css ADDED
@@ -0,0 +1,3 @@
1
+ @strata base;
2
+ @strata components;
3
+ @strata utilities;