mirror of
https://github.com/DavidAnson/markdownlint-cli2-action.git
synced 2026-06-30 15:09:40 +02:00
Add configPointer input, examples for package.json/pyproject.toml.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
CommonMark
|
||||
config
|
||||
configPointer
|
||||
JSON
|
||||
LLMs
|
||||
markdownlint-cli2-action
|
||||
npm
|
||||
|
||||
@@ -96,6 +96,36 @@ jobs:
|
||||
id: test
|
||||
- run: exit 1
|
||||
if: steps.test.outcome != 'failure'
|
||||
package-json:
|
||||
name: package.json (test/errors.md, 2 errors)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- run: rm .markdownlint.json
|
||||
- uses: ./
|
||||
with:
|
||||
config: 'config/package.json'
|
||||
configPointer: '/markdownlint-cli2'
|
||||
globs: 'test/*.md'
|
||||
continue-on-error: true
|
||||
id: test
|
||||
- run: exit 1
|
||||
if: steps.test.outcome != 'failure'
|
||||
pyproject-toml:
|
||||
name: pyproject.toml (test/errors.md, 2 errors)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- run: rm .markdownlint.json
|
||||
- uses: ./
|
||||
with:
|
||||
config: 'config/pyproject.toml'
|
||||
configPointer: '/tool/markdownlint-cli2'
|
||||
globs: 'test/*.md'
|
||||
continue-on-error: true
|
||||
id: test
|
||||
- run: exit 1
|
||||
if: steps.test.outcome != 'failure'
|
||||
fix:
|
||||
name: fix (test/errors.md, 0 errors)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -20,6 +20,14 @@ Path of a file to use for the base configuration object (defaults to none)
|
||||
Equivalent to using the `--config` [command-line option][command-line] and
|
||||
passing the specified configuration file.
|
||||
|
||||
### configPointer (optional)
|
||||
|
||||
[JSON Pointer][json-pointer] to a configuration object within the `--config`
|
||||
file (defaults to none)
|
||||
|
||||
Equivalent to using the `--configPointer` [command-line option][command-line]
|
||||
and passing the specified JSON Pointer.
|
||||
|
||||
### fix (optional)
|
||||
|
||||
Whether to fix supported issues automatically (any truthy value enables)
|
||||
@@ -100,6 +108,26 @@ To specify a custom configuration file:
|
||||
globs: '**/*.md'
|
||||
```
|
||||
|
||||
To specify an embedded object in `package.json`:
|
||||
|
||||
```yaml
|
||||
- uses: DavidAnson/markdownlint-cli2-action@v22
|
||||
with:
|
||||
config: 'package.json'
|
||||
configPointer: '/markdownlint-cli2'
|
||||
globs: '**/*.md'
|
||||
```
|
||||
|
||||
To specify an embedded object in `pyproject.toml`:
|
||||
|
||||
```yaml
|
||||
- uses: DavidAnson/markdownlint-cli2-action@v22
|
||||
with:
|
||||
config: 'pyproject.toml'
|
||||
configPointer: '/tool/markdownlint-cli2'
|
||||
globs: '**/*.md'
|
||||
```
|
||||
|
||||
To prevent linting issues from failing the workflow run:
|
||||
|
||||
```yaml
|
||||
@@ -119,6 +147,7 @@ and/or gradually introducing linting rules to a new repository).
|
||||
[commonmark]: https://commonmark.org/
|
||||
[example-yml]: .github/workflows/example.yml
|
||||
[glob-syntax]: https://github.com/DavidAnson/markdownlint-cli2#use
|
||||
[json-pointer]: https://datatracker.ietf.org/doc/html/rfc6901
|
||||
[markdown]: https://wikipedia.org/wiki/Markdown
|
||||
[markdownlint]: https://github.com/DavidAnson/markdownlint
|
||||
[markdownlint-cli2]: https://github.com/DavidAnson/markdownlint-cli2
|
||||
|
||||
@@ -9,6 +9,10 @@ inputs:
|
||||
description: Path of a file to use for the base configuration object (defaults to none)
|
||||
default: ''
|
||||
required: false
|
||||
configPointer:
|
||||
description: JSON Pointer to a configuration object within the --config file (defaults to none)
|
||||
default: ''
|
||||
required: false
|
||||
fix:
|
||||
description: Whether to fix supported issues automatically (any truthy value enables)
|
||||
default: ''
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"private": true,
|
||||
|
||||
"markdownlint-cli2": {
|
||||
"config": {
|
||||
"single-trailing-newline": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[project]
|
||||
name = "my-project"
|
||||
|
||||
[tool.markdownlint-cli2.config]
|
||||
single-trailing-newline = false
|
||||
Vendored
+313
-1
@@ -7192,6 +7192,8 @@ const path = __nccwpck_require__(6928);
|
||||
const WIN_SLASH = '\\\\/';
|
||||
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
||||
|
||||
const DEFAULT_MAX_EXTGLOB_RECURSION = 0;
|
||||
|
||||
/**
|
||||
* Posix glob regex
|
||||
*/
|
||||
@@ -7255,6 +7257,7 @@ const WINDOWS_CHARS = {
|
||||
*/
|
||||
|
||||
const POSIX_REGEX_SOURCE = {
|
||||
__proto__: null,
|
||||
alnum: 'a-zA-Z0-9',
|
||||
alpha: 'a-zA-Z',
|
||||
ascii: '\\x00-\\x7F',
|
||||
@@ -7272,6 +7275,7 @@ const POSIX_REGEX_SOURCE = {
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
DEFAULT_MAX_EXTGLOB_RECURSION,
|
||||
MAX_LENGTH: 1024 * 64,
|
||||
POSIX_REGEX_SOURCE,
|
||||
|
||||
@@ -7285,6 +7289,7 @@ module.exports = {
|
||||
|
||||
// Replace globs with equivalent patterns to reduce parsing time.
|
||||
REPLACEMENTS: {
|
||||
__proto__: null,
|
||||
'***': '*',
|
||||
'**/**': '**',
|
||||
'**/**/**': '**'
|
||||
@@ -7419,6 +7424,277 @@ const syntaxError = (type, char) => {
|
||||
return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
|
||||
};
|
||||
|
||||
const splitTopLevel = input => {
|
||||
const parts = [];
|
||||
let bracket = 0;
|
||||
let paren = 0;
|
||||
let quote = 0;
|
||||
let value = '';
|
||||
let escaped = false;
|
||||
|
||||
for (const ch of input) {
|
||||
if (escaped === true) {
|
||||
value += ch;
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '\\') {
|
||||
value += ch;
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '"') {
|
||||
quote = quote === 1 ? 0 : 1;
|
||||
value += ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (quote === 0) {
|
||||
if (ch === '[') {
|
||||
bracket++;
|
||||
} else if (ch === ']' && bracket > 0) {
|
||||
bracket--;
|
||||
} else if (bracket === 0) {
|
||||
if (ch === '(') {
|
||||
paren++;
|
||||
} else if (ch === ')' && paren > 0) {
|
||||
paren--;
|
||||
} else if (ch === '|' && paren === 0) {
|
||||
parts.push(value);
|
||||
value = '';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value += ch;
|
||||
}
|
||||
|
||||
parts.push(value);
|
||||
return parts;
|
||||
};
|
||||
|
||||
const isPlainBranch = branch => {
|
||||
let escaped = false;
|
||||
|
||||
for (const ch of branch) {
|
||||
if (escaped === true) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/[?*+@!()[\]{}]/.test(ch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const normalizeSimpleBranch = branch => {
|
||||
let value = branch.trim();
|
||||
let changed = true;
|
||||
|
||||
while (changed === true) {
|
||||
changed = false;
|
||||
|
||||
if (/^@\([^\\()[\]{}|]+\)$/.test(value)) {
|
||||
value = value.slice(2, -1);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPlainBranch(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return value.replace(/\\(.)/g, '$1');
|
||||
};
|
||||
|
||||
const hasRepeatedCharPrefixOverlap = branches => {
|
||||
const values = branches.map(normalizeSimpleBranch).filter(Boolean);
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
for (let j = i + 1; j < values.length; j++) {
|
||||
const a = values[i];
|
||||
const b = values[j];
|
||||
const char = a[0];
|
||||
|
||||
if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a === b || a.startsWith(b) || b.startsWith(a)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const parseRepeatedExtglob = (pattern, requireEnd = true) => {
|
||||
if ((pattern[0] !== '+' && pattern[0] !== '*') || pattern[1] !== '(') {
|
||||
return;
|
||||
}
|
||||
|
||||
let bracket = 0;
|
||||
let paren = 0;
|
||||
let quote = 0;
|
||||
let escaped = false;
|
||||
|
||||
for (let i = 1; i < pattern.length; i++) {
|
||||
const ch = pattern[i];
|
||||
|
||||
if (escaped === true) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '"') {
|
||||
quote = quote === 1 ? 0 : 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (quote === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '[') {
|
||||
bracket++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === ']' && bracket > 0) {
|
||||
bracket--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bracket > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '(') {
|
||||
paren++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === ')') {
|
||||
paren--;
|
||||
|
||||
if (paren === 0) {
|
||||
if (requireEnd === true && i !== pattern.length - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
type: pattern[0],
|
||||
body: pattern.slice(2, i),
|
||||
end: i
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getStarExtglobSequenceOutput = pattern => {
|
||||
let index = 0;
|
||||
const chars = [];
|
||||
|
||||
while (index < pattern.length) {
|
||||
const match = parseRepeatedExtglob(pattern.slice(index), false);
|
||||
|
||||
if (!match || match.type !== '*') {
|
||||
return;
|
||||
}
|
||||
|
||||
const branches = splitTopLevel(match.body).map(branch => branch.trim());
|
||||
if (branches.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const branch = normalizeSimpleBranch(branches[0]);
|
||||
if (!branch || branch.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
chars.push(branch);
|
||||
index += match.end + 1;
|
||||
}
|
||||
|
||||
if (chars.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const source = chars.length === 1
|
||||
? utils.escapeRegex(chars[0])
|
||||
: `[${chars.map(ch => utils.escapeRegex(ch)).join('')}]`;
|
||||
|
||||
return `${source}*`;
|
||||
};
|
||||
|
||||
const repeatedExtglobRecursion = pattern => {
|
||||
let depth = 0;
|
||||
let value = pattern.trim();
|
||||
let match = parseRepeatedExtglob(value);
|
||||
|
||||
while (match) {
|
||||
depth++;
|
||||
value = match.body.trim();
|
||||
match = parseRepeatedExtglob(value);
|
||||
}
|
||||
|
||||
return depth;
|
||||
};
|
||||
|
||||
const analyzeRepeatedExtglob = (body, options) => {
|
||||
if (options.maxExtglobRecursion === false) {
|
||||
return { risky: false };
|
||||
}
|
||||
|
||||
const max =
|
||||
typeof options.maxExtglobRecursion === 'number'
|
||||
? options.maxExtglobRecursion
|
||||
: constants.DEFAULT_MAX_EXTGLOB_RECURSION;
|
||||
|
||||
const branches = splitTopLevel(body).map(branch => branch.trim());
|
||||
|
||||
if (branches.length > 1) {
|
||||
if (
|
||||
branches.some(branch => branch === '') ||
|
||||
branches.some(branch => /^[*?]+$/.test(branch)) ||
|
||||
hasRepeatedCharPrefixOverlap(branches)
|
||||
) {
|
||||
return { risky: true };
|
||||
}
|
||||
}
|
||||
|
||||
for (const branch of branches) {
|
||||
const safeOutput = getStarExtglobSequenceOutput(branch);
|
||||
if (safeOutput) {
|
||||
return { risky: true, safeOutput };
|
||||
}
|
||||
|
||||
if (repeatedExtglobRecursion(branch) > max) {
|
||||
return { risky: true };
|
||||
}
|
||||
}
|
||||
|
||||
return { risky: false };
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the given input string.
|
||||
* @param {String} input
|
||||
@@ -7600,6 +7876,8 @@ const parse = (input, options) => {
|
||||
token.prev = prev;
|
||||
token.parens = state.parens;
|
||||
token.output = state.output;
|
||||
token.startIndex = state.index;
|
||||
token.tokensIndex = tokens.length;
|
||||
const output = (opts.capture ? '(' : '') + token.open;
|
||||
|
||||
increment('parens');
|
||||
@@ -7609,6 +7887,34 @@ const parse = (input, options) => {
|
||||
};
|
||||
|
||||
const extglobClose = token => {
|
||||
const literal = input.slice(token.startIndex, state.index + 1);
|
||||
const body = input.slice(token.startIndex + 2, state.index);
|
||||
const analysis = analyzeRepeatedExtglob(body, opts);
|
||||
|
||||
if ((token.type === 'plus' || token.type === 'star') && analysis.risky) {
|
||||
const safeOutput = analysis.safeOutput
|
||||
? (token.output ? '' : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput)
|
||||
: undefined;
|
||||
const open = tokens[token.tokensIndex];
|
||||
|
||||
open.type = 'text';
|
||||
open.value = literal;
|
||||
open.output = safeOutput || utils.escapeRegex(literal);
|
||||
|
||||
for (let i = token.tokensIndex + 1; i < tokens.length; i++) {
|
||||
tokens[i].value = '';
|
||||
tokens[i].output = '';
|
||||
delete tokens[i].suffix;
|
||||
}
|
||||
|
||||
state.output = token.output + open.output;
|
||||
state.backtrack = true;
|
||||
|
||||
push({ type: 'paren', extglob: true, value, output: '' });
|
||||
decrement('parens');
|
||||
return;
|
||||
}
|
||||
|
||||
let output = token.close + (opts.capture ? ')' : '');
|
||||
let rest;
|
||||
|
||||
@@ -80009,7 +80315,7 @@ const markdownlint_cli2_main = async (/** @type {Parameters} */ params) => {
|
||||
|
||||
|
||||
const logMessage = info;
|
||||
const outputFormatter = (options) => {
|
||||
const outputFormatter = (/** @type {any} */ options) => {
|
||||
const { results } = options;
|
||||
for (const lintError of results) {
|
||||
const {
|
||||
@@ -80030,6 +80336,7 @@ const outputFormatter = (options) => {
|
||||
const information = ruleInformation ? ` ${ruleInformation}` : "";
|
||||
const message =
|
||||
`${fileName}${line}${column} ${name} ${ruleDescription}${detail}${context}${information}`;
|
||||
/** @type {import("@actions/core").AnnotationProperties} */
|
||||
const annotation = {
|
||||
"title": ruleDescription,
|
||||
"file": fileName,
|
||||
@@ -80058,6 +80365,10 @@ const config = getInput("config");
|
||||
if (config) {
|
||||
argv.push("--config", config);
|
||||
}
|
||||
const configPointer = getInput("configPointer");
|
||||
if (configPointer) {
|
||||
argv.push("--configPointer", configPointer);
|
||||
}
|
||||
const fix = Boolean(getInput("fix"));
|
||||
if (fix) {
|
||||
argv.push("--fix");
|
||||
@@ -80070,6 +80381,7 @@ const parameters = {
|
||||
"outputFormatters": [ [ outputFormatter ] ]
|
||||
}
|
||||
};
|
||||
// @ts-ignore
|
||||
markdownlint_cli2_main(parameters).then(
|
||||
(code) => code && setFailed(`Failed with exit code: ${code}`),
|
||||
(error) => setFailed(`Failed due to error: ${error}`)
|
||||
|
||||
+2
-1
@@ -16,13 +16,14 @@ export default [
|
||||
"quotes": "double",
|
||||
"semi": true
|
||||
}),
|
||||
eslintPluginUnicorn.configs["flat/all"],
|
||||
eslintPluginUnicorn.configs.all,
|
||||
{
|
||||
"linterOptions": {
|
||||
"reportUnusedDisableDirectives": true
|
||||
},
|
||||
"rules": {
|
||||
"max-statements": "off",
|
||||
"no-inline-comments": "off",
|
||||
"no-magic-numbers": "off",
|
||||
"no-ternary": "off",
|
||||
"one-var": "off",
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as core from "@actions/core";
|
||||
import { main as markdownlintCli2 } from "markdownlint-cli2";
|
||||
|
||||
const logMessage = core.info;
|
||||
const outputFormatter = (options) => {
|
||||
const outputFormatter = (/** @type {any} */ options) => {
|
||||
const { results } = options;
|
||||
for (const lintError of results) {
|
||||
const {
|
||||
@@ -25,6 +25,7 @@ const outputFormatter = (options) => {
|
||||
const information = ruleInformation ? ` ${ruleInformation}` : "";
|
||||
const message =
|
||||
`${fileName}${line}${column} ${name} ${ruleDescription}${detail}${context}${information}`;
|
||||
/** @type {import("@actions/core").AnnotationProperties} */
|
||||
const annotation = {
|
||||
"title": ruleDescription,
|
||||
"file": fileName,
|
||||
@@ -53,6 +54,10 @@ const config = core.getInput("config");
|
||||
if (config) {
|
||||
argv.push("--config", config);
|
||||
}
|
||||
const configPointer = core.getInput("configPointer");
|
||||
if (configPointer) {
|
||||
argv.push("--configPointer", configPointer);
|
||||
}
|
||||
const fix = Boolean(core.getInput("fix"));
|
||||
if (fix) {
|
||||
argv.push("--fix");
|
||||
@@ -65,6 +70,7 @@ const parameters = {
|
||||
"outputFormatters": [ [ outputFormatter ] ]
|
||||
}
|
||||
};
|
||||
// @ts-ignore
|
||||
markdownlintCli2(parameters).then(
|
||||
(code) => code && core.setFailed(`Failed with exit code: ${code}`),
|
||||
(error) => core.setFailed(`Failed due to error: ${error}`)
|
||||
|
||||
Reference in New Issue
Block a user