twrnc
Version:
simple, expressive API for tailwindcss + react-native
232 lines (231 loc) • 9.89 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.create = void 0;
const resolveConfig_1 = __importDefault(require("tailwindcss/resolveConfig"));
const cache_1 = __importDefault(require("./cache"));
const UtilityParser_1 = __importDefault(require("./UtilityParser"));
const color_1 = require("./resolve/color");
const parse_inputs_1 = require("./parse-inputs");
const helpers_1 = require("./helpers");
const plugin_1 = require("./plugin");
function create(customConfig, platform) {
const config = (0, resolveConfig_1.default)(withContent(customConfig));
const device = {};
const pluginUtils = (0, plugin_1.getAddedUtilities)(config.plugins);
const customStringUtils = {};
const customStyleUtils = Object.entries(pluginUtils)
.map(([rawUtil, style]) => {
const util = rawUtil.replace(/^\./, ``);
if (typeof style === `string`) {
// sacrifice functional purity to only iterate once
customStringUtils[util] = style;
return [util, { kind: `null` }];
}
return [util, (0, helpers_1.complete)(style)];
})
.filter(([, ir]) => ir.kind !== `null`);
patchCustomFontUtils(customConfig, customStyleUtils, config);
function deriveCacheGroup() {
return ([
device.colorScheme === `dark` ? `dark` : false,
device.windowDimensions ? `w${device.windowDimensions.width}` : false,
device.windowDimensions ? `h${device.windowDimensions.height}` : false,
device.fontScale ? `fs${device.fontScale}` : false,
device.pixelDensity === 2 ? `retina` : false,
]
.filter(Boolean)
.join(`--`) || `default`);
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const tailwindFn = (strings, ...values) => {
let str = ``;
strings.forEach((string, i) => {
var _a;
str += string + ((_a = values[i]) !== null && _a !== void 0 ? _a : ``);
});
return style(str);
};
const contextCaches = {};
let cache = new cache_1.default();
tailwindFn.memoBuster = ``;
configureCache();
function configureCache() {
const cacheGroup = deriveCacheGroup();
tailwindFn.memoBuster = `twrnc-memobuster-key--${cacheGroup}`;
const existing = contextCaches[cacheGroup];
if (existing) {
cache = existing;
return;
}
const newCache = new cache_1.default(customStyleUtils);
contextCaches[cacheGroup] = newCache;
// set custom string utils into cache, so they are resolvable at all breakpoints
for (const [key, value] of Object.entries(customStringUtils)) {
newCache.setIr(key, (0, helpers_1.complete)(style(value)));
}
cache = newCache;
}
function style(...inputs) {
let resolved = {};
const dependents = [];
const ordered = [];
const [utilities, userStyle] = (0, parse_inputs_1.parseInputs)(inputs);
// check if we've seen this full set of classes before
// if we have a cached copy, we can skip examining each utility
const joined = utilities.join(` `);
const cached = cache.getStyle(joined);
if (cached) {
return userStyle ? { ...cached, ...userStyle } : cached;
}
for (const utility of utilities) {
let styleIr = cache.getIr(utility);
if (!styleIr) {
const parser = new UtilityParser_1.default(utility, config, cache, device, platform);
styleIr = parser.parse();
}
switch (styleIr.kind) {
case `complete`:
resolved = { ...resolved, ...styleIr.style };
cache.setIr(utility, styleIr);
break;
case `dependent`:
dependents.push(styleIr);
break;
case `ordered`:
ordered.push(styleIr);
break;
case `null`:
cache.setIr(utility, styleIr);
break;
}
}
if (ordered.length > 0) {
ordered.sort((a, b) => a.order - b.order);
for (const orderedStyle of ordered) {
switch (orderedStyle.styleIr.kind) {
case `complete`:
resolved = { ...resolved, ...orderedStyle.styleIr.style };
break;
case `dependent`:
dependents.push(orderedStyle.styleIr);
break;
}
}
}
if (dependents.length > 0) {
for (const dependent of dependents) {
const error = dependent.complete(resolved);
if (error) {
(0, helpers_1.warn)(error);
}
}
(0, color_1.removeOpacityHelpers)(resolved);
}
// cache the full set of classes for future re-renders
// it's important we cache BEFORE merging in userStyle below
if (joined !== ``) {
cache.setStyle(joined, resolved);
}
if (userStyle) {
resolved = { ...resolved, ...userStyle };
}
return resolved;
}
function color(utils) {
var _a, _b;
const styleObj = style(utils
.split(/\s+/g)
.map((util) => util.replace(/^(bg|text|border)-/, ``))
.map((util) => `bg-${util}`)
.join(` `));
if (typeof styleObj.backgroundColor === `string`) {
return styleObj.backgroundColor;
}
else if ((_a = config.theme) === null || _a === void 0 ? void 0 : _a.colors) {
return (_b = (0, color_1.configColor)(utils, config.theme.colors)) !== null && _b !== void 0 ? _b : undefined;
}
else {
return undefined;
}
}
tailwindFn.style = style;
tailwindFn.color = color;
tailwindFn.prefixMatch = (...prefixes) => {
const joined = prefixes.sort().join(`:`);
const cached = cache.getPrefixMatch(joined);
if (cached !== undefined) {
return cached;
}
const parser = new UtilityParser_1.default(`${joined}:flex`, config, cache, device, platform);
const ir = parser.parse();
const prefixMatches = ir.kind !== `null`;
cache.setPrefixMatch(joined, prefixMatches);
return prefixMatches;
};
tailwindFn.setWindowDimensions = (newDimensions) => {
device.windowDimensions = newDimensions;
configureCache();
};
tailwindFn.setFontScale = (newFontScale) => {
device.fontScale = newFontScale;
configureCache();
};
tailwindFn.setPixelDensity = (newPixelDensity) => {
device.pixelDensity = newPixelDensity;
configureCache();
};
tailwindFn.setColorScheme = (newColorScheme) => {
device.colorScheme = newColorScheme;
configureCache();
};
tailwindFn.getColorScheme = () => device.colorScheme;
tailwindFn.updateDeviceContext = (window, fontScale, pixelDensity, colorScheme) => {
device.windowDimensions = window;
device.fontScale = fontScale;
device.pixelDensity = pixelDensity;
if (colorScheme !== `skip`) {
device.colorScheme = colorScheme;
}
configureCache();
};
return tailwindFn;
}
exports.create = create;
exports.default = create;
function withContent(config) {
return {
...config,
// prevent warnings from tailwind about not having a `content` prop
// we don't need one because we have our own jit parser which
// does not rely on knowing content paths to search
content: [`_no_warnings_please`],
};
}
// Allow override default font-<name> style
// @TODO: long-term, i'd like to think of a more generic way to allow
// custom configurations not to get masked by default utilities...
function patchCustomFontUtils(customConfig, customStyleUtils, config) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
if (((_a = customConfig.theme) === null || _a === void 0 ? void 0 : _a.fontWeight) || ((_c = (_b = customConfig.theme) === null || _b === void 0 ? void 0 : _b.extend) === null || _c === void 0 ? void 0 : _c.fontWeight)) {
[
...Object.entries((_e = (_d = customConfig.theme) === null || _d === void 0 ? void 0 : _d.fontWeight) !== null && _e !== void 0 ? _e : {}),
...Object.entries((_h = (_g = (_f = customConfig.theme) === null || _f === void 0 ? void 0 : _f.extend) === null || _g === void 0 ? void 0 : _g.fontWeight) !== null && _h !== void 0 ? _h : {}),
].forEach(([name, value]) => {
customStyleUtils.push([`font-${name}`, (0, helpers_1.complete)({ fontWeight: String(value) })]);
});
}
if (`object` === typeof ((_j = config.theme) === null || _j === void 0 ? void 0 : _j.fontFamily)) {
[
...Object.entries((_l = (_k = customConfig.theme) === null || _k === void 0 ? void 0 : _k.fontFamily) !== null && _l !== void 0 ? _l : {}),
...Object.entries((_p = (_o = (_m = customConfig.theme) === null || _m === void 0 ? void 0 : _m.extend) === null || _o === void 0 ? void 0 : _o.fontFamily) !== null && _p !== void 0 ? _p : {}),
].forEach(([name, value]) => {
const fontFamily = Array.isArray(value) ? value[0] : value;
if (fontFamily) {
customStyleUtils.push([`font-${name}`, (0, helpers_1.complete)({ fontFamily })]);
}
});
}
}