tss-react
Version:
Type safe CSS-in-JS API heavily inspired by react-jss
265 lines (264 loc) • 14.5 kB
JavaScript
;
/* eslint-disable @typescript-eslint/ban-types */
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTss = void 0;
const makeStyles_1 = require("./makeStyles");
const cssAndCx_1 = require("./cssAndCx");
const assert_1 = require("./tools/assert");
const Object_fromEntries_1 = require("./tools/polyfills/Object.fromEntries");
const objectKeys_1 = require("./tools/objectKeys");
const typeGuard_1 = require("./tools/typeGuard");
const getDependencyArrayRef_1 = require("./tools/getDependencyArrayRef");
const mergeClasses_1 = require("./mergeClasses");
const isSSR_1 = require("./tools/isSSR");
function createTss(params) {
counter = 0;
nestedSelectorUsageTrackRecord.splice(0, nestedSelectorUsageTrackRecord.length);
const { useContext, usePlugin, cache: cacheProvidedAtInception } = params;
const { useCache } = (0, makeStyles_1.createUseCache)({ cacheProvidedAtInception });
const { useCssAndCx } = (0, cssAndCx_1.createUseCssAndCx)({ useCache });
const usePluginDefault = ({ classes, cx, css }) => ({ classes, cx, css });
const tss = createTss_internal({
useContext,
useCache,
useCssAndCx,
"usePlugin": usePlugin !== null && usePlugin !== void 0 ? usePlugin : usePluginDefault,
"name": undefined,
"doesUseNestedSelectors": false
});
return { tss };
}
exports.createTss = createTss;
let counter = 0;
const nestedSelectorUsageTrackRecord = [];
function createTss_internal(params) {
const { useContext, useCache, useCssAndCx, usePlugin, name, doesUseNestedSelectors } = params;
return {
"withParams": () => createTss_internal(Object.assign({}, params)),
"withName": nameOrWrappedName => createTss_internal(Object.assign(Object.assign({}, params), { "name": typeof nameOrWrappedName !== "object"
? nameOrWrappedName
: Object.keys(nameOrWrappedName)[0] })),
"withNestedSelectors": () => createTss_internal(Object.assign(Object.assign({}, params), { "doesUseNestedSelectors": true })),
"create": (cssObjectByRuleNameOrGetCssObjectByRuleName) => {
// NOTE: Not isomorphic. Not guaranteed to be the same on client and server.
// Do not attempt to 'simplify' the code without taking this fact into account.
const idOfUseStyles = `x${counter++}`;
// NOTE: Cleanup for hot module reloading.
if (name !== undefined) {
// eslint-disable-next-line no-constant-condition
while (true) {
const wrap = nestedSelectorUsageTrackRecord.find(wrap => wrap.name === name);
if (wrap === undefined) {
break;
}
nestedSelectorUsageTrackRecord.splice(nestedSelectorUsageTrackRecord.indexOf(wrap), 1);
}
}
const getCssObjectByRuleName = typeof cssObjectByRuleNameOrGetCssObjectByRuleName ===
"function"
? cssObjectByRuleNameOrGetCssObjectByRuleName
: () => cssObjectByRuleNameOrGetCssObjectByRuleName;
return function useStyles(params) {
var _a, _b, _c;
const _d = (params !== null && params !== void 0 ? params : {}), { classesOverrides } = _d, paramsAndPluginParams = __rest(_d, ["classesOverrides"]);
const context = useContext();
const { css, cx } = useCssAndCx();
const cache = useCache();
const getClasses = () => {
const refClassesCache = {};
// @ts-expect-error: Type safety non achievable.
const cssObjectByRuleName = getCssObjectByRuleName(Object.assign(Object.assign(Object.assign({}, params), context), (!doesUseNestedSelectors
? {}
: {
"classes": typeof Proxy === "undefined"
? {}
: new Proxy({}, {
"get": (_target, ruleName) => {
/* prettier-ignore */
if (typeof ruleName === "symbol") {
(0, assert_1.assert)(false);
}
if (isSSR_1.isSSR &&
name === undefined) {
throw new Error([
`tss-react: In SSR setups, in order to use nested selectors, you must also give a unique name to the useStyle function.`,
`Solution: Use tss.withName("ComponentName").withNestedSelectors<...>()... to set a name.`
].join("\n"));
}
update_nested_selector_usage_track_record: {
if (name === undefined) {
break update_nested_selector_usage_track_record;
}
/* prettier-ignore */
let wrap = nestedSelectorUsageTrackRecord.find(wrap => wrap.name === name && wrap.idOfUseStyles === idOfUseStyles);
/* prettier-ignore */
if (wrap === undefined) {
/* prettier-ignore */
wrap = { name, idOfUseStyles, "nestedSelectorRuleNames": new Set() };
/* prettier-ignore */
nestedSelectorUsageTrackRecord.push(wrap);
}
/* prettier-ignore */
wrap.nestedSelectorRuleNames.add(ruleName);
}
detect_potential_conflicts: {
if (name === undefined) {
break detect_potential_conflicts;
}
const hasPotentialConflict = nestedSelectorUsageTrackRecord.find(wrap => wrap.name ===
name &&
wrap.idOfUseStyles !==
idOfUseStyles &&
wrap.nestedSelectorRuleNames.has(ruleName)) !== undefined;
if (!hasPotentialConflict) {
break detect_potential_conflicts;
}
throw new Error([
`tss-react: There are in your codebase two different useStyles named "${name}" that`,
`both use use the nested selector ${ruleName}.\n`,
`This may lead to CSS class name collisions, causing nested selectors to target elements outside of the intended scope.\n`,
`Solution: Ensure each useStyles using nested selectors has a unique name.\n`,
`Use: tss.withName("UniqueName").withNestedSelectors<...>()...`
].join(" "));
}
/* prettier-ignore */
return (refClassesCache[ruleName] = `${cache.key}-${name !== undefined ? name : idOfUseStyles}-${ruleName}-ref`);
}
})
})));
let classes = (0, Object_fromEntries_1.objectFromEntries)((0, objectKeys_1.objectKeys)(cssObjectByRuleName).map(ruleName => {
const cssObject = cssObjectByRuleName[ruleName];
if (!cssObject.label) {
cssObject.label = `${name !== undefined ? `${name}-` : ""}${ruleName}`;
}
return [
ruleName,
`${css(cssObject)}${(0, typeGuard_1.typeGuard)(ruleName, ruleName in refClassesCache)
? ` ${refClassesCache[ruleName]}`
: ""}`
];
}));
(0, objectKeys_1.objectKeys)(refClassesCache).forEach(ruleName => {
if (ruleName in classes) {
return;
}
classes[ruleName] =
refClassesCache[ruleName];
});
classes = (0, mergeClasses_1.mergeClasses)(classes, classesOverrides, cx);
return classes;
};
const classes = runGetClassesOrUseCache({
cache,
cssObjectByRuleNameOrGetCssObjectByRuleName,
"classesOverridesRef": (0, getDependencyArrayRef_1.getDependencyArrayRef)(classesOverrides),
"paramsAndPluginParamsRef": (0, getDependencyArrayRef_1.getDependencyArrayRef)(paramsAndPluginParams),
idOfUseStyles,
context,
getClasses
});
// @ts-expect-error: Type safety non achievable.
const pluginResultWrap = usePlugin(Object.assign(Object.assign({ classes,
css,
cx,
idOfUseStyles,
name }, context), paramsAndPluginParams));
return Object.assign({ "classes": (_a = pluginResultWrap.classes) !== null && _a !== void 0 ? _a : classes, "css": (_b = pluginResultWrap.css) !== null && _b !== void 0 ? _b : css, "cx": (_c = pluginResultWrap.cx) !== null && _c !== void 0 ? _c : cx }, context);
};
}
};
}
const mapCache = new WeakMap();
function runGetClassesOrUseCache(params) {
const { cache, cssObjectByRuleNameOrGetCssObjectByRuleName, classesOverridesRef, paramsAndPluginParamsRef, idOfUseStyles, context, getClasses } = params;
use_cache: {
const mapCache_in = mapCache.get(cache);
if (mapCache_in === undefined) {
break use_cache;
}
const mapCache_in_in = mapCache_in.get(cssObjectByRuleNameOrGetCssObjectByRuleName);
if (mapCache_in_in === undefined) {
break use_cache;
}
const mapCache_in_in_in = mapCache_in_in.get(classesOverridesRef);
if (mapCache_in_in_in === undefined) {
break use_cache;
}
const arr = mapCache_in_in_in.get(paramsAndPluginParamsRef);
if (arr === undefined) {
break use_cache;
}
const entry = arr.find(({ context: context_i }) => {
if (context_i === context) {
return true;
}
if ((0, objectKeys_1.objectKeys)(context_i).length !== (0, objectKeys_1.objectKeys)(context).length) {
return false;
}
for (const key in context_i) {
if ((0, getDependencyArrayRef_1.getDependencyArrayRef)(context_i[key]) !==
(0, getDependencyArrayRef_1.getDependencyArrayRef)(context[key])) {
return false;
}
}
return true;
});
if (entry === undefined) {
break use_cache;
}
if ((entry === null || entry === void 0 ? void 0 : entry.idOfUseStyles) !== idOfUseStyles) {
arr.splice(arr.indexOf(entry), 1);
break use_cache;
}
return entry.result;
}
const result = getClasses();
{
if (!mapCache.has(cache)) {
mapCache.set(cache, new WeakMap());
}
const mapCache_in = mapCache.get(cache);
(0, assert_1.assert)(mapCache_in !== undefined);
if (!mapCache_in.has(cssObjectByRuleNameOrGetCssObjectByRuleName)) {
mapCache_in.set(cssObjectByRuleNameOrGetCssObjectByRuleName, new Map());
}
const mapCache_in_in = mapCache_in.get(cssObjectByRuleNameOrGetCssObjectByRuleName);
(0, assert_1.assert)(mapCache_in_in !== undefined);
if (!mapCache_in_in.has(classesOverridesRef)) {
if (mapCache_in_in.size > 200) {
mapCache_in_in.clear();
}
mapCache_in_in.set(classesOverridesRef, new Map());
}
const mapCache_in_in_in = mapCache_in_in.get(classesOverridesRef);
(0, assert_1.assert)(mapCache_in_in_in !== undefined);
if (!mapCache_in_in_in.has(paramsAndPluginParamsRef)) {
clear_cache: {
const threshold = typeof paramsAndPluginParamsRef === "string" ? 257 : 5;
if (mapCache_in_in_in.size < threshold) {
break clear_cache;
}
mapCache_in_in_in.clear();
}
mapCache_in_in_in.set(paramsAndPluginParamsRef, []);
}
let arr = mapCache_in_in_in.get(paramsAndPluginParamsRef);
(0, assert_1.assert)(arr !== undefined);
if (arr.length > 5) {
arr = [];
}
arr.push({ idOfUseStyles, context, result });
}
return result;
}