pxtorem-css
Advanced tools
+40
| #!/usr/bin/env node | ||
| "use strict";var z=Object.create;var y=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,T=Object.prototype.hasOwnProperty;var A=(t,e,i,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of B(e))!T.call(t,s)&&s!==i&&y(t,s,{get:()=>e[s],enumerable:!(r=M(e,s))||r.enumerable});return t};var L=(t,e,i)=>(i=t!=null?z(I(t)):{},A(e||!t||!t.__esModule?y(i,"default",{value:t,enumerable:!0}):i,t));var f=require("fs"),m=require("path"),F=L(require("postcss"));function h(t){return new RegExp(`"[^"]+"|'[^']+'|url\\([^)]+\\)|var\\([^)]+\\)|(\\d*\\.?\\d+)${t}`,"g")}function Q(t){return t.filter(e=>e.match(/^[^*!]+$/))}function j(t){return t.filter(e=>e.match(/^\*.+\*$/)).map(e=>e.slice(1,-1))}function q(t){return t.filter(e=>e.match(/^[^*!]+\*$/)).map(e=>e.slice(0,-1))}function J(t){return t.filter(e=>e.match(/^\*[^*]+$/)).map(e=>e.slice(1))}function _(t){return t.filter(e=>e.match(/^![^*].*$/)).map(e=>e.slice(1))}function H(t){return t.filter(e=>e.match(/^!\*.+\*$/)).map(e=>e.slice(2,-1))}function X(t){return t.filter(e=>e.match(/^!\*[^*]+$/)).map(e=>e.slice(2))}function G(t){return t.filter(e=>e.match(/^![^*]+\*$/)).map(e=>e.slice(1,-1))}var g={exact:Q,contain:j,startWith:q,endWith:J,notExact:_,notContain:H,notStartWith:X,notEndWith:G};function $(t){let e=t.indexOf("*")>-1,i=e&&t.length===1,r={exact:g.exact(t),contain:g.contain(t),startWith:g.startWith(t),endWith:g.endWith(t),notExact:g.notExact(t),notContain:g.notContain(t),notStartWith:g.notStartWith(t),notEndWith:g.notEndWith(t)};return s=>i?!0:(e||r.exact.indexOf(s)>-1||r.contain.some(n=>s.indexOf(n)>-1)||r.startWith.some(n=>s.indexOf(n)===0)||r.endWith.some(n=>s.indexOf(n)===s.length-n.length))&&!(r.notExact.indexOf(s)>-1||r.notContain.some(n=>s.indexOf(n)>-1)||r.notStartWith.some(n=>s.indexOf(n)===0)||r.notEndWith.some(n=>s.indexOf(n)===s.length-n.length))}function E(t){return typeof t=="string"}function C(t){return typeof t=="function"}function k(t,e){return t.text.trim().toLowerCase().includes(e.toLowerCase())}function K(t){if(!t.parent)return;let e=t.parent.index(t);if(!(e<=0))return t.parent.nodes?.[e-1]}function V(t,e,i,r,s){let n=t.source;if(n?.input?.css){let c=n.input.css.split(` | ||
| `),l=(n.start?.line??1)-1;if(l>=0&&l<c.length){let p=c[l];if(p.includes("/*")&&p.toLowerCase().includes(i.toLowerCase()))return!0}}let a=K(t);if(a?.type==="comment"&&k(a,e))return!0;let o=t,u=!1;for(;o?.parent;){let c=o.parent,l=c.index(o);for(let p=l-1;p>=0;p--){let d=c.nodes?.[p];if(d?.type==="comment"){let x=d;if(k(x,s)){u=!1;break}if(k(x,r)){u=!0;break}}}if(u)return!0;o=c}return u}var Y={baseSize:16,precision:5,skipSelectors:[],properties:["*"],replaceOriginal:!0,convertMediaQueries:!1,minValue:0,maxValue:1/0,excludeFiles:null,includeFiles:null,fromUnit:"px",toUnit:"rem",propertyBaseSize:{},disableNextLineComment:"pxtorem-disable-next-line",disableLineComment:"pxtorem-disable-line",disableBlockComment:"pxtorem-disable",enableBlockComment:"pxtorem-enable",convert:null,onConversionComplete:null,verbose:!1};function w(t,e){let i=Math.pow(10,e+1),r=Math.floor(t*i);return Math.round(r/10)*10/i}function Z(t,e,i){return t.some(r=>r.prop===e&&r.value===i)}function ee(t,e){return typeof e!="string"?!1:t.some(i=>typeof i=="string"?e.indexOf(i)!==-1:i.test(e))}function N(t,e){return!e||!t?!1:C(e)?e(t):E(e)?t.indexOf(e)!==-1:e instanceof RegExp?e.test(t):!1}function U(t,e,i,r,s,n,a,o,u,c,l){let p=o[n]??t;return(d,x)=>{if(!x)return d;let b=parseFloat(x);if(b<i||b>r)return c.skipped++,d;if(u){let v=u(b,n,a);if(v===!1)return c.skipped++,d;if(typeof v=="string")return c.converted++,l&&console.log(`[pxtorem-css] ${n}: ${d} \u2192 ${v} (custom)`),v;if(typeof v=="number"){let O=w(v,e);return c.converted++,l&&console.log(`[pxtorem-css] ${n}: ${d} \u2192 ${O}${s} (custom)`),O+s}}let S=w(b/p,e);return c.converted++,l&&console.log(`[pxtorem-css] ${n}: ${d} \u2192 ${S}${s}`),S+s}}function D(t={}){let e={...Y,...t},i=$(e.properties),r=!1,s,n,a={totalDeclarations:0,convertedDeclarations:0,skippedDeclarations:0,filesProcessed:[],details:new Map};return{postcssPlugin:"pxtorem-css",Once(o){n=o.source?.input?.file,e.includeFiles&&n?r=!N(n,e.includeFiles):e.excludeFiles&&n?r=N(n,e.excludeFiles):r=!1,!r&&(n&&!a.filesProcessed.includes(n)&&(a.filesProcessed.push(n),a.details.set(n,{converted:0,skipped:0})),s=C(e.baseSize)?e.baseSize(o.source.input):e.baseSize)},Declaration(o){if(r)return;if(a.totalDeclarations++,o.value.indexOf(e.fromUnit)===-1){a.skippedDeclarations++;return}if(!i(o.prop)){a.skippedDeclarations++;return}let u=o.parent?.type==="rule"?o.parent.selector:void 0;if(ee(e.skipSelectors,u)){a.skippedDeclarations++;return}if(V(o,e.disableNextLineComment,e.disableLineComment,e.disableBlockComment,e.enableBlockComment)){a.skippedDeclarations++,e.verbose&&console.log(`[pxtorem-css] Skipped (disabled): ${o.prop}: ${o.value}`);return}let c=n?a.details.get(n)??{converted:0,skipped:0}:{converted:0,skipped:0},l=h(e.fromUnit),p=U(s,e.precision,e.minValue,e.maxValue,e.toUnit,o.prop,u??"",e.propertyBaseSize,e.convert,c,e.verbose),d=o.value.replace(l,p);n&&a.details.set(n,c),a.convertedDeclarations+=c.converted,a.skippedDeclarations+=c.skipped,d!==o.value&&(o.parent&&Z(o.parent.nodes?.filter(x=>x.type==="decl")||[],o.prop,d)||(e.replaceOriginal?o.value=d:o.cloneAfter({value:d})))},AtRule(o){if(!r&&e.convertMediaQueries&&o.name==="media"){if(o.params.indexOf(e.fromUnit)===-1)return;let u=n?a.details.get(n)??{converted:0,skipped:0}:{converted:0,skipped:0},c=h(e.fromUnit),l=U(s,e.precision,e.minValue,e.maxValue,e.toUnit,"@media","",e.propertyBaseSize,e.convert,u,e.verbose);o.params=o.params.replace(c,l),n&&a.details.set(n,u)}},OnceExit(){e.onConversionComplete&&e.onConversionComplete(a),e.verbose&&(console.log(` | ||
| [pxtorem-css] Conversion Report:`),console.log(` Files: ${a.filesProcessed.length}`),console.log(` Total: ${a.totalDeclarations}`),console.log(` Converted: ${a.convertedDeclarations}`),console.log(` Skipped: ${a.skippedDeclarations} | ||
| `))}}}D.postcss=!0;var R=D;var te="2.0.0",ne=` | ||
| pxtorem-css - Convert px to rem/em/vw/vh in CSS files | ||
| Usage: | ||
| pxtorem [options] <input> | ||
| Options: | ||
| -i, --input <path> Input CSS file or directory | ||
| -o, --output <path> Output file or directory (default: overwrite input) | ||
| -c, --config <path> Path to config file (JSON) | ||
| -b, --base-size <n> Base font size (default: 16) | ||
| -u, --to-unit <unit> Target unit: rem, em, vw, vh, vmin, vmax, % (default: rem) | ||
| -f, --from-unit <unit> Source unit (default: px) | ||
| -p, --precision <n> Decimal precision (default: 5) | ||
| --properties <list> Comma-separated properties to convert (default: *) | ||
| --skip-selectors <list> Comma-separated selectors to skip | ||
| --min-value <n> Minimum px value to convert (default: 0) | ||
| --max-value <n> Maximum px value to convert (default: Infinity) | ||
| --media-queries Convert px in media queries | ||
| --no-replace Add rem as fallback instead of replacing | ||
| -v, --verbose Show conversion details | ||
| -h, --help Show this help message | ||
| --version Show version number | ||
| Examples: | ||
| pxtorem style.css | ||
| pxtorem style.css -b 16 -u rem -p 5 | ||
| pxtorem -i src/styles -o dist/styles | ||
| pxtorem style.css --properties "font-size,margin,padding" | ||
| pxtorem style.css --skip-selectors "body,.no-convert" | ||
| pxtorem style.css --min-value 2 --media-queries | ||
| `;function ie(t){let e={},i=0;for(;i<t.length;){let r=t[i];switch(r){case"-i":case"--input":e.input=t[++i];break;case"-o":case"--output":e.output=t[++i];break;case"-c":case"--config":e.config=t[++i];break;case"-b":case"--base-size":e.baseSize=parseFloat(t[++i]);break;case"-u":case"--to-unit":e.toUnit=t[++i];break;case"-p":case"--precision":e.precision=parseInt(t[++i],10);break;case"-f":case"--from-unit":e.fromUnit=t[++i];break;case"--properties":e.properties=t[++i].split(",").map(s=>s.trim());break;case"--skip-selectors":e.skipSelectors=t[++i].split(",").map(s=>s.trim());break;case"--min-value":e.minValue=parseFloat(t[++i]);break;case"--max-value":e.maxValue=parseFloat(t[++i]);break;case"--media-queries":e.convertMediaQueries=!0;break;case"--no-replace":e.replaceOriginal=!1;break;case"-v":case"--verbose":e.verbose=!0;break;case"-h":case"--help":e.help=!0;break;case"--version":e.version=!0;break;default:!r.startsWith("-")&&!e.input?e.input=r:r.startsWith("-")&&(console.error(`Error: Unknown option "${r}"`),console.log("Use --help to see available options."),process.exit(1));break}i++}return e}var W=["rem","em","vw","vh","vmin","vmax","%"];function re(t){let e=[];t.toUnit&&!W.includes(t.toUnit)&&e.push(`Invalid --to-unit "${t.toUnit}". Valid values: ${W.join(", ")}`),t.baseSize!==void 0&&(isNaN(t.baseSize)||t.baseSize<=0)&&e.push("--base-size must be a positive number"),t.precision!==void 0&&(isNaN(t.precision)||t.precision<0||!Number.isInteger(t.precision))&&e.push("--precision must be a non-negative integer"),t.minValue!==void 0&&isNaN(t.minValue)&&e.push("--min-value must be a number"),t.maxValue!==void 0&&isNaN(t.maxValue)&&e.push("--max-value must be a number"),t.minValue!==void 0&&t.maxValue!==void 0&&t.minValue>t.maxValue&&e.push("--min-value cannot be greater than --max-value"),t.config&&!(0,f.existsSync)(t.config)&&e.push(`Config file not found: ${t.config}`),e.length>0&&(console.error("Validation errors:"),e.forEach(i=>console.error(` \u2717 ${i}`)),process.exit(1))}function se(t){try{let e=(0,f.readFileSync)(t,"utf-8");return JSON.parse(e)}catch(e){console.error(`Error loading config file: ${t}`,e),process.exit(1)}}function oe(){let t=["pxtorem.config.json","pxtorem.json",".pxtoremrc.json"];for(let e of t){let i=(0,m.resolve)(process.cwd(),e);if((0,f.existsSync)(i))return i}return null}function P(t){let e=(0,m.resolve)(process.cwd(),t);(0,f.existsSync)(e)||(console.error(`Error: Path not found: ${t}`),process.exit(1));let i=(0,f.statSync)(e);if(i.isFile())return[e];if(i.isDirectory()){let r=[],s=(0,f.readdirSync)(e,{withFileTypes:!0});for(let n of s)n.isFile()&&/\.(css|scss|sass|less)$/.test(n.name)?r.push((0,m.join)(e,n.name)):n.isDirectory()&&r.push(...P((0,m.join)(e,n.name)));return r}return[]}async function ae(t,e,i){let r=(0,f.readFileSync)(t,"utf-8"),s={converted:0,skipped:0},n=await(0,F.default)([R({...i,onConversionComplete:a=>{s={converted:a.convertedDeclarations,skipped:a.skippedDeclarations}}})]).process(r,{from:t,to:e});return(0,f.writeFileSync)(e,n.css),s}async function le(){let t=process.argv.slice(2),e=ie(t);e.version&&(console.log(`pxtorem-css v${te}`),process.exit(0)),(e.help||!e.input)&&(console.log(ne),process.exit(e.help?0:1)),re(e);let i={},r=e.config||oe();r&&(i=se(r),e.verbose&&console.log(`Using config: ${r}`));let s={...i,...e.baseSize!==void 0&&{baseSize:e.baseSize},...e.toUnit!==void 0&&{toUnit:e.toUnit},...e.fromUnit!==void 0&&{fromUnit:e.fromUnit},...e.precision!==void 0&&{precision:e.precision},...e.properties!==void 0&&{properties:e.properties},...e.skipSelectors!==void 0&&{skipSelectors:e.skipSelectors},...e.minValue!==void 0&&{minValue:e.minValue},...e.maxValue!==void 0&&{maxValue:e.maxValue},...e.convertMediaQueries!==void 0&&{convertMediaQueries:e.convertMediaQueries},...e.replaceOriginal!==void 0&&{replaceOriginal:e.replaceOriginal},...e.verbose!==void 0&&{verbose:e.verbose}},n=P(e.input);n.length===0&&(console.error("No CSS files found."),process.exit(1)),console.log(` | ||
| Processing ${n.length} file(s)... | ||
| `);let a=0,o=0;for(let u of n){let c;if(e.output){let l=(0,m.resolve)(process.cwd(),e.output),p=(0,f.existsSync)(l)&&(0,f.statSync)(l);p&&p.isDirectory()?c=(0,m.join)(l,(0,m.basename)(u)):n.length===1?c=l:c=(0,m.join)(l,(0,m.basename)(u))}else c=u;try{let l=await ae(u,c,s);a+=l.converted,o+=l.skipped;let p=u.replace(process.cwd(),".");console.log(`\u2713 ${p} (${l.converted} converted, ${l.skipped} skipped)`)}catch(l){console.error(`\u2717 ${u}: ${l.message}`)}}console.log(` | ||
| Done! Converted: ${a}, Skipped: ${o} | ||
| `)}le().catch(t=>{console.error(t),process.exit(1)}); |
+157
| import { Input, Plugin } from 'postcss'; | ||
| /** | ||
| * Supported target units for conversion | ||
| */ | ||
| type TargetUnit = 'rem' | 'em' | 'vw' | 'vh' | 'vmin' | 'vmax' | '%'; | ||
| /** | ||
| * Conversion report generated after processing | ||
| */ | ||
| interface ConversionReport { | ||
| /** Total number of declarations processed */ | ||
| totalDeclarations: number; | ||
| /** Number of declarations converted */ | ||
| convertedDeclarations: number; | ||
| /** Number of declarations skipped */ | ||
| skippedDeclarations: number; | ||
| /** List of files processed */ | ||
| filesProcessed: string[]; | ||
| /** Conversion details per file */ | ||
| details: Map<string, { | ||
| converted: number; | ||
| skipped: number; | ||
| }>; | ||
| } | ||
| /** | ||
| * Options for the pxtorem-css plugin | ||
| */ | ||
| interface Options { | ||
| /** | ||
| * Base font size for conversion calculation | ||
| * @default 16 | ||
| */ | ||
| baseSize?: number | ((input: Input) => number); | ||
| /** | ||
| * Decimal precision for converted values | ||
| * @default 5 | ||
| */ | ||
| precision?: number; | ||
| /** | ||
| * Properties to convert. Supports wildcards and negation. | ||
| * - `['*']` - all properties | ||
| * - `['font*']` - properties starting with font | ||
| * - `['*size']` - properties ending with size | ||
| * - `['!border*']` - exclude properties starting with border | ||
| * @default ['*'] | ||
| */ | ||
| properties?: string[]; | ||
| /** | ||
| * Selectors to skip. Strings check for containment, RegExp for matches. | ||
| * @default [] | ||
| */ | ||
| skipSelectors?: (string | RegExp)[]; | ||
| /** | ||
| * Replace values instead of adding fallbacks | ||
| * @default true | ||
| */ | ||
| replaceOriginal?: boolean; | ||
| /** | ||
| * Convert px in media queries | ||
| * @default false | ||
| */ | ||
| convertMediaQueries?: boolean; | ||
| /** | ||
| * Minimum px value to convert. Values below this are skipped. | ||
| * @default 0 | ||
| */ | ||
| minValue?: number; | ||
| /** | ||
| * Maximum px value to convert. Values above this are skipped. | ||
| * @default Infinity | ||
| */ | ||
| maxValue?: number; | ||
| /** | ||
| * File paths to exclude from conversion | ||
| */ | ||
| excludeFiles?: string | RegExp | ((file: string) => boolean) | null; | ||
| /** | ||
| * File paths to include for conversion (overrides excludeFiles) | ||
| */ | ||
| includeFiles?: string | RegExp | ((file: string) => boolean) | null; | ||
| /** | ||
| * Source unit to convert from | ||
| * @default 'px' | ||
| */ | ||
| fromUnit?: string; | ||
| /** | ||
| * Target unit to convert to | ||
| * @default 'rem' | ||
| */ | ||
| toUnit?: TargetUnit; | ||
| /** | ||
| * Property-specific base sizes. Overrides baseSize for specified properties. | ||
| * @example { 'font-size': 14, 'line-height': 20 } | ||
| */ | ||
| propertyBaseSize?: Record<string, number>; | ||
| /** | ||
| * Comment pattern to disable conversion for next line | ||
| * @default 'pxtorem-disable-next-line' | ||
| */ | ||
| disableNextLineComment?: string; | ||
| /** | ||
| * Comment pattern to disable conversion for current line | ||
| * @default 'pxtorem-disable-line' | ||
| */ | ||
| disableLineComment?: string; | ||
| /** | ||
| * Comment pattern to disable conversion for block | ||
| * @default 'pxtorem-disable' | ||
| */ | ||
| disableBlockComment?: string; | ||
| /** | ||
| * Comment pattern to re-enable conversion after disable | ||
| * @default 'pxtorem-enable' | ||
| */ | ||
| enableBlockComment?: string; | ||
| /** | ||
| * Custom conversion function. | ||
| * Return a number for calculated value, string for custom output, or false to skip. | ||
| * @param pixelValue - The original pixel value | ||
| * @param property - The CSS property name | ||
| * @param selector - The CSS selector | ||
| * @returns Custom value or false to skip | ||
| */ | ||
| convert?: (pixelValue: number, property: string, selector: string) => number | string | false; | ||
| /** | ||
| * Callback when processing is complete with conversion report | ||
| */ | ||
| onConversionComplete?: (report: ConversionReport) => void; | ||
| /** | ||
| * Log conversion details to console | ||
| * @default false | ||
| */ | ||
| verbose?: boolean; | ||
| } | ||
| /** | ||
| * PostCSS plugin that converts px to rem/em/vw/vh units | ||
| * | ||
| * @example | ||
| * ```js | ||
| * import pxtorem from 'pxtorem-css'; | ||
| * | ||
| * postcss([ | ||
| * pxtorem({ | ||
| * baseSize: 16, | ||
| * properties: ['*'], | ||
| * toUnit: 'rem' | ||
| * }) | ||
| * ]) | ||
| * ``` | ||
| */ | ||
| declare function pxtorem(options?: Options): Plugin; | ||
| declare namespace pxtorem { | ||
| var postcss: boolean; | ||
| } | ||
| export { type ConversionReport, type Options, type TargetUnit, pxtorem as default, pxtorem }; |
+159
| import { Input, Plugin } from 'postcss'; | ||
| /** | ||
| * Supported target units for conversion | ||
| */ | ||
| type TargetUnit = 'rem' | 'em' | 'vw' | 'vh' | 'vmin' | 'vmax' | '%'; | ||
| /** | ||
| * Conversion report generated after processing | ||
| */ | ||
| interface ConversionReport { | ||
| /** Total number of declarations processed */ | ||
| totalDeclarations: number; | ||
| /** Number of declarations converted */ | ||
| convertedDeclarations: number; | ||
| /** Number of declarations skipped */ | ||
| skippedDeclarations: number; | ||
| /** List of files processed */ | ||
| filesProcessed: string[]; | ||
| /** Conversion details per file */ | ||
| details: Map<string, { | ||
| converted: number; | ||
| skipped: number; | ||
| }>; | ||
| } | ||
| /** | ||
| * Options for the pxtorem-css plugin | ||
| */ | ||
| interface Options { | ||
| /** | ||
| * Base font size for conversion calculation | ||
| * @default 16 | ||
| */ | ||
| baseSize?: number | ((input: Input) => number); | ||
| /** | ||
| * Decimal precision for converted values | ||
| * @default 5 | ||
| */ | ||
| precision?: number; | ||
| /** | ||
| * Properties to convert. Supports wildcards and negation. | ||
| * - `['*']` - all properties | ||
| * - `['font*']` - properties starting with font | ||
| * - `['*size']` - properties ending with size | ||
| * - `['!border*']` - exclude properties starting with border | ||
| * @default ['*'] | ||
| */ | ||
| properties?: string[]; | ||
| /** | ||
| * Selectors to skip. Strings check for containment, RegExp for matches. | ||
| * @default [] | ||
| */ | ||
| skipSelectors?: (string | RegExp)[]; | ||
| /** | ||
| * Replace values instead of adding fallbacks | ||
| * @default true | ||
| */ | ||
| replaceOriginal?: boolean; | ||
| /** | ||
| * Convert px in media queries | ||
| * @default false | ||
| */ | ||
| convertMediaQueries?: boolean; | ||
| /** | ||
| * Minimum px value to convert. Values below this are skipped. | ||
| * @default 0 | ||
| */ | ||
| minValue?: number; | ||
| /** | ||
| * Maximum px value to convert. Values above this are skipped. | ||
| * @default Infinity | ||
| */ | ||
| maxValue?: number; | ||
| /** | ||
| * File paths to exclude from conversion | ||
| */ | ||
| excludeFiles?: string | RegExp | ((file: string) => boolean) | null; | ||
| /** | ||
| * File paths to include for conversion (overrides excludeFiles) | ||
| */ | ||
| includeFiles?: string | RegExp | ((file: string) => boolean) | null; | ||
| /** | ||
| * Source unit to convert from | ||
| * @default 'px' | ||
| */ | ||
| fromUnit?: string; | ||
| /** | ||
| * Target unit to convert to | ||
| * @default 'rem' | ||
| */ | ||
| toUnit?: TargetUnit; | ||
| /** | ||
| * Property-specific base sizes. Overrides baseSize for specified properties. | ||
| * @example { 'font-size': 14, 'line-height': 20 } | ||
| */ | ||
| propertyBaseSize?: Record<string, number>; | ||
| /** | ||
| * Comment pattern to disable conversion for next line | ||
| * @default 'pxtorem-disable-next-line' | ||
| */ | ||
| disableNextLineComment?: string; | ||
| /** | ||
| * Comment pattern to disable conversion for current line | ||
| * @default 'pxtorem-disable-line' | ||
| */ | ||
| disableLineComment?: string; | ||
| /** | ||
| * Comment pattern to disable conversion for block | ||
| * @default 'pxtorem-disable' | ||
| */ | ||
| disableBlockComment?: string; | ||
| /** | ||
| * Comment pattern to re-enable conversion after disable | ||
| * @default 'pxtorem-enable' | ||
| */ | ||
| enableBlockComment?: string; | ||
| /** | ||
| * Custom conversion function. | ||
| * Return a number for calculated value, string for custom output, or false to skip. | ||
| * @param pixelValue - The original pixel value | ||
| * @param property - The CSS property name | ||
| * @param selector - The CSS selector | ||
| * @returns Custom value or false to skip | ||
| */ | ||
| convert?: (pixelValue: number, property: string, selector: string) => number | string | false; | ||
| /** | ||
| * Callback when processing is complete with conversion report | ||
| */ | ||
| onConversionComplete?: (report: ConversionReport) => void; | ||
| /** | ||
| * Log conversion details to console | ||
| * @default false | ||
| */ | ||
| verbose?: boolean; | ||
| } | ||
| /** | ||
| * PostCSS plugin that converts px to rem/em/vw/vh units | ||
| * | ||
| * @example | ||
| * ```js | ||
| * import pxtorem from 'pxtorem-css'; | ||
| * | ||
| * postcss([ | ||
| * pxtorem({ | ||
| * baseSize: 16, | ||
| * properties: ['*'], | ||
| * toUnit: 'rem' | ||
| * }) | ||
| * ]) | ||
| * ``` | ||
| */ | ||
| declare function pxtorem(options?: Options): Plugin; | ||
| declare namespace pxtorem { | ||
| var postcss: boolean; | ||
| } | ||
| // @ts-ignore | ||
| export = pxtorem; | ||
| export { type ConversionReport, type Options, type TargetUnit, pxtorem }; |
| "use strict";var h=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var w=(t,e)=>{for(var l in e)h(t,l,{get:e[l],enumerable:!0})},M=(t,e,l,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of P(e))!F.call(t,o)&&o!==l&&h(t,o,{get:()=>e[o],enumerable:!(r=N(e,o))||r.enumerable});return t};var U=t=>M(h({},"__esModule",{value:!0}),t);var K={};w(K,{default:()=>J,pxtorem:()=>k});module.exports=U(K);function v(t){return new RegExp(`"[^"]+"|'[^']+'|url\\([^)]+\\)|var\\([^)]+\\)|(\\d*\\.?\\d+)${t}`,"g")}function V(t){return t.filter(e=>e.match(/^[^*!]+$/))}function z(t){return t.filter(e=>e.match(/^\*.+\*$/)).map(e=>e.slice(1,-1))}function B(t){return t.filter(e=>e.match(/^[^*!]+\*$/)).map(e=>e.slice(0,-1))}function A(t){return t.filter(e=>e.match(/^\*[^*]+$/)).map(e=>e.slice(1))}function L(t){return t.filter(e=>e.match(/^![^*].*$/)).map(e=>e.slice(1))}function T(t){return t.filter(e=>e.match(/^!\*.+\*$/)).map(e=>e.slice(2,-1))}function I(t){return t.filter(e=>e.match(/^!\*[^*]+$/)).map(e=>e.slice(2))}function Q(t){return t.filter(e=>e.match(/^![^*]+\*$/)).map(e=>e.slice(1,-1))}var p={exact:V,contain:z,startWith:B,endWith:A,notExact:L,notContain:T,notStartWith:I,notEndWith:Q};function $(t){let e=t.indexOf("*")>-1,l=e&&t.length===1,r={exact:p.exact(t),contain:p.contain(t),startWith:p.startWith(t),endWith:p.endWith(t),notExact:p.notExact(t),notContain:p.notContain(t),notStartWith:p.notStartWith(t),notEndWith:p.notEndWith(t)};return o=>l?!0:(e||r.exact.indexOf(o)>-1||r.contain.some(n=>o.indexOf(n)>-1)||r.startWith.some(n=>o.indexOf(n)===0)||r.endWith.some(n=>o.indexOf(n)===o.length-n.length))&&!(r.notExact.indexOf(o)>-1||r.notContain.some(n=>o.indexOf(n)>-1)||r.notStartWith.some(n=>o.indexOf(n)===0)||r.notEndWith.some(n=>o.indexOf(n)===o.length-n.length))}function E(t){return typeof t=="string"}function b(t){return typeof t=="function"}function C(t,e){return t.text.trim().toLowerCase().includes(e.toLowerCase())}function j(t){if(!t.parent)return;let e=t.parent.index(t);if(!(e<=0))return t.parent.nodes?.[e-1]}function O(t,e,l,r,o){let n=t.source;if(n?.input?.css){let a=n.input.css.split(` | ||
| `),f=(n.start?.line??1)-1;if(f>=0&&f<a.length){let d=a[f];if(d.includes("/*")&&d.toLowerCase().includes(l.toLowerCase()))return!0}}let s=j(t);if(s?.type==="comment"&&C(s,e))return!0;let i=t,u=!1;for(;i?.parent;){let a=i.parent,f=a.index(i);for(let d=f-1;d>=0;d--){let c=a.nodes?.[d];if(c?.type==="comment"){let m=c;if(C(m,o)){u=!1;break}if(C(m,r)){u=!0;break}}}if(u)return!0;i=a}return u}var q={baseSize:16,precision:5,skipSelectors:[],properties:["*"],replaceOriginal:!0,convertMediaQueries:!1,minValue:0,maxValue:1/0,excludeFiles:null,includeFiles:null,fromUnit:"px",toUnit:"rem",propertyBaseSize:{},disableNextLineComment:"pxtorem-disable-next-line",disableLineComment:"pxtorem-disable-line",disableBlockComment:"pxtorem-disable",enableBlockComment:"pxtorem-enable",convert:null,onConversionComplete:null,verbose:!1};function W(t,e){let l=Math.pow(10,e+1),r=Math.floor(t*l);return Math.round(r/10)*10/l}function G(t,e,l){return t.some(r=>r.prop===e&&r.value===l)}function H(t,e){return typeof e!="string"?!1:t.some(l=>typeof l=="string"?e.indexOf(l)!==-1:l.test(e))}function D(t,e){return!e||!t?!1:b(e)?e(t):E(e)?t.indexOf(e)!==-1:e instanceof RegExp?e.test(t):!1}function y(t,e,l,r,o,n,s,i,u,a,f){let d=i[n]??t;return(c,m)=>{if(!m)return c;let x=parseFloat(m);if(x<l||x>r)return a.skipped++,c;if(u){let g=u(x,n,s);if(g===!1)return a.skipped++,c;if(typeof g=="string")return a.converted++,f&&console.log(`[pxtorem-css] ${n}: ${c} \u2192 ${g} (custom)`),g;if(typeof g=="number"){let R=W(g,e);return a.converted++,f&&console.log(`[pxtorem-css] ${n}: ${c} \u2192 ${R}${o} (custom)`),R+o}}let S=W(x/d,e);return a.converted++,f&&console.log(`[pxtorem-css] ${n}: ${c} \u2192 ${S}${o}`),S+o}}function k(t={}){let e={...q,...t},l=$(e.properties),r=!1,o,n,s={totalDeclarations:0,convertedDeclarations:0,skippedDeclarations:0,filesProcessed:[],details:new Map};return{postcssPlugin:"pxtorem-css",Once(i){n=i.source?.input?.file,e.includeFiles&&n?r=!D(n,e.includeFiles):e.excludeFiles&&n?r=D(n,e.excludeFiles):r=!1,!r&&(n&&!s.filesProcessed.includes(n)&&(s.filesProcessed.push(n),s.details.set(n,{converted:0,skipped:0})),o=b(e.baseSize)?e.baseSize(i.source.input):e.baseSize)},Declaration(i){if(r)return;if(s.totalDeclarations++,i.value.indexOf(e.fromUnit)===-1){s.skippedDeclarations++;return}if(!l(i.prop)){s.skippedDeclarations++;return}let u=i.parent?.type==="rule"?i.parent.selector:void 0;if(H(e.skipSelectors,u)){s.skippedDeclarations++;return}if(O(i,e.disableNextLineComment,e.disableLineComment,e.disableBlockComment,e.enableBlockComment)){s.skippedDeclarations++,e.verbose&&console.log(`[pxtorem-css] Skipped (disabled): ${i.prop}: ${i.value}`);return}let a=n?s.details.get(n)??{converted:0,skipped:0}:{converted:0,skipped:0},f=v(e.fromUnit),d=y(o,e.precision,e.minValue,e.maxValue,e.toUnit,i.prop,u??"",e.propertyBaseSize,e.convert,a,e.verbose),c=i.value.replace(f,d);n&&s.details.set(n,a),s.convertedDeclarations+=a.converted,s.skippedDeclarations+=a.skipped,c!==i.value&&(i.parent&&G(i.parent.nodes?.filter(m=>m.type==="decl")||[],i.prop,c)||(e.replaceOriginal?i.value=c:i.cloneAfter({value:c})))},AtRule(i){if(!r&&e.convertMediaQueries&&i.name==="media"){if(i.params.indexOf(e.fromUnit)===-1)return;let u=n?s.details.get(n)??{converted:0,skipped:0}:{converted:0,skipped:0},a=v(e.fromUnit),f=y(o,e.precision,e.minValue,e.maxValue,e.toUnit,"@media","",e.propertyBaseSize,e.convert,u,e.verbose);i.params=i.params.replace(a,f),n&&s.details.set(n,u)}},OnceExit(){e.onConversionComplete&&e.onConversionComplete(s),e.verbose&&(console.log(` | ||
| [pxtorem-css] Conversion Report:`),console.log(` Files: ${s.filesProcessed.length}`),console.log(` Total: ${s.totalDeclarations}`),console.log(` Converted: ${s.convertedDeclarations}`),console.log(` Skipped: ${s.skippedDeclarations} | ||
| `))}}}k.postcss=!0;var J=k;0&&(module.exports={pxtorem}); | ||
| module.exports = module.exports.default; |
| function h(t){return new RegExp(`"[^"]+"|'[^']+'|url\\([^)]+\\)|var\\([^)]+\\)|(\\d*\\.?\\d+)${t}`,"g")}function y(t){return t.filter(e=>e.match(/^[^*!]+$/))}function N(t){return t.filter(e=>e.match(/^\*.+\*$/)).map(e=>e.slice(1,-1))}function P(t){return t.filter(e=>e.match(/^[^*!]+\*$/)).map(e=>e.slice(0,-1))}function F(t){return t.filter(e=>e.match(/^\*[^*]+$/)).map(e=>e.slice(1))}function w(t){return t.filter(e=>e.match(/^![^*].*$/)).map(e=>e.slice(1))}function M(t){return t.filter(e=>e.match(/^!\*.+\*$/)).map(e=>e.slice(2,-1))}function U(t){return t.filter(e=>e.match(/^!\*[^*]+$/)).map(e=>e.slice(2))}function V(t){return t.filter(e=>e.match(/^![^*]+\*$/)).map(e=>e.slice(1,-1))}var p={exact:y,contain:N,startWith:P,endWith:F,notExact:w,notContain:M,notStartWith:U,notEndWith:V};function S(t){let e=t.indexOf("*")>-1,a=e&&t.length===1,r={exact:p.exact(t),contain:p.contain(t),startWith:p.startWith(t),endWith:p.endWith(t),notExact:p.notExact(t),notContain:p.notContain(t),notStartWith:p.notStartWith(t),notEndWith:p.notEndWith(t)};return s=>a?!0:(e||r.exact.indexOf(s)>-1||r.contain.some(n=>s.indexOf(n)>-1)||r.startWith.some(n=>s.indexOf(n)===0)||r.endWith.some(n=>s.indexOf(n)===s.length-n.length))&&!(r.notExact.indexOf(s)>-1||r.notContain.some(n=>s.indexOf(n)>-1)||r.notStartWith.some(n=>s.indexOf(n)===0)||r.notEndWith.some(n=>s.indexOf(n)===s.length-n.length))}function R(t){return typeof t=="string"}function v(t){return typeof t=="function"}function b(t,e){return t.text.trim().toLowerCase().includes(e.toLowerCase())}function z(t){if(!t.parent)return;let e=t.parent.index(t);if(!(e<=0))return t.parent.nodes?.[e-1]}function $(t,e,a,r,s){let n=t.source;if(n?.input?.css){let l=n.input.css.split(` | ||
| `),f=(n.start?.line??1)-1;if(f>=0&&f<l.length){let d=l[f];if(d.includes("/*")&&d.toLowerCase().includes(a.toLowerCase()))return!0}}let o=z(t);if(o?.type==="comment"&&b(o,e))return!0;let i=t,u=!1;for(;i?.parent;){let l=i.parent,f=l.index(i);for(let d=f-1;d>=0;d--){let c=l.nodes?.[d];if(c?.type==="comment"){let m=c;if(b(m,s)){u=!1;break}if(b(m,r)){u=!0;break}}}if(u)return!0;i=l}return u}var B={baseSize:16,precision:5,skipSelectors:[],properties:["*"],replaceOriginal:!0,convertMediaQueries:!1,minValue:0,maxValue:1/0,excludeFiles:null,includeFiles:null,fromUnit:"px",toUnit:"rem",propertyBaseSize:{},disableNextLineComment:"pxtorem-disable-next-line",disableLineComment:"pxtorem-disable-line",disableBlockComment:"pxtorem-disable",enableBlockComment:"pxtorem-enable",convert:null,onConversionComplete:null,verbose:!1};function E(t,e){let a=Math.pow(10,e+1),r=Math.floor(t*a);return Math.round(r/10)*10/a}function A(t,e,a){return t.some(r=>r.prop===e&&r.value===a)}function L(t,e){return typeof e!="string"?!1:t.some(a=>typeof a=="string"?e.indexOf(a)!==-1:a.test(e))}function O(t,e){return!e||!t?!1:v(e)?e(t):R(e)?t.indexOf(e)!==-1:e instanceof RegExp?e.test(t):!1}function W(t,e,a,r,s,n,o,i,u,l,f){let d=i[n]??t;return(c,m)=>{if(!m)return c;let x=parseFloat(m);if(x<a||x>r)return l.skipped++,c;if(u){let g=u(x,n,o);if(g===!1)return l.skipped++,c;if(typeof g=="string")return l.converted++,f&&console.log(`[pxtorem-css] ${n}: ${c} \u2192 ${g} (custom)`),g;if(typeof g=="number"){let k=E(g,e);return l.converted++,f&&console.log(`[pxtorem-css] ${n}: ${c} \u2192 ${k}${s} (custom)`),k+s}}let C=E(x/d,e);return l.converted++,f&&console.log(`[pxtorem-css] ${n}: ${c} \u2192 ${C}${s}`),C+s}}function D(t={}){let e={...B,...t},a=S(e.properties),r=!1,s,n,o={totalDeclarations:0,convertedDeclarations:0,skippedDeclarations:0,filesProcessed:[],details:new Map};return{postcssPlugin:"pxtorem-css",Once(i){n=i.source?.input?.file,e.includeFiles&&n?r=!O(n,e.includeFiles):e.excludeFiles&&n?r=O(n,e.excludeFiles):r=!1,!r&&(n&&!o.filesProcessed.includes(n)&&(o.filesProcessed.push(n),o.details.set(n,{converted:0,skipped:0})),s=v(e.baseSize)?e.baseSize(i.source.input):e.baseSize)},Declaration(i){if(r)return;if(o.totalDeclarations++,i.value.indexOf(e.fromUnit)===-1){o.skippedDeclarations++;return}if(!a(i.prop)){o.skippedDeclarations++;return}let u=i.parent?.type==="rule"?i.parent.selector:void 0;if(L(e.skipSelectors,u)){o.skippedDeclarations++;return}if($(i,e.disableNextLineComment,e.disableLineComment,e.disableBlockComment,e.enableBlockComment)){o.skippedDeclarations++,e.verbose&&console.log(`[pxtorem-css] Skipped (disabled): ${i.prop}: ${i.value}`);return}let l=n?o.details.get(n)??{converted:0,skipped:0}:{converted:0,skipped:0},f=h(e.fromUnit),d=W(s,e.precision,e.minValue,e.maxValue,e.toUnit,i.prop,u??"",e.propertyBaseSize,e.convert,l,e.verbose),c=i.value.replace(f,d);n&&o.details.set(n,l),o.convertedDeclarations+=l.converted,o.skippedDeclarations+=l.skipped,c!==i.value&&(i.parent&&A(i.parent.nodes?.filter(m=>m.type==="decl")||[],i.prop,c)||(e.replaceOriginal?i.value=c:i.cloneAfter({value:c})))},AtRule(i){if(!r&&e.convertMediaQueries&&i.name==="media"){if(i.params.indexOf(e.fromUnit)===-1)return;let u=n?o.details.get(n)??{converted:0,skipped:0}:{converted:0,skipped:0},l=h(e.fromUnit),f=W(s,e.precision,e.minValue,e.maxValue,e.toUnit,"@media","",e.propertyBaseSize,e.convert,u,e.verbose);i.params=i.params.replace(l,f),n&&o.details.set(n,u)}},OnceExit(){e.onConversionComplete&&e.onConversionComplete(o),e.verbose&&(console.log(` | ||
| [pxtorem-css] Conversion Report:`),console.log(` Files: ${o.filesProcessed.length}`),console.log(` Total: ${o.totalDeclarations}`),console.log(` Converted: ${o.convertedDeclarations}`),console.log(` Skipped: ${o.skippedDeclarations} | ||
| `))}}}D.postcss=!0;var K=D;export{K as default,D as pxtorem}; |
+21
| MIT License | ||
| Copyright (c) 2024 Rashed Iqbal | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
+69
-28
| { | ||
| "name": "pxtorem-css", | ||
| "version": "1.0.6", | ||
| "description": "A nodejs cli that convert css unit px to rem", | ||
| "main": "app.js", | ||
| "author": "Rashed Iqbal", | ||
| "license": "MIT", | ||
| "bin": { | ||
| "pxtorem": "./bin/pxtorem" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/iqbal-rashed/pxtorem-css" | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/iqbal-rashed/pxtorem-css/issues" | ||
| }, | ||
| "keywords": [ | ||
| "pxtorem", | ||
| "css", | ||
| "unit", | ||
| "converter", | ||
| "px", | ||
| "rem" | ||
| ], | ||
| "dependencies": { | ||
| "chalk": "^4.1.2", | ||
| "commander": "^9.3.0", | ||
| "ora": "^5.4.1" | ||
| "name": "pxtorem-css", | ||
| "version": "2.0.1", | ||
| "description": "A PostCSS plugin and CLI tool that converts px to rem/em/vw/vh units", | ||
| "main": "dist/index.js", | ||
| "module": "dist/index.mjs", | ||
| "types": "dist/index.d.ts", | ||
| "bin": { | ||
| "pxtorem": "./dist/cli.js" | ||
| }, | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.mjs", | ||
| "require": "./dist/index.js", | ||
| "default": "./dist/index.js" | ||
| } | ||
| }, | ||
| "files": [ | ||
| "dist" | ||
| ], | ||
| "scripts": { | ||
| "build": "tsup", | ||
| "dev": "tsup --watch", | ||
| "test": "vitest run", | ||
| "test:watch": "vitest", | ||
| "lint": "eslint .", | ||
| "lint:fix": "eslint . --fix", | ||
| "format": "prettier --write .", | ||
| "format:check": "prettier --check .", | ||
| "prepublishOnly": "npm run lint && npm run test && npm run build" | ||
| }, | ||
| "keywords": [ | ||
| "postcss", | ||
| "postcss-plugin", | ||
| "css", | ||
| "rem", | ||
| "em", | ||
| "vw", | ||
| "vh", | ||
| "pixel", | ||
| "px", | ||
| "pxtorem", | ||
| "converter", | ||
| "cli" | ||
| ], | ||
| "author": "Rashed Iqbal", | ||
| "license": "MIT", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/iqbal-rashed/pxtorem-css" | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/iqbal-rashed/pxtorem-css/issues" | ||
| }, | ||
| "peerDependencies": { | ||
| "postcss": "^8.0.0" | ||
| }, | ||
| "dependencies": { | ||
| "postcss": "^8.4.49" | ||
| }, | ||
| "devDependencies": { | ||
| "@eslint/js": "^9.17.0", | ||
| "@types/node": "^22.10.2", | ||
| "eslint": "^9.17.0", | ||
| "prettier": "^3.4.2", | ||
| "tsup": "^8.3.5", | ||
| "typescript": "^5.7.2", | ||
| "typescript-eslint": "^8.18.2", | ||
| "vitest": "^2.1.8" | ||
| } | ||
| } |
+279
-57
@@ -1,92 +0,314 @@ | ||
| # pxtorem-css | ||
| ### A nodejs cli that convert px to rem in any css file | ||
| <h1 align="center">pxtorem-css</h1> | ||
| <br> | ||
| <p align="center"> | ||
| <strong>A modern PostCSS plugin & CLI to convert px → rem, em, vw, vh and more</strong> | ||
| </p> | ||
| <p align="center"> | ||
| <img src="https://img.shields.io/npm/v/pxtorem-css?style=flat-square&color=blue" alt="npm version" /> | ||
| <img src="https://img.shields.io/npm/dm/pxtorem-css?style=flat-square&color=green" alt="downloads" /> | ||
| <img src="https://img.shields.io/npm/l/pxtorem-css?style=flat-square" alt="license" /> | ||
| <img src="https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript" alt="typescript" /> | ||
| </p> | ||
| ## Description | ||
| --- | ||
| pxtorem-css is a command line interface tool that converts pixel values to rem values in any CSS file. This tool is built using pure Node.js and comes with several advanced features to help you save time when writing CSS code. | ||
| ## ✨ Features | ||
| Features: | ||
| - 🎯 **Multiple Units** — Convert to `rem`, `em`, `vw`, `vh`, `vmin`, `vmax`, `%` | ||
| - ⚡ **Fast & Lightweight** — Zero dependencies (except postcss) | ||
| - 🔧 **Highly Configurable** — Per-property settings, custom transforms | ||
| - 💬 **Comment Control** — Disable conversion with inline comments | ||
| - 📊 **Conversion Reports** — Track what was converted | ||
| - 🖥️ **CLI Included** — Convert files from command line | ||
| - � **TypeScript** — Full type definitions included | ||
| - Converts px to rem in CSS files quickly and easily | ||
| - Allows you to choose the CSS directory and output directory | ||
| - Enables you to include and exclude specific CSS files for conversion | ||
| - Allows you to ignore specific CSS attributes that you don't want to convert | ||
| - You can customize the tool's options with a pxtorem.config.json file in your project | ||
| - Can be installed both locally and globally using npm/ | ||
| --- | ||
| ## Installation | ||
| ## 📦 Installation | ||
| Locally: | ||
| ```bash | ||
| # npm | ||
| npm install pxtorem-css postcss --save-dev | ||
| ```bash | ||
| npm i pxtorem-css | ||
| # yarn | ||
| yarn add pxtorem-css postcss -D | ||
| # pnpm | ||
| pnpm add pxtorem-css postcss -D | ||
| ``` | ||
| Globally: | ||
| --- | ||
| ```bash | ||
| npm i -g pxtorem-css | ||
| ## 🚀 Quick Start | ||
| ### PostCSS Config | ||
| ```js | ||
| // postcss.config.js | ||
| module.exports = { | ||
| plugins: [ | ||
| require('pxtorem-css')({ | ||
| baseSize: 16, | ||
| properties: ['*'], | ||
| }), | ||
| ], | ||
| }; | ||
| ``` | ||
| ## Usage | ||
| ### CLI | ||
| ```bash | ||
| $ pxtorem [options] | ||
| # Convert a file | ||
| npx pxtorem style.css | ||
| Options: | ||
| -init,--init [type] Init pxtorem options json (preset: "pxtorem.config.json") | ||
| -s, --size [type] Select html size (default: "16", preset: "16") | ||
| -d, --dir [type...] Select css directory (default: ["/"], preset: "/") | ||
| -t, --type [type] Select css ext type example: .scss (default: ".css", preset: ".css") | ||
| -i, --ignore [type...] Ignore css attribute (default: [], preset: []) | ||
| -r, --replace [type] For replace file name (default: false, preset: false) | ||
| -o, --output [type] Output directory (default: "", preset: "") | ||
| -in, --include [type...] For include css file path (default: [], preset: []) | ||
| -ex, --exclude [type...] For exclue css file path (default: [], preset: []) | ||
| -c, --config [type] For json config file (default: "", preset: "") | ||
| -h, --help display help for command | ||
| # With options | ||
| npx pxtorem style.css -b 16 -u rem --min-value 2 | ||
| ``` | ||
| --- | ||
| ## 📖 Usage Examples | ||
| <details> | ||
| <summary><strong>Vite</strong></summary> | ||
| ```js | ||
| // vite.config.js | ||
| import { defineConfig } from 'vite'; | ||
| import pxtorem from 'pxtorem-css'; | ||
| export default defineConfig({ | ||
| css: { | ||
| postcss: { | ||
| plugins: [ | ||
| pxtorem({ | ||
| baseSize: 16, | ||
| properties: ['*'], | ||
| }), | ||
| ], | ||
| }, | ||
| }, | ||
| }); | ||
| ``` | ||
| ### Example | ||
| </details> | ||
| `$ pxtorem` : change all directories css file. <br> | ||
| <details> | ||
| <summary><strong>Next.js</strong></summary> | ||
| `$ pxtorem -d public/css` : change all css file inside public/css dir.<br> | ||
| ```js | ||
| // postcss.config.js | ||
| module.exports = { | ||
| plugins: { | ||
| 'pxtorem-css': { | ||
| baseSize: 16, | ||
| properties: ['*'], | ||
| }, | ||
| }, | ||
| }; | ||
| ``` | ||
| `$ pxtorem -d public/css -t .scss` : change all .scss extname file inside public/css dir.<br> | ||
| </details> | ||
| `$ pxtorem -d public/css -t .scss -o public/remcss` : change all .scss extname file inside public/css dir and write file to public/remcss dir.<br> | ||
| <details> | ||
| <summary><strong>Webpack</strong></summary> | ||
| `$ pxtorem -d public/css -i box-shadow margin-left padding-left` : change all css file inside public/css dir except box-shadow margin-left padding-left attribute px.<br> | ||
| ```js | ||
| // postcss.config.js | ||
| module.exports = { | ||
| plugins: [ | ||
| require('pxtorem-css')({ | ||
| baseSize: 16, | ||
| properties: ['*'], | ||
| minValue: 2, | ||
| }), | ||
| require('autoprefixer'), | ||
| ], | ||
| }; | ||
| ``` | ||
| `$ pxtorem -d public/css -r my/name/rem/.ext` : change all css filename example: style.css to mystylerem.css and you must follow this pattern {your custom text before name}/name/{your custom text after name}/.ext<br> | ||
| </details> | ||
| `$ pxtorem -c pxtorem.json` : Customize your options with json file in your project folder<br> | ||
| <details> | ||
| <summary><strong>Node.js Script</strong></summary> | ||
| `$ pxtorem -init` : generate pxtorem.config.json (or you can give custom name by passing value after -init) into your project folder<br> | ||
| ```js | ||
| const fs = require('fs'); | ||
| const postcss = require('postcss'); | ||
| const pxtorem = require('pxtorem-css'); | ||
| ### pxtorem.json Example | ||
| const css = fs.readFileSync('input.css', 'utf8'); | ||
| ```bash | ||
| { | ||
| "size": "16", | ||
| "dir": ["public/css"], | ||
| "type": ".css", | ||
| "ignore": ["margin", "padding", "box-shadow"], | ||
| "replace": "{your custom word}/name/{your custom word}/.ext", | ||
| "output": "", | ||
| "include": [], | ||
| "exclude": [] | ||
| postcss([pxtorem({ baseSize: 16 })]) | ||
| .process(css) | ||
| .then((result) => { | ||
| fs.writeFileSync('output.css', result.css); | ||
| }); | ||
| ``` | ||
| </details> | ||
| --- | ||
| ## ⚙️ Options | ||
| | Option | Type | Default | Description | | ||
| | --------------------- | ---------------------- | ---------- | ---------------------------------------------------------- | | ||
| | `baseSize` | `number \| function` | `16` | Base font size for conversion | | ||
| | `toUnit` | `string` | `'rem'` | Target unit (`rem`, `em`, `vw`, `vh`, `vmin`, `vmax`, `%`) | | ||
| | `fromUnit` | `string` | `'px'` | Source unit to convert | | ||
| | `precision` | `number` | `5` | Decimal precision | | ||
| | `properties` | `string[]` | `['*']` | Properties to convert (supports wildcards) | | ||
| | `skipSelectors` | `(string \| RegExp)[]` | `[]` | Selectors to skip | | ||
| | `minValue` | `number` | `0` | Skip values below this | | ||
| | `maxValue` | `number` | `Infinity` | Skip values above this | | ||
| | `convertMediaQueries` | `boolean` | `false` | Convert in media queries | | ||
| | `replaceOriginal` | `boolean` | `true` | Replace vs add fallback | | ||
| | `propertyBaseSize` | `object` | `{}` | Per-property base sizes | | ||
| | `convert` | `function` | `null` | Custom conversion function | | ||
| | `verbose` | `boolean` | `false` | Log conversions | | ||
| --- | ||
| ## 🎨 Advanced Examples | ||
| ### Property Wildcards | ||
| ```js | ||
| pxtorem({ | ||
| properties: ['font*', '*margin*', '!border*'], | ||
| // ✓ font-size, font-weight, margin, margin-top | ||
| // ✗ border, border-width | ||
| }); | ||
| ``` | ||
| ### Per-Property Base Size | ||
| ```js | ||
| pxtorem({ | ||
| baseSize: 16, | ||
| propertyBaseSize: { | ||
| 'font-size': 14, | ||
| 'line-height': 20, | ||
| }, | ||
| }); | ||
| ``` | ||
| ### Custom Transform | ||
| ```js | ||
| pxtorem({ | ||
| convert: (px, property, selector) => { | ||
| // Skip small values | ||
| if (px < 4) return false; | ||
| // Use CSS variable | ||
| if (px === 16) return 'var(--base-size)'; | ||
| // Round to 0.25rem | ||
| return Math.round((px / 16) * 4) / 4; | ||
| }, | ||
| }); | ||
| ``` | ||
| ### Viewport Units (Mobile-First) | ||
| ```js | ||
| pxtorem({ | ||
| toUnit: 'vw', | ||
| baseSize: 3.75, // 375px / 100vw | ||
| properties: ['*'], | ||
| }); | ||
| ``` | ||
| --- | ||
| ## 💬 Comment Control | ||
| Disable conversion with inline comments: | ||
| ```css | ||
| .element { | ||
| font-size: 16px; /* → 1rem */ | ||
| padding: 20px; /* pxtorem-disable-line */ /* → 20px (skipped) */ | ||
| /* pxtorem-disable */ | ||
| margin: 32px; /* → 32px (skipped) */ | ||
| border: 1px solid; /* → 1px (skipped) */ | ||
| /* pxtorem-enable */ | ||
| width: 100px; /* → 6.25rem */ | ||
| } | ||
| ``` | ||
| ## Contribution | ||
| | Comment | Effect | | ||
| | --------------------------------- | -------------------- | | ||
| | `/* pxtorem-disable-line */` | Skip current line | | ||
| | `/* pxtorem-disable-next-line */` | Skip next line | | ||
| | `/* pxtorem-disable */` | Disable until enable | | ||
| | `/* pxtorem-enable */` | Re-enable | | ||
| If you want to contribute or report any bug, you welcome | ||
| --- | ||
| <br> | ||
| Don't forget to give a star 😍 | ||
| ## 🖥️ CLI Reference | ||
| ```bash | ||
| pxtorem [options] <input> | ||
| ``` | ||
| | Option | Description | | ||
| | ------------------------- | --------------------------------- | | ||
| | `-o, --output <path>` | Output file/directory | | ||
| | `-b, --base-size <n>` | Base font size | | ||
| | `-u, --to-unit <unit>` | Target unit | | ||
| | `-p, --precision <n>` | Decimal precision | | ||
| | `--properties <list>` | Comma-separated properties | | ||
| | `--skip-selectors <list>` | Comma-separated selectors to skip | | ||
| | `--min-value <n>` | Min px value | | ||
| | `--max-value <n>` | Max px value | | ||
| | `--media-queries` | Convert in media queries | | ||
| | `--no-replace` | Add fallback instead of replacing | | ||
| | `-v, --verbose` | Verbose output | | ||
| | `-h, --help` | Show help | | ||
| ### CLI Examples | ||
| ```bash | ||
| # Basic conversion | ||
| pxtorem style.css | ||
| # Custom options | ||
| pxtorem style.css -b 16 -u rem -p 5 --min-value 2 | ||
| # Different output | ||
| pxtorem -o dist/styles.css src/styles.css | ||
| # Directory conversion | ||
| pxtorem -o dist/css src/css | ||
| # Filter properties | ||
| pxtorem style.css --properties "font-size,margin,padding" | ||
| ``` | ||
| --- | ||
| ## 📘 TypeScript | ||
| ```ts | ||
| import pxtorem, { Options, ConversionReport, TargetUnit } from 'pxtorem-css'; | ||
| const options: Options = { | ||
| baseSize: 16, | ||
| toUnit: 'rem', | ||
| onConversionComplete: (report: ConversionReport) => { | ||
| console.log(`Converted: ${report.convertedDeclarations}`); | ||
| }, | ||
| }; | ||
| ``` | ||
| --- | ||
| ## 📄 License | ||
| MIT © [Rashed Iqbal](https://github.com/iqbal-rashed) |
-156
| const chalk = require("chalk"); | ||
| const fs = require("fs"); | ||
| const path = require("path"); | ||
| function getListOfFile(dir, type, fileList) { | ||
| let files = fs.readdirSync(dir); | ||
| fileList = fileList || []; | ||
| files.forEach((f) => { | ||
| if (!f.startsWith(".")) { | ||
| let filePath = path.join(dir, f); | ||
| if (fs.statSync(filePath).isDirectory()) { | ||
| fileList = getListOfFile(filePath, type, fileList); | ||
| } else { | ||
| if (f.endsWith(type)) { | ||
| fileList.push(filePath); | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| return fileList; | ||
| } | ||
| function pxtorem( | ||
| { size, dir, type, ignore, replace, output, include, exclude }, | ||
| spinner | ||
| ) { | ||
| let finalArr = []; | ||
| dir.forEach((v) => { | ||
| const finalDir = path.join(process.cwd(), v); | ||
| finalArr = finalArr.concat(getListOfFile(finalDir, type)); | ||
| }); | ||
| let finalPathArr = finalArr.map((v) => path.resolve(v)) || []; | ||
| if (include.length > 0) { | ||
| include.forEach((v) => { | ||
| if (finalPathArr.indexOf(v) === -1) { | ||
| finalPathArr.push(v); | ||
| } | ||
| }); | ||
| } | ||
| if (exclude.length > 0) { | ||
| exclude.forEach((v) => { | ||
| if (finalPathArr.indexOf(v) !== -1) { | ||
| finalPathArr.splice(finalPathArr.indexOf(v), 1); | ||
| } | ||
| }); | ||
| } | ||
| if (finalPathArr.length === 0) { | ||
| spinner.fail(chalk.red("At the end no css file found")); | ||
| process.exit(1); | ||
| } | ||
| finalPathArr.forEach((v) => { | ||
| try { | ||
| let fileName = path.basename(v); | ||
| if (replace) { | ||
| fileName = fileName.replace( | ||
| new RegExp(path.extname(v), "g"), | ||
| "" | ||
| ); | ||
| fileName = replace.replace(new RegExp("/name/", "g"), fileName); | ||
| fileName = fileName.replace( | ||
| new RegExp("/.ext", "g"), | ||
| path.extname(v) | ||
| ); | ||
| console.log(fileName); | ||
| } | ||
| const readFileData = fs.readFileSync(v, "utf-8"); | ||
| if (!readFileData) { | ||
| throw new Error("Css file empty"); | ||
| } | ||
| if (!size) { | ||
| throw new Error("Html size not valid"); | ||
| } | ||
| const changeData = changePxToRem(readFileData, size, ignore); | ||
| if (output) { | ||
| const destination = path.join(output, fileName); | ||
| fs.writeFileSync(destination, changeData, { | ||
| encoding: "utf-8", | ||
| flag: "w", | ||
| }); | ||
| } else { | ||
| const destination = path.join(path.dirname(v), fileName); | ||
| fs.writeFileSync(destination, changeData, { | ||
| encoding: "utf-8", | ||
| flag: "w", | ||
| }); | ||
| } | ||
| } catch (error) { | ||
| spinner.fail(chalk.red("Something went wrong", error.message)); | ||
| } | ||
| }); | ||
| } | ||
| function changePxToRem(data, size, ignore) { | ||
| const myRegex = /[-]?([a-z]*[-])?[a-z]*?\:.*?.*?px(;)?.*/g; | ||
| const finalRegex = | ||
| /[-]?([a-z]*[-])?[a-z]*?\:.*?([0-9]*[.])?[0-9]*?px+(\s+([0-9]*[.])?[0-9]*?px)*/g; | ||
| const attributeValueRegex = | ||
| /[-]?([a-z]*[-])?[a-z]*?\:.*?([0-9]*[.])?[0-9]*?px/g; | ||
| const valueRegex = /([0-9]*[.])?[0-9]*?px/g; | ||
| let result = data.match(finalRegex); | ||
| if (!result) { | ||
| console.log(chalk.red("Match result not found")); | ||
| process.exit(1); | ||
| } | ||
| let filterResult = []; | ||
| if (Array.isArray(ignore) && ignore.length > 0) { | ||
| filterResult = result.filter((res) => { | ||
| for (let i = 0; i < ignore.length; i++) { | ||
| const element = ignore[i]; | ||
| if (res.includes(element + ":")) { | ||
| return res; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| let finalResult = result.filter((v) => !filterResult.includes(v)) || []; | ||
| let valueResult = finalResult.map((v) => { | ||
| return v.match(valueRegex); | ||
| }); | ||
| // result.forEach((v) => { | ||
| // const pxValue = v.slice(0, -2); | ||
| // const valueRegex = new RegExp(`${pxValue}px`, "g"); | ||
| // finalData = finalData.replace( | ||
| // valueRegex, | ||
| // `${pxValue / parseFloat(size)}rem` | ||
| // ); | ||
| // }); | ||
| let finalData = data; | ||
| for (let i = 0; i < finalResult.length; i++) { | ||
| const element = finalResult[i]; | ||
| const valueArr = valueResult[i]; | ||
| let replaceElement = element; | ||
| for (let j = 0; j < valueArr.length; j++) { | ||
| const value = valueArr[j]; | ||
| const pxValue = value.slice(0, -2); | ||
| replaceElement = replaceElement.replace( | ||
| value, | ||
| `${parseFloat(pxValue) / parseFloat(size)}rem` | ||
| ); | ||
| } | ||
| // console.log(replaceElement); | ||
| finalData = finalData.replace(element, replaceElement); | ||
| } | ||
| return finalData; | ||
| } | ||
| module.exports = pxtorem; |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
42976
190.16%2
-33.33%8
100%279
95.1%315
238.71%8
Infinity%4
300%4
300%1
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed