UNPKG

@nativescript/tailwind

Version:
307 lines (278 loc) 9.18 kB
const { writeFileSync } = require("fs"); const remRE = /\d?\.?\d+\s*r?em/g; function isSupportedProperty(prop, val = null) { const rules = supportedProperties[prop]; if (!rules) return false; if (val) { if (unsupportedValues.some((unit) => val.endsWith(unit))) { return false; } if (Array.isArray(rules)) { return rules.includes(val); } } return true; } function isSupportedSelector(selector) { const hasUnsupportedPseudoSelector = unsupportedPseudoSelectors.some( (pseudo) => selector.includes(pseudo) ); return !hasUnsupportedPseudoSelector; } function isPlaceholderPseudoSelector(selector) { return selector.includes("::placeholder"); } /** * @param {@} options * @returns {import('postcss').Plugin} */ module.exports = (options = { debug: false }) => { return { postcssPlugin: "postcss-nativescript", AtRule: { // remove @media rules because they // are currently not supported // in NativeScript media(mediaAtRule) { mediaAtRule.remove(); }, }, // Uncomment to debug the final output // OnceExit(rule) { // writeFileSync('./tailwind-output.css', rule.toString()); // }, Rule(rule) { // remove empty rules if (rule.nodes.length === 0) { return rule.remove(); } // replace :root and :host pseudo selector, introduced in Tailwind 4+ with .ns-root for var handling. if (rule.selector.includes(":root") || rule.selector.includes(":host")) { const rootClasses = '.ns-root, .ns-modal'; rule.selectors = rule.selectors.map((selector) => selector.replace(/:root/, rootClasses).replace(/:host/, rootClasses) ); } // remove rules with unsupported selectors if (!isSupportedSelector(rule.selector)) { return rule.remove(); } // convert ::placeholder pseudo selector // to use placeholder-color declaration if (isPlaceholderPseudoSelector(rule.selector)) { const placeholderSelectors = []; rule.selectors.forEach((selector) => { if (isPlaceholderPseudoSelector(selector)) { placeholderSelectors.push(selector.replace(/::placeholder/g, "")); } }); if (placeholderSelectors.length) { rule.selectors = placeholderSelectors; rule.walkDecls((decl) => { if (decl.prop === "color") { decl.replaceWith(decl.clone({ prop: "placeholder-color" })); } }); } // rule.selector.replace('::placeholder', '') } // remove :where() pseudo selector, introduced in Tailwind 3.4. if (rule.selector.includes(":where(")) { rule.selectors = rule.selectors.map((selector) => selector.replace(/:where\((.+)\)/, "$1") ); } // replace space and divide selectors to use a simpler selector that works in ns (v4 and newer) if (rule.selector.includes(":not(:last-child)")) { rule.selectors = rule.selectors.map((selector) => { return selector.replace(":not(:last-child)", "* + *"); }); } // replace space and divide selectors to use a simpler selector that works in ns (older versions) if (rule.selector.includes(":not([hidden]) ~ :not([hidden])")) { rule.selectors = rule.selectors.map((selector) => { return selector.replace(":not([hidden]) ~ :not([hidden])", "* + *"); }); } }, Declaration(decl) { // replace visibility: hidden // with visibility: collapse if (decl.prop === "visibility") { switch (decl.value) { case "hidden": return decl.replaceWith(decl.clone({ value: "collapse" })); } } // tailvind v4 changed how the divide and space utilities work, now we need to set two variables called // --tw-divide-x-reverse and --tw-divide-y-reverse but for them to work we need to remove the variable // declarations from the divide and space utilities classes const broken = /--tw-(divide|space)-[xy]-reverse/g; if (decl.prop?.match(broken) && decl.parent.selector?.match(/\.(divide|space)-[xy]/)) { return decl.remove(); } // invalid with core 8.8+ at moment // Note: could be supported at somepoint if (decl.prop === "placeholder-color" && decl.value?.includes("color-mix")) { return decl.remove(); } // invalid with core 8.8+ at moment // Note: could be supported at somepoint if (decl.value?.includes("currentColor")) { return decl.remove(); } // replace vertical-align: middle // with vertical-align: center if (decl.prop === "vertical-align") { switch (decl.value) { case "middle": return decl.replaceWith(decl.clone({ value: "center" })); } } // declarations that define unsupported variables/rules if ( [ "tw-ring", "tw-shadow", "tw-ordinal", "tw-slashed-zero", "tw-numeric", ].some((varName) => decl.prop.startsWith(`--${varName}`)) ) { return decl.remove(); } // Convert em/rem values to device pixel values // assuming 16 as the basis for rem and // treating em as rem if (decl.value.includes("rem") || decl.value.includes("em")) { decl.value = decl.value.replace(remRE, (match, offset, value) => { const converted = "" + parseFloat(match) * 16; options.debug && console.log("replacing r?em value", { match, offset, value, converted, }); return converted; }); options.debug && console.log({ final: decl.value, }); } // remove unsupported properties if ( !decl.prop.startsWith("--") && !isSupportedProperty(decl.prop, decl.value) ) { // options.debug && console.log('removing ', decl.prop, decl.value) return decl.remove(); } }, }; }; module.exports.postcss = true; const supportedProperties = { "align-content": true, "align-items": true, "align-self": true, "android-selected-tab-highlight-color": true, "android-elevation": true, "android-dynamic-elevation-offset": true, animation: true, "animation-delay": true, "animation-direction": true, "animation-duration": true, "animation-fill-mode": true, "animation-iteration-count": true, "animation-name": true, "animation-timing-function": true, background: true, "background-color": true, "background-image": true, "background-position": true, "background-repeat": ["repeat", "repeat-x", "repeat-y", "no-repeat"], "background-size": true, "border-bottom-color": true, "border-bottom-left-radius": true, "border-bottom-right-radius": true, "border-bottom-width": true, "border-color": true, "border-left-color": true, "border-left-width": true, "border-radius": true, "border-right-color": true, "border-right-width": true, "border-top-color": true, "border-top-left-radius": true, "border-top-right-radius": true, "border-top-width": true, "border-width": true, "box-shadow": true, "clip-path": true, color: true, flex: true, "flex-grow": true, "flex-direction": true, "flex-shrink": true, "flex-wrap": true, font: true, "font-family": true, "font-size": true, "font-style": ["italic", "normal"], "font-weight": true, "font-variation-settings": true, height: true, "highlight-color": true, "horizontal-align": ["left", "center", "right", "stretch"], "justify-content": true, "justify-items": true, "justify-self": true, "letter-spacing": true, "line-height": true, margin: true, "margin-bottom": true, "margin-left": true, "margin-right": true, "margin-top": true, "margin-block": true, "margin-block-start": true, "margin-block-end": true, "margin-inline": true, "margin-inline-start": true, "margin-inline-end": true, "min-height": true, "min-width": true, "off-background-color": true, opacity: true, order: true, padding: true, "padding-block": true, "padding-bottom": true, "padding-inline": true, "padding-left": true, "padding-right": true, "padding-top": true, "place-content": true, "placeholder-color": true, "place-items": true, "place-self": true, "selected-tab-text-color": true, "tab-background-color": true, "tab-text-color": true, "tab-text-font-size": true, "text-transform": true, "text-align": ["left", "center", "right"], "text-decoration": ["none", "line-through", "underline"], "text-shadow": true, "text-transform": ["none", "capitalize", "uppercase", "lowercase"], transform: true, rotate: true, "vertical-align": ["top", "center", "bottom", "stretch"], visibility: ["visible", "collapse"], width: true, "z-index": true, }; const unsupportedPseudoSelectors = [":focus-within", ":hover"]; const unsupportedValues = ["max-content", "min-content", "vh", "vw"];