typestyles 0.3.0 → 0.4.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.
- package/dist/build.cjs +71 -0
- package/dist/build.cjs.map +1 -0
- package/dist/build.d.cts +20 -0
- package/dist/build.d.ts +20 -0
- package/dist/build.js +15 -0
- package/dist/build.js.map +1 -0
- package/dist/{chunk-BB7C47TQ.js → chunk-AYFIQGCF.js} +11 -7
- package/dist/chunk-AYFIQGCF.js.map +1 -0
- package/dist/hmr.cjs +1 -0
- package/dist/hmr.cjs.map +1 -1
- package/dist/hmr.js +1 -1
- package/dist/index.cjs +386 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +169 -23
- package/dist/index.d.ts +169 -23
- package/dist/index.js +377 -28
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +2 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +2 -2
- package/package.json +11 -1
- package/dist/chunk-BB7C47TQ.js.map +0 -1
package/dist/build.cjs
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/sheet.ts
|
|
4
|
+
var STYLE_ELEMENT_ID = "typestyles";
|
|
5
|
+
var RUNTIME_DISABLED = typeof __TYPESTYLES_RUNTIME_DISABLED__ !== "undefined" && __TYPESTYLES_RUNTIME_DISABLED__ === "true";
|
|
6
|
+
var pendingRules = [];
|
|
7
|
+
var styleElement = null;
|
|
8
|
+
var ssrBuffer = null;
|
|
9
|
+
var isBrowser = typeof document !== "undefined" && typeof window !== "undefined";
|
|
10
|
+
function getStyleElement() {
|
|
11
|
+
if (styleElement) return styleElement;
|
|
12
|
+
const existing = document.getElementById(
|
|
13
|
+
STYLE_ELEMENT_ID
|
|
14
|
+
);
|
|
15
|
+
if (existing) {
|
|
16
|
+
styleElement = existing;
|
|
17
|
+
return styleElement;
|
|
18
|
+
}
|
|
19
|
+
styleElement = document.createElement("style");
|
|
20
|
+
styleElement.id = STYLE_ELEMENT_ID;
|
|
21
|
+
document.head.appendChild(styleElement);
|
|
22
|
+
return styleElement;
|
|
23
|
+
}
|
|
24
|
+
function flush() {
|
|
25
|
+
if (pendingRules.length === 0) return;
|
|
26
|
+
const rules = pendingRules;
|
|
27
|
+
pendingRules = [];
|
|
28
|
+
if (ssrBuffer) {
|
|
29
|
+
ssrBuffer.push(...rules);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!isBrowser || RUNTIME_DISABLED) return;
|
|
33
|
+
const el = getStyleElement();
|
|
34
|
+
const sheet = el.sheet;
|
|
35
|
+
if (sheet) {
|
|
36
|
+
for (const rule of rules) {
|
|
37
|
+
try {
|
|
38
|
+
sheet.insertRule(rule, sheet.cssRules.length);
|
|
39
|
+
} catch {
|
|
40
|
+
el.appendChild(document.createTextNode(rule));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
el.appendChild(document.createTextNode(rules.join("\n")));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function startCollection() {
|
|
48
|
+
ssrBuffer = [];
|
|
49
|
+
return () => {
|
|
50
|
+
const css = ssrBuffer ? ssrBuffer.join("\n") : "";
|
|
51
|
+
ssrBuffer = null;
|
|
52
|
+
return css;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function flushSync() {
|
|
56
|
+
flush();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/build.ts
|
|
60
|
+
async function collectStylesFromModules(loaders) {
|
|
61
|
+
const endCollection = startCollection();
|
|
62
|
+
for (const load of loaders) {
|
|
63
|
+
await load();
|
|
64
|
+
}
|
|
65
|
+
flushSync();
|
|
66
|
+
return endCollection();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
exports.collectStylesFromModules = collectStylesFromModules;
|
|
70
|
+
//# sourceMappingURL=build.cjs.map
|
|
71
|
+
//# sourceMappingURL=build.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sheet.ts","../src/build.ts"],"names":[],"mappings":";;;AAAA,IAAM,gBAAA,GAAmB,YAAA;AAazB,IAAM,gBAAA,GACJ,OAAO,+BAAA,KAAoC,WAAA,IAC3C,+BAAA,KAAoC,MAAA;AAKtC,IAAI,eAAyB,EAAC;AAiB9B,IAAI,YAAA,GAAwC,IAAA;AAK5C,IAAI,SAAA,GAA6B,IAAA;AAKjC,IAAM,SAAA,GACJ,OAAO,QAAA,KAAa,WAAA,IAAe,OAAO,MAAA,KAAW,WAAA;AAEvD,SAAS,eAAA,GAAoC;AAC3C,EAAA,IAAI,cAAc,OAAO,YAAA;AAGzB,EAAA,MAAM,WAAW,QAAA,CAAS,cAAA;AAAA,IACxB;AAAA,GACF;AACA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,YAAA,GAAe,QAAA;AACf,IAAA,OAAO,YAAA;AAAA,EACT;AAGA,EAAA,YAAA,GAAe,QAAA,CAAS,cAAc,OAAO,CAAA;AAC7C,EAAA,YAAA,CAAa,EAAA,GAAK,gBAAA;AAClB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,YAAY,CAAA;AACtC,EAAA,OAAO,YAAA;AACT;AAEA,SAAS,KAAA,GAAc;AAErB,EAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAE/B,EAAA,MAAM,KAAA,GAAQ,YAAA;AACd,EAAA,YAAA,GAAe,EAAC;AAEhB,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,CAAU,IAAA,CAAK,GAAG,KAAK,CAAA;AACvB,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,aAAa,gBAAA,EAAkB;AAEpC,EAAA,MAAM,KAAK,eAAA,EAAgB;AAC3B,EAAA,MAAM,QAAQ,EAAA,CAAG,KAAA;AAEjB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,IAAI;AACF,QAAA,KAAA,CAAM,UAAA,CAAW,IAAA,EAAM,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AAAA,MAC9C,CAAA,CAAA,MAAQ;AAEN,QAAA,EAAA,CAAG,WAAA,CAAY,QAAA,CAAS,cAAA,CAAe,IAAI,CAAC,CAAA;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,EAAA,CAAG,YAAY,QAAA,CAAS,cAAA,CAAe,MAAM,IAAA,CAAK,IAAI,CAAC,CAAC,CAAA;AAAA,EAC1D;AACF;AAuEO,SAAS,eAAA,GAAgC;AAC9C,EAAA,SAAA,GAAY,EAAC;AACb,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,GAAA,GAAM,SAAA,GAAY,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI,EAAA;AAC/C,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,OAAO,GAAA;AAAA,EACT,CAAA;AACF;AA4CO,SAAS,SAAA,GAAkB;AAChC,EAAA,KAAA,EAAM;AACR;;;AC5MA,eAAsB,yBACpB,OAAA,EACiB;AACjB,EAAA,MAAM,gBAAgB,eAAA,EAAgB;AAEtC,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAK1B,IAAA,MAAM,IAAA,EAAK;AAAA,EACb;AAEA,EAAA,SAAA,EAAU;AACV,EAAA,OAAO,aAAA,EAAc;AACvB","file":"build.cjs","sourcesContent":["const STYLE_ELEMENT_ID = 'typestyles';\n\n/**\n * Tracks which CSS rules have been inserted to avoid duplicates.\n */\nconst insertedRules = new Set<string>();\n\n/**\n * Whether runtime DOM insertion is disabled (for build-time/zero-runtime mode).\n * The Vite plugin (mode: 'build') defines __TYPESTYLES_RUNTIME_DISABLED__ as the\n * string \"true\" at build time, so this is true in production and no <style> is created.\n */\ndeclare const __TYPESTYLES_RUNTIME_DISABLED__: string | undefined;\nconst RUNTIME_DISABLED =\n typeof __TYPESTYLES_RUNTIME_DISABLED__ !== 'undefined' &&\n __TYPESTYLES_RUNTIME_DISABLED__ === 'true';\n\n/**\n * Buffer of CSS rules waiting to be flushed.\n */\nlet pendingRules: string[] = [];\n\n/**\n * All CSS rules ever registered (for SSR extraction).\n * Unlike pendingRules (which is cleared on flush), this retains every rule\n * so getRegisteredCss() can return the full stylesheet at any point.\n */\nconst allRules: string[] = [];\n\n/**\n * Whether a flush is scheduled.\n */\nlet flushScheduled = false;\n\n/**\n * The managed <style> element, lazily created.\n */\nlet styleElement: HTMLStyleElement | null = null;\n\n/**\n * When in SSR collection mode, CSS is captured here instead of injected.\n */\nlet ssrBuffer: string[] | null = null;\n\n/**\n * Whether we're running in a browser environment.\n */\nconst isBrowser =\n typeof document !== 'undefined' && typeof window !== 'undefined';\n\nfunction getStyleElement(): HTMLStyleElement {\n if (styleElement) return styleElement;\n\n // Check for an existing element (e.g., from SSR)\n const existing = document.getElementById(\n STYLE_ELEMENT_ID\n ) as HTMLStyleElement | null;\n if (existing) {\n styleElement = existing;\n return styleElement;\n }\n\n // Create a new one\n styleElement = document.createElement('style');\n styleElement.id = STYLE_ELEMENT_ID;\n document.head.appendChild(styleElement);\n return styleElement;\n}\n\nfunction flush(): void {\n flushScheduled = false;\n if (pendingRules.length === 0) return;\n\n const rules = pendingRules;\n pendingRules = [];\n\n if (ssrBuffer) {\n ssrBuffer.push(...rules);\n return;\n }\n\n if (!isBrowser || RUNTIME_DISABLED) return;\n\n const el = getStyleElement();\n const sheet = el.sheet;\n\n if (sheet) {\n for (const rule of rules) {\n try {\n sheet.insertRule(rule, sheet.cssRules.length);\n } catch {\n // Fallback: append as text (handles edge cases with certain selectors)\n el.appendChild(document.createTextNode(rule));\n }\n }\n } else {\n // Sheet not available yet, append as text\n el.appendChild(document.createTextNode(rules.join('\\n')));\n }\n}\n\nfunction scheduleFlush(): void {\n if (flushScheduled) return;\n flushScheduled = true;\n\n if (ssrBuffer) {\n // In SSR mode, flush synchronously\n flush();\n return;\n }\n\n if (isBrowser && !RUNTIME_DISABLED) {\n // Use microtask for fast, batched insertion\n queueMicrotask(flush);\n }\n}\n\n/**\n * Insert a CSS rule. Deduplicates by rule key.\n */\nexport function insertRule(key: string, css: string): void {\n if (insertedRules.has(key)) return;\n insertedRules.add(key);\n allRules.push(css);\n if (RUNTIME_DISABLED && !ssrBuffer) return;\n pendingRules.push(css);\n scheduleFlush();\n}\n\n/**\n * Insert multiple CSS rules at once.\n */\nexport function insertRules(rules: Array<{ key: string; css: string }>): void {\n let added = false;\n for (const { key, css } of rules) {\n if (insertedRules.has(key)) continue;\n insertedRules.add(key);\n allRules.push(css);\n if (!RUNTIME_DISABLED || ssrBuffer) {\n pendingRules.push(css);\n added = true;\n }\n }\n if (added) scheduleFlush();\n}\n\n/**\n * Replace a CSS rule (used for HMR). Removes the old rule and inserts the new one.\n */\nexport function replaceRule(key: string, css: string): void {\n if (!isBrowser || RUNTIME_DISABLED) return;\n\n insertedRules.delete(key);\n\n // Remove existing rule from the sheet if possible\n const el = getStyleElement();\n const sheet = el.sheet;\n if (sheet) {\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n // We can't reliably match by key in CSSOM, so for HMR we fall back to\n // clearing and re-inserting. This is fine since HMR is dev-only.\n }\n }\n\n insertRule(key, css);\n}\n\n/**\n * Start collecting CSS for SSR. Returns a function to stop collection and get the CSS.\n */\nexport function startCollection(): () => string {\n ssrBuffer = [];\n return () => {\n const css = ssrBuffer ? ssrBuffer.join('\\n') : '';\n ssrBuffer = null;\n return css;\n };\n}\n\n/**\n * Return all registered CSS as a string.\n *\n * Unlike `collectStyles`, this doesn't require wrapping a render function.\n * It simply returns every CSS rule that has been registered via\n * `styles.create`, `tokens.create`, `keyframes.create`, etc.\n *\n * Ideal for SSR frameworks that need the CSS separately from the render\n * pass (e.g. TanStack Start's `head()`, Next.js metadata, Remix links).\n *\n * @example\n * ```ts\n * import { getRegisteredCss } from 'typestyles/server';\n *\n * // In a route's head/meta function:\n * export const head = () => ({\n * styles: [{ id: 'typestyles', children: getRegisteredCss() }],\n * });\n * ```\n */\nexport function getRegisteredCss(): string {\n return allRules.join('\\n');\n}\n\n/**\n * Reset all state (useful for testing).\n */\nexport function reset(): void {\n insertedRules.clear();\n pendingRules = [];\n allRules.length = 0;\n flushScheduled = false;\n ssrBuffer = null;\n if (isBrowser && styleElement) {\n styleElement.remove();\n styleElement = null;\n }\n}\n\n/**\n * Flush all pending rules synchronously. Used for SSR and testing.\n */\nexport function flushSync(): void {\n flush();\n}\n\n/**\n * Invalidate all dedup keys that start with the given prefix.\n * Also removes matching rules from the live stylesheet.\n * Used for HMR — allows modules to re-register their styles after editing.\n */\nexport function invalidatePrefix(prefix: string): void {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n if (ruleMatchesPrefix(rule, prefix)) {\n sheet.deleteRule(i);\n }\n }\n}\n\n/**\n * Invalidate a list of exact keys or prefixes.\n * Each entry in `keys` is treated as an exact key match.\n * Each entry in `prefixes` is treated as a prefix match.\n * Used for HMR to invalidate all styles from a module at once.\n */\nexport function invalidateKeys(keys: string[], prefixes: string[]): void {\n for (const key of keys) {\n insertedRules.delete(key);\n }\n for (const prefix of prefixes) {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n const keySet = new Set(keys);\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n let shouldRemove = false;\n\n for (const prefix of prefixes) {\n if (ruleMatchesPrefix(rule, prefix)) {\n shouldRemove = true;\n break;\n }\n }\n\n if (!shouldRemove) {\n // Check exact key matches — for tokens/themes/keyframes,\n // we match based on rule content patterns\n const ruleText = rule.cssText;\n for (const key of keySet) {\n if (ruleMatchesKey(ruleText, key)) {\n shouldRemove = true;\n break;\n }\n }\n }\n\n if (shouldRemove) {\n sheet.deleteRule(i);\n }\n }\n}\n\nfunction ruleMatchesPrefix(rule: CSSRule, prefix: string): boolean {\n if (prefix.startsWith('font-face:')) {\n const family = prefix.slice('font-face:'.length).split(':')[0];\n // CSSFontFaceRule has type 5 and cssText contains @font-face\n if (rule.cssText.includes('@font-face')) {\n return (\n rule.cssText.includes(`\"${family}\"`) || rule.cssText.includes(`'${family}'`)\n );\n }\n return false;\n }\n if ('selectorText' in rule) {\n return (rule as CSSStyleRule).selectorText.startsWith(prefix);\n }\n if ('name' in rule && prefix.startsWith('keyframes:')) {\n return (rule as CSSKeyframesRule).name === prefix.slice('keyframes:'.length);\n }\n // For at-rules wrapping style rules, check inner rules\n if ('cssRules' in rule) {\n const innerRules = (rule as CSSGroupingRule).cssRules;\n for (let i = 0; i < innerRules.length; i++) {\n if (ruleMatchesPrefix(innerRules[i], prefix)) return true;\n }\n }\n return false;\n}\n\nfunction ruleMatchesKey(cssText: string, key: string): boolean {\n if (key.startsWith('tokens:')) {\n // tokens:color -> :root rule with --color- custom properties\n const namespace = key.slice('tokens:'.length);\n return cssText.includes(`:root`) && cssText.includes(`--${namespace}-`);\n }\n if (key.startsWith('theme:')) {\n // theme:dark -> .theme-dark selector\n const name = key.slice('theme:'.length);\n return cssText.includes(`.theme-${name}`);\n }\n if (key.startsWith('keyframes:')) {\n const name = key.slice('keyframes:'.length);\n return cssText.includes(`@keyframes ${name}`);\n }\n return false;\n}\n","import { startCollection, flushSync } from './sheet.js';\n\n/**\n * Collect all CSS generated by executing one or more loader functions.\n *\n * This is a low-level primitive intended for build tools (Vite, Next.js, etc.)\n * to implement a zero-runtime extraction step.\n *\n * Example usage (Vite/Node):\n *\n * ```ts\n * import { collectStylesFromModules } from 'typestyles/build';\n *\n * const css = await collectStylesFromModules([\n * () => import('./src/styles'),\n * () => import('./src/tokens'),\n * ]);\n * ```\n */\nexport async function collectStylesFromModules(\n loaders: Array<() => unknown | Promise<unknown>>\n): Promise<string> {\n const endCollection = startCollection();\n\n for (const load of loaders) {\n // Each loader can synchronously or asynchronously import modules that\n // register typestyles styles, tokens, keyframes, etc.\n // We await in case a loader returns a promise (e.g. dynamic import).\n // The return value itself is ignored; only the side effects matter.\n await load();\n }\n\n flushSync();\n return endCollection();\n}\n\n"]}
|
package/dist/build.d.cts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collect all CSS generated by executing one or more loader functions.
|
|
3
|
+
*
|
|
4
|
+
* This is a low-level primitive intended for build tools (Vite, Next.js, etc.)
|
|
5
|
+
* to implement a zero-runtime extraction step.
|
|
6
|
+
*
|
|
7
|
+
* Example usage (Vite/Node):
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { collectStylesFromModules } from 'typestyles/build';
|
|
11
|
+
*
|
|
12
|
+
* const css = await collectStylesFromModules([
|
|
13
|
+
* () => import('./src/styles'),
|
|
14
|
+
* () => import('./src/tokens'),
|
|
15
|
+
* ]);
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function collectStylesFromModules(loaders: Array<() => unknown | Promise<unknown>>): Promise<string>;
|
|
19
|
+
|
|
20
|
+
export { collectStylesFromModules };
|
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collect all CSS generated by executing one or more loader functions.
|
|
3
|
+
*
|
|
4
|
+
* This is a low-level primitive intended for build tools (Vite, Next.js, etc.)
|
|
5
|
+
* to implement a zero-runtime extraction step.
|
|
6
|
+
*
|
|
7
|
+
* Example usage (Vite/Node):
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { collectStylesFromModules } from 'typestyles/build';
|
|
11
|
+
*
|
|
12
|
+
* const css = await collectStylesFromModules([
|
|
13
|
+
* () => import('./src/styles'),
|
|
14
|
+
* () => import('./src/tokens'),
|
|
15
|
+
* ]);
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function collectStylesFromModules(loaders: Array<() => unknown | Promise<unknown>>): Promise<string>;
|
|
19
|
+
|
|
20
|
+
export { collectStylesFromModules };
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { startCollection, flushSync } from './chunk-AYFIQGCF.js';
|
|
2
|
+
|
|
3
|
+
// src/build.ts
|
|
4
|
+
async function collectStylesFromModules(loaders) {
|
|
5
|
+
const endCollection = startCollection();
|
|
6
|
+
for (const load of loaders) {
|
|
7
|
+
await load();
|
|
8
|
+
}
|
|
9
|
+
flushSync();
|
|
10
|
+
return endCollection();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { collectStylesFromModules };
|
|
14
|
+
//# sourceMappingURL=build.js.map
|
|
15
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/build.ts"],"names":[],"mappings":";;;AAmBA,eAAsB,yBACpB,OAAA,EACiB;AACjB,EAAA,MAAM,gBAAgB,eAAA,EAAgB;AAEtC,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAK1B,IAAA,MAAM,IAAA,EAAK;AAAA,EACb;AAEA,EAAA,SAAA,EAAU;AACV,EAAA,OAAO,aAAA,EAAc;AACvB","file":"build.js","sourcesContent":["import { startCollection, flushSync } from './sheet.js';\n\n/**\n * Collect all CSS generated by executing one or more loader functions.\n *\n * This is a low-level primitive intended for build tools (Vite, Next.js, etc.)\n * to implement a zero-runtime extraction step.\n *\n * Example usage (Vite/Node):\n *\n * ```ts\n * import { collectStylesFromModules } from 'typestyles/build';\n *\n * const css = await collectStylesFromModules([\n * () => import('./src/styles'),\n * () => import('./src/tokens'),\n * ]);\n * ```\n */\nexport async function collectStylesFromModules(\n loaders: Array<() => unknown | Promise<unknown>>\n): Promise<string> {\n const endCollection = startCollection();\n\n for (const load of loaders) {\n // Each loader can synchronously or asynchronously import modules that\n // register typestyles styles, tokens, keyframes, etc.\n // We await in case a loader returns a promise (e.g. dynamic import).\n // The return value itself is ignored; only the side effects matter.\n await load();\n }\n\n flushSync();\n return endCollection();\n}\n\n"]}
|
|
@@ -7,6 +7,7 @@ var __export = (target, all) => {
|
|
|
7
7
|
// src/sheet.ts
|
|
8
8
|
var STYLE_ELEMENT_ID = "typestyles";
|
|
9
9
|
var insertedRules = /* @__PURE__ */ new Set();
|
|
10
|
+
var RUNTIME_DISABLED = typeof __TYPESTYLES_RUNTIME_DISABLED__ !== "undefined" && __TYPESTYLES_RUNTIME_DISABLED__ === "true";
|
|
10
11
|
var pendingRules = [];
|
|
11
12
|
var allRules = [];
|
|
12
13
|
var flushScheduled = false;
|
|
@@ -36,7 +37,7 @@ function flush() {
|
|
|
36
37
|
ssrBuffer.push(...rules);
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
|
-
if (!isBrowser) return;
|
|
40
|
+
if (!isBrowser || RUNTIME_DISABLED) return;
|
|
40
41
|
const el = getStyleElement();
|
|
41
42
|
const sheet = el.sheet;
|
|
42
43
|
if (sheet) {
|
|
@@ -58,15 +59,16 @@ function scheduleFlush() {
|
|
|
58
59
|
flush();
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
|
-
if (isBrowser) {
|
|
62
|
+
if (isBrowser && !RUNTIME_DISABLED) {
|
|
62
63
|
queueMicrotask(flush);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
function insertRule(key, css) {
|
|
66
67
|
if (insertedRules.has(key)) return;
|
|
67
68
|
insertedRules.add(key);
|
|
68
|
-
pendingRules.push(css);
|
|
69
69
|
allRules.push(css);
|
|
70
|
+
if (RUNTIME_DISABLED && !ssrBuffer) return;
|
|
71
|
+
pendingRules.push(css);
|
|
70
72
|
scheduleFlush();
|
|
71
73
|
}
|
|
72
74
|
function insertRules(rules) {
|
|
@@ -74,9 +76,11 @@ function insertRules(rules) {
|
|
|
74
76
|
for (const { key, css } of rules) {
|
|
75
77
|
if (insertedRules.has(key)) continue;
|
|
76
78
|
insertedRules.add(key);
|
|
77
|
-
pendingRules.push(css);
|
|
78
79
|
allRules.push(css);
|
|
79
|
-
|
|
80
|
+
if (!RUNTIME_DISABLED || ssrBuffer) {
|
|
81
|
+
pendingRules.push(css);
|
|
82
|
+
added = true;
|
|
83
|
+
}
|
|
80
84
|
}
|
|
81
85
|
if (added) scheduleFlush();
|
|
82
86
|
}
|
|
@@ -202,5 +206,5 @@ function ruleMatchesKey(cssText, key) {
|
|
|
202
206
|
}
|
|
203
207
|
|
|
204
208
|
export { __export, flushSync, getRegisteredCss, insertRule, insertRules, invalidateKeys, invalidatePrefix, reset, startCollection };
|
|
205
|
-
//# sourceMappingURL=chunk-
|
|
206
|
-
//# sourceMappingURL=chunk-
|
|
209
|
+
//# sourceMappingURL=chunk-AYFIQGCF.js.map
|
|
210
|
+
//# sourceMappingURL=chunk-AYFIQGCF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sheet.ts"],"names":[],"mappings":"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","file":"chunk-AYFIQGCF.js","sourcesContent":["const STYLE_ELEMENT_ID = 'typestyles';\n\n/**\n * Tracks which CSS rules have been inserted to avoid duplicates.\n */\nconst insertedRules = new Set<string>();\n\n/**\n * Whether runtime DOM insertion is disabled (for build-time/zero-runtime mode).\n * The Vite plugin (mode: 'build') defines __TYPESTYLES_RUNTIME_DISABLED__ as the\n * string \"true\" at build time, so this is true in production and no <style> is created.\n */\ndeclare const __TYPESTYLES_RUNTIME_DISABLED__: string | undefined;\nconst RUNTIME_DISABLED =\n typeof __TYPESTYLES_RUNTIME_DISABLED__ !== 'undefined' &&\n __TYPESTYLES_RUNTIME_DISABLED__ === 'true';\n\n/**\n * Buffer of CSS rules waiting to be flushed.\n */\nlet pendingRules: string[] = [];\n\n/**\n * All CSS rules ever registered (for SSR extraction).\n * Unlike pendingRules (which is cleared on flush), this retains every rule\n * so getRegisteredCss() can return the full stylesheet at any point.\n */\nconst allRules: string[] = [];\n\n/**\n * Whether a flush is scheduled.\n */\nlet flushScheduled = false;\n\n/**\n * The managed <style> element, lazily created.\n */\nlet styleElement: HTMLStyleElement | null = null;\n\n/**\n * When in SSR collection mode, CSS is captured here instead of injected.\n */\nlet ssrBuffer: string[] | null = null;\n\n/**\n * Whether we're running in a browser environment.\n */\nconst isBrowser =\n typeof document !== 'undefined' && typeof window !== 'undefined';\n\nfunction getStyleElement(): HTMLStyleElement {\n if (styleElement) return styleElement;\n\n // Check for an existing element (e.g., from SSR)\n const existing = document.getElementById(\n STYLE_ELEMENT_ID\n ) as HTMLStyleElement | null;\n if (existing) {\n styleElement = existing;\n return styleElement;\n }\n\n // Create a new one\n styleElement = document.createElement('style');\n styleElement.id = STYLE_ELEMENT_ID;\n document.head.appendChild(styleElement);\n return styleElement;\n}\n\nfunction flush(): void {\n flushScheduled = false;\n if (pendingRules.length === 0) return;\n\n const rules = pendingRules;\n pendingRules = [];\n\n if (ssrBuffer) {\n ssrBuffer.push(...rules);\n return;\n }\n\n if (!isBrowser || RUNTIME_DISABLED) return;\n\n const el = getStyleElement();\n const sheet = el.sheet;\n\n if (sheet) {\n for (const rule of rules) {\n try {\n sheet.insertRule(rule, sheet.cssRules.length);\n } catch {\n // Fallback: append as text (handles edge cases with certain selectors)\n el.appendChild(document.createTextNode(rule));\n }\n }\n } else {\n // Sheet not available yet, append as text\n el.appendChild(document.createTextNode(rules.join('\\n')));\n }\n}\n\nfunction scheduleFlush(): void {\n if (flushScheduled) return;\n flushScheduled = true;\n\n if (ssrBuffer) {\n // In SSR mode, flush synchronously\n flush();\n return;\n }\n\n if (isBrowser && !RUNTIME_DISABLED) {\n // Use microtask for fast, batched insertion\n queueMicrotask(flush);\n }\n}\n\n/**\n * Insert a CSS rule. Deduplicates by rule key.\n */\nexport function insertRule(key: string, css: string): void {\n if (insertedRules.has(key)) return;\n insertedRules.add(key);\n allRules.push(css);\n if (RUNTIME_DISABLED && !ssrBuffer) return;\n pendingRules.push(css);\n scheduleFlush();\n}\n\n/**\n * Insert multiple CSS rules at once.\n */\nexport function insertRules(rules: Array<{ key: string; css: string }>): void {\n let added = false;\n for (const { key, css } of rules) {\n if (insertedRules.has(key)) continue;\n insertedRules.add(key);\n allRules.push(css);\n if (!RUNTIME_DISABLED || ssrBuffer) {\n pendingRules.push(css);\n added = true;\n }\n }\n if (added) scheduleFlush();\n}\n\n/**\n * Replace a CSS rule (used for HMR). Removes the old rule and inserts the new one.\n */\nexport function replaceRule(key: string, css: string): void {\n if (!isBrowser || RUNTIME_DISABLED) return;\n\n insertedRules.delete(key);\n\n // Remove existing rule from the sheet if possible\n const el = getStyleElement();\n const sheet = el.sheet;\n if (sheet) {\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n // We can't reliably match by key in CSSOM, so for HMR we fall back to\n // clearing and re-inserting. This is fine since HMR is dev-only.\n }\n }\n\n insertRule(key, css);\n}\n\n/**\n * Start collecting CSS for SSR. Returns a function to stop collection and get the CSS.\n */\nexport function startCollection(): () => string {\n ssrBuffer = [];\n return () => {\n const css = ssrBuffer ? ssrBuffer.join('\\n') : '';\n ssrBuffer = null;\n return css;\n };\n}\n\n/**\n * Return all registered CSS as a string.\n *\n * Unlike `collectStyles`, this doesn't require wrapping a render function.\n * It simply returns every CSS rule that has been registered via\n * `styles.create`, `tokens.create`, `keyframes.create`, etc.\n *\n * Ideal for SSR frameworks that need the CSS separately from the render\n * pass (e.g. TanStack Start's `head()`, Next.js metadata, Remix links).\n *\n * @example\n * ```ts\n * import { getRegisteredCss } from 'typestyles/server';\n *\n * // In a route's head/meta function:\n * export const head = () => ({\n * styles: [{ id: 'typestyles', children: getRegisteredCss() }],\n * });\n * ```\n */\nexport function getRegisteredCss(): string {\n return allRules.join('\\n');\n}\n\n/**\n * Reset all state (useful for testing).\n */\nexport function reset(): void {\n insertedRules.clear();\n pendingRules = [];\n allRules.length = 0;\n flushScheduled = false;\n ssrBuffer = null;\n if (isBrowser && styleElement) {\n styleElement.remove();\n styleElement = null;\n }\n}\n\n/**\n * Flush all pending rules synchronously. Used for SSR and testing.\n */\nexport function flushSync(): void {\n flush();\n}\n\n/**\n * Invalidate all dedup keys that start with the given prefix.\n * Also removes matching rules from the live stylesheet.\n * Used for HMR — allows modules to re-register their styles after editing.\n */\nexport function invalidatePrefix(prefix: string): void {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n if (ruleMatchesPrefix(rule, prefix)) {\n sheet.deleteRule(i);\n }\n }\n}\n\n/**\n * Invalidate a list of exact keys or prefixes.\n * Each entry in `keys` is treated as an exact key match.\n * Each entry in `prefixes` is treated as a prefix match.\n * Used for HMR to invalidate all styles from a module at once.\n */\nexport function invalidateKeys(keys: string[], prefixes: string[]): void {\n for (const key of keys) {\n insertedRules.delete(key);\n }\n for (const prefix of prefixes) {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n const keySet = new Set(keys);\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n let shouldRemove = false;\n\n for (const prefix of prefixes) {\n if (ruleMatchesPrefix(rule, prefix)) {\n shouldRemove = true;\n break;\n }\n }\n\n if (!shouldRemove) {\n // Check exact key matches — for tokens/themes/keyframes,\n // we match based on rule content patterns\n const ruleText = rule.cssText;\n for (const key of keySet) {\n if (ruleMatchesKey(ruleText, key)) {\n shouldRemove = true;\n break;\n }\n }\n }\n\n if (shouldRemove) {\n sheet.deleteRule(i);\n }\n }\n}\n\nfunction ruleMatchesPrefix(rule: CSSRule, prefix: string): boolean {\n if (prefix.startsWith('font-face:')) {\n const family = prefix.slice('font-face:'.length).split(':')[0];\n // CSSFontFaceRule has type 5 and cssText contains @font-face\n if (rule.cssText.includes('@font-face')) {\n return (\n rule.cssText.includes(`\"${family}\"`) || rule.cssText.includes(`'${family}'`)\n );\n }\n return false;\n }\n if ('selectorText' in rule) {\n return (rule as CSSStyleRule).selectorText.startsWith(prefix);\n }\n if ('name' in rule && prefix.startsWith('keyframes:')) {\n return (rule as CSSKeyframesRule).name === prefix.slice('keyframes:'.length);\n }\n // For at-rules wrapping style rules, check inner rules\n if ('cssRules' in rule) {\n const innerRules = (rule as CSSGroupingRule).cssRules;\n for (let i = 0; i < innerRules.length; i++) {\n if (ruleMatchesPrefix(innerRules[i], prefix)) return true;\n }\n }\n return false;\n}\n\nfunction ruleMatchesKey(cssText: string, key: string): boolean {\n if (key.startsWith('tokens:')) {\n // tokens:color -> :root rule with --color- custom properties\n const namespace = key.slice('tokens:'.length);\n return cssText.includes(`:root`) && cssText.includes(`--${namespace}-`);\n }\n if (key.startsWith('theme:')) {\n // theme:dark -> .theme-dark selector\n const name = key.slice('theme:'.length);\n return cssText.includes(`.theme-${name}`);\n }\n if (key.startsWith('keyframes:')) {\n const name = key.slice('keyframes:'.length);\n return cssText.includes(`@keyframes ${name}`);\n }\n return false;\n}\n"]}
|
package/dist/hmr.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/sheet.ts
|
|
4
4
|
var insertedRules = /* @__PURE__ */ new Set();
|
|
5
|
+
typeof __TYPESTYLES_RUNTIME_DISABLED__ !== "undefined" && __TYPESTYLES_RUNTIME_DISABLED__ === "true";
|
|
5
6
|
var isBrowser = typeof document !== "undefined" && typeof window !== "undefined";
|
|
6
7
|
function invalidatePrefix(prefix) {
|
|
7
8
|
for (const key of insertedRules) {
|
package/dist/hmr.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sheet.ts"],"names":[],"mappings":";;;AAKA,IAAM,aAAA,uBAAoB,GAAA,EAAY;AAgCtC,IAAM,SAAA,GACJ,OAAO,QAAA,KAAa,WAAA,IAAe,OAAO,MAAA,KAAW,WAAA;AAmLhD,SAAS,iBAAiB,MAAA,EAAsB;AACrD,EAAA,KAAA,MAAW,OAAO,aAAA,EAAe;AAC/B,IAAA,IAAI,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AAC1B,MAAA,aAAA,CAAc,OAAO,GAAG,CAAA;AAAA,IAC1B;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,SAAA,EAAW;AAGhB,EAAS;AAUX;AAQO,SAAS,cAAA,CAAe,MAAgB,QAAA,EAA0B;AACvE,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,aAAA,CAAc,OAAO,GAAG,CAAA;AAAA,EAC1B;AACA,EAAA,KAAA,MAAW,UAAU,QAAA,EAAU;AAC7B,IAAA,KAAA,MAAW,OAAO,aAAA,EAAe;AAC/B,MAAA,IAAI,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AAC1B,QAAA,aAAA,CAAc,OAAO,GAAG,CAAA;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,SAAA,EAAW;AAGhB,EAAS;AAgCX","file":"hmr.cjs","sourcesContent":["const STYLE_ELEMENT_ID = 'typestyles';\n\n/**\n * Tracks which CSS rules have been inserted to avoid duplicates.\n */\nconst insertedRules = new Set<string>();\n\n/**\n * Buffer of CSS rules waiting to be flushed.\n */\nlet pendingRules: string[] = [];\n\n/**\n * All CSS rules ever registered (for SSR extraction).\n * Unlike pendingRules (which is cleared on flush), this retains every rule\n * so getRegisteredCss() can return the full stylesheet at any point.\n */\nconst allRules: string[] = [];\n\n/**\n * Whether a flush is scheduled.\n */\nlet flushScheduled = false;\n\n/**\n * The managed <style> element, lazily created.\n */\nlet styleElement: HTMLStyleElement | null = null;\n\n/**\n * When in SSR collection mode, CSS is captured here instead of injected.\n */\nlet ssrBuffer: string[] | null = null;\n\n/**\n * Whether we're running in a browser environment.\n */\nconst isBrowser =\n typeof document !== 'undefined' && typeof window !== 'undefined';\n\nfunction getStyleElement(): HTMLStyleElement {\n if (styleElement) return styleElement;\n\n // Check for an existing element (e.g., from SSR)\n const existing = document.getElementById(\n STYLE_ELEMENT_ID\n ) as HTMLStyleElement | null;\n if (existing) {\n styleElement = existing;\n return styleElement;\n }\n\n // Create a new one\n styleElement = document.createElement('style');\n styleElement.id = STYLE_ELEMENT_ID;\n document.head.appendChild(styleElement);\n return styleElement;\n}\n\nfunction flush(): void {\n flushScheduled = false;\n if (pendingRules.length === 0) return;\n\n const rules = pendingRules;\n pendingRules = [];\n\n if (ssrBuffer) {\n ssrBuffer.push(...rules);\n return;\n }\n\n if (!isBrowser) return;\n\n const el = getStyleElement();\n const sheet = el.sheet;\n\n if (sheet) {\n for (const rule of rules) {\n try {\n sheet.insertRule(rule, sheet.cssRules.length);\n } catch {\n // Fallback: append as text (handles edge cases with certain selectors)\n el.appendChild(document.createTextNode(rule));\n }\n }\n } else {\n // Sheet not available yet, append as text\n el.appendChild(document.createTextNode(rules.join('\\n')));\n }\n}\n\nfunction scheduleFlush(): void {\n if (flushScheduled) return;\n flushScheduled = true;\n\n if (ssrBuffer) {\n // In SSR mode, flush synchronously\n flush();\n return;\n }\n\n if (isBrowser) {\n // Use microtask for fast, batched insertion\n queueMicrotask(flush);\n }\n}\n\n/**\n * Insert a CSS rule. Deduplicates by rule key.\n */\nexport function insertRule(key: string, css: string): void {\n if (insertedRules.has(key)) return;\n insertedRules.add(key);\n pendingRules.push(css);\n allRules.push(css);\n scheduleFlush();\n}\n\n/**\n * Insert multiple CSS rules at once.\n */\nexport function insertRules(rules: Array<{ key: string; css: string }>): void {\n let added = false;\n for (const { key, css } of rules) {\n if (insertedRules.has(key)) continue;\n insertedRules.add(key);\n pendingRules.push(css);\n allRules.push(css);\n added = true;\n }\n if (added) scheduleFlush();\n}\n\n/**\n * Replace a CSS rule (used for HMR). Removes the old rule and inserts the new one.\n */\nexport function replaceRule(key: string, css: string): void {\n if (!isBrowser) return;\n\n insertedRules.delete(key);\n\n // Remove existing rule from the sheet if possible\n const el = getStyleElement();\n const sheet = el.sheet;\n if (sheet) {\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n // We can't reliably match by key in CSSOM, so for HMR we fall back to\n // clearing and re-inserting. This is fine since HMR is dev-only.\n }\n }\n\n insertRule(key, css);\n}\n\n/**\n * Start collecting CSS for SSR. Returns a function to stop collection and get the CSS.\n */\nexport function startCollection(): () => string {\n ssrBuffer = [];\n return () => {\n const css = ssrBuffer ? ssrBuffer.join('\\n') : '';\n ssrBuffer = null;\n return css;\n };\n}\n\n/**\n * Return all registered CSS as a string.\n *\n * Unlike `collectStyles`, this doesn't require wrapping a render function.\n * It simply returns every CSS rule that has been registered via\n * `styles.create`, `tokens.create`, `keyframes.create`, etc.\n *\n * Ideal for SSR frameworks that need the CSS separately from the render\n * pass (e.g. TanStack Start's `head()`, Next.js metadata, Remix links).\n *\n * @example\n * ```ts\n * import { getRegisteredCss } from 'typestyles/server';\n *\n * // In a route's head/meta function:\n * export const head = () => ({\n * styles: [{ id: 'typestyles', children: getRegisteredCss() }],\n * });\n * ```\n */\nexport function getRegisteredCss(): string {\n return allRules.join('\\n');\n}\n\n/**\n * Reset all state (useful for testing).\n */\nexport function reset(): void {\n insertedRules.clear();\n pendingRules = [];\n allRules.length = 0;\n flushScheduled = false;\n ssrBuffer = null;\n if (isBrowser && styleElement) {\n styleElement.remove();\n styleElement = null;\n }\n}\n\n/**\n * Flush all pending rules synchronously. Used for SSR and testing.\n */\nexport function flushSync(): void {\n flush();\n}\n\n/**\n * Invalidate all dedup keys that start with the given prefix.\n * Also removes matching rules from the live stylesheet.\n * Used for HMR — allows modules to re-register their styles after editing.\n */\nexport function invalidatePrefix(prefix: string): void {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n if (ruleMatchesPrefix(rule, prefix)) {\n sheet.deleteRule(i);\n }\n }\n}\n\n/**\n * Invalidate a list of exact keys or prefixes.\n * Each entry in `keys` is treated as an exact key match.\n * Each entry in `prefixes` is treated as a prefix match.\n * Used for HMR to invalidate all styles from a module at once.\n */\nexport function invalidateKeys(keys: string[], prefixes: string[]): void {\n for (const key of keys) {\n insertedRules.delete(key);\n }\n for (const prefix of prefixes) {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n const keySet = new Set(keys);\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n let shouldRemove = false;\n\n for (const prefix of prefixes) {\n if (ruleMatchesPrefix(rule, prefix)) {\n shouldRemove = true;\n break;\n }\n }\n\n if (!shouldRemove) {\n // Check exact key matches — for tokens/themes/keyframes,\n // we match based on rule content patterns\n const ruleText = rule.cssText;\n for (const key of keySet) {\n if (ruleMatchesKey(ruleText, key)) {\n shouldRemove = true;\n break;\n }\n }\n }\n\n if (shouldRemove) {\n sheet.deleteRule(i);\n }\n }\n}\n\nfunction ruleMatchesPrefix(rule: CSSRule, prefix: string): boolean {\n if (prefix.startsWith('font-face:')) {\n const family = prefix.slice('font-face:'.length).split(':')[0];\n // CSSFontFaceRule has type 5 and cssText contains @font-face\n if (rule.cssText.includes('@font-face')) {\n return (\n rule.cssText.includes(`\"${family}\"`) || rule.cssText.includes(`'${family}'`)\n );\n }\n return false;\n }\n if ('selectorText' in rule) {\n return (rule as CSSStyleRule).selectorText.startsWith(prefix);\n }\n if ('name' in rule && prefix.startsWith('keyframes:')) {\n return (rule as CSSKeyframesRule).name === prefix.slice('keyframes:'.length);\n }\n // For at-rules wrapping style rules, check inner rules\n if ('cssRules' in rule) {\n const innerRules = (rule as CSSGroupingRule).cssRules;\n for (let i = 0; i < innerRules.length; i++) {\n if (ruleMatchesPrefix(innerRules[i], prefix)) return true;\n }\n }\n return false;\n}\n\nfunction ruleMatchesKey(cssText: string, key: string): boolean {\n if (key.startsWith('tokens:')) {\n // tokens:color -> :root rule with --color- custom properties\n const namespace = key.slice('tokens:'.length);\n return cssText.includes(`:root`) && cssText.includes(`--${namespace}-`);\n }\n if (key.startsWith('theme:')) {\n // theme:dark -> .theme-dark selector\n const name = key.slice('theme:'.length);\n return cssText.includes(`.theme-${name}`);\n }\n if (key.startsWith('keyframes:')) {\n const name = key.slice('keyframes:'.length);\n return cssText.includes(`@keyframes ${name}`);\n }\n return false;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/sheet.ts"],"names":[],"mappings":";;;AAKA,IAAM,aAAA,uBAAoB,GAAA,EAAY;AASpC,OAAO,+BAAA,KAAoC,WAAA,IAC3C,+BAAA,KAAoC;AAgCtC,IAAM,SAAA,GACJ,OAAO,QAAA,KAAa,WAAA,IAAe,OAAO,MAAA,KAAW,WAAA;AAsLhD,SAAS,iBAAiB,MAAA,EAAsB;AACrD,EAAA,KAAA,MAAW,OAAO,aAAA,EAAe;AAC/B,IAAA,IAAI,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AAC1B,MAAA,aAAA,CAAc,OAAO,GAAG,CAAA;AAAA,IAC1B;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,SAAA,EAAW;AAGhB,EAAS;AAUX;AAQO,SAAS,cAAA,CAAe,MAAgB,QAAA,EAA0B;AACvE,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,aAAA,CAAc,OAAO,GAAG,CAAA;AAAA,EAC1B;AACA,EAAA,KAAA,MAAW,UAAU,QAAA,EAAU;AAC7B,IAAA,KAAA,MAAW,OAAO,aAAA,EAAe;AAC/B,MAAA,IAAI,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AAC1B,QAAA,aAAA,CAAc,OAAO,GAAG,CAAA;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,SAAA,EAAW;AAGhB,EAAS;AAgCX","file":"hmr.cjs","sourcesContent":["const STYLE_ELEMENT_ID = 'typestyles';\n\n/**\n * Tracks which CSS rules have been inserted to avoid duplicates.\n */\nconst insertedRules = new Set<string>();\n\n/**\n * Whether runtime DOM insertion is disabled (for build-time/zero-runtime mode).\n * The Vite plugin (mode: 'build') defines __TYPESTYLES_RUNTIME_DISABLED__ as the\n * string \"true\" at build time, so this is true in production and no <style> is created.\n */\ndeclare const __TYPESTYLES_RUNTIME_DISABLED__: string | undefined;\nconst RUNTIME_DISABLED =\n typeof __TYPESTYLES_RUNTIME_DISABLED__ !== 'undefined' &&\n __TYPESTYLES_RUNTIME_DISABLED__ === 'true';\n\n/**\n * Buffer of CSS rules waiting to be flushed.\n */\nlet pendingRules: string[] = [];\n\n/**\n * All CSS rules ever registered (for SSR extraction).\n * Unlike pendingRules (which is cleared on flush), this retains every rule\n * so getRegisteredCss() can return the full stylesheet at any point.\n */\nconst allRules: string[] = [];\n\n/**\n * Whether a flush is scheduled.\n */\nlet flushScheduled = false;\n\n/**\n * The managed <style> element, lazily created.\n */\nlet styleElement: HTMLStyleElement | null = null;\n\n/**\n * When in SSR collection mode, CSS is captured here instead of injected.\n */\nlet ssrBuffer: string[] | null = null;\n\n/**\n * Whether we're running in a browser environment.\n */\nconst isBrowser =\n typeof document !== 'undefined' && typeof window !== 'undefined';\n\nfunction getStyleElement(): HTMLStyleElement {\n if (styleElement) return styleElement;\n\n // Check for an existing element (e.g., from SSR)\n const existing = document.getElementById(\n STYLE_ELEMENT_ID\n ) as HTMLStyleElement | null;\n if (existing) {\n styleElement = existing;\n return styleElement;\n }\n\n // Create a new one\n styleElement = document.createElement('style');\n styleElement.id = STYLE_ELEMENT_ID;\n document.head.appendChild(styleElement);\n return styleElement;\n}\n\nfunction flush(): void {\n flushScheduled = false;\n if (pendingRules.length === 0) return;\n\n const rules = pendingRules;\n pendingRules = [];\n\n if (ssrBuffer) {\n ssrBuffer.push(...rules);\n return;\n }\n\n if (!isBrowser || RUNTIME_DISABLED) return;\n\n const el = getStyleElement();\n const sheet = el.sheet;\n\n if (sheet) {\n for (const rule of rules) {\n try {\n sheet.insertRule(rule, sheet.cssRules.length);\n } catch {\n // Fallback: append as text (handles edge cases with certain selectors)\n el.appendChild(document.createTextNode(rule));\n }\n }\n } else {\n // Sheet not available yet, append as text\n el.appendChild(document.createTextNode(rules.join('\\n')));\n }\n}\n\nfunction scheduleFlush(): void {\n if (flushScheduled) return;\n flushScheduled = true;\n\n if (ssrBuffer) {\n // In SSR mode, flush synchronously\n flush();\n return;\n }\n\n if (isBrowser && !RUNTIME_DISABLED) {\n // Use microtask for fast, batched insertion\n queueMicrotask(flush);\n }\n}\n\n/**\n * Insert a CSS rule. Deduplicates by rule key.\n */\nexport function insertRule(key: string, css: string): void {\n if (insertedRules.has(key)) return;\n insertedRules.add(key);\n allRules.push(css);\n if (RUNTIME_DISABLED && !ssrBuffer) return;\n pendingRules.push(css);\n scheduleFlush();\n}\n\n/**\n * Insert multiple CSS rules at once.\n */\nexport function insertRules(rules: Array<{ key: string; css: string }>): void {\n let added = false;\n for (const { key, css } of rules) {\n if (insertedRules.has(key)) continue;\n insertedRules.add(key);\n allRules.push(css);\n if (!RUNTIME_DISABLED || ssrBuffer) {\n pendingRules.push(css);\n added = true;\n }\n }\n if (added) scheduleFlush();\n}\n\n/**\n * Replace a CSS rule (used for HMR). Removes the old rule and inserts the new one.\n */\nexport function replaceRule(key: string, css: string): void {\n if (!isBrowser || RUNTIME_DISABLED) return;\n\n insertedRules.delete(key);\n\n // Remove existing rule from the sheet if possible\n const el = getStyleElement();\n const sheet = el.sheet;\n if (sheet) {\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n // We can't reliably match by key in CSSOM, so for HMR we fall back to\n // clearing and re-inserting. This is fine since HMR is dev-only.\n }\n }\n\n insertRule(key, css);\n}\n\n/**\n * Start collecting CSS for SSR. Returns a function to stop collection and get the CSS.\n */\nexport function startCollection(): () => string {\n ssrBuffer = [];\n return () => {\n const css = ssrBuffer ? ssrBuffer.join('\\n') : '';\n ssrBuffer = null;\n return css;\n };\n}\n\n/**\n * Return all registered CSS as a string.\n *\n * Unlike `collectStyles`, this doesn't require wrapping a render function.\n * It simply returns every CSS rule that has been registered via\n * `styles.create`, `tokens.create`, `keyframes.create`, etc.\n *\n * Ideal for SSR frameworks that need the CSS separately from the render\n * pass (e.g. TanStack Start's `head()`, Next.js metadata, Remix links).\n *\n * @example\n * ```ts\n * import { getRegisteredCss } from 'typestyles/server';\n *\n * // In a route's head/meta function:\n * export const head = () => ({\n * styles: [{ id: 'typestyles', children: getRegisteredCss() }],\n * });\n * ```\n */\nexport function getRegisteredCss(): string {\n return allRules.join('\\n');\n}\n\n/**\n * Reset all state (useful for testing).\n */\nexport function reset(): void {\n insertedRules.clear();\n pendingRules = [];\n allRules.length = 0;\n flushScheduled = false;\n ssrBuffer = null;\n if (isBrowser && styleElement) {\n styleElement.remove();\n styleElement = null;\n }\n}\n\n/**\n * Flush all pending rules synchronously. Used for SSR and testing.\n */\nexport function flushSync(): void {\n flush();\n}\n\n/**\n * Invalidate all dedup keys that start with the given prefix.\n * Also removes matching rules from the live stylesheet.\n * Used for HMR — allows modules to re-register their styles after editing.\n */\nexport function invalidatePrefix(prefix: string): void {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n if (ruleMatchesPrefix(rule, prefix)) {\n sheet.deleteRule(i);\n }\n }\n}\n\n/**\n * Invalidate a list of exact keys or prefixes.\n * Each entry in `keys` is treated as an exact key match.\n * Each entry in `prefixes` is treated as a prefix match.\n * Used for HMR to invalidate all styles from a module at once.\n */\nexport function invalidateKeys(keys: string[], prefixes: string[]): void {\n for (const key of keys) {\n insertedRules.delete(key);\n }\n for (const prefix of prefixes) {\n for (const key of insertedRules) {\n if (key.startsWith(prefix)) {\n insertedRules.delete(key);\n }\n }\n }\n\n if (!isBrowser) return;\n\n const el = styleElement;\n if (!el) return;\n const sheet = el.sheet;\n if (!sheet) return;\n\n const keySet = new Set(keys);\n for (let i = sheet.cssRules.length - 1; i >= 0; i--) {\n const rule = sheet.cssRules[i];\n let shouldRemove = false;\n\n for (const prefix of prefixes) {\n if (ruleMatchesPrefix(rule, prefix)) {\n shouldRemove = true;\n break;\n }\n }\n\n if (!shouldRemove) {\n // Check exact key matches — for tokens/themes/keyframes,\n // we match based on rule content patterns\n const ruleText = rule.cssText;\n for (const key of keySet) {\n if (ruleMatchesKey(ruleText, key)) {\n shouldRemove = true;\n break;\n }\n }\n }\n\n if (shouldRemove) {\n sheet.deleteRule(i);\n }\n }\n}\n\nfunction ruleMatchesPrefix(rule: CSSRule, prefix: string): boolean {\n if (prefix.startsWith('font-face:')) {\n const family = prefix.slice('font-face:'.length).split(':')[0];\n // CSSFontFaceRule has type 5 and cssText contains @font-face\n if (rule.cssText.includes('@font-face')) {\n return (\n rule.cssText.includes(`\"${family}\"`) || rule.cssText.includes(`'${family}'`)\n );\n }\n return false;\n }\n if ('selectorText' in rule) {\n return (rule as CSSStyleRule).selectorText.startsWith(prefix);\n }\n if ('name' in rule && prefix.startsWith('keyframes:')) {\n return (rule as CSSKeyframesRule).name === prefix.slice('keyframes:'.length);\n }\n // For at-rules wrapping style rules, check inner rules\n if ('cssRules' in rule) {\n const innerRules = (rule as CSSGroupingRule).cssRules;\n for (let i = 0; i < innerRules.length; i++) {\n if (ruleMatchesPrefix(innerRules[i], prefix)) return true;\n }\n }\n return false;\n}\n\nfunction ruleMatchesKey(cssText: string, key: string): boolean {\n if (key.startsWith('tokens:')) {\n // tokens:color -> :root rule with --color- custom properties\n const namespace = key.slice('tokens:'.length);\n return cssText.includes(`:root`) && cssText.includes(`--${namespace}-`);\n }\n if (key.startsWith('theme:')) {\n // theme:dark -> .theme-dark selector\n const name = key.slice('theme:'.length);\n return cssText.includes(`.theme-${name}`);\n }\n if (key.startsWith('keyframes:')) {\n const name = key.slice('keyframes:'.length);\n return cssText.includes(`@keyframes ${name}`);\n }\n return false;\n}\n"]}
|
package/dist/hmr.js
CHANGED