Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ota-meshi/eslint-plugin-yml
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.12.0
Choose a base ref
...
head repository: ota-meshi/eslint-plugin-yml
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.13.0
Choose a head ref
  • 11 commits
  • 21 files changed
  • 4 contributors

Commits on Nov 19, 2021

  1. Update dependency eslint-plugin-yml to ^0.12.0 (#131)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    renovate[bot] and renovate-bot authored Nov 19, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    pellared Robert Pająk
    Copy the full SHA
    13d206d View commit details

Commits on Dec 11, 2021

  1. Verified

    This commit was signed with the committer’s verified signature.
    pellared Robert Pająk
    Copy the full SHA
    da666a8 View commit details

Commits on Dec 12, 2021

  1. update demo site

    ota-meshi committed Dec 12, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    pellared Robert Pająk
    Copy the full SHA
    2352b12 View commit details

Commits on Dec 24, 2021

  1. Update package.json

    ota-meshi authored Dec 24, 2021
    Copy the full SHA
    591e0a8 View commit details
  2. Create FUNDING.yml

    ota-meshi authored Dec 24, 2021
    Copy the full SHA
    841ebe3 View commit details
  3. fix

    ota-meshi committed Dec 24, 2021
    Copy the full SHA
    3b21527 View commit details

Commits on Jan 7, 2022

  1. Copy the full SHA
    7714c5a View commit details
  2. Update readme

    ota-meshi authored Jan 7, 2022
    Copy the full SHA
    3afef16 View commit details

Commits on Jan 17, 2022

  1. Update renovate.json

    ota-meshi authored Jan 17, 2022
    Copy the full SHA
    66382f4 View commit details

Commits on Jan 27, 2022

  1. Copy the full SHA
    be3efa7 View commit details
  2. 0.13.0

    ota-meshi committed Jan 27, 2022
    Copy the full SHA
    a086dd6 View commit details
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: ota-meshi
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -146,6 +146,18 @@ Example **.vscode/settings.json**:
}
```

### JetBrains WebStorm IDEs

In any of the JetBrains IDEs you can [configure the linting scope](https://www.jetbrains.com/help/webstorm/eslint.html#ws_eslint_configure_scope).
Following the steps in their help document, you can add YAML files to the scope like so:

1. Open the **Settings/Preferences** dialog, go to **Languages and Frameworks** | **JavaScript** | **Code Quality Tools** | **ESLint**, and select **Automatic ESLint configuration** or **Manual ESLint configuration**.
2. In the **Run for files** field, update the pattern that defines the set of files to be linted to include YAML files as well:
```
{**/*,*}.{js,ts,jsx,tsx,html,vue,yaml,yml}
^^^^ ^^^
```

<!--USAGE_GUIDE_END-->
<!--USAGE_SECTION_END-->

1 change: 1 addition & 0 deletions docs/.vuepress/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
rules: {
"eslint-comments/no-unused-disable": "off",
"require-jsdoc": "off",
},
}
25 changes: 8 additions & 17 deletions docs/.vuepress/components/components/EslintPluginEditor.vue
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@

<script>
import EslintEditor from "vue-eslint-editor"
import { Linter } from "eslint/lib/linter"
import plugin from "../../../.."
export default {
@@ -58,7 +59,6 @@ export default {
data() {
return {
eslint4b: null,
yamlESLintParser: null,
vueESLintParser: null,
format: {
@@ -100,20 +100,14 @@ export default {
parser: this.parser,
parserOptions: {
sourceType: "script",
ecmaVersion: 2020,
ecmaVersion: 2021,
},
}
},
linter() {
if (
!this.eslint4b ||
!this.yamlESLintParser ||
!this.vueESLintParser
) {
if (!this.yamlESLintParser || !this.vueESLintParser) {
return null
}
const Linter = this.eslint4b
const linter = new Linter()
linter.defineParser("yaml-eslint-parser", this.yamlESLintParser)
linter.defineParser("vue-eslint-parser", this.vueESLintParser)
@@ -128,14 +122,11 @@ export default {
},
async mounted() {
// Load linter asynchronously.
const [{ default: eslint4b }, yamlESLintParser, vueESLintParser] =
await Promise.all([
import("eslint4b"),
import("yaml-eslint-parser"),
import("espree").then(() => import("vue-eslint-parser")),
])
this.eslint4b = eslint4b
// Load parser asynchronously.
const [yamlESLintParser, vueESLintParser] = await Promise.all([
import("yaml-eslint-parser"),
import("espree").then(() => import("vue-eslint-parser")),
])
this.yamlESLintParser = yamlESLintParser
this.vueESLintParser = vueESLintParser
152 changes: 72 additions & 80 deletions docs/.vuepress/components/components/RulesSettings.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,41 @@
<template>
<div class="rules-settings">
<div class="tools">
<div
class="tools-title"
@click="state.toolsClose = !state.toolsClose"
>
Tools
<button
class="tools-button"
:class="{ 'tools-button--close': state.toolsClose }"
>
<svg
xmlns="http://www.w3.org/2000/svg"
height="10"
viewBox="0 0 10 10"
width="10"
>
<path d="M2.5 10l5-5-5-5v10z" fill="#ddd" />
</svg>
</button>
</div>
<template v-if="!state.toolsClose">
<div class="tool">
<button class="tool-button" @click="onAllOffClick">
Turn OFF All Rules
</button>
</div>
<div class="tool">
<label>
<span class="tool-label">Filter:</span>
<input
v-model="filterValue"
type="search"
placeholder="Rule Filter"
class="tool-form"
/>
</label>
</div>
</template>
<label class="tool">
<span class="tool-label">Filter:</span>
<input
v-model="filterValue"
type="search"
placeholder="Rule Filter"
class="tool-form"
/>
</label>
<label class="tool">
<input
:checked="
categories.every((category) =>
category.rules.every((rule) =>
isErrorState(rule.ruleId),
),
)
"
type="checkbox"
:indeterminate.prop="
categories.some((category) =>
category.rules.some((rule) =>
isErrorState(rule.ruleId),
),
) &&
categories.some((category) =>
category.rules.some(
(rule) => !isErrorState(rule.ruleId),
),
)
"
@input="onAllClick($event)"
/>
<span class="tool-label">All Rules</span>
</label>
</div>
<ul class="categories">
<template v-for="category in categories">
@@ -84,7 +82,7 @@
(rule) => !isErrorState(rule.ruleId),
)
"
@input="onAllClick(category, $event)"
@input="onCategoryClick(category, $event)"
/>
{{ category.title }}
</label>
@@ -95,7 +93,7 @@
class="rules"
>
<li
v-for="rule in filterRules(category.rules)"
v-for="rule in category.rules"
:key="rule.ruleId"
class="rule"
:class="rule.classes"
@@ -117,6 +115,7 @@
viewBox="0 0 100 100"
width="15"
height="15"
class="icon outbound"
>
<path
fill="currentColor"
@@ -158,35 +157,32 @@ export default {
]
}),
),
state: {
toolsClose: true,
},
filterValue: "",
}
},
computed: {
categories() {
return categories.map((c) => {
let rules = this.filterRules(c.rules)
if (this.filterValue) {
rules = rules.filter((r) =>
r.ruleId.includes(this.filterValue),
)
}
return {
...c,
rules,
rules: this.filterRules(c.rules),
}
})
},
},
methods: {
filterRules(rules) {
return rules.filter((rule) => rule.ruleId !== "jsonc/auto")
let filteredRules = rules
if (this.filterValue) {
filteredRules = filteredRules.filter((r) =>
r.ruleId.includes(this.filterValue),
)
}
return filteredRules
},
onAllClick(category, e) {
onCategoryClick(category, e) {
const rules = Object.assign({}, this.rules)
for (const rule of this.filterRules(category.rules)) {
for (const rule of category.rules) {
if (e.target.checked) {
rules[rule.ruleId] = "error"
} else {
@@ -195,8 +191,18 @@ export default {
}
this.$emit("update:rules", rules)
},
onAllOffClick() {
this.$emit("update:rules", {})
onAllClick(e) {
const rules = Object.assign({}, this.rules)
for (const category of this.categories) {
for (const rule of category.rules) {
if (e.target.checked) {
rules[rule.ruleId] = "error"
} else {
delete rules[rule.ruleId]
}
}
}
this.$emit("update:rules", rules)
},
onClick(ruleId, e) {
const rules = Object.assign({}, this.rules)
@@ -221,25 +227,12 @@ export default {
.tool {
display: flex;
}
.tool,
.tools-title {
padding: 4px;
}
.tool > label {
display: flex;
width: 100%;
}
.tool > button {
margin: auto;
}
.tool-label {
width: 3.5rem;
flex-shrink: 0;
padding: 0 4px;
}
.tool-form {
@@ -259,24 +252,19 @@ export default {
transform: rotate(90deg);
}
.filter .tool-label {
color: #ddd;
}
.categories {
font-size: 14px;
list-style-type: none;
}
.category {
position: relative;
color: #fff;
}
.category-button {
position: absolute;
left: -12px;
top: 4px;
top: 2px;
background-color: transparent;
color: #ddd;
border: none;
@@ -294,13 +282,11 @@ export default {
font-weight: bold;
}
.eslint-plugin-yml__category .category-title,
.eslint-plugin-yml__rule a {
.eslint-plugin-yml-category .category-title {
color: #f8c555;
}
.eslint-category .category-title,
.eslint-rule a {
.eslint-core-category .category-title {
color: #8080f2;
}
@@ -318,11 +304,17 @@ export default {
.rule a {
margin-left: auto;
display: inline-flex;
align-items: center;
}
a {
text-decoration: none;
}
.eslint-core-rule a > svg {
color: #8080f2;
}
.eslint-plugin-yml-rule a > svg {
color: #f8c555;
}
</style>
44 changes: 26 additions & 18 deletions docs/.vuepress/components/playground-block.vue
Original file line number Diff line number Diff line change
@@ -28,10 +28,11 @@
i
"
class="message"
:class="getRule(msg.ruleId).classes"
>
[{{ msg.line }}:{{ msg.column }}]:
{{ msg.message }} (<a
:href="getURL(msg.ruleId)"
:href="getRule(msg.ruleId).url"
target="_blank"
>
{{ msg.ruleId }} </a
@@ -45,25 +46,24 @@
</template>

<script>
import * as coreRules from "../../../node_modules/eslint4b/dist/core-rules"
import plugin from "../../.."
import PgEditor from "./components/PgEditor.vue"
import RulesSettings from "./components/RulesSettings.vue"
import SnsBar from "./components/SnsBar.vue"
import { deserializeState, serializeState } from "./state"
import { DEFAULT_RULES_CONFIG } from "./rules"
import { DEFAULT_RULES_CONFIG, getRule } from "./rules"
const DEFAULT_CODE = JSON.stringify({ foo: "bar" }, null, 4)
const ruleURLs = {}
for (const k of Object.keys(plugin.rules)) {
const rule = plugin.rules[k]
ruleURLs[rule.meta.docs.ruleId] = rule.meta.docs.url
}
for (const k of Object.keys(coreRules)) {
const rule = coreRules[k]
ruleURLs[k] = rule.meta.docs.url
const DEFAULT_CODE = `
---
blockStyle:
packageName: eslint-plugin-yml
description: This ESLint plugin provides linting rules for YAML.
github: https://github.com/ota-meshi/eslint-plugin-yml
flowStyle: {
package_name: eslint-plugin-yml,
description: This ESLint plugin provides linting rules for YAML.,
github: https://github.com/ota-meshi/eslint-plugin-yml,
}
`
export default {
name: "PlaygroundBlock",
@@ -115,8 +115,8 @@ export default {
onChange({ messages }) {
this.messages = messages
},
getURL(ruleId) {
return ruleURLs[ruleId] || ""
getRule(ruleId) {
return getRule(ruleId)
},
onUrlHashChange() {
const serializedString =
@@ -156,13 +156,13 @@ function equalsRules(a, b) {
height: calc(100% - 100px);
border: 1px solid #cfd4db;
background-color: #282c34;
color: #f8c555;
color: #fff;
}
.main-content > .rules-settings {
height: 100%;
overflow: auto;
width: 25%;
width: 30%;
box-sizing: border-box;
}
@@ -190,4 +190,12 @@ function equalsRules(a, b) {
padding: 8px;
font-size: 12px;
}
.eslint-core-rule a {
color: #8080f2;
}
.eslint-plugin-yml-rule a {
color: #f8c555;
}
</style>
90 changes: 54 additions & 36 deletions docs/.vuepress/components/rules/index.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,55 @@
/* eslint node/no-unsupported-features/es-syntax: off -- not node */
import * as coreRules from "../../../../node_modules/eslint4b/dist/core-rules"
// eslint-disable-next-line node/no-missing-import -- no build
/* eslint node/no-missing-import: 0 -- DEMO */
// eslint-disable-next-line eslint-comments/disable-enable-pair -- DEMO
/* eslint-disable node/no-unsupported-features/es-syntax -- DEMO */
import { Linter } from "eslint/lib/linter"
import plugin from "../../../../"

const coreRules = Object.fromEntries(new Linter().getRules())

const CATEGORY_TITLES = {
recommended: "eslint-plugin-yml",
"eslint-core-rules@Possible Errors": "ESLint core rules(Possible Errors)",
"eslint-core-rules@Best Practices": "ESLint core rules(Best Practices)",
"eslint-core-rules@Strict Mode": "ESLint core rules(Strict Mode)",
"eslint-core-rules@Variables": "ESLint core rules(Variables)",
"eslint-core-rules@Node.js and CommonJS":
"ESLint core rules(Node.js and CommonJS)",
"eslint-core-rules@Stylistic Issues": "ESLint core rules(Stylistic Issues)",
"eslint-core-rules@ECMAScript 6": "ESLint core rules(ECMAScript 6)",
yml: "eslint-plugin-yml",
"eslint-core-rules@problem": "ESLint core rules(Possible Errors)",
"eslint-core-rules@suggestion": "ESLint core rules(Suggestions)",
"eslint-core-rules@layout": "ESLint core rules(Layout & Formatting)",
}
const CATEGORY_INDEX = {
recommended: 2,
"eslint-core-rules@Possible Errors": 6,
"eslint-core-rules@Best Practices": 7,
"eslint-core-rules@Strict Mode": 8,
"eslint-core-rules@Variables": 9,
"eslint-core-rules@Node.js and CommonJS": 10,
"eslint-core-rules@Stylistic Issues": 11,
"eslint-core-rules@ECMAScript 6": 12,
yml: 2,
"eslint-core-rules@problem": 20,
"eslint-core-rules@suggestion": 21,
"eslint-core-rules@layout": 22,
}
const CATEGORY_CLASSES = {
recommended: "eslint-plugin-yml__category",
"eslint-core-rules@Possible Errors": "eslint-category",
"eslint-core-rules@Best Practices": "eslint-category",
"eslint-core-rules@Strict Mode": "eslint-category",
"eslint-core-rules@Variables": "eslint-category",
"eslint-core-rules@Node.js and CommonJS": "eslint-category",
"eslint-core-rules@Stylistic Issues": "eslint-category",
"eslint-core-rules@ECMAScript 6": "eslint-category",
yml: "eslint-plugin-yml-category",
"eslint-core-rules@problem": "eslint-core-category",
"eslint-core-rules@suggestion": "eslint-core-category",
"eslint-core-rules@layout": "eslint-core-category",
}

const allRules = []

for (const k of Object.keys(plugin.rules)) {
const rule = plugin.rules[k]
rule.meta.docs.category = "recommended"
rule.meta.docs.category = "yml"
allRules.push({
classes: "eslint-plugin-yml__rule",
category: "recommended",
classes: "eslint-plugin-yml-rule",
category: "yml",
ruleId: rule.meta.docs.ruleId,
url: rule.meta.docs.url,
initChecked: CATEGORY_INDEX[rule.meta.docs.category] <= 3,
})
}

for (const k of Object.keys(coreRules)) {
const rule = coreRules[k]
if (rule.meta.deprecated) {
continue
}
allRules.push({
classes: "eslint-rule",
category: `eslint-core-rules@${rule.meta.docs.category}`,
fallbackTitle: `ESLint core rules(${rule.meta.docs.category})`,
classes: "eslint-core-rule",
category: `eslint-core-rules@${rule.meta.type}`,
ruleId: k,
url: rule.meta.docs.url,
initChecked: false, // rule.meta.docs.recommended,
initChecked: rule.meta.docs.recommended,
})
}

@@ -94,7 +87,14 @@ categories.sort((a, b) =>
)

export const DEFAULT_RULES_CONFIG = allRules.reduce((c, r) => {
if (r.ruleId === "vue/no-parsing-error") {
if (
[
"no-trailing-spaces",
"no-multiple-empty-lines",
"comma-spacing",
"no-multi-spaces",
].includes(r.ruleId)
) {
c[r.ruleId] = "error"
} else {
c[r.ruleId] = r.initChecked ? "error" : "off"
@@ -103,3 +103,21 @@ export const DEFAULT_RULES_CONFIG = allRules.reduce((c, r) => {
}, {})

export const rules = allRules

export function getRule(ruleId) {
if (!ruleId) {
return null
}
for (const category of categories) {
for (const rule of category.rules) {
if (rule.ruleId === ruleId) {
return rule
}
}
}
return {
ruleId,
url: "",
classes: "",
}
}
5 changes: 3 additions & 2 deletions docs/.vuepress/components/state/deserialize.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint node/no-unsupported-features/es-syntax: off -- not node */
import pako from "../../../../node_modules/pako"
// eslint-disable-next-line node/no-extraneous-import -- ignore
import pako from "pako"

/**
* Deserialize a given serialized string then update this object.
@@ -21,7 +22,7 @@ export function deserializeState(serializedString) {
const uint8Arr = pako.inflate(
Uint8Array.from(compressedString, (c) => c.charCodeAt(0)),
)
// eslint-disable-next-line node/no-unsupported-features/node-builtins -- ignore

const jsonText = new TextDecoder().decode(uint8Arr)
const json = JSON.parse(jsonText)

3 changes: 2 additions & 1 deletion docs/.vuepress/components/state/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint node/no-unsupported-features/es-syntax: off -- not node */
// eslint-disable-next-line node/no-unsupported-features/es-syntax -- DEMO
export * from "./deserialize"
// eslint-disable-next-line node/no-unsupported-features/es-syntax -- DEMO
export * from "./serialize"
5 changes: 3 additions & 2 deletions docs/.vuepress/components/state/serialize.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint node/no-unsupported-features/es-syntax: off -- not node */
import pako from "../../../../node_modules/pako"
// eslint-disable-next-line node/no-extraneous-import -- ignore
import pako from "pako"

/**
* Get only enabled rules to make the serialized data smaller.
@@ -26,7 +27,7 @@ export function serializeState(state) {
rules: state.rules ? getEnabledRules(state.rules) : undefined,
}
const jsonString = JSON.stringify(saveData)
// eslint-disable-next-line node/no-unsupported-features/node-builtins -- ignore

const uint8Arr = new TextEncoder().encode(jsonString)
const compressedString = String.fromCharCode(...pako.deflate(uint8Arr))
const base64 =
3 changes: 2 additions & 1 deletion docs/.vuepress/components/stylelint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module.exports = {
extends: ["stylelint-config-recommended-vue", "stylelint-config-standard"],
extends: ["stylelint-config-standard", "stylelint-config-recommended-vue"],
rules: {
"no-descending-specificity": null,
indentation: null,
"selector-class-pattern": null,
},
}
40 changes: 27 additions & 13 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
@@ -21,7 +21,15 @@ module.exports = {
resolve: {
alias: {
module: require.resolve("./shim/module"),
eslint: path.resolve(__dirname, "./shim/eslint"),
eslint$: path.resolve(__dirname, "./shim/eslint"),
esquery: path.resolve(
__dirname,
"../../node_modules/esquery/dist/esquery.min.js",
),
"@eslint/eslintrc/universal": path.resolve(
__dirname,
"../../node_modules/@eslint/eslintrc/dist/eslintrc-universal.cjs",
),
},
},
}
@@ -63,18 +71,24 @@ module.exports = {
)
.map(ruleToLink),
},
{
title: "Extension Rules",
collapsable: false,
children: rules
.filter(
(rule) =>
rule.meta.docs.extensionRule &&
!rule.meta.deprecated,
)
.map(ruleToLink),
},

...(rules.some(
(rule) =>
rule.meta.docs.extensionRule && !rule.meta.deprecated,
)
? [
{
title: "Extension Rules",
collapsable: false,
children: rules
.filter(
(rule) =>
rule.meta.docs.extensionRule &&
!rule.meta.deprecated,
)
.map(ruleToLink),
},
]
: []),
// Rules in no category.
...(rules.some((rule) => rule.meta.deprecated)
? [
8 changes: 7 additions & 1 deletion docs/.vuepress/enhanceApp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint node/no-unsupported-features/es-syntax: off -- not node */
// eslint-disable-next-line node/no-unsupported-features/es-syntax -- DEMO
export default () =>
// {
// Vue, // the version of Vue being used in the VuePress app
@@ -11,5 +11,11 @@ export default () =>
if (typeof window.global === "undefined") {
window.global = {}
}
if (typeof window.process === "undefined") {
window.process = {
env: {},
cwd: () => undefined,
}
}
}
}
8 changes: 6 additions & 2 deletions docs/.vuepress/shim/eslint/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const Linter = require("eslint4b")
// eslint-disable-next-line eslint-comments/disable-enable-pair -- DEMO
/* eslint-disable node/no-unsupported-features/es-syntax -- DEMO */
import { Linter } from "eslint/lib/linter"
class CLIEngine {}
module.exports = { Linter, CLIEngine }

export { Linter, CLIEngine }
export default { Linter, CLIEngine }
2 changes: 1 addition & 1 deletion docs/rules/no-empty-document.md
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ This rule reports empty mapping documents.
# eslint yml/no-empty-document: 'error'
---
# ✓ GOOD
"GOOD": "foo",
"GOOD": "foo"
...
---
# ✗ BAD
9 changes: 9 additions & 0 deletions docs/rules/sort-keys.md
Original file line number Diff line number Diff line change
@@ -65,6 +65,15 @@ The option receives multiple objects with the following properties:
- `hasProperties` ... Defines an array of property names. Checks only objects that have the defined properties.
- `order` (Required) ... Defines how to enforce the order. You can use an object or an array.
- Array ... Defines an array of properties to enforce the order.
- String ... Defines the property name.
- Object ... The object has the following properties:
- `keyPattern` ... Defines a pattern to match the property name. Default is to match all.
- `order` ... The object has the following properties:
- `type`:
- `"asc"` ... Enforce properties to be in ascending order. This is default.
- `"desc"` ... Enforce properties to be in descending order.
- `caseSensitive` ... If `true`, enforce properties to be in case-sensitive order. Default is `true`.
- `natural` ... If `true`, enforce properties to be in natural order. Default is `false`.
- Object ... The object has the following properties:
- `type`:
- `"asc"` ... Enforce properties to be in ascending order. This is default.
12 changes: 12 additions & 0 deletions docs/user-guide/README.md
Original file line number Diff line number Diff line change
@@ -100,6 +100,18 @@ Example **.vscode/settings.json**:
}
```

### JetBrains WebStorm IDEs

In any of the JetBrains IDEs you can [configure the linting scope](https://www.jetbrains.com/help/webstorm/eslint.html#ws_eslint_configure_scope).
Following the steps in their help document, you can add YAML files to the scope like so:

1. Open the **Settings/Preferences** dialog, go to **Languages and Frameworks** | **JavaScript** | **Code Quality Tools** | **ESLint**, and select **Automatic ESLint configuration** or **Manual ESLint configuration**.
2. In the **Run for files** field, update the pattern that defines the set of files to be linted to include YAML files as well:
```
{**/*,*}.{js,ts,jsx,tsx,html,vue,yaml,yml}
^^^^ ^^^
```

<!--USAGE_GUIDE_END-->

## :question: FAQ
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-yml",
"version": "0.12.0",
"version": "0.13.0",
"description": "This ESLint plugin provides linting rules for YAML.",
"main": "lib/index.js",
"files": [
@@ -42,6 +42,7 @@
"yml"
],
"author": "Yosuke Ota",
"funding": "https://github.com/sponsors/ota-meshi",
"license": "MIT",
"bugs": {
"url": "https://github.com/ota-meshi/eslint-plugin-yml/issues"
@@ -84,11 +85,11 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-regexp": "^1.0.0",
"eslint-plugin-vue": "^8.0.0",
"eslint-plugin-yml": "^0.11.0",
"eslint-plugin-yml": "^0.12.0",
"eslint4b": "^7.3.1",
"espree": "^9.0.0",
"mocha": "^9.0.0",
"monaco-editor": "^0.30.0",
"monaco-editor": "^0.31.0",
"nyc": "^15.1.0",
"prettier": "^2.2.1",
"raw-loader": "^4.0.1",
6 changes: 5 additions & 1 deletion renovate.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"extends": ["config:base", ":preserveSemverRanges"],
"extends": [
"config:base",
":preserveSemverRanges",
":disableDependencyDashboard"
],
"packageRules": [
{
"updateTypes": ["minor", "patch", "pin", "digest"],
119 changes: 91 additions & 28 deletions src/rules/sort-keys.ts
Original file line number Diff line number Diff line change
@@ -26,14 +26,21 @@ type PatternOption = {
pathPattern: string
hasProperties: string[]
order:
| {
type?: OrderTypeOption
caseSensitive?: boolean
natural?: boolean
}
| string[]
| OrderObject
| (
| string
| {
keyPattern?: string
order?: OrderObject
}
)[]
minKeys?: number
}
type OrderObject = {
type?: OrderTypeOption
caseSensitive?: boolean
natural?: boolean
}
type ParsedOption = {
isTargetMapping: (node: AST.YAMLMapping) => boolean
ignore: (s: string) => boolean
@@ -163,13 +170,54 @@ function parseOptions(
}${type}ending`,
}
}

const parsedOrder: {
test: (s: string) => boolean
isValidNestOrder: Validator
}[] = []
for (const o of order) {
if (typeof o === "string") {
parsedOrder.push({
test: (s) => s === o,
isValidNestOrder: () => true,
})
} else {
const keyPattern = o.keyPattern
? new RegExp(o.keyPattern)
: null
const nestOrder = o.order ?? {}
const type: OrderTypeOption = nestOrder.type ?? "asc"
const insensitive = nestOrder.caseSensitive === false
const natural = Boolean(nestOrder.natural)
parsedOrder.push({
test: (s) => (keyPattern ? keyPattern.test(s) : true),
isValidNestOrder: buildValidatorFromType(
type,
insensitive,
natural,
),
})
}
}
return {
isTargetMapping,
ignore: (s) => !order.includes(s),
ignore: (s) => parsedOrder.every((p) => !p.test(s)),
isValidOrder(a, b) {
const aIndex = order.indexOf(a)
const bIndex = order.indexOf(b)
return aIndex <= bIndex
for (const p of parsedOrder) {
const matchA = p.test(a)
const matchB = p.test(b)
if (!matchA || !matchB) {
if (matchA) {
return true
}
if (matchB) {
return false
}
continue
}
return p.isValidNestOrder(a, b)
}
return false
},
minKeys,
orderText: "specified",
@@ -214,7 +262,22 @@ function parseOptions(
})
}

const allowOrderTypes: OrderTypeOption[] = ["asc", "desc"]
const ALLOW_ORDER_TYPES: OrderTypeOption[] = ["asc", "desc"]
const ORDER_OBJECT_SCHEMA = {
type: "object",
properties: {
type: {
enum: ALLOW_ORDER_TYPES,
},
caseSensitive: {
type: "boolean",
},
natural: {
type: "boolean",
},
},
additionalProperties: false,
} as const

//------------------------------------------------------------------------------
// Rule Definition
@@ -246,24 +309,24 @@ export default createRule("sort-keys", {
oneOf: [
{
type: "array",
items: { type: "string" },
uniqueItems: true,
},
{
type: "object",
properties: {
type: {
enum: allowOrderTypes,
},
caseSensitive: {
type: "boolean",
},
natural: {
type: "boolean",
},
items: {
anyOf: [
{ type: "string" },
{
type: "object",
properties: {
keyPattern: {
type: "string",
},
order: ORDER_OBJECT_SCHEMA,
},
additionalProperties: false,
},
],
},
additionalProperties: false,
uniqueItems: true,
},
ORDER_OBJECT_SCHEMA,
],
},
minKeys: {
@@ -281,7 +344,7 @@ export default createRule("sort-keys", {
type: "array",
items: [
{
enum: allowOrderTypes,
enum: ALLOW_ORDER_TYPES,
},
{
type: "object",
205 changes: 205 additions & 0 deletions tests/src/rules/sort-keys.ts
Original file line number Diff line number Diff line change
@@ -73,6 +73,67 @@ tester.run(
code: JSON.stringify(rule.meta.schema),
options: OPTIONS_FOR_JSON_SCHEMA,
},

// nest
{
code: `
{
"a":1,
"b":2,
"c":3,
"d":4,
"e":5,
"f":6,
"g":7,
"z":26
}
`,
options: [
{
pathPattern: "^$",
order: [
"a",
"b",
{
keyPattern: "[cd]",
order: { type: "asc" },
},
{
keyPattern: "[e-g]",
order: { type: "asc" },
},
"z",
],
},
],
},
{
code: `
{
"a":1,
"b":2,
"c":3,
"d":4,
"e":5,
"f":6,
"g":7,
"z":26
}
`,
options: [
{
pathPattern: "^$",
order: [
"a",
"b",
{
order: { type: "asc" },
},
"z",
],
},
],
},
],
invalid: [
// package.json
@@ -223,6 +284,150 @@ tester.run(
"Expected mapping keys to be in ascending order. 'e' should be before 'f'.",
],
},

// nest
{
code: `
{
"a":1,
"b":2,
"d":4,
"c":3,
"e":5,
"g":7,
"f":6,
"z":26
}
`,
output: `
{
"a":1,
"b":2,
"c":3,
"d":4,
"e":5,
"f":6,
"g":7,
"z":26
}
`,
options: [
{
pathPattern: "^$",
order: [
"a",
"b",
{
keyPattern: "[cd]",
order: { type: "asc" },
},
{
keyPattern: "[e-g]",
order: { type: "asc" },
},
"z",
],
},
],
errors: [
"Expected mapping keys to be in specified order. 'c' should be before 'd'.",
"Expected mapping keys to be in specified order. 'f' should be before 'g'.",
],
},
{
code: `
{
"a":1,
"b":2,
"z":26,
"c":3,
"d":4,
"e":5,
"f":6,
"g":7
}
`,
output: `
{
"a":1,
"b":2,
"c":3,
"z":26,
"d":4,
"e":5,
"f":6,
"g":7
}
`,
options: [
{
pathPattern: "^$",
order: [
"a",
"b",
{
keyPattern: "[cd]",
order: { type: "asc" },
},
{
keyPattern: "[e-g]",
order: { type: "asc" },
},
"z",
],
},
],
errors: [
"Expected mapping keys to be in specified order. 'c' should be before 'z'.",
],
},
{
code: `
{
"a":1,
"b":2,
"c":3,
"d":4,
"z":26,
"e":5,
"f":6,
"g":7
}
`,
output: `
{
"a":1,
"b":2,
"c":3,
"d":4,
"e":5,
"z":26,
"f":6,
"g":7
}
`,
options: [
{
pathPattern: "^$",
order: [
"a",
"b",
{
keyPattern: "[cd]",
order: { type: "asc" },
},
{
keyPattern: "[e-g]",
order: { type: "asc" },
},
"z",
],
},
],
errors: [
"Expected mapping keys to be in specified order. 'e' should be before 'z'.",
],
},
],
},
),