@@ -2,15 +2,19 @@ import { env } from 'node:process';
22
33import { valid , parse } from 'semver' ;
44import { visit } from 'unist-util-visit' ;
5- import yaml from 'yaml' ;
5+ import { isMap , isSeq , LineCounter , parseDocument } from 'yaml' ;
66
7- import { enforceArray } from '../../utils/array.mjs' ;
87import {
98 extractYamlContent ,
109 normalizeYamlSyntax ,
1110} from '../../utils/parser/index.mjs' ;
1211import createQueries from '../../utils/queries/index.mjs' ;
1312import { LINT_MESSAGES } from '../constants.mjs' ;
13+ import {
14+ createYamlIssueReporter ,
15+ findPropertyByName ,
16+ normalizeNode ,
17+ } from '../utils/yaml.mjs' ;
1418
1519const NODE_RELEASED_VERSIONS = env . NODE_RELEASED_VERSIONS ?. split ( ',' ) ;
1620
@@ -42,20 +46,50 @@ const isIgnoredVersion = version => {
4246/**
4347 * Determines if a given version is invalid.
4448 *
45- * @param {string } version - The version to check.
49+ * @param {Scalar } version - The version to check.
4650 * @param {unknown } _ - Unused parameter.
4751 * @param {{ length: number } } context - Array containing the length property.
4852 * @returns {boolean } True if the version is invalid, otherwise false.
4953 */
5054const isInvalid = NODE_RELEASED_VERSIONS
5155 ? ( version , _ , { length } ) =>
5256 ! (
53- isValidReplaceMe ( version , length ) ||
54- isIgnoredVersion ( version ) ||
55- NODE_RELEASED_VERSIONS . includes ( version . replace ( / ^ v / , '' ) )
57+ isValidReplaceMe ( version . value , length ) ||
58+ isIgnoredVersion ( version . value ) ||
59+ NODE_RELEASED_VERSIONS . includes ( version . value . replace ( / ^ v / , '' ) )
5660 )
5761 : ( version , _ , { length } ) =>
58- ! ( isValidReplaceMe ( version , length ) || valid ( version ) ) ;
62+ ! ( isValidReplaceMe ( version . value , length ) || valid ( version . value ) ) ;
63+
64+ /**
65+ *
66+ * @param {object } root0
67+ * @param {import('../types.d.ts').LintContext } root0.context
68+ * @param {import('yaml').Node } root0.node
69+ * @param {(message: string, node: import('yaml').Node<unknown>) => import('../types.d.ts').IssueDescriptor } root0.report
70+ */
71+ export const extractVersions = ( { context, node, report } ) => {
72+ if ( ! isMap ( node ) ) {
73+ context . report (
74+ report (
75+ LINT_MESSAGES . invalidChangeProperty . replace ( '{{type}}' , node . NODE_TYPE ) ,
76+ node
77+ )
78+ ) ;
79+
80+ return null ;
81+ }
82+
83+ const versionNode = findPropertyByName ( node , 'version' ) ;
84+
85+ if ( ! versionNode ) {
86+ context . report ( report ( LINT_MESSAGES . missingChangeVersion , node ) ) ;
87+
88+ return null ;
89+ }
90+
91+ return normalizeNode ( versionNode . value ) ;
92+ } ;
5993
6094/**
6195 * Identifies invalid change versions from metadata entries.
@@ -69,30 +103,52 @@ export const invalidChangeVersion = context => {
69103
70104 const normalizedYaml = normalizeYamlSyntax ( yamlContent ) ;
71105
72- // TODO: Use YAML AST to provide better issues positions
73- /**
74- * @type {ApiDocRawMetadataEntry }
75- */
76- const { changes } = yaml . parse ( normalizedYaml ) ;
106+ const lineCounter = new LineCounter ( ) ;
107+ const document = parseDocument ( normalizedYaml , { lineCounter } ) ;
77108
78- if ( ! changes ) {
109+ const report = createYamlIssueReporter ( node , lineCounter ) ;
110+
111+ // Skip if yaml isn't a mapping
112+ if ( ! isMap ( document . contents ) ) {
79113 return ;
80114 }
81115
82- changes . forEach ( ( { version } ) =>
83- enforceArray ( version )
116+ const changesNode = findPropertyByName ( document . contents , 'changes' ) ;
117+
118+ // Skip if changes node is not present
119+ if ( ! changesNode ) {
120+ return ;
121+ }
122+
123+ // Validate changes node is a sequence
124+ if ( ! isSeq ( changesNode . value ) ) {
125+ return context . report (
126+ report (
127+ LINT_MESSAGES . invalidChangeProperty . replace (
128+ '{{type}}' ,
129+ changesNode . value . NODE_TYPE
130+ ) ,
131+ changesNode . key
132+ )
133+ ) ;
134+ }
135+
136+ changesNode . value . items . forEach ( node =>
137+ extractVersions ( { context, node, report } )
138+ . filter ( Boolean ) // Filter already reported empt items
84139 . filter ( isInvalid )
85140 . forEach ( version =>
86- context . report ( {
87- level : 'error' ,
88- message : version
89- ? LINT_MESSAGES . invalidChangeVersion . replace (
90- '{{version}}' ,
91- version
92- )
93- : LINT_MESSAGES . missingChangeVersion ,
94- position : node . position ,
95- } )
141+ context . report (
142+ report (
143+ version ?. value
144+ ? LINT_MESSAGES . invalidChangeVersion . replace (
145+ '{{version}}' ,
146+ version . value
147+ )
148+ : LINT_MESSAGES . missingChangeVersion ,
149+ version
150+ )
151+ )
96152 )
97153 ) ;
98154 } ) ;
0 commit comments