11import type { NodePath } from '@babel/core' ;
22import { traverse } from '@babel/core' ;
3- import type { Node } from '@babel/types' ;
3+ import type { Node , TemplateLiteral } from '@babel/types' ;
44import {
55 isBinaryExpression ,
66 isMemberExpression ,
@@ -19,37 +19,54 @@ import {
1919import type { Options } from './options' ;
2020import { definePrinter } from './print/index' ;
2121import type { BaseNode } from './types/ast' ;
22+ import type {
23+ GlimmerExpressionExtra ,
24+ GlimmerTemplateExtra ,
25+ } from './types/glimmer' ;
26+ import type {
27+ RawGlimmerArrayExpression ,
28+ RawGlimmerClassProperty ,
29+ } from './types/raw' ;
2230import {
23- hasGlimmerArrayExpression ,
31+ hasRawGlimmerArrayExpression ,
2432 isRawGlimmerArrayExpression ,
2533 isRawGlimmerCallExpression ,
2634 isRawGlimmerClassProperty ,
2735} from './types/raw' ;
2836import { hasAmbiguousNextLine } from './utils/ambiguity' ;
29- import { assert } from './utils/index' ;
37+ import { assert , squish } from './utils/index' ;
3038
31- const typescript = babelParsers [ 'babel-ts' ] as Parser < BaseNode > ;
39+ const typescript = babelParsers [ 'babel-ts' ] as Parser < BaseNode | undefined > ;
3240
33- const preprocess : Required < Parser < BaseNode > > [ 'preprocess' ] = (
34- text ,
35- options
41+ const preprocess : Required < Parser < BaseNode | undefined > > [ 'preprocess' ] = (
42+ text : string ,
43+ options : Options
3644) => {
37- const preprocessed = preprocessEmbeddedTemplates ( text , {
38- getTemplateLocals,
39-
40- templateTag : TEMPLATE_TAG_NAME ,
41- templateTagReplacement : TEMPLATE_TAG_PLACEHOLDER ,
42-
43- includeSourceMaps : false ,
44- includeTemplateTokens : false ,
45-
46- relativePath : options . filepath ,
47- } ) . output ;
45+ let preprocessed : string ;
46+ if ( text . includes ( TEMPLATE_TAG_PLACEHOLDER ) ) {
47+ // This happens when Prettier is being run via eslint + eslint-plugin-ember
48+ // See https://github.com/ember-cli/eslint-plugin-ember/issues/1659
49+ options . __inputWasPreprocessed = true ;
50+ preprocessed = text ;
51+ } else {
52+ options . __inputWasPreprocessed = false ;
53+ preprocessed = preprocessEmbeddedTemplates ( text , {
54+ getTemplateLocals,
55+
56+ templateTag : TEMPLATE_TAG_NAME ,
57+ templateTagReplacement : TEMPLATE_TAG_PLACEHOLDER ,
58+
59+ includeSourceMaps : false ,
60+ includeTemplateTokens : false ,
61+
62+ relativePath : options . filepath ,
63+ } ) . output ;
64+ }
4865
4966 return desugarDefaultExportTemplates ( preprocessed ) ;
5067} ;
5168
52- export const parser : Parser < BaseNode > = {
69+ export const parser : Parser < BaseNode | undefined > = {
5370 ...typescript ,
5471 astFormat : PRINTER_NAME ,
5572
@@ -68,11 +85,12 @@ export const parser: Parser<BaseNode> = {
6885 traverse ( ast as Node , {
6986 enter : makeEnter ( options ) ,
7087 } ) ;
88+ assert ( 'expected ast' , ast ) ;
7189 return ast ;
7290 } ,
7391} ;
7492
75- function makeEnter ( options : Options ) {
93+ function makeEnter ( options : Options ) : ( path : NodePath ) => void {
7694 return ( path : NodePath ) => {
7795 const node = path . node ;
7896 const parentNode = path . parentPath ?. node ;
@@ -85,38 +103,60 @@ function makeEnter(options: Options) {
85103 throw new SyntaxError ( 'Ember <template> tag used as an object property.' ) ;
86104 } else if (
87105 isBinaryExpression ( node ) &&
88- ( hasGlimmerArrayExpression ( node . left ) ||
89- hasGlimmerArrayExpression ( node . right ) )
106+ ( hasRawGlimmerArrayExpression ( node . left ) ||
107+ hasRawGlimmerArrayExpression ( node . right ) )
90108 ) {
91109 throw new SyntaxError ( 'Ember <template> tag used in binary expression.' ) ;
92110 } else if (
93111 isTaggedTemplateExpression ( node ) &&
94- hasGlimmerArrayExpression ( node . tag )
112+ hasRawGlimmerArrayExpression ( node . tag )
95113 ) {
96114 throw new SyntaxError (
97115 'Ember <template> tag used as tagged template expression.'
98116 ) ;
99117 } else if (
100118 isMemberExpression ( node ) &&
101- hasGlimmerArrayExpression ( node . object )
119+ hasRawGlimmerArrayExpression ( node . object )
102120 ) {
103121 throw new SyntaxError ( 'Ember <template> tag used as member expression.' ) ;
104122 }
105123
106- if ( isRawGlimmerArrayExpression ( node ) || isRawGlimmerClassProperty ( node ) ) {
107- const extra = {
108- hasGlimmerExpression : true ,
109- forceSemi : hasAmbiguousNextLine ( path , options ) ,
110- } ;
111- if ( typeof node . extra === 'object' ) {
112- node . extra = { ...node . extra , ...extra } ;
113- } else {
114- node . extra = extra ;
115- }
124+ if ( isRawGlimmerArrayExpression ( node ) ) {
125+ tagGlimmerExpression ( node , hasAmbiguousNextLine ( path , options ) ) ;
126+ tagGlimmerTemplate ( node . elements [ 0 ] . arguments [ 0 ] ) ;
127+ } else if ( isRawGlimmerClassProperty ( node ) ) {
128+ tagGlimmerExpression ( node , hasAmbiguousNextLine ( path , options ) ) ;
129+ tagGlimmerTemplate ( node . key . arguments [ 0 ] ) ;
116130 }
117131 } ;
118132}
119133
134+ function tagGlimmerExpression (
135+ node : RawGlimmerArrayExpression | RawGlimmerClassProperty ,
136+ forceSemi : boolean
137+ ) : void {
138+ const extra : GlimmerExpressionExtra = {
139+ hasGlimmerExpression : true ,
140+ forceSemi,
141+ } ;
142+ if ( typeof node . extra === 'object' ) {
143+ node . extra = { ...node . extra , ...extra } ;
144+ } else {
145+ node . extra = extra ;
146+ }
147+ }
148+
149+ function tagGlimmerTemplate ( node : TemplateLiteral ) : void {
150+ const extra : GlimmerTemplateExtra = {
151+ isGlimmerTemplate : true ,
152+ } ;
153+ if ( typeof node . extra === 'object' ) {
154+ node . extra = { ...node . extra , ...extra } ;
155+ } else {
156+ node . extra = extra ;
157+ }
158+ }
159+
120160/**
121161 * Desugar template tag default exports because they parse as
122162 * ExpressionStatement, which has a bunch of irrelevant custom semicolon
@@ -129,11 +169,11 @@ function makeEnter(options: Options) {
129169function desugarDefaultExportTemplates ( preprocessed : string ) : string {
130170 const placeholderOpen = `[${ TEMPLATE_TAG_PLACEHOLDER } ` ; // intentionally missing ]
131171
132- // ^ \s*(\()?\s*\[__GLIMMER_TEMPLATE
172+ // (^|;) \s*(\()?\s*\[__GLIMMER_TEMPLATE
133173 const sugaredDefaultExport = new RegExp (
134- `^ \\s*(\\()?\\s*\\${ placeholderOpen } `
174+ `(^|;) \\s*(\\()?\\s*\\${ placeholderOpen } `
135175 ) ;
136- const desugaredDefaultExport = `export default $1 ${ placeholderOpen } ` ;
176+ const desugaredDefaultExport = `$1 export default $2 ${ placeholderOpen } ` ;
137177
138178 const lines = preprocessed . split ( / \r ? \n / ) ;
139179 const desugaredLines : string [ ] = [ ] ;
@@ -151,7 +191,10 @@ function desugarDefaultExportTemplates(preprocessed: string): string {
151191
152192 assert ( 'expected non-negative blockLevel' , blockLevel > - 1 ) ;
153193
154- if ( ! previousLine ?. includes ( 'prettier-ignore' ) && blockLevel === 0 ) {
194+ const previousLineIsPrettierIgnore =
195+ previousLine && squish ( previousLine ) === '// prettier-ignore' ;
196+
197+ if ( ! previousLineIsPrettierIgnore && blockLevel === 0 ) {
155198 line = line . replace ( sugaredDefaultExport , desugaredDefaultExport ) ;
156199 }
157200
0 commit comments