@master/eslint-plugin-css 2.0.0-beta.185 → 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 -19
- package/lib/rules/class-order.js +0 -282
- package/lib/util/ast.js +0 -370
- package/lib/util/parser.js +0 -21
- package/lib/util/regex.js +0 -16
- package/lib/util/removeDuplicatesFromArray.js +0 -7
- package/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js +0 -18
- package/lib/util/settings.js +0 -39
- package/tests/class-order/core.js +0 -112
- package/tests/class-order/svelte.js +0 -25
- package/tests/class-order/vue.js +0 -25
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
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Rules enforcing best practices while using Master CSS
|
|
3
|
-
* @author Miles
|
|
4
|
-
*/
|
|
5
|
-
'use strict'
|
|
6
|
-
|
|
7
|
-
//------------------------------------------------------------------------------
|
|
8
|
-
// Requirements
|
|
9
|
-
//------------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
const requireIndex = require('requireindex')
|
|
12
|
-
|
|
13
|
-
//------------------------------------------------------------------------------
|
|
14
|
-
// Plugin Definition
|
|
15
|
-
//------------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// import all rules in lib/rules
|
|
19
|
-
module.exports.rules = requireIndex(__dirname + '/rules')
|
package/lib/rules/class-order.js
DELETED
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-case-declarations */
|
|
2
|
-
/**
|
|
3
|
-
* @fileoverview Use a consistent orders for the Master CSS classnames, based on property then on variants
|
|
4
|
-
* @author Miles
|
|
5
|
-
*/
|
|
6
|
-
'use strict'
|
|
7
|
-
|
|
8
|
-
// Modified from https://github.com/francoismassart/eslint-plugin-tailwindcss
|
|
9
|
-
|
|
10
|
-
const astUtil = require('../util/ast')
|
|
11
|
-
const removeDuplicatesFromClassnamesAndWhitespaces = require('../util/removeDuplicatesFromClassnamesAndWhitespaces')
|
|
12
|
-
const getOption = require('../util/settings')
|
|
13
|
-
const parserUtil = require('../util/parser')
|
|
14
|
-
|
|
15
|
-
const { reorderForReadableClasses } = require('@master/css')
|
|
16
|
-
|
|
17
|
-
//------------------------------------------------------------------------------
|
|
18
|
-
// Rule Definition
|
|
19
|
-
//------------------------------------------------------------------------------
|
|
20
|
-
|
|
21
|
-
// Predefine message for use in context.report conditional.
|
|
22
|
-
// messageId will still be usable in tests.
|
|
23
|
-
const INVALID_CLASSNAMES_ORDER_MSG = 'No consistent class order followed.'
|
|
24
|
-
|
|
25
|
-
module.exports = {
|
|
26
|
-
meta: {
|
|
27
|
-
docs: {
|
|
28
|
-
description: 'Enforce a consistent and logical order of classes',
|
|
29
|
-
category: 'Stylistic Issues',
|
|
30
|
-
recommended: false,
|
|
31
|
-
url: 'https://beta.css.master.co/docs/code-linting#enforce-a-consistent-and-logical-order-of-classes',
|
|
32
|
-
},
|
|
33
|
-
messages: {
|
|
34
|
-
invalidOrder: INVALID_CLASSNAMES_ORDER_MSG,
|
|
35
|
-
},
|
|
36
|
-
fixable: 'code',
|
|
37
|
-
schema: [
|
|
38
|
-
{
|
|
39
|
-
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
callees: {
|
|
42
|
-
type: 'array',
|
|
43
|
-
items: { type: 'string', minLength: 0 },
|
|
44
|
-
uniqueItems: true,
|
|
45
|
-
},
|
|
46
|
-
ignoredKeys: {
|
|
47
|
-
type: 'array',
|
|
48
|
-
items: { type: 'string', minLength: 0 },
|
|
49
|
-
uniqueItems: true,
|
|
50
|
-
},
|
|
51
|
-
config: {
|
|
52
|
-
// returned from `loadConfig()` utility
|
|
53
|
-
type: ['string', 'object'],
|
|
54
|
-
},
|
|
55
|
-
removeDuplicates: {
|
|
56
|
-
// default: true,
|
|
57
|
-
type: 'boolean',
|
|
58
|
-
},
|
|
59
|
-
tags: {
|
|
60
|
-
type: 'array',
|
|
61
|
-
items: { type: 'string', minLength: 0 },
|
|
62
|
-
uniqueItems: true,
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
create: function (context) {
|
|
70
|
-
const callees = getOption(context, 'callees')
|
|
71
|
-
const skipClassAttribute = getOption(context, 'skipClassAttribute')
|
|
72
|
-
const tags = getOption(context, 'tags')
|
|
73
|
-
const masterCssConfig = getOption(context, 'config')
|
|
74
|
-
const classRegex = getOption(context, 'classRegex')
|
|
75
|
-
|
|
76
|
-
//----------------------------------------------------------------------
|
|
77
|
-
// Helpers
|
|
78
|
-
//----------------------------------------------------------------------
|
|
79
|
-
/**
|
|
80
|
-
* Recursive function crawling into child nodes
|
|
81
|
-
* @param {ASTNode} node The root node of the current parsing
|
|
82
|
-
* @param {ASTNode} arg The child node of node
|
|
83
|
-
* @returns {void}
|
|
84
|
-
*/
|
|
85
|
-
const sortNodeArgumentValue = (node, arg = null) => {
|
|
86
|
-
let originalClassNamesValue = null
|
|
87
|
-
let start = null
|
|
88
|
-
let end = null
|
|
89
|
-
let prefix = ''
|
|
90
|
-
let suffix = ''
|
|
91
|
-
if (arg === null) {
|
|
92
|
-
originalClassNamesValue = astUtil.extractValueFromNode(node)
|
|
93
|
-
const range = astUtil.extractRangeFromNode(node)
|
|
94
|
-
if (node.type === 'TextAttribute') {
|
|
95
|
-
start = range[0]
|
|
96
|
-
end = range[1]
|
|
97
|
-
} else {
|
|
98
|
-
start = range[0] + 1
|
|
99
|
-
end = range[1] - 1
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
switch (arg.type) {
|
|
103
|
-
case 'Identifier':
|
|
104
|
-
return
|
|
105
|
-
case 'TemplateLiteral':
|
|
106
|
-
arg.expressions.forEach((exp) => {
|
|
107
|
-
sortNodeArgumentValue(node, exp)
|
|
108
|
-
})
|
|
109
|
-
arg.quasis.forEach((quasis) => {
|
|
110
|
-
sortNodeArgumentValue(node, quasis)
|
|
111
|
-
})
|
|
112
|
-
return
|
|
113
|
-
case 'ConditionalExpression':
|
|
114
|
-
sortNodeArgumentValue(node, arg.consequent)
|
|
115
|
-
sortNodeArgumentValue(node, arg.alternate)
|
|
116
|
-
return
|
|
117
|
-
case 'LogicalExpression':
|
|
118
|
-
sortNodeArgumentValue(node, arg.right)
|
|
119
|
-
return
|
|
120
|
-
case 'ArrayExpression':
|
|
121
|
-
arg.elements.forEach((el) => {
|
|
122
|
-
sortNodeArgumentValue(node, el)
|
|
123
|
-
})
|
|
124
|
-
return
|
|
125
|
-
case 'ObjectExpression':
|
|
126
|
-
const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames'
|
|
127
|
-
const isVue = node.key && node.key.type === 'VDirectiveKey'
|
|
128
|
-
arg.properties.forEach((prop) => {
|
|
129
|
-
const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value
|
|
130
|
-
sortNodeArgumentValue(node, propVal)
|
|
131
|
-
})
|
|
132
|
-
return
|
|
133
|
-
case 'Property':
|
|
134
|
-
sortNodeArgumentValue(node, arg.key)
|
|
135
|
-
break
|
|
136
|
-
case 'Literal':
|
|
137
|
-
originalClassNamesValue = arg.value
|
|
138
|
-
start = arg.range[0] + 1
|
|
139
|
-
end = arg.range[1] - 1
|
|
140
|
-
break
|
|
141
|
-
case 'SvelteLiteral':
|
|
142
|
-
originalClassNamesValue = arg.value
|
|
143
|
-
if (originalClassNamesValue === '') {
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
start = arg.range[0]
|
|
147
|
-
end = arg.range[1]
|
|
148
|
-
break;
|
|
149
|
-
case 'TemplateElement':
|
|
150
|
-
originalClassNamesValue = arg.value.raw
|
|
151
|
-
if (originalClassNamesValue === '') {
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
start = arg.range[0]
|
|
155
|
-
end = arg.range[1]
|
|
156
|
-
// https://github.com/eslint/eslint/issues/13360
|
|
157
|
-
// The problem is that range computation includes the backticks (`test`)
|
|
158
|
-
// but value.raw does not include them, so there is a mismatch.
|
|
159
|
-
// start/end does not include the backticks, therefore it matches value.raw.
|
|
160
|
-
const txt = context.getSourceCode().getText(arg)
|
|
161
|
-
prefix = astUtil.getTemplateElementPrefix(txt, originalClassNamesValue)
|
|
162
|
-
suffix = astUtil.getTemplateElementSuffix(txt, originalClassNamesValue)
|
|
163
|
-
originalClassNamesValue = astUtil.getTemplateElementBody(txt, prefix, suffix)
|
|
164
|
-
break
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let { classNames, whitespaces, headSpace, tailSpace } =
|
|
169
|
-
astUtil.extractClassnamesFromValue(originalClassNamesValue)
|
|
170
|
-
|
|
171
|
-
if (classNames.length <= 1) {
|
|
172
|
-
// Don't run sorting for a single or empty className
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
let orderedClassNames = reorderForReadableClasses(classNames, masterCssConfig)
|
|
177
|
-
.filter(eachOrderedClassName => classNames.includes(eachOrderedClassName))
|
|
178
|
-
|
|
179
|
-
orderedClassNames = orderedClassNames.concat(classNames.filter(x => !orderedClassNames.includes(x)))
|
|
180
|
-
|
|
181
|
-
removeDuplicatesFromClassnamesAndWhitespaces(orderedClassNames, whitespaces, headSpace, tailSpace)
|
|
182
|
-
|
|
183
|
-
// Generates the validated/sorted attribute value
|
|
184
|
-
let validatedClassNamesValue = ''
|
|
185
|
-
for (let i = 0; i < orderedClassNames.length; i++) {
|
|
186
|
-
const w = whitespaces[i] ?? ''
|
|
187
|
-
const cls = orderedClassNames[i]
|
|
188
|
-
validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`
|
|
189
|
-
if (headSpace && tailSpace && i === orderedClassNames.length - 1) {
|
|
190
|
-
validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? ''
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (originalClassNamesValue !== validatedClassNamesValue) {
|
|
195
|
-
validatedClassNamesValue = prefix + validatedClassNamesValue + suffix
|
|
196
|
-
context.report({
|
|
197
|
-
node: node,
|
|
198
|
-
messageId: 'invalidOrder',
|
|
199
|
-
fix: function (fixer) {
|
|
200
|
-
return fixer.replaceTextRange([start, end], validatedClassNamesValue)
|
|
201
|
-
},
|
|
202
|
-
})
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
//----------------------------------------------------------------------
|
|
207
|
-
// Public
|
|
208
|
-
//----------------------------------------------------------------------
|
|
209
|
-
|
|
210
|
-
const attributeVisitor = function (node) {
|
|
211
|
-
if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) {
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
if (astUtil.isLiteralAttributeValue(node)) {
|
|
215
|
-
sortNodeArgumentValue(node)
|
|
216
|
-
} else if (node.value && node.value.type === 'JSXExpressionContainer') {
|
|
217
|
-
sortNodeArgumentValue(node, node.value.expression)
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const callExpressionVisitor = function (node) {
|
|
222
|
-
const calleeStr = astUtil.calleeToString(node.callee)
|
|
223
|
-
if (callees.findIndex((name) => calleeStr === name) === -1) {
|
|
224
|
-
return
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
node.arguments.forEach((arg) => {
|
|
228
|
-
sortNodeArgumentValue(node, arg)
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const scriptVisitor = {
|
|
233
|
-
JSXAttribute: attributeVisitor,
|
|
234
|
-
SvelteAttribute: function (node) {
|
|
235
|
-
if (!node.key?.name) return
|
|
236
|
-
if (!new RegExp(classRegex).test(node.key.name) || skipClassAttribute) {
|
|
237
|
-
return
|
|
238
|
-
}
|
|
239
|
-
for (const eachValue of node.value) {
|
|
240
|
-
sortNodeArgumentValue(node, eachValue)
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
TextAttribute: attributeVisitor,
|
|
244
|
-
CallExpression: callExpressionVisitor,
|
|
245
|
-
TaggedTemplateExpression: function (node) {
|
|
246
|
-
if (!tags.includes(node.tag.name)) {
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
sortNodeArgumentValue(node, node.quasi)
|
|
251
|
-
},
|
|
252
|
-
}
|
|
253
|
-
const templateVisitor = {
|
|
254
|
-
CallExpression: callExpressionVisitor,
|
|
255
|
-
/*
|
|
256
|
-
Tagged templates inside data bindings
|
|
257
|
-
https://github.com/vuejs/vue/issues/9721
|
|
258
|
-
*/
|
|
259
|
-
VAttribute: function (node) {
|
|
260
|
-
switch (true) {
|
|
261
|
-
case !astUtil.isValidVueAttribute(node, classRegex):
|
|
262
|
-
return
|
|
263
|
-
case astUtil.isVLiteralValue(node):
|
|
264
|
-
sortNodeArgumentValue(node, null)
|
|
265
|
-
break
|
|
266
|
-
case astUtil.isArrayExpression(node):
|
|
267
|
-
node.value.expression.elements.forEach((arg) => {
|
|
268
|
-
sortNodeArgumentValue(node, arg)
|
|
269
|
-
})
|
|
270
|
-
break
|
|
271
|
-
case astUtil.isObjectExpression(node):
|
|
272
|
-
node.value.expression.properties.forEach((prop) => {
|
|
273
|
-
sortNodeArgumentValue(node, prop)
|
|
274
|
-
})
|
|
275
|
-
break
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor)
|
|
281
|
-
},
|
|
282
|
-
}
|
package/lib/util/ast.js
DELETED
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Utility functions for AST
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
'use strict'
|
|
6
|
-
|
|
7
|
-
const { separatorRegEx } = require('./regex')
|
|
8
|
-
// context.parserPath
|
|
9
|
-
// /.../eslint-plugin-tailwindcss/node_modules/espree/espree.js
|
|
10
|
-
// /.../eslint-plugin-tailwindcss/node_modules/@angular-eslint/template-parser/dist/index.js
|
|
11
|
-
|
|
12
|
-
const removeDuplicatesFromArray = require('./removeDuplicatesFromArray')
|
|
13
|
-
|
|
14
|
-
function calleeToString(calleeNode) {
|
|
15
|
-
if (calleeNode.type === 'Identifier') {
|
|
16
|
-
return calleeNode.name
|
|
17
|
-
}
|
|
18
|
-
if (calleeNode.type === 'MemberExpression') {
|
|
19
|
-
return `${calleeNode.object.name}.${calleeNode.property.name}`
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Find out if node is `class` or `className`
|
|
25
|
-
*
|
|
26
|
-
* @param {ASTNode} node The AST node being checked
|
|
27
|
-
* @param {String} classRegex Regex to test the attribute that is being checked against
|
|
28
|
-
* @returns {Boolean}
|
|
29
|
-
*/
|
|
30
|
-
function isClassAttribute(node, classRegex) {
|
|
31
|
-
if (!node.name) {
|
|
32
|
-
return false
|
|
33
|
-
}
|
|
34
|
-
let name = ''
|
|
35
|
-
switch (node.type) {
|
|
36
|
-
case 'TextAttribute':
|
|
37
|
-
name = node.name
|
|
38
|
-
break
|
|
39
|
-
default:
|
|
40
|
-
name = node.name.name
|
|
41
|
-
}
|
|
42
|
-
return new RegExp(classRegex).test(name)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Find out if node is `class`
|
|
47
|
-
*
|
|
48
|
-
* @param {ASTNode} node The AST node being checked
|
|
49
|
-
* @param {String} classRegex Regex to test the attribute that is being checked against
|
|
50
|
-
* @returns {Boolean}
|
|
51
|
-
*/
|
|
52
|
-
function isVueClassAttribute(node, classRegex) {
|
|
53
|
-
const re = new RegExp(classRegex)
|
|
54
|
-
let name = ''
|
|
55
|
-
switch (true) {
|
|
56
|
-
case node.key && node.key.name && re.test(node.key.name):
|
|
57
|
-
// class="vue-classes-as-litteral"
|
|
58
|
-
return true
|
|
59
|
-
case node.key &&
|
|
60
|
-
node.key.name &&
|
|
61
|
-
node.key.name.name &&
|
|
62
|
-
node.key.argument &&
|
|
63
|
-
node.key.argument.name &&
|
|
64
|
-
/^bind$/.test(node.key.name.name) &&
|
|
65
|
-
re.test(node.key.argument.name):
|
|
66
|
-
// v-bind:class="vue-classes-as-bind"
|
|
67
|
-
// :class="vue-classes-as-bind"
|
|
68
|
-
return true
|
|
69
|
-
default:
|
|
70
|
-
return false
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Find out if node's value attribute is just simple text
|
|
76
|
-
*
|
|
77
|
-
* @param {ASTNode} node The AST node being checked
|
|
78
|
-
* @returns {Boolean}
|
|
79
|
-
*/
|
|
80
|
-
function isVLiteralValue(node) {
|
|
81
|
-
return node.value && node.value.type === 'VLiteral'
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Find out if node's value attribute is an ArrayExpression
|
|
86
|
-
*
|
|
87
|
-
* @param {ASTNode} node The AST node being checked
|
|
88
|
-
* @returns {Boolean}
|
|
89
|
-
*/
|
|
90
|
-
function isArrayExpression(node) {
|
|
91
|
-
return node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ArrayExpression'
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Find out if node's value attribute is an ObjectExpression
|
|
96
|
-
*
|
|
97
|
-
* @param {ASTNode} node The AST node being checked
|
|
98
|
-
* @returns {Boolean}
|
|
99
|
-
*/
|
|
100
|
-
function isObjectExpression(node) {
|
|
101
|
-
return node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ObjectExpression'
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Find out if node's value attribute is just simple text
|
|
106
|
-
*
|
|
107
|
-
* @param {ASTNode} node The AST node being checked
|
|
108
|
-
* @returns {Boolean}
|
|
109
|
-
*/
|
|
110
|
-
function isVueValidAttributeValue(node) {
|
|
111
|
-
switch (true) {
|
|
112
|
-
case isVLiteralValue(node): // Simple string
|
|
113
|
-
case isArrayExpression(node): // ['tw-unknown-class']
|
|
114
|
-
case isObjectExpression(node): // {'tw-unknown-class': true}
|
|
115
|
-
return true
|
|
116
|
-
default:
|
|
117
|
-
return false
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Find out if node's value attribute is just simple text
|
|
123
|
-
*
|
|
124
|
-
* @param {ASTNode} node The AST node being checked
|
|
125
|
-
* @returns {Boolean}
|
|
126
|
-
*/
|
|
127
|
-
function isLiteralAttributeValue(node) {
|
|
128
|
-
if (node.type === 'TextAttribute' && node.name === 'class' && typeof node.value === 'string') {
|
|
129
|
-
return true
|
|
130
|
-
}
|
|
131
|
-
if (node.value) {
|
|
132
|
-
switch (node.value.type) {
|
|
133
|
-
case 'Literal':
|
|
134
|
-
return true
|
|
135
|
-
case 'JSXExpressionContainer':
|
|
136
|
-
// className={"..."}
|
|
137
|
-
return node.value.expression.type === 'Literal'
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return false
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Find out if the node is a valid candidate for our rules
|
|
145
|
-
*
|
|
146
|
-
* @param {ASTNode} node The AST node being checked
|
|
147
|
-
* @param {String} classRegex Regex to test the attribute that is being checked against
|
|
148
|
-
* @returns {Boolean}
|
|
149
|
-
*/
|
|
150
|
-
function isValidJSXAttribute(node, classRegex) {
|
|
151
|
-
if (!isClassAttribute(node, classRegex)) {
|
|
152
|
-
// Only run for class[Name] attributes
|
|
153
|
-
return false
|
|
154
|
-
}
|
|
155
|
-
if (!isLiteralAttributeValue(node)) {
|
|
156
|
-
// No support for dynamic or conditional classnames
|
|
157
|
-
return false
|
|
158
|
-
}
|
|
159
|
-
return true
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Find out if the node is a valid candidate for our rules
|
|
164
|
-
*
|
|
165
|
-
* @param {ASTNode} node The AST node being checked
|
|
166
|
-
* @param {String} classRegex Regex to test the attribute that is being checked against
|
|
167
|
-
* @returns {Boolean}
|
|
168
|
-
*/
|
|
169
|
-
function isValidVueAttribute(node, classRegex) {
|
|
170
|
-
if (!isVueClassAttribute(node, classRegex)) {
|
|
171
|
-
// Only run for class attributes
|
|
172
|
-
return false
|
|
173
|
-
}
|
|
174
|
-
if (!isVueValidAttributeValue(node)) {
|
|
175
|
-
// No support for dynamic or conditional classnames
|
|
176
|
-
return false
|
|
177
|
-
}
|
|
178
|
-
return true
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function extractRangeFromNode(node) {
|
|
182
|
-
if (node.type === 'TextAttribute' && node.name === 'class') {
|
|
183
|
-
return [node.valueSpan.fullStart.offset, node.valueSpan.end.offset]
|
|
184
|
-
}
|
|
185
|
-
switch (node.value.type) {
|
|
186
|
-
case 'JSXExpressionContainer':
|
|
187
|
-
return node.value.expression.range
|
|
188
|
-
default:
|
|
189
|
-
return node.value.range
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function extractValueFromNode(node) {
|
|
194
|
-
if (node.type === 'TextAttribute' && node.name === 'class') {
|
|
195
|
-
return node.value
|
|
196
|
-
}
|
|
197
|
-
switch (node.value.type) {
|
|
198
|
-
case 'JSXExpressionContainer':
|
|
199
|
-
return node.value.expression.value
|
|
200
|
-
case 'VExpressionContainer':
|
|
201
|
-
switch (node.value.expression.type) {
|
|
202
|
-
case 'ArrayExpression':
|
|
203
|
-
return node.value.expression.elements
|
|
204
|
-
case 'ObjectExpression':
|
|
205
|
-
return node.value.expression.properties
|
|
206
|
-
}
|
|
207
|
-
return node.value.expression.value
|
|
208
|
-
default:
|
|
209
|
-
return node.value.value
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function extractClassnamesFromValue(classStr) {
|
|
214
|
-
if (typeof classStr !== 'string') {
|
|
215
|
-
return { classNames: [], whitespaces: [], headSpace: false, tailSpace: false }
|
|
216
|
-
}
|
|
217
|
-
let parts = classStr.split(separatorRegEx)
|
|
218
|
-
if (parts[0] === '') {
|
|
219
|
-
parts.shift()
|
|
220
|
-
}
|
|
221
|
-
if (parts[parts.length - 1] === '') {
|
|
222
|
-
parts.pop()
|
|
223
|
-
}
|
|
224
|
-
let headSpace = separatorRegEx.test(parts[0])
|
|
225
|
-
let tailSpace = separatorRegEx.test(parts[parts.length - 1])
|
|
226
|
-
const isClass = (_, i) => (headSpace ? i % 2 !== 0 : i % 2 === 0)
|
|
227
|
-
const isNotClass = (_, i) => (headSpace ? i % 2 === 0 : i % 2 !== 0)
|
|
228
|
-
let classNames = parts.filter(isClass)
|
|
229
|
-
let whitespaces = parts.filter(isNotClass)
|
|
230
|
-
return { classNames: classNames, whitespaces: whitespaces, headSpace: headSpace, tailSpace: tailSpace }
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Inspect and parse an abstract syntax node and run a callback function
|
|
235
|
-
*
|
|
236
|
-
* @param {ASTNode} rootNode The current root node being parsed by eslint
|
|
237
|
-
* @param {ASTNode} childNode The AST node child argument being checked
|
|
238
|
-
* @param {Function} cb The callback function
|
|
239
|
-
* @param {Boolean} skipConditional Optional, indicate distinct parsing for conditional nodes
|
|
240
|
-
* @param {Boolean} isolate Optional, set internally to isolate parsing and validation on conditional children
|
|
241
|
-
* @param {Array} ignoredKeys Optional, set object keys which should not be parsed e.g. for `cva`
|
|
242
|
-
* @returns {void}
|
|
243
|
-
*/
|
|
244
|
-
function parseNodeRecursive(rootNode, childNode, cb, skipConditional = false, isolate = false, ignoredKeys = []) {
|
|
245
|
-
// TODO allow vue non litteral
|
|
246
|
-
let originalClassNamesValue
|
|
247
|
-
let classNames
|
|
248
|
-
if (childNode === null) {
|
|
249
|
-
originalClassNamesValue = extractValueFromNode(rootNode);
|
|
250
|
-
({ classNames } = extractClassnamesFromValue(originalClassNamesValue))
|
|
251
|
-
classNames = removeDuplicatesFromArray(classNames)
|
|
252
|
-
if (classNames.length === 0) {
|
|
253
|
-
// Don't run for empty className
|
|
254
|
-
return
|
|
255
|
-
}
|
|
256
|
-
cb(classNames, rootNode)
|
|
257
|
-
} else if (childNode === undefined) {
|
|
258
|
-
// Ignore invalid child candidates (probably inside complex TemplateLiteral)
|
|
259
|
-
return
|
|
260
|
-
} else {
|
|
261
|
-
const forceIsolation = skipConditional ? true : isolate
|
|
262
|
-
let trim = false
|
|
263
|
-
switch (childNode.type) {
|
|
264
|
-
case 'TemplateLiteral':
|
|
265
|
-
childNode.expressions.forEach((exp) => {
|
|
266
|
-
parseNodeRecursive(rootNode, exp, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
267
|
-
})
|
|
268
|
-
childNode.quasis.forEach((quasis) => {
|
|
269
|
-
parseNodeRecursive(rootNode, quasis, cb, skipConditional, isolate, ignoredKeys)
|
|
270
|
-
})
|
|
271
|
-
return
|
|
272
|
-
case 'ConditionalExpression':
|
|
273
|
-
parseNodeRecursive(rootNode, childNode.consequent, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
274
|
-
parseNodeRecursive(rootNode, childNode.alternate, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
275
|
-
return
|
|
276
|
-
case 'LogicalExpression':
|
|
277
|
-
parseNodeRecursive(rootNode, childNode.right, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
278
|
-
return
|
|
279
|
-
case 'ArrayExpression':
|
|
280
|
-
childNode.elements.forEach((el) => {
|
|
281
|
-
parseNodeRecursive(rootNode, el, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
282
|
-
})
|
|
283
|
-
return
|
|
284
|
-
case 'ObjectExpression':
|
|
285
|
-
childNode.properties.forEach((prop) => {
|
|
286
|
-
const isUsedByClassNamesPlugin = rootNode.callee && rootNode.callee.name === 'classnames'
|
|
287
|
-
|
|
288
|
-
if (prop.type === 'SpreadElement') {
|
|
289
|
-
// Ignore spread elements
|
|
290
|
-
return
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (prop.key.type === 'Identifier' && ignoredKeys.includes(prop.key.name)) {
|
|
294
|
-
// Ignore specific keys defined in settings
|
|
295
|
-
return
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
parseNodeRecursive(
|
|
299
|
-
rootNode,
|
|
300
|
-
isUsedByClassNamesPlugin ? prop.key : prop.value,
|
|
301
|
-
cb,
|
|
302
|
-
skipConditional,
|
|
303
|
-
forceIsolation,
|
|
304
|
-
ignoredKeys
|
|
305
|
-
)
|
|
306
|
-
})
|
|
307
|
-
return
|
|
308
|
-
case 'Property':
|
|
309
|
-
parseNodeRecursive(rootNode, childNode.key, cb, skipConditional, forceIsolation, ignoredKeys)
|
|
310
|
-
return
|
|
311
|
-
case 'Literal':
|
|
312
|
-
trim = true
|
|
313
|
-
originalClassNamesValue = childNode.value
|
|
314
|
-
break
|
|
315
|
-
case 'TemplateElement':
|
|
316
|
-
originalClassNamesValue = childNode.value.raw
|
|
317
|
-
break
|
|
318
|
-
}
|
|
319
|
-
({ classNames } = extractClassnamesFromValue(originalClassNamesValue))
|
|
320
|
-
classNames = removeDuplicatesFromArray(classNames)
|
|
321
|
-
if (classNames.length === 0) {
|
|
322
|
-
// Don't run for empty className
|
|
323
|
-
return
|
|
324
|
-
}
|
|
325
|
-
const targetNode = isolate ? null : rootNode
|
|
326
|
-
cb(classNames, targetNode)
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function getTemplateElementPrefix(text, raw) {
|
|
331
|
-
const idx = text.indexOf(raw)
|
|
332
|
-
if (idx === 0) {
|
|
333
|
-
return ''
|
|
334
|
-
}
|
|
335
|
-
return text.split(raw).shift()
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function getTemplateElementSuffix(text, raw) {
|
|
339
|
-
if (text.indexOf(raw) === -1) {
|
|
340
|
-
return ''
|
|
341
|
-
}
|
|
342
|
-
return text.split(raw).pop()
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function getTemplateElementBody(text, prefix, suffix) {
|
|
346
|
-
let arr = text.split(prefix)
|
|
347
|
-
arr.shift()
|
|
348
|
-
let body = arr.join(prefix)
|
|
349
|
-
arr = body.split(suffix)
|
|
350
|
-
arr.pop()
|
|
351
|
-
return arr.join(suffix)
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
module.exports = {
|
|
355
|
-
calleeToString,
|
|
356
|
-
extractRangeFromNode,
|
|
357
|
-
extractValueFromNode,
|
|
358
|
-
extractClassnamesFromValue,
|
|
359
|
-
isClassAttribute,
|
|
360
|
-
isLiteralAttributeValue,
|
|
361
|
-
isArrayExpression,
|
|
362
|
-
isObjectExpression,
|
|
363
|
-
isValidJSXAttribute,
|
|
364
|
-
isValidVueAttribute,
|
|
365
|
-
isVLiteralValue,
|
|
366
|
-
parseNodeRecursive,
|
|
367
|
-
getTemplateElementPrefix,
|
|
368
|
-
getTemplateElementSuffix,
|
|
369
|
-
getTemplateElementBody,
|
|
370
|
-
}
|
package/lib/util/parser.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @see parserServices https://eslint.org/docs/developer-guide/working-with-rules#the-context-object
|
|
3
|
-
* @param {Object} context
|
|
4
|
-
* @param {Function} templateBodyVisitor
|
|
5
|
-
* @param {Function} scriptVisitor
|
|
6
|
-
* @returns
|
|
7
|
-
*/
|
|
8
|
-
function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor) {
|
|
9
|
-
if (context.parserServices == null || context.parserServices.defineTemplateBodyVisitor == null) {
|
|
10
|
-
// Default parser
|
|
11
|
-
return scriptVisitor
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Using "vue-eslint-parser" requires this setup
|
|
15
|
-
// @see https://eslint.org/docs/developer-guide/working-with-rules#the-context-object
|
|
16
|
-
return context.parserServices.defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = {
|
|
20
|
-
defineTemplateBodyVisitor,
|
|
21
|
-
}
|
package/lib/util/regex.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Escapes a string to be used in a regular expression
|
|
3
|
-
* Copied from https://stackoverflow.com/a/3561711.
|
|
4
|
-
* @param {string} input
|
|
5
|
-
* @returns {string}
|
|
6
|
-
*/
|
|
7
|
-
function escapeRegex(input) {
|
|
8
|
-
return input.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const separatorRegEx = /([\t\n\f\r ]+)/
|
|
12
|
-
|
|
13
|
-
module.exports = {
|
|
14
|
-
escapeRegex,
|
|
15
|
-
separatorRegEx,
|
|
16
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
function removeDuplicatesFromClassnamesAndWhitespaces(orderedClassNames, whitespaces, headSpace, tailSpace) {
|
|
4
|
-
let previous = orderedClassNames[0]
|
|
5
|
-
const offset = (!headSpace && !tailSpace) || tailSpace ? -1 : 0
|
|
6
|
-
for (let i = 1; i < orderedClassNames.length; i++) {
|
|
7
|
-
const cls = orderedClassNames[i]
|
|
8
|
-
// This function assumes that the list of classNames is ordered, so just comparing to the previous className is enough
|
|
9
|
-
if (cls === previous) {
|
|
10
|
-
orderedClassNames.splice(i, 1)
|
|
11
|
-
whitespaces.splice(i + offset, 1)
|
|
12
|
-
i--
|
|
13
|
-
}
|
|
14
|
-
previous = cls
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = removeDuplicatesFromClassnamesAndWhitespaces
|
package/lib/util/settings.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
const exploreConfig = require('explore-config').default
|
|
3
|
-
|
|
4
|
-
function getOption(context, name) {
|
|
5
|
-
// Options (defined at rule level)
|
|
6
|
-
const options = context.options[0] || {}
|
|
7
|
-
if (options[name] != undefined) {
|
|
8
|
-
return options[name]
|
|
9
|
-
}
|
|
10
|
-
// Settings (defined at plugin level, shared accross rules)
|
|
11
|
-
if (context.settings && context.settings.mastercss && context.settings.mastercss[name] != undefined) {
|
|
12
|
-
return context.settings.mastercss[name]
|
|
13
|
-
}
|
|
14
|
-
// Fallback to defaults
|
|
15
|
-
switch (name) {
|
|
16
|
-
case 'callees':
|
|
17
|
-
return ['classnames', 'clsx', 'ctl', 'cva']
|
|
18
|
-
case 'ignoredKeys':
|
|
19
|
-
return ['compoundVariants', 'defaultVariants']
|
|
20
|
-
case 'classRegex':
|
|
21
|
-
return '^class(Name)?$'
|
|
22
|
-
case 'config':
|
|
23
|
-
return exploreConfig('master.css.*', {})
|
|
24
|
-
case 'cssFiles':
|
|
25
|
-
return ['**/*.css', '!**/node_modules', '!**/.*', '!**/dist', '!**/build']
|
|
26
|
-
case 'cssFilesRefreshRate':
|
|
27
|
-
return 5_000
|
|
28
|
-
case 'removeDuplicates':
|
|
29
|
-
return true
|
|
30
|
-
case 'skipClassAttribute':
|
|
31
|
-
return false
|
|
32
|
-
case 'tags':
|
|
33
|
-
return []
|
|
34
|
-
case 'whitelist':
|
|
35
|
-
return []
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = getOption
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Use a consistent orders for the Master CSS classnames, based on property then on variants
|
|
3
|
-
* @author Miles
|
|
4
|
-
*/
|
|
5
|
-
'use strict'
|
|
6
|
-
|
|
7
|
-
// Modified from https://github.com/francoismassart/eslint-plugin-tailwindcss
|
|
8
|
-
|
|
9
|
-
//------------------------------------------------------------------------------
|
|
10
|
-
// Requirements
|
|
11
|
-
//------------------------------------------------------------------------------
|
|
12
|
-
|
|
13
|
-
var rule = require('../../lib/rules/class-order')
|
|
14
|
-
var RuleTester = require('eslint').RuleTester
|
|
15
|
-
var parserOptions = {
|
|
16
|
-
ecmaVersion: 2019,
|
|
17
|
-
sourceType: 'module',
|
|
18
|
-
ecmaFeatures: {
|
|
19
|
-
jsx: true,
|
|
20
|
-
},
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
var ruleTester = new RuleTester({ parserOptions })
|
|
24
|
-
ruleTester.run('class order', rule, {
|
|
25
|
-
valid: [
|
|
26
|
-
{
|
|
27
|
-
code: `<div class="bg:black fg:white f:24 m:8 p:8">Simple, basic</div>`,
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
code: `<div test="bg:black fg:white f:24 m:8 p:8">Simple, using 'test' prop</div>`,
|
|
31
|
-
options: [
|
|
32
|
-
{
|
|
33
|
-
classRegex: '^test$',
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
invalid: [
|
|
39
|
-
// jsx
|
|
40
|
-
{
|
|
41
|
-
code: `
|
|
42
|
-
export interface FakePropsInterface {
|
|
43
|
-
readonly name?: string;
|
|
44
|
-
}
|
|
45
|
-
function Fake({
|
|
46
|
-
name = 'yolo'
|
|
47
|
-
}: FakeProps) {
|
|
48
|
-
return (
|
|
49
|
-
<>
|
|
50
|
-
<h1 className={"m:8 bg:black p:8 fg:white f:24"}>Welcome {name}</h1>
|
|
51
|
-
<p>Bye {name}</p>
|
|
52
|
-
</>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
export default Fake;
|
|
56
|
-
`,
|
|
57
|
-
output: `
|
|
58
|
-
export interface FakePropsInterface {
|
|
59
|
-
readonly name?: string;
|
|
60
|
-
}
|
|
61
|
-
function Fake({
|
|
62
|
-
name = 'yolo'
|
|
63
|
-
}: FakeProps) {
|
|
64
|
-
return (
|
|
65
|
-
<>
|
|
66
|
-
<h1 className={"bg:black fg:white f:24 m:8 p:8"}>Welcome {name}</h1>
|
|
67
|
-
<p>Bye {name}</p>
|
|
68
|
-
</>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
export default Fake;
|
|
72
|
-
`,
|
|
73
|
-
parser: require.resolve('@typescript-eslint/parser'),
|
|
74
|
-
errors: [{ messageId: 'invalidOrder' }],
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
code: `<div class="fg:white f:24 m:8 p:8 bg:black">Classnames will be ordered</div>`,
|
|
78
|
-
output: `<div class="bg:black fg:white f:24 m:8 p:8">Classnames will be ordered</div>`,
|
|
79
|
-
errors: [{ messageId: 'invalidOrder' }],
|
|
80
|
-
},
|
|
81
|
-
// html, angular
|
|
82
|
-
{
|
|
83
|
-
code: `
|
|
84
|
-
<div class="
|
|
85
|
-
m:8
|
|
86
|
-
bg:black
|
|
87
|
-
p:8
|
|
88
|
-
fg:white
|
|
89
|
-
f:24
|
|
90
|
-
">
|
|
91
|
-
:)
|
|
92
|
-
</div>`,
|
|
93
|
-
output: `
|
|
94
|
-
<div class="
|
|
95
|
-
bg:black
|
|
96
|
-
fg:white
|
|
97
|
-
f:24
|
|
98
|
-
m:8
|
|
99
|
-
p:8
|
|
100
|
-
">
|
|
101
|
-
:)
|
|
102
|
-
</div>`,
|
|
103
|
-
errors: [{ messageId: 'invalidOrder' }],
|
|
104
|
-
parser: require.resolve('@angular-eslint/template-parser'),
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
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>`,
|
|
108
|
-
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>`,
|
|
109
|
-
errors: [{ messageId: 'invalidOrder' }],
|
|
110
|
-
}
|
|
111
|
-
],
|
|
112
|
-
})
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
var rule = require('../../lib/rules/class-order')
|
|
4
|
-
var RuleTester = require('eslint').RuleTester
|
|
5
|
-
var parserOptions = {
|
|
6
|
-
ecmaVersion: 2019,
|
|
7
|
-
sourceType: 'module',
|
|
8
|
-
ecmaFeatures: {
|
|
9
|
-
jsx: true,
|
|
10
|
-
},
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
var ruleTester = new RuleTester({ parserOptions })
|
|
14
|
-
ruleTester.run('svelte class order', rule, {
|
|
15
|
-
valid: [{ code: `<div class="bg:black fg:white f:24 m:8 p:8">Simple, basic</div>` }],
|
|
16
|
-
invalid: [
|
|
17
|
-
{
|
|
18
|
-
code: `<div class="m:8 bg:black p:8 fg:white f:24 {flipped ? 'flipped' : ''}">Enhancing readability</div>`,
|
|
19
|
-
output: `<div class="bg:black fg:white f:24 m:8 p:8 {flipped ? 'flipped' : ''}">Enhancing readability</div>`,
|
|
20
|
-
errors: [{ messageId: 'invalidOrder' }],
|
|
21
|
-
filename: 'test.svelte',
|
|
22
|
-
parser: require.resolve('svelte-eslint-parser'),
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
})
|
package/tests/class-order/vue.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
var rule = require('../../lib/rules/class-order')
|
|
4
|
-
var RuleTester = require('eslint').RuleTester
|
|
5
|
-
var parserOptions = {
|
|
6
|
-
ecmaVersion: 2019,
|
|
7
|
-
sourceType: 'module',
|
|
8
|
-
ecmaFeatures: {
|
|
9
|
-
jsx: true,
|
|
10
|
-
},
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
var ruleTester = new RuleTester({ parserOptions })
|
|
14
|
-
ruleTester.run('vue class order', rule, {
|
|
15
|
-
valid: [{ code: `<div class="bg:black fg:white f:24 m:8 p:8">Simple, basic</div>` }],
|
|
16
|
-
invalid: [
|
|
17
|
-
{
|
|
18
|
-
code: `<template><div class="m:8 bg:black p:8 fg:white f:24">Enhancing readability</div></template>`,
|
|
19
|
-
output: `<template><div class="bg:black fg:white f:24 m:8 p:8">Enhancing readability</div></template>`,
|
|
20
|
-
errors: [{ messageId: 'invalidOrder' }],
|
|
21
|
-
filename: 'test.vue',
|
|
22
|
-
parser: require.resolve('vue-eslint-parser'),
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
})
|