@cassida/compiler 0.1.0 → 0.1.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/dist/hasher.d.ts CHANGED
@@ -4,5 +4,30 @@ export interface HashOptions {
4
4
  readonly prefix?: string;
5
5
  readonly length?: number;
6
6
  }
7
+ /**
8
+ * Build-time class-name hasher.
9
+ *
10
+ * Uses MurmurHash3 (x86, 32-bit) — a non-cryptographic hash with good
11
+ * distribution and low collision rate over ASCII inputs.
12
+ *
13
+ * Why not `node:crypto`? It pulls Node-only APIs into modules that
14
+ * eventually transit through bundlers. When the bundler thinks a
15
+ * downstream module *might* still reach `cas()` at runtime (e.g. when
16
+ * tree-shake is conservative), it traces the import graph into
17
+ * `hasher.ts` and chokes on `node:crypto`'s missing `createHash`
18
+ * export in browser-stub mode. A self-contained JS implementation
19
+ * avoids that whole class of build-time failures and ships zero
20
+ * dependencies.
21
+ *
22
+ * Hash strength is non-cryptographic by design — collisions are
23
+ * detected at build time by the emitter (`CssEmitter.add` throws on
24
+ * a class name with two distinct canonical bags), so a chance
25
+ * collision surfaces as a clear error, not a silent miscompile.
26
+ *
27
+ * For lengths > 8 hex chars, additional 32-bit rounds with
28
+ * incrementing seed are concatenated until the requested length is
29
+ * met. This preserves the existing API (length 4..40) without
30
+ * forcing a bigger algorithm or dual-package complexity.
31
+ */
7
32
  export declare function hash(canonical: string, options?: HashOptions): string;
8
33
  //# sourceMappingURL=hasher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hasher.d.ts","sourceRoot":"","sources":["../src/hasher.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,SAAS,CAAC;AACrC,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAKzE"}
1
+ {"version":3,"file":"hasher.d.ts","sourceRoot":"","sources":["../src/hasher.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,SAAS,CAAC;AACrC,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAgBzE"}
package/dist/hasher.js CHANGED
@@ -1,10 +1,103 @@
1
- import { createHash } from 'node:crypto';
2
1
  export const DEFAULT_PREFIX = 'cas-';
3
2
  export const DEFAULT_LENGTH = 8;
3
+ /**
4
+ * Build-time class-name hasher.
5
+ *
6
+ * Uses MurmurHash3 (x86, 32-bit) — a non-cryptographic hash with good
7
+ * distribution and low collision rate over ASCII inputs.
8
+ *
9
+ * Why not `node:crypto`? It pulls Node-only APIs into modules that
10
+ * eventually transit through bundlers. When the bundler thinks a
11
+ * downstream module *might* still reach `cas()` at runtime (e.g. when
12
+ * tree-shake is conservative), it traces the import graph into
13
+ * `hasher.ts` and chokes on `node:crypto`'s missing `createHash`
14
+ * export in browser-stub mode. A self-contained JS implementation
15
+ * avoids that whole class of build-time failures and ships zero
16
+ * dependencies.
17
+ *
18
+ * Hash strength is non-cryptographic by design — collisions are
19
+ * detected at build time by the emitter (`CssEmitter.add` throws on
20
+ * a class name with two distinct canonical bags), so a chance
21
+ * collision surfaces as a clear error, not a silent miscompile.
22
+ *
23
+ * For lengths > 8 hex chars, additional 32-bit rounds with
24
+ * incrementing seed are concatenated until the requested length is
25
+ * met. This preserves the existing API (length 4..40) without
26
+ * forcing a bigger algorithm or dual-package complexity.
27
+ */
4
28
  export function hash(canonical, options = {}) {
5
29
  const prefix = options.prefix ?? DEFAULT_PREFIX;
6
30
  const length = options.length ?? DEFAULT_LENGTH;
7
- const digest = createHash('sha1').update(canonical).digest('hex');
8
- return prefix + digest.slice(0, length);
31
+ // Encode once outside the round loop. For length > 8 we run multiple
32
+ // 32-bit rounds with incrementing seeds, but the input bytes don't
33
+ // change — re-encoding per round is pure waste and grows linear in
34
+ // the canonical-bag size for every extra hex char requested.
35
+ const bytes = utf8Bytes(canonical);
36
+ let hex = '';
37
+ let seed = 0;
38
+ while (hex.length < length) {
39
+ hex += murmur3_32(bytes, seed).toString(16).padStart(8, '0');
40
+ seed++;
41
+ }
42
+ return prefix + hex.slice(0, length);
43
+ }
44
+ /**
45
+ * MurmurHash3 x86 32-bit. Public-domain algorithm by Austin Appleby.
46
+ *
47
+ * Takes a pre-encoded UTF-8 byte sequence so the caller can amortize
48
+ * encoding across multiple seeded rounds. All arithmetic is kept in
49
+ * unsigned 32-bit territory via `>>> 0` and `Math.imul`, both of
50
+ * which V8 / SpiderMonkey JIT fast-path. Returns a non-negative
51
+ * 32-bit integer.
52
+ */
53
+ function murmur3_32(bytes, seed = 0) {
54
+ const len = bytes.length;
55
+ const nblocks = Math.floor(len / 4);
56
+ const c1 = 0xcc9e2d51;
57
+ const c2 = 0x1b873593;
58
+ let h1 = seed >>> 0;
59
+ for (let i = 0; i < nblocks; i++) {
60
+ const o = i * 4;
61
+ let k1 = (bytes[o] |
62
+ (bytes[o + 1] << 8) |
63
+ (bytes[o + 2] << 16) |
64
+ (bytes[o + 3] << 24)) >>>
65
+ 0;
66
+ k1 = Math.imul(k1, c1) >>> 0;
67
+ k1 = ((k1 << 15) | (k1 >>> 17)) >>> 0;
68
+ k1 = Math.imul(k1, c2) >>> 0;
69
+ h1 = (h1 ^ k1) >>> 0;
70
+ h1 = ((h1 << 13) | (h1 >>> 19)) >>> 0;
71
+ h1 = (Math.imul(h1, 5) + 0xe6546b64) >>> 0;
72
+ }
73
+ // tail
74
+ let k1 = 0;
75
+ const tailStart = nblocks * 4;
76
+ const rem = len & 3;
77
+ if (rem >= 3)
78
+ k1 ^= bytes[tailStart + 2] << 16;
79
+ if (rem >= 2)
80
+ k1 ^= bytes[tailStart + 1] << 8;
81
+ if (rem >= 1) {
82
+ k1 ^= bytes[tailStart];
83
+ k1 = Math.imul(k1, c1) >>> 0;
84
+ k1 = ((k1 << 15) | (k1 >>> 17)) >>> 0;
85
+ k1 = Math.imul(k1, c2) >>> 0;
86
+ h1 = (h1 ^ k1) >>> 0;
87
+ }
88
+ // finalization
89
+ h1 = (h1 ^ len) >>> 0;
90
+ h1 = (h1 ^ (h1 >>> 16)) >>> 0;
91
+ h1 = Math.imul(h1, 0x85ebca6b) >>> 0;
92
+ h1 = (h1 ^ (h1 >>> 13)) >>> 0;
93
+ h1 = Math.imul(h1, 0xc2b2ae35) >>> 0;
94
+ h1 = (h1 ^ (h1 >>> 16)) >>> 0;
95
+ return h1 >>> 0;
96
+ }
97
+ // Module-level encoder. `TextEncoder` is stateless once constructed,
98
+ // so reusing one instance avoids per-hash allocation.
99
+ const utf8Encoder = new TextEncoder();
100
+ function utf8Bytes(s) {
101
+ return utf8Encoder.encode(s);
9
102
  }
10
103
  //# sourceMappingURL=hasher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hasher.js","sourceRoot":"","sources":["../src/hasher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAOhC,MAAM,UAAU,IAAI,CAAC,SAAiB,EAAE,UAAuB,EAAE;IAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClE,OAAO,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"hasher.js","sourceRoot":"","sources":["../src/hasher.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,16 +1,21 @@
1
1
  {
2
2
  "name": "@cassida/compiler",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Cassida build-time CSS resolver: registry, canonicalizer, hasher, emitter.",
5
5
  "license": "MIT",
6
+ "sideEffects": false,
6
7
  "type": "module",
7
8
  "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
8
10
  "types": "./dist/index.d.ts",
9
11
  "exports": {
10
12
  ".": {
11
13
  "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js"
13
- }
14
+ "import": "./dist/index.js",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./config.schema.json": "./config.schema.json",
18
+ "./package.json": "./package.json"
14
19
  },
15
20
  "files": [
16
21
  "dist",