1import fs from 'fs'; 2import { resolve } from 'path'; 3import assert from 'assert'; 4 5import { unified } from 'unified'; 6import remarkParse from 'remark-parse'; 7 8const source = resolve(process.argv[2]); 9 10const skipDeprecationComment = /^<!-- md-lint skip-deprecation (DEP\d{4}) -->$/; 11 12const generateDeprecationCode = (codeAsNumber) => 13 `DEP${codeAsNumber.toString().padStart(4, '0')}`; 14 15const addMarkdownPathToErrorStack = (error, node) => { 16 const { line, column } = node.position.start; 17 const [header, ...lines] = error.stack.split('\n'); 18 error.stack = 19 header + 20 `\n at <anonymous> (${source}:${line}:${column})\n` + 21 lines.join('\n'); 22 return error; 23}; 24 25const testHeading = (headingNode, expectedDeprecationCode) => { 26 try { 27 assert.strictEqual( 28 headingNode?.children[0]?.value.substring(0, 9), 29 `${expectedDeprecationCode}: `, 30 'Ill-formed or out-of-order deprecation code.', 31 ); 32 } catch (e) { 33 throw addMarkdownPathToErrorStack(e, headingNode); 34 } 35}; 36 37const testYAMLComment = (commentNode) => { 38 try { 39 assert.match( 40 commentNode?.value?.substring(0, 21), 41 /^<!-- YAML\r?\nchanges:\r?\n/, 42 'Missing or ill-formed YAML comment.', 43 ); 44 } catch (e) { 45 throw addMarkdownPathToErrorStack(e, commentNode); 46 } 47}; 48 49const testDeprecationType = (paragraphNode) => { 50 try { 51 assert.strictEqual( 52 paragraphNode?.children[0]?.value?.substring(0, 6), 53 'Type: ', 54 'Missing deprecation type.', 55 ); 56 } catch (e) { 57 throw addMarkdownPathToErrorStack(e, paragraphNode); 58 } 59}; 60 61const tree = unified() 62 .use(remarkParse) 63 .parse(fs.readFileSync(source)); 64 65let expectedDeprecationCodeNumber = 0; 66for (let i = 0; i < tree.children.length; i++) { 67 const node = tree.children[i]; 68 if (node.type === 'html' && skipDeprecationComment.test(node.value)) { 69 const expectedDeprecationCode = 70 generateDeprecationCode(++expectedDeprecationCodeNumber); 71 const deprecationCodeAsText = node.value.match(skipDeprecationComment)[1]; 72 73 try { 74 assert.strictEqual( 75 deprecationCodeAsText, 76 expectedDeprecationCode, 77 'Deprecation codes are not ordered correctly.', 78 ); 79 } catch (e) { 80 throw addMarkdownPathToErrorStack(e, node); 81 } 82 } 83 if (node.type === 'heading' && node.depth === 3) { 84 const expectedDeprecationCode = 85 generateDeprecationCode(++expectedDeprecationCodeNumber); 86 87 testHeading(node, expectedDeprecationCode); 88 89 testYAMLComment(tree.children[i + 1]); 90 testDeprecationType(tree.children[i + 2]); 91 } 92} 93