mirror of
https://github.com/OrcaSlicer/OrcaSlicer_WIKI.git
synced 2026-05-17 00:25:45 +03:00
Case checkers (#206)
This commit is contained in:
39
.github/workflows/validate_internal_link.yml
vendored
39
.github/workflows/validate_internal_link.yml
vendored
@@ -104,6 +104,11 @@ jobs:
|
||||
failures.push(formatFailure(reference, 'sameDocEmptyAnchor', reference.target));
|
||||
continue;
|
||||
}
|
||||
const decodedAnchor = decodeLinkComponent(classification.anchorRaw);
|
||||
if (!isKebabCase(decodedAnchor)) {
|
||||
failures.push(formatFailure(reference, 'anchorNotKebabCase', `#${classification.anchorRaw}`));
|
||||
continue;
|
||||
}
|
||||
const anchors = getAnchors(reference.filePath);
|
||||
if (!anchors.has(classification.anchorSlug)) {
|
||||
failures.push(formatFailure(reference, 'missingSameDocAnchor', `#${classification.anchorRaw}`));
|
||||
@@ -132,6 +137,12 @@ jobs:
|
||||
continue;
|
||||
}
|
||||
|
||||
const decodedAnchor = decodeLinkComponent(classification.anchorRaw);
|
||||
if (!isKebabCase(decodedAnchor)) {
|
||||
failures.push(formatFailure(reference, 'anchorNotKebabCase', `${docResult.linkPath}#${classification.anchorRaw}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
const anchors = getAnchors(docResult.relativePath);
|
||||
if (!anchors.has(classification.anchorSlug)) {
|
||||
failures.push(formatFailure(reference, 'missingCrossDocAnchor', `${docResult.linkPath}#${classification.anchorRaw}`));
|
||||
@@ -238,6 +249,12 @@ jobs:
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!isSnakeCase(sanitized)) {
|
||||
result.error = 'docNotSnakeCase';
|
||||
result.detail = rawPath;
|
||||
return result;
|
||||
}
|
||||
|
||||
ensureMarkdownIndex();
|
||||
const matches = findMarkdownDocuments(sanitized);
|
||||
if (!matches.length) {
|
||||
@@ -311,6 +328,24 @@ jobs:
|
||||
return markdownNameIndex.get(baseName) || [];
|
||||
}
|
||||
|
||||
function isSnakeCase(value) {
|
||||
return /^[a-z0-9]+(?:_[a-z0-9]+)*$/.test(value);
|
||||
}
|
||||
|
||||
function isKebabCase(value) {
|
||||
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
|
||||
}
|
||||
|
||||
function decodeLinkComponent(value) {
|
||||
let decoded = value.trim();
|
||||
try {
|
||||
decoded = decodeURIComponent(decoded);
|
||||
} catch (_) {
|
||||
// Ignore decode failure.
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
function getAnchors(relativePath) {
|
||||
if (headingCache.has(relativePath)) {
|
||||
return headingCache.get(relativePath);
|
||||
@@ -417,6 +452,8 @@ jobs:
|
||||
return `${reference.filePath} line ${reference.line}: document links must not include directories; use just the filename (got "${details}").`;
|
||||
case 'extensionNotAllowed':
|
||||
return `${reference.filePath} line ${reference.line}: link target "${details}" must omit the .md suffix.`;
|
||||
case 'docNotSnakeCase':
|
||||
return `${reference.filePath} line ${reference.line}: document name "${details}" must be snake_case.`;
|
||||
case 'missingDocName':
|
||||
return `${reference.filePath} line ${reference.line}: document link "${details}" must include a file name (without .md).`;
|
||||
case 'missingDocument':
|
||||
@@ -425,6 +462,8 @@ jobs:
|
||||
return `${reference.filePath} line ${reference.line}: document link matches multiple files (${details}).`;
|
||||
case 'crossDocEmptyAnchor':
|
||||
return `${reference.filePath} line ${reference.line}: link to ${details} must include a heading name after '#'.`;
|
||||
case 'anchorNotKebabCase':
|
||||
return `${reference.filePath} line ${reference.line}: heading reference ${details} must be kebab-case.`;
|
||||
case 'missingCrossDocAnchor':
|
||||
return `${reference.filePath} line ${reference.line}: heading ${details} was not found.`;
|
||||
case 'invalidHashCount':
|
||||
|
||||
53
.github/workflows/validate_tab_links.yml
vendored
53
.github/workflows/validate_tab_links.yml
vendored
@@ -76,13 +76,19 @@ jobs:
|
||||
continue;
|
||||
}
|
||||
|
||||
const matches = findMarkdownDocuments(docName);
|
||||
const decodedDocName = decodeLinkComponent(docName);
|
||||
if (!isSnakeCase(decodedDocName)) {
|
||||
failures.push(formatFailure(reference, 'docNotSnakeCase', docName));
|
||||
continue;
|
||||
}
|
||||
|
||||
const matches = findMarkdownDocuments(decodedDocName);
|
||||
if (!matches.length) {
|
||||
failures.push(formatFailure(reference, 'missingDocument', `${docName}.md`));
|
||||
failures.push(formatFailure(reference, 'missingDocument', `${decodedDocName}.md`));
|
||||
continue;
|
||||
}
|
||||
if (matches.length > 1) {
|
||||
failures.push(formatFailure(reference, 'ambiguousDocument', `${docName} -> ${matches.slice(0, 5).join(', ')}`));
|
||||
failures.push(formatFailure(reference, 'ambiguousDocument', `${decodedDocName} -> ${matches.slice(0, 5).join(', ')}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -102,6 +108,12 @@ jobs:
|
||||
continue;
|
||||
}
|
||||
|
||||
const decodedAnchor = decodeLinkComponent(anchorRaw);
|
||||
if (!isKebabCase(decodedAnchor)) {
|
||||
failures.push(formatFailure(reference, 'anchorNotKebabCase', `${decodedDocName}#${anchorRaw}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
const anchors = getAnchors(relativePath);
|
||||
const anchorSlug = normalizeAnchor(anchorRaw);
|
||||
if (!anchorSlug) {
|
||||
@@ -109,7 +121,7 @@ jobs:
|
||||
continue;
|
||||
}
|
||||
if (!anchors.has(anchorSlug)) {
|
||||
failures.push(formatFailure(reference, 'missingCrossDocAnchor', `${docName}#${anchorRaw}`));
|
||||
failures.push(formatFailure(reference, 'missingCrossDocAnchor', `${decodedDocName}#${anchorRaw}`));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +293,24 @@ jobs:
|
||||
return markdownNameIndex.get(baseName) || [];
|
||||
}
|
||||
|
||||
function isSnakeCase(value) {
|
||||
return /^[a-z0-9]+(?:_[a-z0-9]+)*$/.test(value);
|
||||
}
|
||||
|
||||
function isKebabCase(value) {
|
||||
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
|
||||
}
|
||||
|
||||
function decodeLinkComponent(value) {
|
||||
let decoded = value.trim();
|
||||
try {
|
||||
decoded = decodeURIComponent(decoded);
|
||||
} catch (_) {
|
||||
// ignore decode failures
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
function getAnchors(relativePath) {
|
||||
if (headingCache.has(relativePath)) {
|
||||
return headingCache.get(relativePath);
|
||||
@@ -348,13 +378,8 @@ jobs:
|
||||
if (!raw) {
|
||||
return '';
|
||||
}
|
||||
let decoded = raw.trim();
|
||||
try {
|
||||
decoded = decodeURIComponent(decoded);
|
||||
} catch (_) {
|
||||
// ignore decode failures
|
||||
}
|
||||
return slugify(decoded, { preserveCase: true });
|
||||
const decoded = decodeLinkComponent(raw);
|
||||
return slugify(decoded);
|
||||
}
|
||||
|
||||
function buildLineOffsets(text) {
|
||||
@@ -401,12 +426,18 @@ jobs:
|
||||
case 'extensionNotAllowed':
|
||||
failure.message = `${lineInfo}: link "${details}" must omit the .md suffix.`;
|
||||
break;
|
||||
case 'docNotSnakeCase':
|
||||
failure.message = `${lineInfo}: document name "${details}" must be snake_case.`;
|
||||
break;
|
||||
case 'missingDocument':
|
||||
failure.message = `${lineInfo}: document ${details} does not exist in the wiki.`;
|
||||
break;
|
||||
case 'ambiguousDocument':
|
||||
failure.message = `${lineInfo}: document reference is ambiguous (${details}).`;
|
||||
break;
|
||||
case 'anchorNotKebabCase':
|
||||
failure.message = `${lineInfo}: heading reference ${details} must be kebab-case.`;
|
||||
break;
|
||||
case 'missingCrossDocAnchor':
|
||||
failure.message = `${lineInfo}: heading ${details} was not found.`;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user