@master/eslint-plugin-css 0.0.0-site.1793659883.g69c9aebcf628
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/README.md +291 -0
- package/dist/configs/base.d.ts +3 -0
- package/dist/configs/base.js +4 -0
- package/dist/configs/recommended.d.ts +3 -0
- package/dist/configs/recommended.js +12 -0
- package/dist/create-rule.d.ts +5 -0
- package/dist/create-rule.js +4 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +15 -0
- package/dist/internal/src/workspace-directories.js +67 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +28 -0
- package/dist/rules/no-conflicting-classes.d.ts +4 -0
- package/dist/rules/no-conflicting-classes.js +39 -0
- package/dist/rules/no-invalid-classes.d.ts +4 -0
- package/dist/rules/no-invalid-classes.js +41 -0
- package/dist/rules/no-unapproved-raw-values.d.ts +8 -0
- package/dist/rules/no-unapproved-raw-values.js +63 -0
- package/dist/rules/prefer-canonical-classes.d.ts +5 -0
- package/dist/rules/prefer-canonical-classes.js +48 -0
- package/dist/rules/prefer-component-classes.d.ts +4 -0
- package/dist/rules/prefer-component-classes.js +45 -0
- package/dist/rules/sort-classes.d.ts +4 -0
- package/dist/rules/sort-classes.js +38 -0
- package/dist/settings-schema.d.ts +4 -0
- package/dist/settings-schema.js +26 -0
- package/dist/settings.d.ts +16 -0
- package/dist/settings.js +7 -0
- package/dist/utils/define-source-visitors.d.ts +13 -0
- package/dist/utils/define-source-visitors.js +51 -0
- package/dist/utils/define-visitors.d.ts +11 -0
- package/dist/utils/define-visitors.js +102 -0
- package/dist/utils/dependency-stamp.d.ts +10 -0
- package/dist/utils/dependency-stamp.js +27 -0
- package/dist/utils/is-same-or-child-path.d.ts +2 -0
- package/dist/utils/is-same-or-child-path.js +7 -0
- package/dist/utils/javascript-literal.d.ts +3 -0
- package/dist/utils/javascript-literal.js +51 -0
- package/dist/utils/master-css.d.ts +3 -0
- package/dist/utils/master-css.js +4 -0
- package/dist/utils/report-lint-diagnostics.d.ts +20 -0
- package/dist/utils/report-lint-diagnostics.js +54 -0
- package/dist/utils/resolve-class-node.d.ts +26 -0
- package/dist/utils/resolve-class-node.js +106 -0
- package/dist/utils/resolve-context.d.ts +15 -0
- package/dist/utils/resolve-context.js +177 -0
- package/dist/utils/source-tooling.d.ts +7 -0
- package/dist/utils/source-tooling.js +40 -0
- package/dist/utils/vue-literal.d.ts +8 -0
- package/dist/utils/vue-literal.js +61 -0
- package/dist/utils/with-context-release.d.ts +3 -0
- package/dist/utils/with-context-release.js +15 -0
- package/dist/utils/with-visit-class-node.d.ts +6 -0
- package/dist/utils/with-visit-class-node.js +50 -0
- package/dist/utils/workspace-cache.d.ts +11 -0
- package/dist/utils/workspace-cache.js +30 -0
- package/package.json +50 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import create_rule_default from "../create-rule.js";
|
|
2
|
+
import defineVisitors from "../utils/define-visitors.js";
|
|
3
|
+
import resolveContext from "../utils/resolve-context.js";
|
|
4
|
+
import reportLintDiagnostics from "../utils/report-lint-diagnostics.js";
|
|
5
|
+
import defineSourceVisitors, { shouldUseSourceVisitors } from "../utils/define-source-visitors.js";
|
|
6
|
+
import withContextRelease from "../utils/with-context-release.js";
|
|
7
|
+
import { createMasterCSSLintDiagnostics, defaultCanonicalClassNameOptions } from "@master/css-tooling/lint";
|
|
8
|
+
// src/rules/prefer-canonical-classes.ts
|
|
9
|
+
var prefer_canonical_classes_default = create_rule_default({
|
|
10
|
+
name: "prefer-canonical-classes",
|
|
11
|
+
meta: {
|
|
12
|
+
type: "suggestion",
|
|
13
|
+
docs: { description: "Prefer canonical Master CSS classes" },
|
|
14
|
+
messages: { preferClass: "{{message}}" },
|
|
15
|
+
fixable: "code",
|
|
16
|
+
schema: [{
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
preferStaticUtilities: { type: "boolean" },
|
|
20
|
+
preferCompositionUtilities: { type: "boolean" }
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false
|
|
23
|
+
}]
|
|
24
|
+
},
|
|
25
|
+
defaultOptions: [defaultCanonicalClassNameOptions],
|
|
26
|
+
create(context) {
|
|
27
|
+
const { settings, tooling, release } = resolveContext(context);
|
|
28
|
+
const options = {
|
|
29
|
+
...defaultCanonicalClassNameOptions,
|
|
30
|
+
...context.options[0] || {}
|
|
31
|
+
};
|
|
32
|
+
if (shouldUseSourceVisitors(context)) return withContextRelease(defineSourceVisitors({
|
|
33
|
+
context,
|
|
34
|
+
tooling,
|
|
35
|
+
ruleId: "prefer-canonical-classes",
|
|
36
|
+
ruleOptions: options
|
|
37
|
+
}), release);
|
|
38
|
+
const reportCanonicalClassList = (node, resolved) => {
|
|
39
|
+
reportLintDiagnostics(context, node, resolved, createMasterCSSLintDiagnostics(tooling.analyzeLintClassList(resolved.analysisText ?? resolved.raw, resolved.classValues, { canonicalOptions: options }).diagnostics.filter(({ ruleId }) => ruleId === "prefer-canonical-classes")));
|
|
40
|
+
};
|
|
41
|
+
return withContextRelease(defineVisitors({
|
|
42
|
+
context,
|
|
43
|
+
settings,
|
|
44
|
+
tooling
|
|
45
|
+
}, reportCanonicalClassList), release);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
export { prefer_canonical_classes_default as default };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import create_rule_default from "../create-rule.js";
|
|
2
|
+
import defineVisitors from "../utils/define-visitors.js";
|
|
3
|
+
import resolveContext from "../utils/resolve-context.js";
|
|
4
|
+
import reportLintDiagnostics from "../utils/report-lint-diagnostics.js";
|
|
5
|
+
import defineSourceVisitors, { shouldUseSourceVisitors } from "../utils/define-source-visitors.js";
|
|
6
|
+
import withContextRelease from "../utils/with-context-release.js";
|
|
7
|
+
import { createMasterCSSLintDiagnostics } from "@master/css-tooling/lint";
|
|
8
|
+
// src/rules/prefer-component-classes.ts
|
|
9
|
+
var prefer_component_classes_default = create_rule_default({
|
|
10
|
+
name: "prefer-component-classes",
|
|
11
|
+
meta: {
|
|
12
|
+
type: "suggestion",
|
|
13
|
+
docs: { description: "Prefer a component class over many classes that style other elements through combinators" },
|
|
14
|
+
messages: { remoteStylingLimit: "{{message}}" },
|
|
15
|
+
fixable: null,
|
|
16
|
+
schema: [{
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: { max: {
|
|
19
|
+
type: "integer",
|
|
20
|
+
minimum: 0
|
|
21
|
+
} },
|
|
22
|
+
additionalProperties: false
|
|
23
|
+
}]
|
|
24
|
+
},
|
|
25
|
+
defaultOptions: [{}],
|
|
26
|
+
create(context) {
|
|
27
|
+
const { settings, tooling, release } = resolveContext(context);
|
|
28
|
+
const options = context.options[0] || {};
|
|
29
|
+
if (shouldUseSourceVisitors(context)) return withContextRelease(defineSourceVisitors({
|
|
30
|
+
context,
|
|
31
|
+
tooling,
|
|
32
|
+
ruleId: "prefer-component-classes",
|
|
33
|
+
ruleOptions: options
|
|
34
|
+
}), release);
|
|
35
|
+
return withContextRelease(defineVisitors({
|
|
36
|
+
context,
|
|
37
|
+
settings,
|
|
38
|
+
tooling
|
|
39
|
+
}, (node, resolved) => {
|
|
40
|
+
const diagnostics = createMasterCSSLintDiagnostics(tooling.analyzeLintClassList(resolved.analysisText ?? resolved.raw, resolved.classValues, { remoteStylingPolicy: options }).diagnostics.filter(({ ruleId }) => ruleId === "prefer-component-classes"));
|
|
41
|
+
reportLintDiagnostics(context, node, resolved, diagnostics);
|
|
42
|
+
}), release);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
export { prefer_component_classes_default as default };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import create_rule_default from "../create-rule.js";
|
|
2
|
+
import defineVisitors from "../utils/define-visitors.js";
|
|
3
|
+
import resolveContext from "../utils/resolve-context.js";
|
|
4
|
+
import reportLintDiagnostics from "../utils/report-lint-diagnostics.js";
|
|
5
|
+
import defineSourceVisitors, { shouldUseSourceVisitors } from "../utils/define-source-visitors.js";
|
|
6
|
+
import withContextRelease from "../utils/with-context-release.js";
|
|
7
|
+
import { createMasterCSSLintDiagnostics } from "@master/css-tooling/lint";
|
|
8
|
+
// src/rules/sort-classes.ts
|
|
9
|
+
var sort_classes_default = create_rule_default({
|
|
10
|
+
name: "sort-classes",
|
|
11
|
+
meta: {
|
|
12
|
+
type: "layout",
|
|
13
|
+
fixable: "code",
|
|
14
|
+
docs: { description: "Sort Master CSS classes" },
|
|
15
|
+
messages: { invalidClassOrder: "{{message}}" },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create: function(context) {
|
|
20
|
+
const { settings, tooling, release } = resolveContext(context);
|
|
21
|
+
if (shouldUseSourceVisitors(context)) return withContextRelease(defineSourceVisitors({
|
|
22
|
+
context,
|
|
23
|
+
tooling,
|
|
24
|
+
ruleId: "sort-classes"
|
|
25
|
+
}), release);
|
|
26
|
+
return withContextRelease(defineVisitors({
|
|
27
|
+
context,
|
|
28
|
+
settings,
|
|
29
|
+
tooling
|
|
30
|
+
}, (node, resolved) => {
|
|
31
|
+
const { nodes } = resolved;
|
|
32
|
+
if (nodes.length <= 1) return;
|
|
33
|
+
const diagnostics = createMasterCSSLintDiagnostics(tooling.analyzeLintClassList(resolved.analysisText ?? resolved.raw, resolved.classValues).diagnostics.filter(({ ruleId }) => ruleId === "sort-classes"));
|
|
34
|
+
reportLintDiagnostics(context, node, resolved, diagnostics);
|
|
35
|
+
}), release);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
export { sort_classes_default as default };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { JSONSchema4 } from "@typescript-eslint/utils/json-schema";
|
|
2
|
+
declare const masterCSSSettingsSchema: JSONSchema4;
|
|
3
|
+
declare const noInvalidClassesOptionsSchema: JSONSchema4;
|
|
4
|
+
export { masterCSSSettingsSchema as default, masterCSSSettingsSchema, noInvalidClassesOptionsSchema };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/settings-schema.ts
|
|
2
|
+
const sourceMatcherSchema = {
|
|
3
|
+
type: "array",
|
|
4
|
+
items: {
|
|
5
|
+
type: "string",
|
|
6
|
+
minLength: 1
|
|
7
|
+
},
|
|
8
|
+
uniqueItems: true
|
|
9
|
+
};
|
|
10
|
+
const masterCSSSettingsSchema = {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
classAttributes: sourceMatcherSchema,
|
|
14
|
+
classFunctions: sourceMatcherSchema,
|
|
15
|
+
classDeclarations: sourceMatcherSchema,
|
|
16
|
+
ignoredKeys: sourceMatcherSchema,
|
|
17
|
+
manifest: { type: "object" }
|
|
18
|
+
},
|
|
19
|
+
additionalProperties: false
|
|
20
|
+
};
|
|
21
|
+
const noInvalidClassesOptionsSchema = {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: { disallowUnknownClass: { type: "boolean" } },
|
|
24
|
+
additionalProperties: false
|
|
25
|
+
};
|
|
26
|
+
export { masterCSSSettingsSchema as default, masterCSSSettingsSchema, noInvalidClassesOptionsSchema };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MasterCSSManifest } from "./utils/master-css.js";
|
|
2
|
+
declare const settings: {
|
|
3
|
+
classAttributes: readonly string[];
|
|
4
|
+
classFunctions: readonly string[];
|
|
5
|
+
classDeclarations: readonly string[];
|
|
6
|
+
ignoredKeys: readonly string[];
|
|
7
|
+
manifest: any;
|
|
8
|
+
};
|
|
9
|
+
interface Settings {
|
|
10
|
+
readonly classAttributes?: readonly string[];
|
|
11
|
+
readonly classFunctions?: readonly string[];
|
|
12
|
+
readonly classDeclarations?: readonly string[];
|
|
13
|
+
readonly ignoredKeys: readonly string[];
|
|
14
|
+
manifest?: MasterCSSManifest;
|
|
15
|
+
}
|
|
16
|
+
export { Settings, settings as default };
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MasterCSSLintContentRuleOptions, MasterCSSLintRuleId } from "@master/css-tooling/lint";
|
|
2
|
+
import { MasterCSSToolingSession } from "@master/css-tooling";
|
|
3
|
+
import { RuleContext, RuleListener } from "@typescript-eslint/utils/ts-eslint";
|
|
4
|
+
type SourceRuleOptions = MasterCSSLintContentRuleOptions[keyof MasterCSSLintContentRuleOptions];
|
|
5
|
+
interface DefineSourceVisitorsOptions {
|
|
6
|
+
context: RuleContext<any, any[]>;
|
|
7
|
+
ruleId: MasterCSSLintRuleId;
|
|
8
|
+
ruleOptions?: SourceRuleOptions;
|
|
9
|
+
tooling: MasterCSSToolingSession;
|
|
10
|
+
}
|
|
11
|
+
declare function shouldUseSourceVisitors(context: RuleContext<any, any[]>): boolean;
|
|
12
|
+
declare function defineSourceVisitors({ context, ruleId, ruleOptions, tooling }: DefineSourceVisitorsOptions): RuleListener;
|
|
13
|
+
export { defineSourceVisitors as default, shouldUseSourceVisitors };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { messageIdByCode, stringifyLintDiagnosticMessageData } from "./report-lint-diagnostics.js";
|
|
2
|
+
import { lintMasterCSSContent, masterCSSLintRuleIds } from "@master/css-tooling/lint";
|
|
3
|
+
// src/utils/define-source-visitors.ts
|
|
4
|
+
const SOURCE_LINT_FILE_RE = /\.mdx$/i;
|
|
5
|
+
function shouldUseSourceVisitors(context) {
|
|
6
|
+
const filename = context.physicalFilename || context.filename;
|
|
7
|
+
return typeof filename === "string" && SOURCE_LINT_FILE_RE.test(filename);
|
|
8
|
+
}
|
|
9
|
+
function getFilename(context) {
|
|
10
|
+
return context.physicalFilename || context.filename || "source.mdx";
|
|
11
|
+
}
|
|
12
|
+
function createSingleRuleSet(ruleId) {
|
|
13
|
+
return Object.fromEntries(masterCSSLintRuleIds.map((eachRuleId) => [eachRuleId, eachRuleId === ruleId]));
|
|
14
|
+
}
|
|
15
|
+
function createRuleOptions(ruleId, ruleOptions) {
|
|
16
|
+
return ruleOptions ? { [ruleId]: ruleOptions } : void 0;
|
|
17
|
+
}
|
|
18
|
+
function reportSourceDiagnostic(context, node, diagnostic) {
|
|
19
|
+
const { sourceCode } = context;
|
|
20
|
+
const messageId = messageIdByCode[diagnostic.code];
|
|
21
|
+
const fix = diagnostic.fixes?.[0];
|
|
22
|
+
const descriptor = {
|
|
23
|
+
node,
|
|
24
|
+
loc: {
|
|
25
|
+
start: sourceCode.getLocFromIndex(diagnostic.range.start),
|
|
26
|
+
end: sourceCode.getLocFromIndex(diagnostic.range.end)
|
|
27
|
+
},
|
|
28
|
+
...messageId ? {
|
|
29
|
+
messageId,
|
|
30
|
+
data: stringifyLintDiagnosticMessageData(diagnostic)
|
|
31
|
+
} : { message: diagnostic.message }
|
|
32
|
+
};
|
|
33
|
+
if (fix) descriptor.fix = (fixer) => fixer.replaceTextRange([fix.range.start, fix.range.end], fix.text);
|
|
34
|
+
context.report(descriptor);
|
|
35
|
+
}
|
|
36
|
+
function defineSourceVisitors({ context, ruleId, ruleOptions, tooling }) {
|
|
37
|
+
return { Program(node) {
|
|
38
|
+
const result = lintMasterCSSContent({
|
|
39
|
+
content: context.sourceCode.getText(),
|
|
40
|
+
filePath: getFilename(context),
|
|
41
|
+
rules: createSingleRuleSet(ruleId),
|
|
42
|
+
ruleOptions: createRuleOptions(ruleId, ruleOptions),
|
|
43
|
+
lintSession: tooling
|
|
44
|
+
});
|
|
45
|
+
for (const diagnostic of result.diagnostics) {
|
|
46
|
+
if (diagnostic.ruleId !== ruleId) continue;
|
|
47
|
+
reportSourceDiagnostic(context, node, diagnostic);
|
|
48
|
+
}
|
|
49
|
+
} };
|
|
50
|
+
}
|
|
51
|
+
export { defineSourceVisitors as default, shouldUseSourceVisitors };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
import { Settings } from "../settings.js";
|
|
3
|
+
import { MasterCSSToolingSession } from "@master/css-tooling";
|
|
4
|
+
import { RuleContext, RuleListener } from "@typescript-eslint/utils/ts-eslint";
|
|
5
|
+
import resolveClassNode from "./resolve-class-node.js";
|
|
6
|
+
declare function defineVisitors({ context, settings, tooling }: {
|
|
7
|
+
context: RuleContext<any, any[]>;
|
|
8
|
+
settings: Settings;
|
|
9
|
+
tooling: Pick<MasterCSSToolingSession, 'tokenizeClassList' | 'decodeHTMLAttribute'>;
|
|
10
|
+
}, visitNode: (node: TSESTree.Node, resolved: ReturnType<typeof resolveClassNode>) => void): RuleListener;
|
|
11
|
+
export { defineVisitors as default };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import withVisitClassNode from "./with-visit-class-node.js";
|
|
2
|
+
// src/utils/define-visitors.ts
|
|
3
|
+
function defineVisitors({ context, settings, tooling }, visitNode) {
|
|
4
|
+
const classAttributeRegex = new RegExp(`^(?:${settings.classAttributes.join("|")})$`);
|
|
5
|
+
const classFunctionsRegex = new RegExp(`^(?:${settings.classFunctions.join("|")})$`);
|
|
6
|
+
const classDeclarationsRegex = new RegExp(`^(?:${settings.classDeclarations.join("|")})$`);
|
|
7
|
+
const visitClassNode = withVisitClassNode(visitNode, context, tooling);
|
|
8
|
+
const getStaticName = (node) => {
|
|
9
|
+
if (!node) return;
|
|
10
|
+
if (node.type === "Identifier") return node.name;
|
|
11
|
+
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
12
|
+
};
|
|
13
|
+
const getMemberExpressionName = (node) => {
|
|
14
|
+
if (!node || node.type !== "MemberExpression") return;
|
|
15
|
+
const objectName = getStaticName(node.object);
|
|
16
|
+
const propertyName = getStaticName(node.property);
|
|
17
|
+
if (!objectName || !propertyName) return;
|
|
18
|
+
return `${objectName}.${propertyName}`;
|
|
19
|
+
};
|
|
20
|
+
const getCalleeName = (node) => {
|
|
21
|
+
const calleeNode = node.callee || node.tag;
|
|
22
|
+
return getStaticName(calleeNode) || getMemberExpressionName(calleeNode);
|
|
23
|
+
};
|
|
24
|
+
const allowCalleeNode = (node) => {
|
|
25
|
+
const calleeName = getCalleeName(node);
|
|
26
|
+
return calleeName ? classFunctionsRegex.test(calleeName) : false;
|
|
27
|
+
};
|
|
28
|
+
const getPropertyName = (prop) => getStaticName(prop.key);
|
|
29
|
+
const visitUtilityDefinitions = (node) => {
|
|
30
|
+
if (!node) return;
|
|
31
|
+
if (node.type === "ArrayExpression") {
|
|
32
|
+
node.elements.forEach((element) => {
|
|
33
|
+
if (!element || element.type === "ObjectExpression") return;
|
|
34
|
+
visitClassNode(element);
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (node.type !== "ObjectExpression") {
|
|
39
|
+
visitClassNode(node);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const visitClassDeclarationNode = (name, node) => {
|
|
44
|
+
if (name !== "utilities") {
|
|
45
|
+
visitClassNode(node);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
visitUtilityDefinitions(node);
|
|
49
|
+
};
|
|
50
|
+
const CallExpression = function(node) {
|
|
51
|
+
if (!allowCalleeNode(node)) return;
|
|
52
|
+
node.arguments.forEach((node) => {
|
|
53
|
+
visitClassNode(node);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const scriptVisitor = {
|
|
57
|
+
CallExpression,
|
|
58
|
+
JSXAttribute: function(node) {
|
|
59
|
+
if (!node.name || !classAttributeRegex.test(node.name.name)) return;
|
|
60
|
+
if (node.value) visitClassNode(node.value);
|
|
61
|
+
},
|
|
62
|
+
SvelteAttribute: function(node) {
|
|
63
|
+
if (!node.key?.name || !classAttributeRegex.test(node.key.name)) return;
|
|
64
|
+
for (const eachValue of node.value) visitClassNode(eachValue);
|
|
65
|
+
},
|
|
66
|
+
TextAttribute: function(node) {
|
|
67
|
+
if (!node.name || !classAttributeRegex.test(node.name)) return;
|
|
68
|
+
visitClassNode(node);
|
|
69
|
+
},
|
|
70
|
+
TaggedTemplateExpression: function(node) {
|
|
71
|
+
if (allowCalleeNode(node)) {
|
|
72
|
+
visitClassNode(node.quasi);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
VariableDeclaration: function(node) {
|
|
77
|
+
node.declarations.forEach((decl) => {
|
|
78
|
+
if (decl.id.type === "Identifier" && classDeclarationsRegex.test(decl.id.name)) visitClassDeclarationNode(decl.id.name, decl.init);
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
ObjectExpression: function(node) {
|
|
82
|
+
node.properties.forEach((prop) => {
|
|
83
|
+
if (prop.type === "Property") {
|
|
84
|
+
const propName = getPropertyName(prop);
|
|
85
|
+
if (typeof propName === "string" && classDeclarationsRegex.test(propName)) visitClassDeclarationNode(propName, prop.value);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const templateBodyVisitor = {
|
|
91
|
+
CallExpression,
|
|
92
|
+
VAttribute: function(node) {
|
|
93
|
+
const name = node.key?.argument?.name || node.key?.name;
|
|
94
|
+
if (!name || !classAttributeRegex.test(name)) return;
|
|
95
|
+
if (node.value && node.value.type === "VLiteral") visitClassNode(node.value);
|
|
96
|
+
else if (node.value?.type === "VExpressionContainer" && node.value.expression) visitClassNode(node.value.expression);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
if (context.sourceCode.parserServices == null || context.sourceCode.parserServices.defineTemplateBodyVisitor == null) return scriptVisitor;
|
|
100
|
+
else return context.sourceCode.parserServices.defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor);
|
|
101
|
+
}
|
|
102
|
+
export { defineVisitors as default };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface DependencyStamp {
|
|
2
|
+
path: string;
|
|
3
|
+
mtimeNs: bigint;
|
|
4
|
+
ctimeNs: bigint;
|
|
5
|
+
size: bigint;
|
|
6
|
+
ino: bigint;
|
|
7
|
+
}
|
|
8
|
+
declare function stampDependency(path: string): DependencyStamp;
|
|
9
|
+
declare function isDependencyCurrent(stamp: DependencyStamp): boolean;
|
|
10
|
+
export { DependencyStamp, isDependencyCurrent, stampDependency };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
// src/utils/dependency-stamp.ts
|
|
3
|
+
function stampDependency(path) {
|
|
4
|
+
try {
|
|
5
|
+
const { mtimeNs, ctimeNs, size, ino } = statSync(path, { bigint: true });
|
|
6
|
+
return {
|
|
7
|
+
path,
|
|
8
|
+
mtimeNs,
|
|
9
|
+
ctimeNs,
|
|
10
|
+
size,
|
|
11
|
+
ino
|
|
12
|
+
};
|
|
13
|
+
} catch {
|
|
14
|
+
return {
|
|
15
|
+
path,
|
|
16
|
+
mtimeNs: -1n,
|
|
17
|
+
ctimeNs: -1n,
|
|
18
|
+
size: -1n,
|
|
19
|
+
ino: -1n
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function isDependencyCurrent(stamp) {
|
|
24
|
+
const current = stampDependency(stamp.path);
|
|
25
|
+
return current.mtimeNs === stamp.mtimeNs && current.ctimeNs === stamp.ctimeNs && current.size === stamp.size && current.ino === stamp.ino;
|
|
26
|
+
}
|
|
27
|
+
export { isDependencyCurrent, stampDependency };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
// src/utils/is-same-or-child-path.ts
|
|
3
|
+
function isSameOrChildPath(parentPath, childPath) {
|
|
4
|
+
const relativePath = path.relative(parentPath, childPath);
|
|
5
|
+
return relativePath === "" || !!relativePath && relativePath !== ".." && !relativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(relativePath);
|
|
6
|
+
}
|
|
7
|
+
export { isSameOrChildPath as default };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare function javascriptLiteralRange(raw: string, cooked: string): (start: number, end: number) => [number, number];
|
|
2
|
+
declare function encodeJavaScriptLiteral(text: string, quote: string, newline?: string): string;
|
|
3
|
+
export { encodeJavaScriptLiteral, javascriptLiteralRange };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/utils/javascript-literal.ts
|
|
2
|
+
function javascriptLiteralRange(raw, cooked) {
|
|
3
|
+
const starts = [];
|
|
4
|
+
const ends = [];
|
|
5
|
+
for (let index = 0; index < raw.length;) {
|
|
6
|
+
const start = index++;
|
|
7
|
+
let width = 1;
|
|
8
|
+
if (raw[start] === "\\") {
|
|
9
|
+
const character = raw[index++];
|
|
10
|
+
if (character === "\r" || character === "\n" || character === "\u2028" || character === "\u2029") {
|
|
11
|
+
if (character === "\r" && raw[index] === "\n") index++;
|
|
12
|
+
width = 0;
|
|
13
|
+
} else if (character === "u") {
|
|
14
|
+
if (raw[index] === "{") {
|
|
15
|
+
const end = raw.indexOf("}", index + 1);
|
|
16
|
+
if (end === -1) return;
|
|
17
|
+
width = Number.parseInt(raw.slice(index + 1, end), 16) > 65535 ? 2 : 1;
|
|
18
|
+
index = end + 1;
|
|
19
|
+
} else index += 4;
|
|
20
|
+
} else if (character === "x") index += 2;
|
|
21
|
+
else if (/[0-7]/.test(character)) {
|
|
22
|
+
const limit = character <= "3" ? 2 : 1;
|
|
23
|
+
for (let count = 0; count < limit && /[0-7]/.test(raw[index] ?? ""); count++) index++;
|
|
24
|
+
}
|
|
25
|
+
} else if (raw[start] === "\r" && raw[index] === "\n") index++;
|
|
26
|
+
if (index > raw.length) return;
|
|
27
|
+
for (let unit = 0; unit < width; unit++) {
|
|
28
|
+
starts.push(start);
|
|
29
|
+
ends.push(index);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (starts.length !== cooked.length) return;
|
|
33
|
+
return (start, end) => {
|
|
34
|
+
const rawStart = starts[start] ?? raw.length;
|
|
35
|
+
return [rawStart, start === end ? rawStart : ends[end - 1] ?? raw.length];
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function encodeJavaScriptLiteral(text, quote, newline = "\n") {
|
|
39
|
+
let result = "";
|
|
40
|
+
for (let index = 0; index < text.length; index++) {
|
|
41
|
+
const character = text[index];
|
|
42
|
+
const code = text.charCodeAt(index);
|
|
43
|
+
if (character === "\\" || character === quote || quote === "`" && character === "$" && text[index + 1] === "{") result += "\\" + character;
|
|
44
|
+
else if (quote === "`" && character === "\n") result += newline;
|
|
45
|
+
else if (character === " ") result += character;
|
|
46
|
+
else if (code < 32 || code === 8232 || code === 8233 || code >= 55296 && code <= 57343) result += "\\u" + code.toString(16).padStart(4, "0");
|
|
47
|
+
else result += character;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
export { encodeJavaScriptLiteral, javascriptLiteralRange };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
import { MasterCSSLintDiagnostic, MasterCSSLintFix } from "@master/css-tooling/lint";
|
|
3
|
+
import { RuleContext } from "@typescript-eslint/utils/ts-eslint";
|
|
4
|
+
import { ResolvedClassNode } from "./resolve-class-node.js";
|
|
5
|
+
type FixRange = [number, number];
|
|
6
|
+
interface ReportLintDiagnosticsOptions {
|
|
7
|
+
getFix?: (diagnostic: MasterCSSLintDiagnostic, fix: MasterCSSLintFix) => {
|
|
8
|
+
range: FixRange;
|
|
9
|
+
text: string;
|
|
10
|
+
} | undefined;
|
|
11
|
+
}
|
|
12
|
+
declare const messageIdByCode: Record<string, string>;
|
|
13
|
+
declare function stringifyLintDiagnosticData(data?: MasterCSSLintDiagnostic['data']): {
|
|
14
|
+
[k: string]: string;
|
|
15
|
+
};
|
|
16
|
+
declare function stringifyLintDiagnosticMessageData(diagnostic: Pick<MasterCSSLintDiagnostic, 'data' | 'message'>): {
|
|
17
|
+
[k: string]: string;
|
|
18
|
+
};
|
|
19
|
+
declare function reportLintDiagnostics(context: RuleContext<any, any[]>, node: TSESTree.Node, resolved: ResolvedClassNode, diagnostics: readonly MasterCSSLintDiagnostic[], options?: ReportLintDiagnosticsOptions): void;
|
|
20
|
+
export { reportLintDiagnostics as default, messageIdByCode, stringifyLintDiagnosticData, stringifyLintDiagnosticMessageData };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/utils/report-lint-diagnostics.ts
|
|
2
|
+
const messageIdByCode = {
|
|
3
|
+
"invalid-class-order": "invalidClassOrder",
|
|
4
|
+
"invalid-class": "invalidClass",
|
|
5
|
+
"unknown-class": "disallowUnknownClass",
|
|
6
|
+
"conflicting-class": "collisionClass",
|
|
7
|
+
"partially-conflicting-class": "partialCollisionClass",
|
|
8
|
+
"prefer-canonical-class": "preferClass",
|
|
9
|
+
"prefer-native-declaration": "preferClass",
|
|
10
|
+
"prefer-variant-block": "preferClass",
|
|
11
|
+
"unapproved-raw-value": "unapprovedRawValue",
|
|
12
|
+
"remote-styling-limit": "remoteStylingLimit"
|
|
13
|
+
};
|
|
14
|
+
function stringifyLintDiagnosticData(data = {}) {
|
|
15
|
+
return Object.fromEntries(Object.entries(data).filter(([, value]) => value !== void 0).map(([key, value]) => [key, Array.isArray(value) ? value.join(", ") : String(value)]));
|
|
16
|
+
}
|
|
17
|
+
function stringifyLintDiagnosticMessageData(diagnostic) {
|
|
18
|
+
return stringifyLintDiagnosticData({
|
|
19
|
+
...diagnostic.data,
|
|
20
|
+
message: diagnostic.message
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function defaultFix(resolved, fix) {
|
|
24
|
+
if (fix.scope === "directive") return;
|
|
25
|
+
const [start, end] = resolved.sourceRange?.(fix.range.start, fix.range.end) ?? [fix.range.start, fix.range.end];
|
|
26
|
+
return {
|
|
27
|
+
range: [resolved.start + start, resolved.start + end],
|
|
28
|
+
text: resolved.encodeReplacement?.(fix.text) ?? fix.text
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function reportLintDiagnostics(context, node, resolved, diagnostics, options = {}) {
|
|
32
|
+
const { sourceCode } = context;
|
|
33
|
+
for (const diagnostic of diagnostics) {
|
|
34
|
+
const range = resolved.sourceRange?.(diagnostic.range.start, diagnostic.range.end) ?? [diagnostic.range.start, diagnostic.range.end];
|
|
35
|
+
const start = resolved.start + range[0];
|
|
36
|
+
const end = resolved.start + range[1];
|
|
37
|
+
const messageId = messageIdByCode[diagnostic.code];
|
|
38
|
+
const fix = diagnostic.fix && resolved.canFix !== false ? options.getFix?.(diagnostic, diagnostic.fix) || defaultFix(resolved, diagnostic.fix) : void 0;
|
|
39
|
+
const descriptor = {
|
|
40
|
+
node,
|
|
41
|
+
loc: {
|
|
42
|
+
start: sourceCode.getLocFromIndex(start),
|
|
43
|
+
end: sourceCode.getLocFromIndex(end)
|
|
44
|
+
},
|
|
45
|
+
...messageId ? {
|
|
46
|
+
messageId,
|
|
47
|
+
data: stringifyLintDiagnosticMessageData(diagnostic)
|
|
48
|
+
} : { message: diagnostic.message }
|
|
49
|
+
};
|
|
50
|
+
if (fix) descriptor.fix = (fixer) => fixer.replaceTextRange(fix.range, fix.text);
|
|
51
|
+
context.report(descriptor);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export { reportLintDiagnostics as default, messageIdByCode, stringifyLintDiagnosticData, stringifyLintDiagnosticMessageData };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { MasterCSSToolingSession } from "@master/css-tooling";
|
|
2
|
+
import { RuleContext } from "@typescript-eslint/utils/ts-eslint";
|
|
3
|
+
type ResolvedClassListUnescape = string | false;
|
|
4
|
+
interface ResolvedClassListNode {
|
|
5
|
+
type: 'class' | 'space';
|
|
6
|
+
value: string;
|
|
7
|
+
raw: string;
|
|
8
|
+
range: [number, number];
|
|
9
|
+
loc: any;
|
|
10
|
+
}
|
|
11
|
+
interface ResolvedClassNode {
|
|
12
|
+
nodes: ResolvedClassListNode[];
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
raw: string;
|
|
16
|
+
value: string;
|
|
17
|
+
unescape: ResolvedClassListUnescape;
|
|
18
|
+
classNodes: ResolvedClassListNode[];
|
|
19
|
+
classValues: string[];
|
|
20
|
+
analysisText?: string;
|
|
21
|
+
sourceRange?: (start: number, end: number) => [number, number];
|
|
22
|
+
encodeReplacement?: (text: string) => string;
|
|
23
|
+
canFix?: boolean;
|
|
24
|
+
}
|
|
25
|
+
declare function resolveClassNode(node: any, context: RuleContext<any, any[]>, lintSession: Pick<MasterCSSToolingSession, 'tokenizeClassList' | 'decodeHTMLAttribute'>): ResolvedClassNode | undefined;
|
|
26
|
+
export { ResolvedClassListNode, ResolvedClassListUnescape, ResolvedClassNode, resolveClassNode as default };
|