Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This ESLint plugin provides linting rules relate to better ways to help you avoi
- Provides linting rules for Scoped CSS.
- Supports CSS syntax including level 4 selectors.
- Supports `<style lang="scss">`.
- Supports `<style lang="stylus">`.
- Parses `<style>`, `<template>` and `<script>` blocks.

You can check on the [Online DEMO](https://future-architect.github.io/eslint-plugin-vue-scoped-css/playground/).
Expand Down
14 changes: 13 additions & 1 deletion docs/.vuepress/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { rules } = require("../../dist/utils/rules")
const categories = require("./categories")
// eslint-disable-next-line @mysticatea/node/no-extraneous-require
const webpack = require("webpack")

const uncategorizedRules = rules.filter(
rule => !rule.meta.docs.category && !rule.meta.deprecated
Expand Down Expand Up @@ -43,8 +45,18 @@ module.exports = {
configureWebpack(_config, _isServer) {
return {
resolve: {
alias: {},
alias: {
// eslint-disable-next-line @mysticatea/node/no-extraneous-require
stylus: require.resolve("stylus/lib/stylus"),
glob: require.resolve("./shim/glob"),
"safer-buffer": require.resolve("./shim/safer-buffer"),
},
},
plugins: [
new webpack.DefinePlugin({
"process.version": JSON.stringify("v12.13.0"),
}),
],
}
},

Expand Down
1 change: 1 addition & 0 deletions docs/.vuepress/shim/glob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
1 change: 1 addition & 0 deletions docs/.vuepress/shim/safer-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This ESLint plugin provides linting rules relate to better ways to help you avoi
- Provides linting rules for Scoped CSS.
- Supports CSS syntax including level 4 selectors.
- Supports `<style lang="scss">`.
- Supports `<style lang="stylus">`.
- Parses `<style>`, `<template>` and `<script>` blocks.

You can check on the [Online DEMO](./playground/).
Expand Down
18 changes: 10 additions & 8 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { rules } from "./utils/rules"
import { rules as ruleList } from "./utils/rules"
import { Rule } from "./types"

const allRules = rules.reduce((obj, r) => {
const configs = {
base: require("./configs/base"),
recommended: require("./configs/recommended"),
all: require("./configs/all"),
}

const rules = ruleList.reduce((obj, r) => {
obj[r.meta.docs.ruleName] = r
return obj
}, {} as { [key: string]: Rule })

export = {
configs: {
base: require("./configs/base"),
recommended: require("./configs/recommended"),
all: require("./configs/all"),
},
rules: allRules,
configs,
rules,
}
7 changes: 5 additions & 2 deletions lib/rules/no-unused-keyframes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module.exports = {
function report(node: VCSSAtRule) {
const paramsStartIndex =
node.range[0] + // start index of at-rule
1 + // `@`
node.identifier.length + // `@`
node.name.length + // `nest`
(node.node.raws.afterName || "").length // comments and spaces
const paramsEndIndex = paramsStartIndex + node.rawParamsText.length
Expand Down Expand Up @@ -71,7 +71,10 @@ module.exports = {
style.traverseNodes({
enterNode(node) {
if (node.type === "VCSSAtRule") {
if (/-?keyframes$/u.test(node.name)) {
if (
/-?keyframes$/u.test(node.name) &&
node.identifier === "@"
) {
// register keyframes
keyframes.push({
params: Template.ofParams(node),
Expand Down
11 changes: 8 additions & 3 deletions lib/styles/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export class VCSSStyleRule extends HasParentNode<
props: {
parent: VCSSContainerNode
selectorText?: string
rawSelectorText?: string
rawSelectorText: string | null
selectors?: VCSSSelectorNode[]
nodes?: VCSSNode[]
},
Expand Down Expand Up @@ -268,6 +268,7 @@ export class VCSSDeclarationProperty extends HasParentNode<
export class VCSSAtRule extends HasParentNode<"VCSSAtRule", VCSSContainerNode> {
public nodes: VCSSNode[]
public readonly name: string
public readonly identifier: string
public readonly paramsText: string
public readonly rawParamsText: string
public rawSelectorText?: string
Expand All @@ -289,8 +290,9 @@ export class VCSSAtRule extends HasParentNode<"VCSSAtRule", VCSSContainerNode> {
end: number,
props: {
parent: VCSSContainerNode
identifier: string
paramsText?: string
rawParamsText?: string
rawParamsText: string | null
selectors?: VCSSSelectorNode[]
nodes?: VCSSNode[]
},
Expand All @@ -299,6 +301,7 @@ export class VCSSAtRule extends HasParentNode<"VCSSAtRule", VCSSContainerNode> {
this.node = node

this.name = getProp(props, node, "name")
this.identifier = props.identifier
this.paramsText = props.paramsText ?? node.params
if (props.rawParamsText != null) {
this.rawParamsText = props.rawParamsText
Expand Down Expand Up @@ -1008,7 +1011,9 @@ export type VCSSSelectorNode =
| VCSSSelectorCombinator
| VCSSUnknownSelector
| VCSSSelectorContainerNode
export type VCSSSelectorContainerNode = VCSSSelector | VCSSSelectorPseudo
export type VCSSSelectorContainerNode = (VCSSSelector | VCSSSelectorPseudo) & {
nodes: VCSSSelectorValueNode[]
}
export type VCSSSelectorValueNode =
| VCSSTypeSelector
| VCSSIDSelector
Expand Down
3 changes: 3 additions & 0 deletions lib/styles/context/style/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ function getInvalidEOFError(
let errors = body?.errors
let inDocumentFragment = false
if (errors == null) {
/* istanbul ignore if */
if (!context.parserServices.getDocumentFragment) {
return null
}
const df = context.parserServices.getDocumentFragment()
inDocumentFragment = true
errors = df?.errors
/* istanbul ignore if */
if (errors == null) {
return null
}
Expand Down Expand Up @@ -62,6 +64,7 @@ function getStyleElements(context: RuleContext): AST.VElement[] {
const sourceCode = context.getSourceCode()
const { ast } = sourceCode
const templateBody = ast.templateBody as AST.ESLintProgram | undefined
/* istanbul ignore if */
if (templateBody) {
document = templateBody.parent as AST.VDocumentFragment
}
Expand Down
66 changes: 41 additions & 25 deletions lib/styles/parser/css-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,12 @@ export class CSSParser {
const duplicate = new Set<string>()
for (const error of errors) {
const errorLoc =
getESLintLineAndColumnFromPostCSSPosition(
offsetLocation,
error,
) || offsetLocation
error.line != null && error.column != null
? getESLintLineAndColumnFromPostCSSPosition(
offsetLocation,
error,
)
: offsetLocation
const message = error.reason || error.message

const key = `[${errorLoc.line}:${errorLoc.column}]: ${message}`
Expand Down Expand Up @@ -241,7 +243,7 @@ export class CSSParser {
loc: SourceLocation,
start: number,
end: number,
) {
): VCSSNode | null {
return new VCSSStyleSheet(node, loc, start, end, { lang: this.lang })
}

Expand All @@ -260,24 +262,27 @@ export class CSSParser {
start: number,
end: number,
parent: VCSSContainerNode,
) {
const astNode = new VCSSStyleRule(node, loc, start, end, { parent })
): VCSSNode | null {
const astNode = new VCSSStyleRule(node, loc, start, end, {
parent,
rawSelectorText: this.getRaw(node, "selector")?.raw ?? null,
})
astNode.selectors = this.selectorParser.parse(
astNode.rawSelectorText,
astNode.loc.start,
astNode,
)

if (node.raws.between?.trim()) {
if (this.getRaw(node, "between")?.trim()) {
this.parseRuleRawsBetween(node, astNode)
}

return astNode
}

protected parseRuleRawsBetween(node: PostCSSRule, astNode: VCSSNode) {
const { between } = node.raws
const rawSelector = node.raws.selector?.raw ?? node.selector
const between = this.getRaw(node, "between")
const rawSelector = this.getRaw(node, "selector")?.raw ?? node.selector
const betweenStart = astNode.range[0] + rawSelector.length
const postcssRoot = this.parseInternal(between || "") as PostCSSRoot

Expand All @@ -302,15 +307,19 @@ export class CSSParser {
start: number,
end: number,
parent: VCSSContainerNode,
) {
const astNode = new VCSSAtRule(node, loc, start, end, { parent })
): VCSSNode | null {
const astNode = new VCSSAtRule(node, loc, start, end, {
parent,
rawParamsText: this.getRaw(node, "params")?.raw ?? null,
identifier: this.getRaw(node as any, "identifier") ?? "@",
})
if (node.name === "nest") {
// The parameters following `@nest` are parsed as selectors.
const paramsStartIndex =
astNode.range[0] + // start index of at-rule
1 + // `@`
astNode.identifier.length + // `@`
astNode.name.length + // `nest`
(node.raws.afterName || "").length // comments and spaces
(this.getRaw(node, "afterName") || "").length // comments and spaces

astNode.selectors = this.selectorParser.parse(
astNode.rawParamsText,
Expand All @@ -319,22 +328,22 @@ export class CSSParser {
)
}

if (node.raws.afterName?.trim()) {
if (this.getRaw(node, "afterName")?.trim()) {
this.parseAtruleRawsAfterName(node, astNode)
}
if (node.raws.between?.trim()) {
if (this.getRaw(node, "between")?.trim()) {
this.parseAtruleRawsBetween(node, astNode)
}

return astNode
}

private parseAtruleRawsAfterName(node: PostCSSAtRule, astNode: VCSSAtRule) {
const { afterName } = node.raws
const afterName = this.getRaw(node, "afterName")

const afterNameStart =
astNode.range[0] + // start index of at-rule
1 + // `@`
astNode.identifier.length + // `@`
astNode.name.length // `nest`
const postcssRoot = this.parseInternal(afterName || "") as PostCSSRoot

Expand All @@ -345,14 +354,14 @@ export class CSSParser {
}

private parseAtruleRawsBetween(node: PostCSSAtRule, astNode: VCSSAtRule) {
const { between } = node.raws
const between = this.getRaw(node, "between")

const rawParams = node.raws.params?.raw ?? node.params
const rawParams = this.getRaw(node, "params")?.raw ?? node.params
const betweenStart =
astNode.range[0] + // start index of at-rule
1 + // `@`
astNode.identifier.length + // `@`
astNode.name.length + // `nest`
(node.raws.afterName || "").length + // comments and spaces
(this.getRaw(node, "afterName") || "").length + // comments and spaces
rawParams.length

const postcssRoot = this.parseInternal(between || "") as PostCSSRoot
Expand All @@ -377,7 +386,7 @@ export class CSSParser {
start: number,
end: number,
parent: VCSSContainerNode,
) {
): VCSSNode | null {
// adjust star hack
// `*color: red`
// ^
Expand Down Expand Up @@ -418,7 +427,7 @@ export class CSSParser {
start: number,
end: number,
parent: VCSSContainerNode,
): null {
): VCSSNode | null {
this.commentContainer.push(
new VCSSComment(node, node.text, loc, start, end, { parent }),
)
Expand All @@ -440,13 +449,20 @@ export class CSSParser {
start: number,
end: number,
parent: VCSSContainerNode,
) {
): VCSSNode | null {
return new VCSSUnknown(node, loc, start, end, {
parent,
unknownType: node.type,
})
}

protected getRaw<N extends PostCSSNode, K extends keyof N["raws"] & string>(
node: N,
keyName: K,
): N["raws"][K] {
return (node.raws as any)[keyName]
}

/* eslint-enable class-methods-use-this */
}

Expand Down
4 changes: 3 additions & 1 deletion lib/styles/parser/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { CSSParser } from "./css-parser"
import { SCSSParser } from "./scss-parser"
import { StylusParser } from "./stylus-parser"
import { SourceCode, LineAndColumnData } from "../../types"
import { VCSSStyleSheet } from "../ast"
import { isSupportedStyleLang } from "../utils"

const PARSERS = {
scss: SCSSParser,
css: CSSParser,
stylus: StylusParser,
}

/**
Expand All @@ -21,7 +23,7 @@ export function parse(
sourceCode: SourceCode,
offsetLocation: LineAndColumnData,
css: string,
lang: "css" | "scss" | string,
lang: string,
): VCSSStyleSheet {
const Parser = isSupportedStyleLang(lang) ? PARSERS[lang] : CSSParser
const parser = new Parser(sourceCode, lang)
Expand Down
Loading