@@ -155,6 +155,15 @@ function getJsErrors(code: string, resourcePath: string) {
155155 return getJsErrorsForSourceFile ( sourceFile , program , host ) ;
156156}
157157
158+ async function getXmlErrorForFile ( content : string ) {
159+ const { XMLValidator} = await import ( "fast-xml-parser" ) ;
160+ const validOrError = XMLValidator . validate ( content ) ;
161+ if ( validOrError === true ) {
162+ return ; // No errors
163+ }
164+ return validOrError . err ;
165+ }
166+
158167export function getFactoryPosition ( moduleDeclaration : ExistingModuleDeclarationInfo ) : { start : number ; end : number } {
159168 const { moduleDeclaration : declaration } = moduleDeclaration ;
160169 const factory = "factory" in declaration ? declaration . factory : declaration . callback ;
@@ -190,6 +199,23 @@ export default async function ({
190199 }
191200 }
192201
202+ const res : AutofixResult = new Map ( ) ;
203+ if ( jsResources . length ) {
204+ log . verbose ( `Applying autofixes for ${ jsResources . length } JS resources` ) ;
205+ await autofixJs ( jsResources , messages , context , res ) ;
206+ }
207+ if ( xmlResources . length ) {
208+ log . verbose ( `Applying autofixes for ${ xmlResources . length } XML resources` ) ;
209+ await autofixXml ( xmlResources , messages , context , res ) ;
210+ }
211+
212+ return res ;
213+ }
214+
215+ async function autofixJs (
216+ jsResources : Resource [ ] , messages : Map < ResourcePath , RawLintMessage [ ] > , context : LinterContext ,
217+ res : AutofixResult
218+ ) : Promise < void > {
193219 const sourceFiles : SourceFiles = new Map ( ) ;
194220 const jsResourcePaths = [ ] ;
195221 for ( const resource of jsResources ) {
@@ -210,7 +236,6 @@ export default async function ({
210236 const program = createProgram ( jsResourcePaths , compilerHost ) ;
211237
212238 const checker = program . getTypeChecker ( ) ;
213- const res : AutofixResult = new Map ( ) ;
214239 for ( const [ resourcePath , sourceFile ] of sourceFiles ) {
215240 // Checking for syntax errors in the original source file.
216241 // We should not apply autofixes to files with syntax errors
@@ -246,26 +271,41 @@ export default async function ({
246271 const jsErrors = getJsErrors ( newContent , resourcePath ) ;
247272 if ( jsErrors . length ) {
248273 const contentWithMarkers = newContent . split ( "\n" ) ;
249- const message = `Syntax error after applying autofix for '${ resourcePath } ':\n - ` +
250- jsErrors
251- . sort ( ( a , b ) => {
252- if ( a . start === undefined || b . start === undefined ) {
253- return 0 ;
254- }
255- return a . start - b . start ;
256- } ) . map ( ( d ) => {
257- if ( d . line !== undefined && d . character !== undefined ) {
258- // Insert line below the finding and mark the character
259- const line = d . line + 1 ;
260- contentWithMarkers . splice ( line , 0 , " " . repeat ( d . character ) + "^" ) ;
261- }
262- let res = d . messageText ;
263- if ( d . codeSnippet ) {
264- res += ` (\`${ d . codeSnippet } \`)` ;
265- }
266- return res ;
267- } ) . join ( "\n - " ) ;
274+ let lineOffset = 0 ;
275+ const message = `Syntax error after applying autofix for '${ resourcePath } '. ` +
276+ `This is likely a UI5 linter internal issue. Please check the verbose log. ` +
277+ `Please report this using the bug report template: ` +
278+ `https://github.com/UI5/linter/issues/new?template=bug-report.md` ;
279+ const errors = jsErrors
280+ . sort ( ( a , b ) => {
281+ if ( a . start === undefined || b . start === undefined ) {
282+ return 0 ;
283+ }
284+ return a . start - b . start ;
285+ } ) . map ( ( d ) => {
286+ let res = d . messageText ;
287+ if ( d . line !== undefined && d . character !== undefined ) {
288+ // Prepend line and character
289+ res = `${ d . line + 1 } :${ d . character + 1 } ${ res } ` ;
290+
291+ // Fill contentWithMarkers array for debug logging
292+ // Insert line below the finding and mark the character
293+ const lineContent = contentWithMarkers [ d . line + lineOffset ] ;
294+ // Count number of tabs until the character
295+ const tabCount = lineContent . slice ( 0 , d . character ) . split ( "\t" ) . length - 1 ;
296+ const leadingTabs = "\t" . repeat ( tabCount ) ;
297+ const markerLine = d . line + lineOffset + 1 ;
298+ contentWithMarkers . splice ( markerLine , 0 ,
299+ leadingTabs + " " . repeat ( d . character - tabCount ) + "^" ) ;
300+ lineOffset ++ ;
301+ }
302+ if ( d . codeSnippet ) {
303+ res += ` (\`${ d . codeSnippet } \`)` ;
304+ }
305+ return res ;
306+ } ) . join ( "\n - " ) ;
268307 log . verbose ( message ) ;
308+ log . verbose ( errors ) ;
269309 log . verbose ( resourcePath + ":\n" + contentWithMarkers . join ( "\n" ) ) ;
270310 context . addLintingMessage ( resourcePath , MESSAGE . AUTOFIX_ERROR , { message} ) ;
271311 } else {
@@ -274,16 +314,51 @@ export default async function ({
274314 }
275315 }
276316 }
317+ }
277318
319+ async function autofixXml (
320+ xmlResources : Resource [ ] , messages : Map < ResourcePath , RawLintMessage [ ] > , context : LinterContext ,
321+ res : AutofixResult
322+ ) : Promise < void > {
278323 for ( const resource of xmlResources ) {
279324 const resourcePath = resource . getPath ( ) ;
325+ const existingXmlError = await getXmlErrorForFile ( await resource . getString ( ) ) ;
326+ if ( existingXmlError ) {
327+ log . verbose ( `Skipping autofix for '${ resourcePath } '. Syntax error reported in original source file:\n` +
328+ `[${ existingXmlError . line } :${ existingXmlError . col } ] ${ existingXmlError . msg } ` ) ;
329+ continue ;
330+ }
280331 const newContent = await applyFixesXml ( resource , messages . get ( resourcePath ) ! ) ;
281- if ( newContent ) {
282- res . set ( resourcePath , newContent ) ;
332+ if ( ! newContent ) {
333+ continue ;
283334 }
284- }
285335
286- return res ;
336+ const newXmlError = await getXmlErrorForFile ( newContent ) ;
337+ if ( newXmlError ) {
338+ const message = `Syntax error after applying autofix for '${ resourcePath } '. ` +
339+ `This is likely a UI5 linter internal issue. Please check the verbose log. ` +
340+ `Please report this using the bug report template: ` +
341+ `https://github.com/UI5/linter/issues/new?template=bug-report.md` ;
342+ const error = `Reported error (${ newXmlError . line } :${ newXmlError . col } ): ${ newXmlError . msg } ` ;
343+ log . verbose ( message ) ;
344+ log . verbose ( error ) ;
345+ const contentWithMarkers = newContent . split ( "\n" ) ;
346+ if ( newXmlError . line !== undefined && newXmlError . col !== undefined ) {
347+ const line = newXmlError . line - 1 ;
348+ // Insert line below the finding and mark the character
349+ const lineContent = contentWithMarkers [ line ] ;
350+ // Count number of tabs until the character
351+ const tabCount = lineContent . slice ( 0 , newXmlError . col ) . split ( "\t" ) . length - 1 ;
352+ const leadingTabs = "\t" . repeat ( tabCount ) ;
353+ const markerLine = line + 1 ;
354+ contentWithMarkers . splice ( markerLine , 0 , leadingTabs + " " . repeat ( newXmlError . col - tabCount ) + "^" ) ;
355+ }
356+ log . verbose ( resourcePath + ":\n" + contentWithMarkers . join ( "\n" ) ) ;
357+ context . addLintingMessage ( resourcePath , MESSAGE . AUTOFIX_ERROR , { message} ) ;
358+ continue ;
359+ }
360+ res . set ( resourcePath , newContent ) ;
361+ }
287362}
288363
289364function applyFixesJs (
0 commit comments