@master/eslint-plugin-css 2.0.0-beta.186 → 2.0.0-beta.187
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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.js +1 -0
- package/dist/rules/class-collision.d.ts +3 -0
- package/dist/rules/class-collision.js +1 -0
- package/dist/rules/class-order.d.ts +3 -0
- package/dist/rules/class-order.js +1 -0
- package/dist/rules/class-validation.d.ts +3 -0
- package/dist/rules/class-validation.js +1 -0
- package/dist/settings.d.ts +7 -0
- package/dist/settings.js +1 -0
- package/dist/utils/are-declarations-equal.d.ts +1 -0
- package/dist/utils/are-declarations-equal.js +1 -0
- package/dist/utils/ast.d.ts +31 -0
- package/dist/utils/ast.js +1 -0
- package/dist/utils/define-visitors.d.ts +5 -0
- package/dist/utils/define-visitors.js +1 -0
- package/dist/utils/resolve-context.d.ts +5 -0
- package/dist/utils/resolve-context.js +1 -0
- package/package.json +1 -1
- package/lib/index.js +0 -5
- package/lib/rules/class-order.js +0 -202
- package/lib/rules/class-validation.js +0 -123
- package/lib/utils/ast.js +0 -218
- package/lib/utils/resolve-context.js +0 -21
- package/tests/frameworks/react.js +0 -55
- package/tests/frameworks/svelte.js +0 -24
- package/tests/frameworks/vue.js +0 -24
- package/tests/order.js +0 -66
- package/tests/validation.js +0 -69
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var f=Object.create;var t=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var l=Object.getOwnPropertyNames;var n=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty;var u=(o,p,r,m)=>{if(p&&typeof p=="object"||typeof p=="function")for(let i of l(p))!s.call(o,i)&&i!==r&&t(o,i,{get:()=>p[i],enumerable:!(m=g(p,i))||m.enumerable});return o};var x=(o,p,r)=>(r=o!=null?f(n(o)):{},u(p||!o||!o.__esModule?t(r,"default",{value:o,enumerable:!0}):r,o));var e=x(require("./plugin"));module.exports=e.default;
|
package/dist/plugin.d.ts
ADDED
package/dist/plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create;var r=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var u=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var v=(s,l)=>{for(var o in l)r(s,o,{get:l[o],enumerable:!0})},t=(s,l,o,c)=>{if(l&&typeof l=="object"||typeof l=="function")for(let i of f(l))!g.call(s,i)&&i!==o&&r(s,i,{get:()=>l[i],enumerable:!(c=p(l,i))||c.enumerable});return s};var a=(s,l,o)=>(o=s!=null?e(u(s)):{},t(l||!s||!s.__esModule?r(o,"default",{value:s,enumerable:!0}):o,s)),x=s=>t(r({},"__esModule",{value:!0}),s);var V={};v(V,{default:()=>O});module.exports=x(V);var m=a(require("./rules/class-collision")),n=a(require("./rules/class-order")),d=a(require("./rules/class-validation"));const C={rules:{"class-collision":m.default,"class-order":n.default,"class-validation":d.default}};var O=C;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var M=Object.create;var f=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var q=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var D=(e,o)=>{for(var s in o)f(e,s,{get:o[s],enumerable:!0})},R=(e,o,s,c)=>{if(o&&typeof o=="object"||typeof o=="function")for(let l of V(o))!A.call(e,l)&&l!==s&&f(e,l,{get:()=>o[l],enumerable:!(c=N(o,l))||c.enumerable});return e};var m=(e,o,s)=>(s=e!=null?M(q(e)):{},R(o||!e||!e.__esModule?f(s,"default",{value:e,enumerable:!0}):s,e)),K=e=>R(f({},"__esModule",{value:!0}),e);var U={};D(U,{default:()=>O});module.exports=K(U);var S=m(require("../utils/ast")),$=m(require("../utils/are-declarations-equal")),v=m(require("../utils/define-visitors")),x=m(require("../utils/resolve-context")),y=require("@master/css-validator"),O={meta:{docs:{description:"Avoid applying classes with the same CSS declaration",category:"Stylistic Issues",recommended:!1,url:"https://beta.css.master.co/docs/code-linting#class-collision-detection"},messages:{collisionClass:"{{message}}"},fixable:"code"},create(e){const{options:o,settings:s,config:c}=(0,x.default)(e);return(0,v.default)({context:e,options:o,settings:s,config:c},(E,L=null)=>{S.parseNodeRecursive(E,L,(n,C,T,b,j)=>{const k=e.sourceCode.lines,w=C.loc.start.line,I=C.loc.end.line,p={};console.log(n),n.forEach(i=>{p[i]=(0,y.createValidRules)(i,{config:c})[0]});for(let i=0;i<n.length;i++){const u=n[i],g=p[u],d=[];if(g)for(let a=0;a<n.length;a++){const r=n[a],t=p[r];i!==a&&t&&(0,$.default)(g.declarations,t.declarations)&&g.stateToken===t.stateToken&&d.push(r)}if(d.length>0){const a=d.map(t=>`"${t}"`).join(" and ");let r=T;for(const t of d){const h=t.replace(/(\\|\.|\(|\)|\[|\]|\{|\}|\+|\*|\?|\^|\$|\||\/)/g,"\\$1");r=r.replace(new RegExp(`\\s+${h}|${h}\\s+`),"")}e.report({loc:S.findLoc(u,k,w,I),messageId:"collisionClass",data:{message:`"${u}" applies the same CSS declarations as ${a}.`},fix:function(t){return t.replaceTextRange([b,j],r)}})}}},!1,!1,s.ignoredKeys,e)})}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var $=Object.create;var y=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var q=(e,a)=>{for(var n in a)y(e,n,{get:a[n],enumerable:!0})},T=(e,a,n,C)=>{if(a&&typeof a=="object"||typeof a=="function")for(let r of I(a))!P.call(e,r)&&r!==n&&y(e,r,{get:()=>a[r],enumerable:!(C=F(a,r))||C.enumerable});return e};var N=(e,a,n)=>(n=e!=null?$(O(e)):{},T(a||!e||!e.__esModule?y(n,"default",{value:e,enumerable:!0}):n,e)),A=e=>T(y({},"__esModule",{value:!0}),e);var U={};q(U,{default:()=>B});module.exports=A(U);var m=N(require("../utils/ast")),V=N(require("../utils/define-visitors")),R=N(require("../utils/resolve-context")),S=require("@master/css"),B={meta:{docs:{description:"Enforce a consistent and logical order of classes",category:"Stylistic Issues",recommended:!1,url:"https://beta.css.master.co/docs/code-linting#consistent-class-order"},messages:{invalidClassOrder:"No consistent class order followed."},fixable:"code"},create:function(e){const{options:a,settings:n,config:C}=(0,R.default)(e),r=(l,s=null)=>{let i=null,p=null,d=null,E="",v="";if(s===null){i=m.extractValueFromNode(l);const t=m.extractRangeFromNode(l);l.type==="TextAttribute"?(p=t[0],d=t[1]):(p=t[0]+1,d=t[1]-1)}else switch(s.type){case"Identifier":return;case"TemplateLiteral":s.expressions.forEach(o=>{r(l,o)}),s.quasis.forEach(o=>{r(l,o)});return;case"ConditionalExpression":r(l,s.consequent),r(l,s.alternate);return;case"LogicalExpression":r(l,s.right);return;case"ArrayExpression":s.elements.forEach(o=>{r(l,o)});return;case"ObjectExpression":const t=l.callee&&l.callee.name==="classnames",x=l.key&&l.key.type==="VDirectiveKey";s.properties.forEach(o=>{const b=t||x?o.key:o.value;r(l,b)});return;case"Property":r(l,s.key);break;case"Literal":i=s.value,p=s.range[0]+1,d=s.range[1]-1;break;case"SvelteLiteral":if(i=s.value,i==="")return;p=s.range[0],d=s.range[1];break;case"TemplateElement":if(i=s.value.raw,i==="")return;p=s.range[0],d=s.range[1];const c=e.sourceCode.getText(s);E=m.getTemplateElementPrefix(c,i),v=m.getTemplateElementSuffix(c,i),i=m.getTemplateElementBody(c,E,v);break}const{classNames:h,whitespaces:g,headSpace:k,tailSpace:L}=m.extractClassnamesFromValue(i);if(h.length<=1)return;let f=(0,S.reorderForReadableClasses)(h,C).filter(t=>h.includes(t));f=h.filter(t=>!f.includes(t)).concat(f).filter(t=>t.trim()!=="");let u="";for(let t=0;t<f.length;t++){const x=g[t]??"",c=f[t];u+=k?`${x}${c}`:`${c}${x}`,c&&(!L&&t===f.length-1&&(u=u.replace(/\s+$/,"")),k&&L&&t===f.length-1&&(u+=g[g.length-1]??""))}if(i!==u){u=E+u+v;const x=e.sourceCode.lines,c=l.loc.start.line,o=l.loc.end.line,b={loc:m.findLoc(i,x,c,o),messageId:"invalidClassOrder",fix:function(w){return w.replaceTextRange([p,d],u)}};e.report(b)}};return(0,V.default)({context:e,options:a,settings:n,config:C},r)}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var N=Object.create;var n=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,I=Object.prototype.hasOwnProperty;var b=(s,e)=>{for(var o in e)n(s,o,{get:e[o],enumerable:!0})},g=(s,e,o,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of S(e))!I.call(s,r)&&r!==o&&n(s,r,{get:()=>e[r],enumerable:!(a=R(e,r))||a.enumerable});return s};var l=(s,e,o)=>(o=s!=null?N(U(s)):{},g(e||!s||!s.__esModule?n(o,"default",{value:s,enumerable:!0}):o,s)),M=s=>g(n({},"__esModule",{value:!0}),s);var E={};b(E,{default:()=>D});module.exports=M(E);var t=l(require("../utils/ast")),p=l(require("../utils/define-visitors")),C=l(require("../utils/resolve-context")),w=require("@master/css-validator"),D={meta:{docs:{description:"Detect syntax errors early when writing classes",category:"Stylistic Issues",recommended:!1,url:"https://beta.css.master.co/docs/code-linting#syntax-error-checks"},messages:{invalidClass:"{{message}}",disallowUnknownClass:"{{message}}"},fixable:null},create:function(s){const{options:e,settings:o,config:a}=(0,C.default)(s);return(0,p.default)({context:s,options:e,settings:o,config:a},(v,y=null)=>{t.parseNodeRecursive(v,y,(k,c)=>{const d=s.sourceCode.lines,m=c.loc.start.line,f=c.loc.end.line;for(const i of k){const{isMasterCSS:L,errors:u}=(0,w.validate)(i,{config:a});if(u.length>0)for(const h of u)L?s.report({loc:t.findLoc(i,d,m,f),messageId:"invalidClass",data:{message:h.message+"."}}):e.disallowUnknownClass&&s.report({loc:t.findLoc(i,d,m,f),messageId:"disallowUnknownClass",data:{message:`"${i}" is not a valid or known class.`}})}},!1,!1,o.ignoredKeys,s)})}};
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty;var n=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var o=Object.prototype.hasOwnProperty;var r=(a,s)=>{for(var t in s)e(a,t,{get:s[t],enumerable:!0})},d=(a,s,t,l)=>{if(s&&typeof s=="object"||typeof s=="function")for(let c of i(s))!o.call(a,c)&&c!==t&&e(a,c,{get:()=>s[c],enumerable:!(l=n(s,c))||l.enumerable});return a};var g=a=>d(e({},"__esModule",{value:!0}),a);var u={};r(u,{default:()=>f});module.exports=g(u);const m={calleeMatching:"^(classnames|clsx|ctl|cva|cv|classVariant|styled(?:\\.\\w+)?)",classMatching:"^class(Name)?$",ignoredKeys:["compoundVariants","defaultVariants"],config:"master.css.*"};var f=m;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function areDeclarationsEqual(aDeclarations: any, bDeclarations: any): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var s=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var u=(t,e)=>{for(var r in e)s(t,r,{get:e[r],enumerable:!0})},y=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of f(e))!l.call(t,n)&&n!==r&&s(t,n,{get:()=>e[n],enumerable:!(o=c(e,n))||o.enumerable});return t};var p=t=>y(s({},"__esModule",{value:!0}),t);var O={};u(O,{default:()=>a});module.exports=p(O);function a(t,e){const r=Object.keys(t),o=Object.keys(e);if(r.length!==o.length)return!1;for(const n of r)if(!Object.prototype.hasOwnProperty.call(e,n))return!1;return!0}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Rule } from 'eslint';
|
|
2
|
+
export declare function extractRangeFromNode(node: any): any;
|
|
3
|
+
export declare function extractValueFromNode(node: any): any;
|
|
4
|
+
export declare function extractClassnamesFromValue(classStr: any): {
|
|
5
|
+
classNames: string[];
|
|
6
|
+
whitespaces: string[];
|
|
7
|
+
headSpace: boolean;
|
|
8
|
+
tailSpace: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Inspect and parse an abstract syntax node and run a callback function
|
|
12
|
+
*
|
|
13
|
+
* @param {ASTNode} rootNode The current root node being parsed by eslint
|
|
14
|
+
* @param {ASTNode} childNode The AST node child argument being checked
|
|
15
|
+
* @param {Function} cb The callback function
|
|
16
|
+
* @param {Boolean} skipConditional Optional, indicate distinct parsing for conditional nodes
|
|
17
|
+
* @param {Boolean} isolate Optional, set internally to isolate parsing and validation on conditional children
|
|
18
|
+
* @param {Array} ignoredKeys Optional, set object keys which should not be parsed e.g. for `cva`
|
|
19
|
+
* @returns {void}
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseNodeRecursive(rootNode: any, childNode: any, cb: any, skipConditional?: boolean, isolate?: boolean, ignoredKeys?: any[], context?: Rule.RuleContext): void;
|
|
22
|
+
export declare function getTemplateElementPrefix(text: any, raw: any): any;
|
|
23
|
+
export declare function getTemplateElementSuffix(text: any, raw: any): any;
|
|
24
|
+
export declare function getTemplateElementBody(text: any, prefix: any, suffix: any): any;
|
|
25
|
+
export declare function findLoc(text: any, lines: any, startLine: any, endLine: any): {
|
|
26
|
+
start: any;
|
|
27
|
+
end: {
|
|
28
|
+
line: any;
|
|
29
|
+
column: any;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var E=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var F=(e,t)=>{for(var r in t)E(e,r,{get:t[r],enumerable:!0})},P=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let u of j(t))!V.call(e,u)&&u!==r&&E(e,u,{get:()=>t[u],enumerable:!(s=O(t,u))||s.enumerable});return e};var q=e=>P(E({},"__esModule",{value:!0}),e);var J={};F(J,{extractClassnamesFromValue:()=>y,extractRangeFromNode:()=>L,extractValueFromNode:()=>w,findLoc:()=>I,getTemplateElementBody:()=>B,getTemplateElementPrefix:()=>h,getTemplateElementSuffix:()=>R,parseNodeRecursive:()=>x});module.exports=q(J);const S=/([\t\n\f\r ]+)/;function L(e){if(e.type==="TextAttribute"&&e.name==="class")return[e.valueSpan.fullStart.offset,e.valueSpan.end.offset];switch(e.value.type){case"JSXExpressionContainer":return e.value.expression.range;default:return e.value.range}}function w(e){if(e.type==="TextAttribute"&&e.name==="class")return e.value;switch(e.value.type){case"JSXExpressionContainer":return e.value.expression.value;case"VExpressionContainer":switch(e.value.expression.type){case"ArrayExpression":return e.value.expression.elements;case"ObjectExpression":return e.value.expression.properties}return e.value.expression.value;default:return e.value.value}}function y(e){if(typeof e!="string")return{classNames:[],whitespaces:[],headSpace:!1,tailSpace:!1};const t=e.split(S);t[0]===""&&t.shift(),t[t.length-1]===""&&t.pop();const r=S.test(t[0]),s=S.test(t[t.length-1]),u=(a,f)=>r?f%2!==0:f%2===0,n=(a,f)=>r?f%2===0:f%2!==0,m=t.filter(u),l=t.filter(n);return{classNames:m,whitespaces:l,headSpace:r,tailSpace:s}}function x(e,t,r,s=!1,u=!1,n=[],m=null){let l,a,f=null,i=null,g="",v="";if(t===null){if(l=w(e),{classNames:a}=y(l),a=[...new Set(a)],a.length===0)return;const c=L(e);e.type==="TextAttribute"?(f=c[0],i=c[1]):(f=c[0]+1,i=c[1]-1),r(a,e,l,f,i,g,v)}else{if(t===void 0)return;{const c=s?!0:u;let A=!1;switch(t.type){case"TemplateLiteral":t.expressions.forEach(p=>{x(e,p,r,s,c,n)}),t.quasis.forEach(p=>{x(e,p,r,s,u,n)});return;case"ConditionalExpression":x(e,t.consequent,r,s,c,n),x(e,t.alternate,r,s,c,n);return;case"LogicalExpression":x(e,t.right,r,s,c,n);return;case"ArrayExpression":t.elements.forEach(p=>{x(e,p,r,s,c,n)});return;case"ObjectExpression":t.properties.forEach(p=>{const C=e.callee&&e.callee.name==="classnames";p.type!=="SpreadElement"&&(p.key.type==="Identifier"&&n.includes(p.key.name)||x(e,C?p.key:p.value,r,s,c,n))});return;case"Property":x(e,t.key,r,s,c,n);return;case"Literal":A=!0,l=t.value,f=t.range[0]+1,i=t.range[1]-1;break;case"SvelteLiteral":l=t.value,f=t.range[0],i=t.range[1];break;case"TemplateElement":l=t.value.raw,f=t.range[0],i=t.range[1];const T=m?.sourceCode.getText(t)??"";g=h(T,l),v=R(T,l);break}if({classNames:a}=y(l),a=[...new Set(a)],a.length===0)return;r(a,u?null:e,l,f,i,g,v)}}}function h(e,t){return e.indexOf(t)===0?"":e.split(t).shift()}function R(e,t){return e.indexOf(t)===-1?"":e.split(t).pop()}function B(e,t,r){let s=e.split(t);return s.shift(),s=s.join(t).split(r),s.pop(),s.join(r)}function I(e,t,r,s){const u=e.match(/.+(?:\r\n|\n)?/g);let n=0,m=null,l=!1;for(let a=r;a<=s;a++){const i=t[a-1].indexOf(u[n].replace(/\r\n|\n/,""));if(i!==-1){if(n===0&&(m={line:a,column:i}),n===u.length-1)return{start:m,end:{line:a,column:i+e.length}};l=!0,n++}else l&&(l=!1,n=0,m=null)}return null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var p=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var o=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var E=(s,r)=>{for(var t in r)p(s,t,{get:r[t],enumerable:!0})},v=(s,r,t,l)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of o(r))!x.call(s,i)&&i!==t&&p(s,i,{get:()=>r[i],enumerable:!(l=m(r,i))||l.enumerable});return s};var g=s=>v(p({},"__esModule",{value:!0}),s);var V={};E(V,{default:()=>f});module.exports=g(V);function f({context:s,settings:r},t){const l=e=>{let a="";const n=e.callee||e.tag;return n.type==="Identifier"&&(a=n.name),n.type==="MemberExpression"&&(a=`${n.object.name}.${n.property.name}`),new RegExp(r.calleeMatching).test(a)},i=function(e){l(e)&&e.arguments.forEach(a=>{t(e,a)})},u=new RegExp(r.classMatching),c={CallExpression:i,JSXAttribute:function(e){!e.name||!u.test(e.name.name)||(e.value&&e.value.type==="Literal"?t(e):e.value&&e.value.type==="JSXExpressionContainer"&&t(e,e.value.expression))},SvelteAttribute:function(e){if(!(!e.key?.name||!u.test(e.key.name)))for(const a of e.value)t(e,a)},TextAttribute:function(e){!e.name||!u.test(e.name)||t(e)},TaggedTemplateExpression:function(e){if(l(e)){t(e,e.quasi);return}}},y={CallExpression:i,VAttribute:function(e){e.value&&e.value.type==="VLiteral"?t(e):e.value&&e.value.type==="VExpressionContainer"&&e.value.expression.type==="ArrayExpression"?e.value.expression.elements.forEach(a=>{t(e,a)}):e.value&&e.value.type==="VExpressionContainer"&&e.value.expression.type==="ObjectExpression"&&e.value.expression.properties.forEach(a=>{t(e,a)})}};return s.sourceCode.parserServices==null||s.sourceCode.parserServices.defineTemplateBodyVisitor==null?c:s.sourceCode.parserServices.defineTemplateBodyVisitor(y,c)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=Object.create;var n=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var u=Object.getPrototypeOf,b=Object.prototype.hasOwnProperty;var d=(o,t)=>{for(var s in t)n(o,s,{get:t[s],enumerable:!0})},r=(o,t,s,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of a(t))!b.call(o,e)&&e!==s&&n(o,e,{get:()=>t[e],enumerable:!(i=l(t,e))||i.enumerable});return o};var f=(o,t,s)=>(s=o!=null?m(u(o)):{},r(t||!o||!o.__esModule?n(s,"default",{value:o,enumerable:!0}):s,o)),j=o=>r(n({},"__esModule",{value:!0}),o);var v={};d(v,{default:()=>p});module.exports=j(v);var g=f(require("../settings")),c=f(require("explore-config"));function p(o){const t=Object.assign(g.default,o.settings?.["@master/css"]),s=t?.config;return{settings:t,options:o.options[0]||{},config:typeof s=="object"?s:(0,c.default)(t?.config||"")}}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@master/eslint-plugin-css","scripts":{"
|
|
1
|
+
{"name":"@master/eslint-plugin-css","scripts":{"build":"techor pack 'src/**/*.ts' --format cjs","dev":"pnpm run build --watch --no-declare","test":"jest","type-check":"tsc --noEmit","lint":"eslint src"},"keywords":["mastercss","eslint","eslintplugin"],"license":"MIT","description":"Master CSS ESLint Plugin","author":"Aoyue Design LLC.","funding":"https://css.master.co/docs/donate","homepage":"https://css.master.co","bugs":{"url":"https://github.com/master-co/css/issues"},"repository":{"type":"git","url":"https://github.com/master-co/css.git","directory":"packages/eslint-plugin"},"sideEffects":false,"main":"./dist/index.js","types":"./dist/index.d.ts","exports":"./dist/index.js","files":["dist"],"publishConfig":{"access":"public"},"dependencies":{"@master/css":"^2.0.0-beta.187","@master/css-validator":"^2.0.0-beta.187","explore-config":"^2.2.10"},"devDependencies":{"@angular-eslint/template-parser":"^16.2.0","@types/node":"^20.6.0","@typescript-eslint/parser":"^5.50.0","@typescript-eslint/utils":"^6.9.0","eslint":"^8.0.0","svelte-eslint-parser":"^0.33.1","vue-eslint-parser":"^9.3.1"},"version":"2.0.0-beta.187"}
|
package/lib/index.js
DELETED
package/lib/rules/class-order.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const astUtil = require('../utils/ast')
|
|
4
|
-
const resolveContext = require('../utils/resolve-context')
|
|
5
|
-
const { reorderForReadableClasses } = require('@master/css')
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
meta: {
|
|
9
|
-
docs: {
|
|
10
|
-
description: 'Enforce a consistent and logical order of classes',
|
|
11
|
-
category: 'Stylistic Issues',
|
|
12
|
-
recommended: false,
|
|
13
|
-
url: 'https://beta.css.master.co/docs/code-linting#enforce-a-consistent-and-logical-order-of-classes',
|
|
14
|
-
},
|
|
15
|
-
messages: {
|
|
16
|
-
invalidClassOrder: 'No consistent class order followed.',
|
|
17
|
-
},
|
|
18
|
-
fixable: 'code'
|
|
19
|
-
},
|
|
20
|
-
create: function (context) {
|
|
21
|
-
const { options, settings, config } = resolveContext(context)
|
|
22
|
-
const sortNodeArgumentValue = (node, arg = null) => {
|
|
23
|
-
let originalClassNamesValue = null
|
|
24
|
-
let start = null
|
|
25
|
-
let end = null
|
|
26
|
-
let prefix = ''
|
|
27
|
-
let suffix = ''
|
|
28
|
-
if (arg === null) {
|
|
29
|
-
originalClassNamesValue = astUtil.extractValueFromNode(node)
|
|
30
|
-
const range = astUtil.extractRangeFromNode(node)
|
|
31
|
-
if (node.type === 'TextAttribute') {
|
|
32
|
-
start = range[0]
|
|
33
|
-
end = range[1]
|
|
34
|
-
} else {
|
|
35
|
-
start = range[0] + 1
|
|
36
|
-
end = range[1] - 1
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
switch (arg.type) {
|
|
40
|
-
case 'Identifier':
|
|
41
|
-
return
|
|
42
|
-
case 'TemplateLiteral':
|
|
43
|
-
arg.expressions.forEach((exp) => {
|
|
44
|
-
sortNodeArgumentValue(node, exp)
|
|
45
|
-
})
|
|
46
|
-
arg.quasis.forEach((quasis) => {
|
|
47
|
-
sortNodeArgumentValue(node, quasis)
|
|
48
|
-
})
|
|
49
|
-
return
|
|
50
|
-
case 'ConditionalExpression':
|
|
51
|
-
sortNodeArgumentValue(node, arg.consequent)
|
|
52
|
-
sortNodeArgumentValue(node, arg.alternate)
|
|
53
|
-
return
|
|
54
|
-
case 'LogicalExpression':
|
|
55
|
-
sortNodeArgumentValue(node, arg.right)
|
|
56
|
-
return
|
|
57
|
-
case 'ArrayExpression':
|
|
58
|
-
arg.elements.forEach((el) => {
|
|
59
|
-
sortNodeArgumentValue(node, el)
|
|
60
|
-
})
|
|
61
|
-
return
|
|
62
|
-
case 'ObjectExpression':
|
|
63
|
-
const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames'
|
|
64
|
-
const isVue = node.key && node.key.type === 'VDirectiveKey'
|
|
65
|
-
arg.properties.forEach((prop) => {
|
|
66
|
-
const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value
|
|
67
|
-
sortNodeArgumentValue(node, propVal)
|
|
68
|
-
})
|
|
69
|
-
return
|
|
70
|
-
case 'Property':
|
|
71
|
-
sortNodeArgumentValue(node, arg.key)
|
|
72
|
-
break
|
|
73
|
-
case 'Literal':
|
|
74
|
-
originalClassNamesValue = arg.value
|
|
75
|
-
start = arg.range[0] + 1
|
|
76
|
-
end = arg.range[1] - 1
|
|
77
|
-
break
|
|
78
|
-
case 'SvelteLiteral':
|
|
79
|
-
originalClassNamesValue = arg.value
|
|
80
|
-
if (originalClassNamesValue === '') {
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
start = arg.range[0]
|
|
84
|
-
end = arg.range[1]
|
|
85
|
-
break
|
|
86
|
-
case 'TemplateElement':
|
|
87
|
-
originalClassNamesValue = arg.value.raw
|
|
88
|
-
if (originalClassNamesValue === '') {
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
start = arg.range[0]
|
|
92
|
-
end = arg.range[1]
|
|
93
|
-
// https://github.com/eslint/eslint/issues/13360
|
|
94
|
-
// The problem is that range computation includes the backticks (`test`)
|
|
95
|
-
// but value.raw does not include them, so there is a mismatch.
|
|
96
|
-
// start/end does not include the backticks, therefore it matches value.raw.
|
|
97
|
-
const txt = context.getSourceCode().getText(arg)
|
|
98
|
-
prefix = astUtil.getTemplateElementPrefix(txt, originalClassNamesValue)
|
|
99
|
-
suffix = astUtil.getTemplateElementSuffix(txt, originalClassNamesValue)
|
|
100
|
-
originalClassNamesValue = astUtil.getTemplateElementBody(txt, prefix, suffix)
|
|
101
|
-
break
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let { classNames, whitespaces, headSpace, tailSpace } =
|
|
106
|
-
astUtil.extractClassnamesFromValue(originalClassNamesValue)
|
|
107
|
-
|
|
108
|
-
if (classNames.length <= 1) {
|
|
109
|
-
// Don't run sorting for a single or empty className
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
let orderedClassNames = reorderForReadableClasses(classNames, config)
|
|
114
|
-
.filter(eachOrderedClassName => classNames.includes(eachOrderedClassName))
|
|
115
|
-
|
|
116
|
-
orderedClassNames = classNames.filter(x => !orderedClassNames.includes(x))
|
|
117
|
-
.concat(orderedClassNames)
|
|
118
|
-
.filter(x => x.trim() !== '')
|
|
119
|
-
|
|
120
|
-
// Generates the validated/sorted attribute value
|
|
121
|
-
let validatedClassNamesValue = ''
|
|
122
|
-
for (let i = 0; i < orderedClassNames.length; i++) {
|
|
123
|
-
const w = whitespaces[i] ?? ''
|
|
124
|
-
const cls = orderedClassNames[i]
|
|
125
|
-
validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`
|
|
126
|
-
if (headSpace && tailSpace && i === orderedClassNames.length - 1) {
|
|
127
|
-
validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? ''
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (originalClassNamesValue !== validatedClassNamesValue) {
|
|
132
|
-
validatedClassNamesValue = prefix + validatedClassNamesValue + suffix
|
|
133
|
-
context.report({
|
|
134
|
-
node: node,
|
|
135
|
-
messageId: 'invalidClassOrder',
|
|
136
|
-
fix: function (fixer) {
|
|
137
|
-
return fixer.replaceTextRange([start, end], validatedClassNamesValue)
|
|
138
|
-
},
|
|
139
|
-
})
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const callExpressionVisitor = function (node) {
|
|
144
|
-
const calleeStr = astUtil.calleeToString(node.callee)
|
|
145
|
-
if (settings.callees.findIndex((name) => calleeStr === name) === -1) {
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
node.arguments.forEach((arg) => {
|
|
150
|
-
sortNodeArgumentValue(node, arg)
|
|
151
|
-
})
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const scriptVisitor = {
|
|
155
|
-
CallExpression: callExpressionVisitor,
|
|
156
|
-
JSXAttribute: function (node) {
|
|
157
|
-
if (!node.name || !new RegExp(settings.classMatching).test(node.name.name)) return
|
|
158
|
-
if (node.value && node.value.type === 'Literal') {
|
|
159
|
-
sortNodeArgumentValue(node)
|
|
160
|
-
} else if (node.value && node.value.type === 'JSXExpressionContainer') {
|
|
161
|
-
sortNodeArgumentValue(node, node.value.expression)
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
SvelteAttribute: function (node) {
|
|
165
|
-
if (!node.key?.name || !new RegExp(settings.classMatching).test(node.key.name)) return
|
|
166
|
-
for (const eachValue of node.value) {
|
|
167
|
-
sortNodeArgumentValue(node, eachValue)
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
TextAttribute: function (node) {
|
|
171
|
-
if (!node.name || !new RegExp(settings.classMatching).test(node.name)) return
|
|
172
|
-
sortNodeArgumentValue(node)
|
|
173
|
-
},
|
|
174
|
-
TaggedTemplateExpression: function (node) {
|
|
175
|
-
if (!settings.tags.includes(node.tag.name)) {
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
sortNodeArgumentValue(node, node.quasi)
|
|
179
|
-
},
|
|
180
|
-
}
|
|
181
|
-
const templateBodyVisitor = {
|
|
182
|
-
CallExpression: callExpressionVisitor,
|
|
183
|
-
VAttribute: function (node) {
|
|
184
|
-
if (node.value && node.value.type === 'VLiteral') {
|
|
185
|
-
sortNodeArgumentValue(node)
|
|
186
|
-
} else if (node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ArrayExpression') {
|
|
187
|
-
node.value.expression.elements.forEach((arg) => {
|
|
188
|
-
sortNodeArgumentValue(node, arg)
|
|
189
|
-
})
|
|
190
|
-
} else if (node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ObjectExpression') {
|
|
191
|
-
sortNodeArgumentValue(node, prop)
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (context.parserServices == null || context.parserServices.defineTemplateBodyVisitor == null) {
|
|
197
|
-
return scriptVisitor
|
|
198
|
-
} else {
|
|
199
|
-
return context.parserServices.defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor)
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const astUtil = require('../utils/ast')
|
|
4
|
-
const resolveContext = require('../utils/resolve-context')
|
|
5
|
-
const { validate } = require('@master/css-validator')
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
meta: {
|
|
9
|
-
docs: {
|
|
10
|
-
description: 'Check the validity of classes with your configuration',
|
|
11
|
-
category: 'Stylistic Issues',
|
|
12
|
-
recommended: false,
|
|
13
|
-
url: 'https://beta.css.master.co/docs/code-linting#check-the-validity-of-classes-with-your-configuration',
|
|
14
|
-
},
|
|
15
|
-
messages: {
|
|
16
|
-
invalidClass: '{{message}}',
|
|
17
|
-
disallowTraditionalClass: '{{message}}',
|
|
18
|
-
},
|
|
19
|
-
fixable: null
|
|
20
|
-
},
|
|
21
|
-
create: function (context) {
|
|
22
|
-
const { options, settings, config } = resolveContext(context)
|
|
23
|
-
const checkNodeArgumentValue = (node, arg = null) => {
|
|
24
|
-
astUtil.parseNodeRecursive(
|
|
25
|
-
node,
|
|
26
|
-
arg,
|
|
27
|
-
(classNames, node) => {
|
|
28
|
-
const sourceCode = context.getSourceCode()
|
|
29
|
-
const sourceCodeLines = sourceCode.lines
|
|
30
|
-
const nodeStartLine = node.loc.start.line
|
|
31
|
-
const nodeEndLine = node.loc.end.line
|
|
32
|
-
for (const className of classNames) {
|
|
33
|
-
const { isMasterCSS, errors } = validate(className, { config })
|
|
34
|
-
if (errors.length > 0) {
|
|
35
|
-
for (const error of errors) {
|
|
36
|
-
if (isMasterCSS) {
|
|
37
|
-
context.report({
|
|
38
|
-
loc: astUtil.findLoc(className, sourceCodeLines, nodeStartLine, nodeEndLine),
|
|
39
|
-
messageId: 'invalidClass',
|
|
40
|
-
data: {
|
|
41
|
-
message: error.message + '.',
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
} else if (!isMasterCSS && options.disallowTraditionalClass) {
|
|
45
|
-
context.report({
|
|
46
|
-
loc: astUtil.findLoc(className, sourceCodeLines, nodeStartLine, nodeEndLine),
|
|
47
|
-
messageId: 'disallowTraditionalClass',
|
|
48
|
-
data: {
|
|
49
|
-
message: `Disallow a traditional class \`${className}\`.`,
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
false,
|
|
58
|
-
false,
|
|
59
|
-
settings.ignoredKeys
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const callExpressionVisitor = function (node) {
|
|
64
|
-
const calleeStr = astUtil.calleeToString(node.callee)
|
|
65
|
-
if (settings.callees.findIndex((name) => calleeStr === name) === -1) {
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
node.arguments.forEach((arg) => {
|
|
70
|
-
checkNodeArgumentValue(node, arg)
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const scriptVisitor = {
|
|
75
|
-
CallExpression: callExpressionVisitor,
|
|
76
|
-
JSXAttribute: function (node) {
|
|
77
|
-
if (!node.name || !new RegExp(settings.classMatching).test(node.name.name)) return
|
|
78
|
-
if (node.value && node.value.type === 'Literal') {
|
|
79
|
-
checkNodeArgumentValue(node)
|
|
80
|
-
} else if (node.value && node.value.type === 'JSXExpressionContainer') {
|
|
81
|
-
checkNodeArgumentValue(node, node.value.expression)
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
SvelteAttribute: function (node) {
|
|
85
|
-
if (!node.key?.name || !new RegExp(settings.classMatching).test(node.key.name)) return
|
|
86
|
-
for (const eachValue of node.value) {
|
|
87
|
-
checkNodeArgumentValue(node, eachValue)
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
TextAttribute: function (node) {
|
|
91
|
-
if (!node.name || !new RegExp(settings.classMatching).test(node.name)) return
|
|
92
|
-
checkNodeArgumentValue(node)
|
|
93
|
-
},
|
|
94
|
-
TaggedTemplateExpression: function (node) {
|
|
95
|
-
if (!settings.tags.includes(node.tag.name)) {
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
checkNodeArgumentValue(node, node.quasi)
|
|
99
|
-
},
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const templateBodyVisitor = {
|
|
103
|
-
CallExpression: callExpressionVisitor,
|
|
104
|
-
VAttribute: function (node) {
|
|
105
|
-
if (node.value && node.value.type === 'VLiteral') {
|
|
106
|
-
checkNodeArgumentValue(node)
|
|
107
|
-
} else if (node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ArrayExpression') {
|
|
108
|
-
node.value.expression.elements.forEach((arg) => {
|
|
109
|
-
checkNodeArgumentValue(node, arg)
|
|
110
|
-
})
|
|
111
|
-
} else if (node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ObjectExpression') {
|
|
112
|
-
checkNodeArgumentValue(node, prop)
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (context.parserServices == null || context.parserServices.defineTemplateBodyVisitor == null) {
|
|
118
|
-
return scriptVisitor
|
|
119
|
-
} else {
|
|
120
|
-
return context.parserServices.defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor)
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
}
|
package/lib/utils/ast.js
DELETED
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const separatorRegEx = /([\t\n\f\r ]+)/
|
|
4
|
-
|
|
5
|
-
function calleeToString(calleeNode) {
|
|
6
|
-
if (calleeNode.type === 'Identifier') {
|
|
7
|
-
return calleeNode.name
|
|
8
|
-
}
|
|
9
|
-
if (calleeNode.type === 'MemberExpression') {
|
|
10
|
-
return `${calleeNode.object.name}.${calleeNode.property.name}`
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function extractRangeFromNode(node) {
|
|
15
|
-
if (node.type === 'TextAttribute' && node.name === 'class') {
|
|
16
|
-
return [node.valueSpan.fullStart.offset, node.valueSpan.end.offset]
|
|
17
|
-
}
|
|
18
|
-
switch (node.value.type) {
|
|
19
|
-
case 'JSXExpressionContainer':
|
|
20
|
-
return node.value.expression.range
|
|
21
|
-
default:
|
|
22
|
-
return node.value.range
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function extractValueFromNode(node) {
|
|
27
|
-
if (node.type === 'TextAttribute' && node.name === 'class') {
|
|
28
|
-
return node.value
|
|
29
|
-
}
|
|
30
|
-
switch (node.value.type) {
|
|
31
|
-
case 'JSXExpressionContainer':
|
|
32
|
-
return node.value.expression.value
|
|
33
|
-
case 'VExpressionContainer':
|
|
34
|
-
switch (node.value.expression.type) {
|
|
35
|
-
case 'ArrayExpression':
|
|
36
|
-
return node.value.expression.elements
|
|
37
|
-
case 'ObjectExpression':
|
|
38
|
-
return node.value.expression.properties
|
|
39
|
-
}
|
|
40
|
-
return node.value.expression.value
|
|
41
|
-
default:
|
|
42
|
-
return node.value.value
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function extractClassnamesFromValue(classStr) {
|
|
47
|
-
if (typeof classStr !== 'string') {
|
|
48
|
-
return { classNames: [], whitespaces: [], headSpace: false, tailSpace: false }
|
|
49
|
-
}
|
|
50
|
-
let parts = classStr.split(separatorRegEx)
|
|
51
|
-
if (parts[0] === '') {
|
|
52
|
-
parts.shift()
|
|
53
|
-
}
|
|
54
|
-
if (parts[parts.length - 1] === '') {
|
|
55
|
-
parts.pop()
|
|
56
|
-
}
|
|
57
|
-
let headSpace = separatorRegEx.test(parts[0])
|
|
58
|
-
let tailSpace = separatorRegEx.test(parts[parts.length - 1])
|
|
59
|
-
const isClass = (_, i) => (headSpace ? i % 2 !== 0 : i % 2 === 0)
|
|
60
|
-
const isNotClass = (_, i) => (headSpace ? i % 2 === 0 : i % 2 !== 0)
|
|
61
|
-
let classNames = parts.filter(isClass)
|
|
62
|
-
let whitespaces = parts.filter(isNotClass)
|
|
63
|
-
return { classNames: classNames, whitespaces: whitespaces, headSpace: headSpace, tailSpace: tailSpace }
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Inspect and parse an abstract syntax node and run a callback function
|
|
69
|
-
*
|
|
70
|
-
* @param {ASTNode} rootNode The current root node being parsed by eslint
|
|
71
|
-
* @param {ASTNode} childNode The AST node child argument being checked
|
|
72
|
-
* @param {Function} cb The callback function
|
|
73
|
-
* @param {Boolean} skipConditional Optional, indicate distinct parsing for conditional nodes
|
|
74
|
-
* @param {Boolean} isolate Optional, set internally to isolate parsing and validation on conditional children
|
|
75
|
-
* @param {Array} ignoredKeys Optional, set object keys which should not be parsed e.g. for `cva`
|
|
76
|
-
* @returns {void}
|
|
77
|
-
*/
|
|
78
|
-
function parseNodeRecursive(rootNode, childNode, cb, skipConditional = false, isolate = false, ignoredKeys = []) {
|
|
79
|
-
// TODO allow vue non litteral
|
|
80
|
-
let originalClassNamesValue
|
|
81
|
-
let classNames
|
|
82
|
-
if (childNode === null) {
|
|
83
|
-
originalClassNamesValue = extractValueFromNode(rootNode);
|
|
84
|
-
({ classNames } = extractClassnamesFromValue(originalClassNamesValue))
|
|
85
|
-
classNames = [... new Set(classNames)]
|
|
86
|
-
if (classNames.length === 0) {
|
|
87
|
-
// Don't run for empty className
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
cb(classNames, rootNode)
|
|
91
|
-
} else if (childNode === undefined) {
|
|
92
|
-
// Ignore invalid child candidates (probably inside complex TemplateLiteral)
|
|
93
|
-
return
|
|
94
|
-
} else {
|
|
95
|
-
const forceIsolation = skipConditional ? true : isolate
|
|
96
|
-
let trim = false
|
|
97
|
-
switch (childNode.type) {
|
|
98
|
-
case 'TemplateLiteral':
|
|
99
|
-
childNode.expressions.forEach((exp) => {
|
|
100
|
-
parseNodeRecursive(rootNode, exp, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
101
|
-
})
|
|
102
|
-
childNode.quasis.forEach((quasis) => {
|
|
103
|
-
parseNodeRecursive(rootNode, quasis, cb, skipConditional, isolate, ignoredKeys)
|
|
104
|
-
})
|
|
105
|
-
return
|
|
106
|
-
case 'ConditionalExpression':
|
|
107
|
-
parseNodeRecursive(rootNode, childNode.consequent, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
108
|
-
parseNodeRecursive(rootNode, childNode.alternate, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
109
|
-
return
|
|
110
|
-
case 'LogicalExpression':
|
|
111
|
-
parseNodeRecursive(rootNode, childNode.right, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
112
|
-
return
|
|
113
|
-
case 'ArrayExpression':
|
|
114
|
-
childNode.elements.forEach((el) => {
|
|
115
|
-
parseNodeRecursive(rootNode, el, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
116
|
-
})
|
|
117
|
-
return
|
|
118
|
-
case 'ObjectExpression':
|
|
119
|
-
childNode.properties.forEach((prop) => {
|
|
120
|
-
const isUsedByClassNamesPlugin = rootNode.callee && rootNode.callee.name === 'classnames'
|
|
121
|
-
|
|
122
|
-
if (prop.type === 'SpreadElement') {
|
|
123
|
-
// Ignore spread elements
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (prop.key.type === 'Identifier' && ignoredKeys.includes(prop.key.name)) {
|
|
128
|
-
// Ignore specific keys defined in settings
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
parseNodeRecursive(
|
|
133
|
-
rootNode,
|
|
134
|
-
isUsedByClassNamesPlugin ? prop.key : prop.value,
|
|
135
|
-
cb,
|
|
136
|
-
skipConditional,
|
|
137
|
-
forceIsolation,
|
|
138
|
-
ignoredKeys
|
|
139
|
-
)
|
|
140
|
-
})
|
|
141
|
-
return
|
|
142
|
-
case 'Property':
|
|
143
|
-
parseNodeRecursive(rootNode, childNode.key, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
144
|
-
return
|
|
145
|
-
case 'Literal':
|
|
146
|
-
trim = true
|
|
147
|
-
originalClassNamesValue = childNode.value
|
|
148
|
-
break
|
|
149
|
-
case 'TemplateElement':
|
|
150
|
-
originalClassNamesValue = childNode.value.raw
|
|
151
|
-
break
|
|
152
|
-
}
|
|
153
|
-
({ classNames } = extractClassnamesFromValue(originalClassNamesValue))
|
|
154
|
-
classNames = removeDuplicatesFromArray(classNames)
|
|
155
|
-
if (classNames.length === 0) {
|
|
156
|
-
// Don't run for empty className
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
const targetNode = isolate ? null : rootNode
|
|
160
|
-
cb(classNames, targetNode)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function getTemplateElementPrefix(text, raw) {
|
|
165
|
-
const idx = text.indexOf(raw)
|
|
166
|
-
if (idx === 0) {
|
|
167
|
-
return ''
|
|
168
|
-
}
|
|
169
|
-
return text.split(raw).shift()
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function getTemplateElementSuffix(text, raw) {
|
|
173
|
-
if (text.indexOf(raw) === -1) {
|
|
174
|
-
return ''
|
|
175
|
-
}
|
|
176
|
-
return text.split(raw).pop()
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function getTemplateElementBody(text, prefix, suffix) {
|
|
180
|
-
let arr = text.split(prefix)
|
|
181
|
-
arr.shift()
|
|
182
|
-
let body = arr.join(prefix)
|
|
183
|
-
arr = body.split(suffix)
|
|
184
|
-
arr.pop()
|
|
185
|
-
return arr.join(suffix)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function findLoc(text, lines, startLine, endLine) {
|
|
189
|
-
for (let i = startLine; i <= endLine; i++) {
|
|
190
|
-
const sourceCodeLine = lines[i - 1]
|
|
191
|
-
const index = sourceCodeLine.indexOf(text)
|
|
192
|
-
if (index !== -1) {
|
|
193
|
-
return {
|
|
194
|
-
start: {
|
|
195
|
-
line: i,
|
|
196
|
-
column: index
|
|
197
|
-
},
|
|
198
|
-
end: {
|
|
199
|
-
line: i,
|
|
200
|
-
column: index + text.length
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return null
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
module.exports = {
|
|
209
|
-
calleeToString,
|
|
210
|
-
extractRangeFromNode,
|
|
211
|
-
extractValueFromNode,
|
|
212
|
-
extractClassnamesFromValue,
|
|
213
|
-
getTemplateElementPrefix,
|
|
214
|
-
getTemplateElementSuffix,
|
|
215
|
-
getTemplateElementBody,
|
|
216
|
-
parseNodeRecursive,
|
|
217
|
-
findLoc
|
|
218
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const exploreConfig = require('explore-config').default
|
|
4
|
-
|
|
5
|
-
const resolveContext = function (context) {
|
|
6
|
-
const settings = Object.assign({
|
|
7
|
-
callees: ['classnames', 'clsx', 'ctl', 'cva', 'cv', 'classVariant', 'styled'],
|
|
8
|
-
ignoredKeys: ['compoundVariants', 'defaultVariants'],
|
|
9
|
-
classMatching: '^class(Name)?$',
|
|
10
|
-
config: 'master.css.*',
|
|
11
|
-
tags: []
|
|
12
|
-
}, context.settings?.['@master/css'])
|
|
13
|
-
const config = settings?.config
|
|
14
|
-
return {
|
|
15
|
-
settings,
|
|
16
|
-
options: context.options[0] || {},
|
|
17
|
-
config: typeof config === 'object' ? config : exploreConfig(settings?.config || '')
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = resolveContext
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const rule = require('../../lib/rules/class-order')
|
|
4
|
-
const RuleTester = require('eslint').RuleTester
|
|
5
|
-
const ruleTester = new RuleTester({
|
|
6
|
-
parserOptions: {
|
|
7
|
-
ecmaFeatures: {
|
|
8
|
-
jsx: true,
|
|
9
|
-
},
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
ruleTester.run('class order', rule, {
|
|
14
|
-
valid: [
|
|
15
|
-
{ code: '<h1 className={"bg:black fg:white f:24 m:8 p:8"}>Welcome {name}</h1>' }
|
|
16
|
-
],
|
|
17
|
-
invalid: [
|
|
18
|
-
{
|
|
19
|
-
code: `
|
|
20
|
-
export interface FakePropsInterface {
|
|
21
|
-
readonly name?: string;
|
|
22
|
-
}
|
|
23
|
-
function Fake({
|
|
24
|
-
name = 'yolo'
|
|
25
|
-
}: FakeProps) {
|
|
26
|
-
return (
|
|
27
|
-
<>
|
|
28
|
-
<h1 className={"m:8 bg:black p:8 fg:white f:24"}>Welcome {name}</h1>
|
|
29
|
-
<p>Bye {name}</p>
|
|
30
|
-
</>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
export default Fake;
|
|
34
|
-
`,
|
|
35
|
-
output: `
|
|
36
|
-
export interface FakePropsInterface {
|
|
37
|
-
readonly name?: string;
|
|
38
|
-
}
|
|
39
|
-
function Fake({
|
|
40
|
-
name = 'yolo'
|
|
41
|
-
}: FakeProps) {
|
|
42
|
-
return (
|
|
43
|
-
<>
|
|
44
|
-
<h1 className={"bg:black fg:white f:24 m:8 p:8"}>Welcome {name}</h1>
|
|
45
|
-
<p>Bye {name}</p>
|
|
46
|
-
</>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
export default Fake;
|
|
50
|
-
`,
|
|
51
|
-
parser: require.resolve('@typescript-eslint/parser'),
|
|
52
|
-
errors: [{ messageId: 'invalidClassOrder' }],
|
|
53
|
-
},
|
|
54
|
-
]
|
|
55
|
-
})
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const rule = require('../../lib/rules/class-order')
|
|
4
|
-
const RuleTester = require('eslint').RuleTester
|
|
5
|
-
const ruleTester = new RuleTester({
|
|
6
|
-
parserOptions: {
|
|
7
|
-
ecmaFeatures: {
|
|
8
|
-
jsx: true,
|
|
9
|
-
},
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
ruleTester.run('svelte class order', rule, {
|
|
14
|
-
valid: [{ code: `<div class="bg:black fg:white f:24 m:8 p:8">Simple, basic</div>` }],
|
|
15
|
-
invalid: [
|
|
16
|
-
{
|
|
17
|
-
code: `<div class="m:8 bg:black p:8 fg:white f:24 {flipped ? 'flipped' : ''}">Enhancing readability</div>`,
|
|
18
|
-
output: `<div class="bg:black fg:white f:24 m:8 p:8 {flipped ? 'flipped' : ''}">Enhancing readability</div>`,
|
|
19
|
-
errors: [{ messageId: 'invalidClassOrder' }],
|
|
20
|
-
filename: 'test.svelte',
|
|
21
|
-
parser: require.resolve('svelte-eslint-parser'),
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
})
|
package/tests/frameworks/vue.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const rule = require('../../lib/rules/class-order')
|
|
4
|
-
const RuleTester = require('eslint').RuleTester
|
|
5
|
-
const ruleTester = new RuleTester({
|
|
6
|
-
parserOptions: {
|
|
7
|
-
ecmaFeatures: {
|
|
8
|
-
jsx: true,
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
ruleTester.run('vue class order', rule, {
|
|
14
|
-
valid: [{ code: `<div class="bg:black fg:white f:24 m:8 p:8">Simple, basic</div>` }],
|
|
15
|
-
invalid: [
|
|
16
|
-
{
|
|
17
|
-
code: `<template><div class="m:8 bg:black p:8 fg:white f:24">Enhancing readability</div></template>`,
|
|
18
|
-
output: `<template><div class="bg:black fg:white f:24 m:8 p:8">Enhancing readability</div></template>`,
|
|
19
|
-
errors: [{ messageId: 'invalidClassOrder' }],
|
|
20
|
-
filename: 'test.vue',
|
|
21
|
-
parser: require.resolve('vue-eslint-parser'),
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
})
|
package/tests/order.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const rule = require('../lib/rules/class-order')
|
|
4
|
-
const RuleTester = require('eslint').RuleTester
|
|
5
|
-
const ruleTester = new RuleTester({
|
|
6
|
-
parserOptions: {
|
|
7
|
-
ecmaFeatures: {
|
|
8
|
-
jsx: true,
|
|
9
|
-
},
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
ruleTester.run('class order', rule, {
|
|
14
|
-
valid: [
|
|
15
|
-
{
|
|
16
|
-
code: `<div class="bg:black fg:white f:24 m:8 p:8">Simple, basic</div>`,
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
code: `<div class="card mt:20">Traditional class + syntax</div>`,
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
code: `<div test="bg:black fg:white f:24 m:8 p:8">Simple, using 'test' prop</div>`,
|
|
23
|
-
options: [
|
|
24
|
-
{
|
|
25
|
-
classMatching: '^test$',
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
invalid: [
|
|
31
|
-
{
|
|
32
|
-
code: `<div class="fg:white f:24 m:8 p:8 bg:black">Classnames will be ordered</div>`,
|
|
33
|
-
output: `<div class="bg:black fg:white f:24 m:8 p:8">Classnames will be ordered</div>`,
|
|
34
|
-
errors: [{ messageId: 'invalidClassOrder' }],
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
code: `
|
|
38
|
-
<div class="
|
|
39
|
-
m:8
|
|
40
|
-
bg:black
|
|
41
|
-
p:8
|
|
42
|
-
fg:white
|
|
43
|
-
f:24
|
|
44
|
-
">
|
|
45
|
-
:)
|
|
46
|
-
</div>`,
|
|
47
|
-
output: `
|
|
48
|
-
<div class="
|
|
49
|
-
bg:black
|
|
50
|
-
fg:white
|
|
51
|
-
f:24
|
|
52
|
-
m:8
|
|
53
|
-
p:8
|
|
54
|
-
">
|
|
55
|
-
:)
|
|
56
|
-
</div>`,
|
|
57
|
-
errors: [{ messageId: 'invalidClassOrder' }],
|
|
58
|
-
parser: require.resolve('@angular-eslint/template-parser'),
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
code: `<div class="flex uppercase m:0 m:0>li text-decoration:none>li>a px:4>li align-items:baseline fg:gray-30>li>a gap-x:28 font:12 font:semibold pb:6>li pt:20 pt:10>li {bb:3|solid|black}>li:has(>.router-link-active) {fg:black}>li:has(>.router-link-active)>a fg:gray-10>li>a:hover box-shadow:none>li>a:focus">Group</div>`,
|
|
62
|
-
output: `<div class="flex uppercase align-items:baseline box-shadow:none>li>a:focus fg:gray-30>li>a fg:gray-10>li>a:hover gap-x:28 font:12 font:semibold {bb:3|solid|black}>li:has(>.router-link-active) {fg:black}>li:has(>.router-link-active)>a m:0 m:0>li pb:6>li pt:20 pt:10>li px:4>li text-decoration:none>li>a">Group</div>`,
|
|
63
|
-
errors: [{ messageId: 'invalidClassOrder' }],
|
|
64
|
-
}
|
|
65
|
-
],
|
|
66
|
-
})
|
package/tests/validation.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const rule = require('../lib/rules/class-validation')
|
|
4
|
-
const RuleTester = require('eslint').RuleTester
|
|
5
|
-
|
|
6
|
-
new RuleTester({
|
|
7
|
-
parserOptions: {
|
|
8
|
-
ecmaFeatures: {
|
|
9
|
-
jsx: true,
|
|
10
|
-
},
|
|
11
|
-
}
|
|
12
|
-
}).run('invalid', rule, {
|
|
13
|
-
valid: [
|
|
14
|
-
{
|
|
15
|
-
code: `<div class="bg:black fg:white f:24 m:8 p:8">Simple, basic</div>`,
|
|
16
|
-
}
|
|
17
|
-
],
|
|
18
|
-
invalid: [
|
|
19
|
-
{
|
|
20
|
-
code: `<div class="bg:black m:mistake rrr">Simple, basic</div>`,
|
|
21
|
-
errors: [
|
|
22
|
-
{ messageId: 'invalidClass' },
|
|
23
|
-
{ messageId: 'disallowTraditionalClass' }
|
|
24
|
-
],
|
|
25
|
-
options: [
|
|
26
|
-
{
|
|
27
|
-
disallowTraditionalClass: true
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
code: `<div class="bg:black m:mistake rrr">Simple, basic</div>`,
|
|
33
|
-
errors: [
|
|
34
|
-
{ messageId: 'invalidClass' },
|
|
35
|
-
]
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
new RuleTester({
|
|
41
|
-
parserOptions: {
|
|
42
|
-
ecmaFeatures: {
|
|
43
|
-
jsx: true,
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
settings: {
|
|
47
|
-
'@master/css': {
|
|
48
|
-
config: {
|
|
49
|
-
styles: { btn: 'block' }
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}).run('invalid', rule, {
|
|
54
|
-
valid: [],
|
|
55
|
-
invalid: [
|
|
56
|
-
{
|
|
57
|
-
code: `<div class="bg:black m:mistake rrr btn">Simple, basic</div>`,
|
|
58
|
-
errors: [
|
|
59
|
-
{ messageId: 'invalidClass' },
|
|
60
|
-
{ messageId: 'disallowTraditionalClass' }
|
|
61
|
-
],
|
|
62
|
-
options: [
|
|
63
|
-
{
|
|
64
|
-
disallowTraditionalClass: true
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
})
|