1- import { parsers as prettierBabelParsers } from 'prettier/plugins/babel' ;
21import * as prettierEstreePlugin from 'prettier/plugins/estree' ;
32import type { Parser , ParserOptions , Plugin , SupportLanguage } from 'prettier' ;
43import {
@@ -18,7 +17,15 @@ const SOURCE_TYPE_COMBINATIONS: SourceType[] = ['module', 'commonjs'];
1817type Range = [ start : number , end : number ] ;
1918
2019type Locatable = {
20+ __contentEnd ?: number ;
21+ alternate ?: Locatable | null ;
22+ body ?: Locatable ;
23+ consequent ?: Locatable ;
24+ declaration ?: { decorators ?: Locatable [ ] } ;
25+ declarations ?: Locatable [ ] ;
26+ decorators ?: Locatable [ ] ;
2127 end ?: number ;
28+ label ?: Locatable | null ;
2229 range ?: Range ;
2330 start ?: number ;
2431 type ?: string ;
@@ -40,9 +47,95 @@ type EstreePlugin = typeof prettierEstreePlugin & {
4047
4148const estreePlugin = prettierEstreePlugin as EstreePlugin ;
4249const estreePrinter = estreePlugin . printers . estree ;
43- const babelParser = prettierBabelParsers . babel ;
44- const locStart = babelParser . locStart as ( node : Locatable ) => number ;
45- const locEnd = babelParser . locEnd as ( node : Locatable ) => number ;
50+
51+ const CONTENT_END_NODE_TYPES = new Set ( [
52+ 'ExpressionStatement' ,
53+ 'Directive' ,
54+ 'ImportDeclaration' ,
55+ 'ExportDefaultDeclaration' ,
56+ 'ExportNamedDeclaration' ,
57+ 'ExportAllDeclaration' ,
58+ 'ReturnStatement' ,
59+ 'ThrowStatement' ,
60+ 'DoWhileStatement' ,
61+ ] ) ;
62+
63+ /** Mirrors Prettier's JavaScript location helpers without loading its Babel plugin. */
64+ const locStart = ( node : Locatable ) : number => {
65+ const start = ( node . range ?. [ 0 ] ?? node . start ) as number ;
66+ const firstDecorator = ( node . declaration ?. decorators ?? node . decorators ) ?. [ 0 ] ;
67+
68+ return firstDecorator ? Math . min ( locStart ( firstDecorator ) , start ) : start ;
69+ } ;
70+
71+ const locEndWithFullText = ( node : Locatable ) : number => ( node . range ?. [ 1 ] ?? node . end ) as number ;
72+
73+ const locEnd = ( node : Locatable ) : number => {
74+ switch ( node . type ) {
75+ case 'IfStatement' :
76+ return locEnd ( ( node . alternate ?? node . consequent ) as Locatable ) ;
77+
78+ case 'ForInStatement' :
79+ case 'ForOfStatement' :
80+ case 'ForStatement' :
81+ case 'LabeledStatement' :
82+ case 'WithStatement' :
83+ case 'WhileStatement' :
84+ return locEnd ( node . body as Locatable ) ;
85+
86+ case 'BreakStatement' :
87+ return node . label ? locEnd ( node . label ) : locStart ( node ) + 'break' . length ;
88+
89+ case 'ContinueStatement' :
90+ return node . label ? locEnd ( node . label ) : locStart ( node ) + 'continue' . length ;
91+
92+ case 'DebuggerStatement' :
93+ return locStart ( node ) + 'debugger' . length ;
94+
95+ case 'VariableDeclaration' :
96+ return locEnd ( node . declarations ?. at ( - 1 ) as Locatable ) ;
97+
98+ default :
99+ return CONTENT_END_NODE_TYPES . has ( node . type ?? '' )
100+ ? ( node . __contentEnd ?? locEndWithFullText ( node ) )
101+ : locEndWithFullText ( node ) ;
102+ }
103+ } ;
104+
105+ const DOCBLOCK_REGEXP = / ^ \s * ( \/ \* \* ? ( .| \r ? \n ) * ?\* \/ ) / ;
106+ const COMMENT_END_REGEXP = / \* \/ $ / ;
107+ const COMMENT_START_REGEXP = / ^ \/ \* \* ? / ;
108+ const DOCBLOCK_LINE_START_REGEXP = / ( \r ? \n | ^ ) * \* ? / g;
109+ const PRAGMA_REGEXP = / (?: ^ | \r ? \n ) * @ ( \S + ) * ( [ ^ \n \r ] * ) / g;
110+ const FORMAT_PRAGMAS = new Set ( [ 'format' , 'prettier' ] ) ;
111+ const FORMAT_IGNORE_PRAGMAS = new Set ( [ 'noformat' , 'noprettier' ] ) ;
112+
113+ /** Matches Prettier's leading JavaScript docblock pragma handling. */
114+ const hasPragmaFrom = ( originalText : string , pragmas : Set < string > ) : boolean => {
115+ let text = originalText ;
116+
117+ if ( text . startsWith ( '#!' ) ) {
118+ const lineEnd = text . indexOf ( '\n' ) ;
119+ text = text . slice ( ( lineEnd === - 1 ? text . length : lineEnd ) + 1 ) ;
120+ }
121+
122+ const docblock = ( text . match ( DOCBLOCK_REGEXP ) ?. [ 0 ] ?? '' )
123+ . trimStart ( )
124+ . replace ( COMMENT_START_REGEXP , '' )
125+ . replace ( COMMENT_END_REGEXP , '' )
126+ . replaceAll ( DOCBLOCK_LINE_START_REGEXP , '$1' ) ;
127+
128+ for ( const match of docblock . matchAll ( PRAGMA_REGEXP ) ) {
129+ if ( pragmas . has ( match [ 1 ] ) ) {
130+ return true ;
131+ }
132+ }
133+
134+ return false ;
135+ } ;
136+
137+ const hasPragma = ( text : string ) : boolean => hasPragmaFrom ( text , FORMAT_PRAGMAS ) ;
138+ const hasIgnorePragma = ( text : string ) : boolean => hasPragmaFrom ( text , FORMAT_IGNORE_PRAGMAS ) ;
46139
47140const getVisitorKeys = estreePrinter . getVisitorKeys as ( ( node : AstNode ) => string [ ] ) | undefined ;
48141
@@ -118,18 +211,6 @@ const stripComments = (originalText: string, comments: PrettierComment[]): strin
118211 return text ;
119212} ;
120213
121- const CONTENT_END_NODE_TYPES = new Set ( [
122- 'ExpressionStatement' ,
123- 'Directive' ,
124- 'ImportDeclaration' ,
125- 'ExportDefaultDeclaration' ,
126- 'ExportNamedDeclaration' ,
127- 'ExportAllDeclaration' ,
128- 'ReturnStatement' ,
129- 'ThrowStatement' ,
130- 'DoWhileStatement' ,
131- ] ) ;
132-
133214const setContentEnd = (
134215 node : AstNode ,
135216 originalText : string ,
@@ -437,8 +518,8 @@ const createParser = (
437518 parse : ( text : string , options : ParserOptions < AstNode > ) => AstNode ,
438519) : Parser < AstNode > => ( {
439520 astFormat : AST_FORMAT ,
440- hasIgnorePragma : babelParser . hasIgnorePragma ,
441- hasPragma : babelParser . hasPragma ,
521+ hasIgnorePragma,
522+ hasPragma,
442523 locEnd,
443524 locStart,
444525 parse,
0 commit comments