11import type { FileInfo , API , Options } from 'jscodeshift' ;
2- import postcss , { Root , Result , Plugin , Node as PostCSSNode } from 'postcss' ;
2+ import postcss , {
3+ Root ,
4+ Result ,
5+ Plugin ,
6+ Container ,
7+ Declaration ,
8+ Node as PostCSSNode ,
9+ Rule as PostCSSRule ,
10+ Comment as PostCSSComment ,
11+ AtRule ,
12+ } from 'postcss' ;
313import valueParser , {
414 Node ,
515 ParsedValue ,
@@ -259,7 +269,7 @@ interface PluginOptions extends Options, NamespaceOptions {}
259269
260270interface Report {
261271 node : PostCSSNode ;
262- severity : 'warning' | 'error' | 'suggestion' ;
272+ severity : 'warning' | 'error' ;
263273 message : string ;
264274}
265275
@@ -286,14 +296,32 @@ type StylelintRule<P = any, S = any> = StylelintRuleBase<P, S> & {
286296} ;
287297// End: Extracted from stylelint
288298
299+ type Walker < N extends PostCSSNode > = ( node : N ) => false | void ;
300+
289301export type PolarisMigrator = (
290302 primaryOption : true ,
291303 secondaryOptions : {
292304 options : { [ key : string ] : any } ;
293305 methods : {
294306 report : ( report : Report ) => void ;
295- flushReports : ( ) => void ;
296- getReportsForNode : ( node : PostCSSNode ) => Report [ ] | undefined ;
307+ each : < T extends Container > ( root : T , walker : Walker < PostCSSNode > ) => void ;
308+ walk : < T extends Container > ( root : T , walker : Walker < PostCSSNode > ) => void ;
309+ walkComments : < T extends Container > (
310+ root : T ,
311+ walker : Walker < PostCSSComment > ,
312+ ) => void ;
313+ walkAtRules : < T extends Container > (
314+ root : T ,
315+ atRuleWalker : Walker < AtRule > ,
316+ ) => void ;
317+ walkDecls : < T extends Container > (
318+ root : T ,
319+ declWalker : Walker < Declaration > ,
320+ ) => void ;
321+ walkRules : < T extends Container > (
322+ root : T ,
323+ ruleWalker : Walker < PostCSSRule > ,
324+ ) => void ;
297325 } ;
298326 } ,
299327 context : PluginContext ,
@@ -376,18 +404,49 @@ export function createSassMigrator(name: string, ruleFn: PolarisMigrator) {
376404
377405 for ( const report of reportsForNode ) {
378406 node . before (
379- report . severity === 'suggestion'
380- ? createInlineComment ( report . message )
381- : createInlineComment ( `${ report . severity } : ${ report . message } ` , {
382- prose : true ,
383- } ) ,
407+ createInlineComment ( `${ report . severity } : ${ report . message } ` , {
408+ prose : true ,
409+ } ) ,
384410 ) ;
385411 }
386412 }
387413 reports . clear ( ) ;
388414 } ;
389415
390- const getReportsForNode = ( node : PostCSSNode ) => reports . get ( node ) ;
416+ function createWalker < T extends PostCSSNode > ( args : {
417+ walker : ( node : T ) => false | void ;
418+ serialiseSuggestion : ( node : T ) => string ;
419+ } ) : ( node : T ) => false | void {
420+ const { walker, serialiseSuggestion} = args ;
421+
422+ return ( node : T ) => {
423+ let oldNode : T ;
424+ if ( context . fix ) {
425+ oldNode = node . clone ( ) ;
426+ }
427+
428+ const result = walker ( node ) ;
429+
430+ const isPartialFix =
431+ context . fix &&
432+ reports . has ( node ) &&
433+ node . toString ( ) !== oldNode ! . toString ( ) ;
434+
435+ flushReportsAsComments ( ) ;
436+
437+ // Our migrations have an opinion on partial fixes (when multiple
438+ // issues are found in a single node, and some but not all can be
439+ // fixed): We dump out the partial fix result as a comment
440+ // immediately above the node.
441+ if ( isPartialFix ) {
442+ node . before ( createInlineComment ( serialiseSuggestion ( node ) ) ) ;
443+ // Undo changes
444+ node . replaceWith ( oldNode ! ) ;
445+ }
446+
447+ return result ;
448+ } ;
449+ }
391450
392451 return ruleFn (
393452 primary ,
@@ -399,8 +458,54 @@ export function createSassMigrator(name: string, ruleFn: PolarisMigrator) {
399458 options : secondaryOptions ,
400459 methods : {
401460 report : addDedupedReport ,
402- flushReports : flushReportsAsComments ,
403- getReportsForNode,
461+ each ( root , walker ) {
462+ root . each (
463+ createWalker ( {
464+ walker,
465+ serialiseSuggestion : ( node ) => node . toString ( ) ,
466+ } ) ,
467+ ) ;
468+ } ,
469+ walk ( root , walker ) {
470+ root . walk (
471+ createWalker ( {
472+ walker,
473+ serialiseSuggestion : ( node ) => node . toString ( ) ,
474+ } ) ,
475+ ) ;
476+ } ,
477+ walkAtRules ( root , walker ) {
478+ root . walkAtRules (
479+ createWalker ( {
480+ walker,
481+ serialiseSuggestion : ( node ) => `@${ node . name } ${ node . params } ` ,
482+ } ) ,
483+ ) ;
484+ } ,
485+ walkComments ( root , walker ) {
486+ root . walkComments (
487+ createWalker ( {
488+ walker,
489+ serialiseSuggestion : ( node ) => node . text ,
490+ } ) ,
491+ ) ;
492+ } ,
493+ walkDecls ( root , walker ) {
494+ root . walkDecls (
495+ createWalker ( {
496+ walker,
497+ serialiseSuggestion : ( node ) => `${ node . prop } : ${ node . value } ` ,
498+ } ) ,
499+ ) ;
500+ } ,
501+ walkRules ( root , walker ) {
502+ root . walkRules (
503+ createWalker ( {
504+ walker,
505+ serialiseSuggestion : ( node ) => node . selector ,
506+ } ) ,
507+ ) ;
508+ } ,
404509 } ,
405510 } ,
406511 context ,
0 commit comments