@@ -9,11 +9,6 @@ const util = require('ember-template-imports/src/util');
9
9
const TRANSFORM_CACHE = new Map ( ) ;
10
10
const TEXT_CACHE = new Map ( ) ;
11
11
12
- // source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
13
- function escapeRegExp ( string ) {
14
- return string . replace ( / [ $ ( ) * + . ? [ \\ \] ^ { | } ] / g, '\\$&' ) ; // $& means the whole matched string
15
- }
16
-
17
12
function arrayEq ( arr1 , arr2 ) {
18
13
return arr1 . length === arr2 . length && arr1 . every ( ( val , idx ) => val === arr2 [ idx ] ) ;
19
14
}
@@ -22,11 +17,16 @@ function arrayEq(arr1, arr2) {
22
17
const oneLineTemplateRegex = / \[ _ _ G L I M M E R _ T E M P L A T E \( ` ( .* ) ` , { .* } \) ] / ;
23
18
24
19
// regex to capture opening template tag
25
- const openingTemplateTagRegex = / \[ _ _ G L I M M E R _ T E M P L A T E \( ` $ / ;
20
+ const openingTemplateTagRegex = / \[ _ _ G L I M M E R _ T E M P L A T E \( ` / ;
26
21
27
22
// regex to capture closing template tag
28
23
const closingTemplateTagRegex = / ` , { .* } \) ] / ;
29
24
25
+ // regex with capture group on scope token variables. In the following example:
26
+ // `, { strictMode: true, scope: () => ({on,noop}) })]
27
+ // the capture group would contain `on,noop`
28
+ const getScopeTokenRegex = / s c o p e : \( \) = > \( { ( .* ) } \) } \) / ;
29
+
30
30
/**
31
31
* This function is responsible for running the embedded templates transform
32
32
* from ember-template-imports.
@@ -145,50 +145,57 @@ function mapRange(messages, filename) {
145
145
return message ;
146
146
}
147
147
148
- // when the lines do not match, we've hit a lint error on the line containing the
149
- // <template> tag, meaning we need to re-work the message
150
- const modified = { ...message } ;
151
- let token = transformedLine . slice ( message . column - 1 , message . endColumn - 1 ) ;
148
+ // when the lines do not match, we've hit a lint error on a line containing the
149
+ // <template> tag, the closing </template> tag, or possibly both.
152
150
153
- // lint error is specifically on JUST the opening <template> tag
154
- if ( openingTemplateTagRegex . test ( token ) ) {
155
- token = `<${ util . TEMPLATE_TAG_NAME } >` ;
151
+ const modified = { ...message } ;
152
+ const token = transformedLine . slice ( message . column - 1 , message . endColumn - 1 ) ;
153
+ const lineHasOpeningTag = openingTemplateTagRegex . test ( transformedLine ) ;
154
+ const lineHasClosingTag = closingTemplateTagRegex . test ( transformedLine ) ;
156
155
157
- // this case is simple: we know the starting column will be correct, so we just
158
- // need to adjust the endColumn for the difference in length between the transformed
159
- // token and the original token.
160
- modified . endColumn = modified . column + token . length ;
161
- } else if ( oneLineTemplateRegex . test ( token ) ) {
156
+ if ( oneLineTemplateRegex . test ( token ) ) {
162
157
// lint error is on a full, one-line <template>foo</template>
163
158
const templateContext = token . match ( oneLineTemplateRegex ) [ 1 ] ;
164
- token = `<${ util . TEMPLATE_TAG_NAME } >${ templateContext } <${ util . TEMPLATE_TAG_NAME } >` ;
159
+ const newToken = `<${ util . TEMPLATE_TAG_NAME } >${ templateContext } <${ util . TEMPLATE_TAG_NAME } >` ;
165
160
166
161
// this case is simple: we know we have a one-line template invocation, and the
167
162
// start `column` will be the same regardless of syntax. simply calculate the
168
163
// length of the full token `<template>...</template>` and set the endColumn.
169
- modified . endColumn = modified . column + token . length ;
170
- } else {
171
- // if we haven't fallen into one of the cases above, we are likely dealing with
172
- // a scope token in the template placeholder, typically for being used but not
173
- // defined. the `token` should be the specific template API which the error is on,
174
- // so we need to find the usage of the token in the template
175
-
176
- // TODO: this is still bug-prone, and should be refactored to do something like:
177
- // 1. for given token, find associated template tag
178
- // 2. conduct search for token only within associated template
179
- // 3. ensure we are not matching token inside comments
180
- for ( const [ index , line ] of originalLines . entries ( ) ) {
181
- const newColumn = line . search ( new RegExp ( `\\b${ escapeRegExp ( token ) } \\b` ) ) ;
182
- if ( newColumn > - 1 ) {
183
- modified . line = index + 1 ;
184
- modified . column = newColumn + 1 ;
185
- modified . endColumn = newColumn + token . length + 1 ;
186
- break ;
164
+ modified . endColumn = modified . column + newToken . length + 1 ;
165
+ return modified ;
166
+ }
167
+
168
+ if ( lineHasClosingTag ) {
169
+ const scopeTokens = transformedLine . match ( getScopeTokenRegex ) ?. [ 1 ] ?. split ( ',' ) ?? [ ] ;
170
+ if ( scopeTokens . includes ( token ) ) {
171
+ // when we have an error specifically with a scope token, we output the error
172
+ // on the starting <template> tag. Currently, we do not know the position of
173
+ // the token in the template, so there were efforts to do regex searches to
174
+ // find the token. Long story short, these all were very bug-prone, so now we
175
+ // just modify the message slightly and return it on the opening template tag.
176
+
177
+ let idx = message . line - 1 ;
178
+ let curLine = lines [ idx ] ;
179
+
180
+ while ( idx ) {
181
+ const templateTag = curLine . search ( openingTemplateTagRegex ) ;
182
+ if ( templateTag > - 1 ) {
183
+ modified . line = idx + 1 ;
184
+ modified . endLine = idx + 1 ;
185
+ modified . column = templateTag + 1 ;
186
+ modified . endColumn = templateTag + `<${ util . TEMPLATE_TAG_NAME } >` . length + 1 ;
187
+ modified . message = `Error in template: ${ message . message } ` ;
188
+ return modified ;
189
+ }
190
+ curLine = lines [ -- idx ] ;
187
191
}
188
192
}
193
+ } else if ( lineHasOpeningTag ) {
194
+ // token is before the <template> tag, no modifications necessary
195
+ if ( transformedLine . indexOf ( token ) < transformedLine . search ( openingTemplateTagRegex ) ) {
196
+ return message ;
197
+ }
189
198
}
190
-
191
- return modified ;
192
199
} else {
193
200
// 2. handle multi-line diagnostics from eslint
194
201
const originalRange = originalLines . slice ( message . line - 1 , message . endLine ) ;
@@ -215,12 +222,16 @@ function mapRange(messages, filename) {
215
222
const closingTagIndex = originalEndLine . indexOf ( closingTemplateTag ) ;
216
223
217
224
if ( closingTagIndex ) {
218
- modified . endColumn = closingTagIndex + closingTemplateTag . length ;
225
+ modified . endColumn = closingTagIndex + closingTemplateTag . length + 1 ;
219
226
}
220
227
}
221
228
222
229
return modified ;
223
230
}
231
+
232
+ // if all else fails: return the original message. It may not have the correct line/col,
233
+ // but it is better to return a mis-aligned diagnostic message than none at all.
234
+ return message ;
224
235
} ) ;
225
236
}
226
237
0 commit comments