@@ -78,19 +78,24 @@ function getCoreRule(name) {
78
78
* Wrap the rule context object to override methods which access to tokens (such as getTokenAfter).
79
79
* @param {RuleContext } context The rule context object.
80
80
* @param {ParserServices.TokenStore } tokenStore The token store object for template.
81
+ * @param {Object } options The option of this rule.
82
+ * @param {boolean } [options.applyDocument] If `true`, apply check to document fragment.
81
83
* @returns {RuleContext }
82
84
*/
83
- function wrapContextToOverrideTokenMethods ( context , tokenStore ) {
85
+ function wrapContextToOverrideTokenMethods ( context , tokenStore , options ) {
84
86
const eslintSourceCode = context . getSourceCode ( )
87
+ const rootNode = options . applyDocument
88
+ ? context . parserServices . getDocumentFragment &&
89
+ context . parserServices . getDocumentFragment ( )
90
+ : eslintSourceCode . ast . templateBody
85
91
/** @type {Token[] | null } */
86
92
let tokensAndComments = null
87
93
function getTokensAndComments ( ) {
88
94
if ( tokensAndComments ) {
89
95
return tokensAndComments
90
96
}
91
- const templateBody = eslintSourceCode . ast . templateBody
92
- tokensAndComments = templateBody
93
- ? tokenStore . getTokens ( templateBody , {
97
+ tokensAndComments = rootNode
98
+ ? tokenStore . getTokens ( rootNode , {
94
99
includeComments : true
95
100
} )
96
101
: [ ]
@@ -99,8 +104,7 @@ function wrapContextToOverrideTokenMethods(context, tokenStore) {
99
104
100
105
/** @param {number } index */
101
106
function getNodeByRangeIndex ( index ) {
102
- const templateBody = eslintSourceCode . ast . templateBody
103
- if ( ! templateBody ) {
107
+ if ( ! rootNode ) {
104
108
return eslintSourceCode . ast
105
109
}
106
110
@@ -110,7 +114,7 @@ function wrapContextToOverrideTokenMethods(context, tokenStore) {
110
114
const skipNodes = [ ]
111
115
let breakFlag = false
112
116
113
- traverseNodes ( templateBody , {
117
+ traverseNodes ( rootNode , {
114
118
enterNode ( node , parent ) {
115
119
if ( breakFlag ) {
116
120
return
@@ -242,6 +246,7 @@ module.exports = {
242
246
* @param {string[] } [options.categories] The categories of this rule.
243
247
* @param {boolean } [options.skipDynamicArguments] If `true`, skip validation within dynamic arguments.
244
248
* @param {boolean } [options.skipDynamicArgumentsReport] If `true`, skip report within dynamic arguments.
249
+ * @param {boolean } [options.applyDocument] If `true`, apply check to document fragment.
245
250
* @param { (context: RuleContext, options: { coreHandlers: RuleListener }) => TemplateListener } [options.create] If define, extend core rule.
246
251
* @returns {RuleModule } The wrapped rule implementation.
247
252
*/
@@ -251,6 +256,7 @@ module.exports = {
251
256
categories,
252
257
skipDynamicArguments,
253
258
skipDynamicArgumentsReport,
259
+ applyDocument,
254
260
create
255
261
} = options || { }
256
262
return {
@@ -262,7 +268,9 @@ module.exports = {
262
268
// The `context.getSourceCode()` cannot access the tokens of templates.
263
269
// So override the methods which access to tokens by the `tokenStore`.
264
270
if ( tokenStore ) {
265
- context = wrapContextToOverrideTokenMethods ( context , tokenStore )
271
+ context = wrapContextToOverrideTokenMethods ( context , tokenStore , {
272
+ applyDocument
273
+ } )
266
274
}
267
275
268
276
if ( skipDynamicArgumentsReport ) {
@@ -277,12 +285,19 @@ module.exports = {
277
285
Object . assign ( { } , coreHandlers )
278
286
)
279
287
if ( handlers . Program ) {
280
- handlers [ "VElement[parent.type!='VElement']" ] = handlers . Program
288
+ handlers [
289
+ applyDocument
290
+ ? 'VDocumentFragment'
291
+ : "VElement[parent.type!='VElement']"
292
+ ] = /** @type {any } */ ( handlers . Program )
281
293
delete handlers . Program
282
294
}
283
295
if ( handlers [ 'Program:exit' ] ) {
284
- handlers [ "VElement[parent.type!='VElement']:exit" ] =
285
- handlers [ 'Program:exit' ]
296
+ handlers [
297
+ applyDocument
298
+ ? 'VDocumentFragment:exit'
299
+ : "VElement[parent.type!='VElement']:exit"
300
+ ] = /** @type {any } */ ( handlers [ 'Program:exit' ] )
286
301
delete handlers [ 'Program:exit' ]
287
302
}
288
303
@@ -309,6 +324,10 @@ module.exports = {
309
324
compositingVisitors ( handlers , create ( context , { coreHandlers } ) )
310
325
}
311
326
327
+ if ( applyDocument ) {
328
+ // Apply the handlers to document.
329
+ return defineDocumentVisitor ( context , handlers )
330
+ }
312
331
// Apply the handlers to templates.
313
332
return defineTemplateBodyVisitor ( context , handlers )
314
333
} ,
@@ -1812,6 +1831,30 @@ function defineTemplateBodyVisitor(
1812
1831
options
1813
1832
)
1814
1833
}
1834
+ /**
1835
+ * Register the given visitor to parser services.
1836
+ * If the parser service of `vue-eslint-parser` was not found,
1837
+ * this generates a warning.
1838
+ *
1839
+ * @param {RuleContext } context The rule context to use parser services.
1840
+ * @param {TemplateListener } documentVisitor The visitor to traverse the document.
1841
+ * @param { { triggerSelector: "Program" | "Program:exit" } } [options] The options.
1842
+ * @returns {RuleListener } The merged visitor.
1843
+ */
1844
+ function defineDocumentVisitor ( context , documentVisitor , options ) {
1845
+ if ( context . parserServices . defineDocumentVisitor == null ) {
1846
+ const filename = context . getFilename ( )
1847
+ if ( path . extname ( filename ) === '.vue' ) {
1848
+ context . report ( {
1849
+ loc : { line : 1 , column : 0 } ,
1850
+ message :
1851
+ 'Use the latest vue-eslint-parser. See also https://eslint.vuejs.org/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error.'
1852
+ } )
1853
+ }
1854
+ return { }
1855
+ }
1856
+ return context . parserServices . defineDocumentVisitor ( documentVisitor , options )
1857
+ }
1815
1858
1816
1859
/**
1817
1860
* @template T
0 commit comments