@@ -79,6 +79,41 @@ function getCoreRule(name) {
7979 return map . get ( name ) || null
8080}
8181
82+ /**
83+ * @template {object} T
84+ * @param {T } target
85+ * @param {Partial<T>[] } propsArray
86+ * @returns {T }
87+ */
88+ function newProxy ( target , ...propsArray ) {
89+ const result = new Proxy (
90+ { } ,
91+ {
92+ get ( _object , key ) {
93+ for ( const props of propsArray ) {
94+ if ( key in props ) {
95+ // @ts -expect-error
96+ return props [ key ]
97+ }
98+ }
99+ // @ts -expect-error
100+ return target [ key ]
101+ } ,
102+
103+ has ( _object , key ) {
104+ return key in target
105+ } ,
106+ ownKeys ( _object ) {
107+ return Reflect . ownKeys ( target )
108+ } ,
109+ getPrototypeOf ( _object ) {
110+ return Reflect . getPrototypeOf ( target )
111+ }
112+ }
113+ )
114+ return /** @type {T } */ ( result )
115+ }
116+
82117/**
83118 * Wrap the rule context object to override methods which access to tokens (such as getTokenAfter).
84119 * @param {RuleContext } context The rule context object.
@@ -147,18 +182,16 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
147182 } )
148183 return result
149184 }
150- const sourceCode = new Proxy ( Object . assign ( { } , eslintSourceCode ) , {
151- get ( _object , key ) {
152- if ( key === 'tokensAndComments' ) {
185+ const sourceCode = newProxy (
186+ eslintSourceCode ,
187+ {
188+ get tokensAndComments ( ) {
153189 return getTokensAndComments ( )
154- }
155- if ( key === 'getNodeByRangeIndex' ) {
156- return getNodeByRangeIndex
157- }
158- // @ts -expect-error
159- return key in tokenStore ? tokenStore [ key ] : eslintSourceCode [ key ]
160- }
161- } )
190+ } ,
191+ getNodeByRangeIndex
192+ } ,
193+ tokenStore
194+ )
162195
163196 const containerScopes = new WeakMap ( )
164197
@@ -183,23 +216,13 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
183216 const eslintScope = createRequire ( require . resolve ( 'eslint' ) ) (
184217 'eslint-scope'
185218 )
186- const expStmt = new Proxy ( exprContainer , {
187- get ( _object , key ) {
188- if ( key === 'type' ) {
189- return 'ExpressionStatement'
190- }
191- // @ts -expect-error
192- return exprContainer [ key ]
193- }
219+ const expStmt = newProxy ( exprContainer , {
220+ // @ts -expect-error
221+ type : 'ExpressionStatement'
194222 } )
195- const scopeProgram = new Proxy ( programNode , {
196- get ( _object , key ) {
197- if ( key === 'body' ) {
198- return [ expStmt ]
199- }
200- // @ts -expect-error
201- return programNode [ key ]
202- }
223+ const scopeProgram = newProxy ( programNode , {
224+ // @ts -expect-error
225+ body : [ expStmt ]
203226 } )
204227 const scope = eslintScope . analyze ( scopeProgram , {
205228 ignoreEval : true ,
@@ -218,9 +241,7 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
218241
219242 return null
220243 }
221- return {
222- // @ts -expect-error
223- __proto__ : context ,
244+ return newProxy ( context , {
224245 getSourceCode ( ) {
225246 return sourceCode
226247 } ,
@@ -232,7 +253,7 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
232253
233254 return context . getDeclaredVariables ( node )
234255 }
235- }
256+ } )
236257}
237258
238259/**
@@ -262,9 +283,7 @@ function wrapContextToOverrideReportMethodToSkipDynamicArgument(context) {
262283 leaveNode ( ) { }
263284 } )
264285
265- return {
266- // @ts -expect-error
267- __proto__ : context ,
286+ return newProxy ( context , {
268287 report ( descriptor , ...args ) {
269288 let range = null
270289 if ( descriptor . loc ) {
@@ -289,7 +308,7 @@ function wrapContextToOverrideReportMethodToSkipDynamicArgument(context) {
289308 }
290309 context . report ( descriptor , ...args )
291310 }
292- }
311+ } )
293312}
294313
295314// ------------------------------------------------------------------------------
@@ -322,6 +341,25 @@ module.exports = {
322341 */
323342 defineDocumentVisitor,
324343
344+ /**
345+ * @callback WrapCoreRuleCreate
346+ * @param {RuleContext } ruleContext
347+ * @param {WrapCoreRuleCreateContext } wrapContext
348+ * @returns {TemplateListener }
349+ *
350+ * @typedef {object } WrapCoreRuleCreateContext
351+ * @property {RuleListener } coreHandlers
352+ */
353+ /**
354+ * @callback WrapCoreRulePreprocess
355+ * @param {RuleContext } ruleContext
356+ * @param {WrapCoreRulePreprocessContext } wrapContext
357+ * @returns {void }
358+ *
359+ * @typedef {object } WrapCoreRulePreprocessContext
360+ * @property { (override: Partial<RuleContext>) => RuleContext } wrapContextToOverrideProperties Wrap the rule context object to override
361+ * @property { (visitor: TemplateListener) => void } defineVisitor Define template body visitor
362+ */
325363 /**
326364 * Wrap a given core rule to apply it to Vue.js template.
327365 * @param {string } coreRuleName The name of the core rule implementation to wrap.
@@ -330,7 +368,8 @@ module.exports = {
330368 * @param {boolean } [options.skipDynamicArguments] If `true`, skip validation within dynamic arguments.
331369 * @param {boolean } [options.skipDynamicArgumentsReport] If `true`, skip report within dynamic arguments.
332370 * @param {boolean } [options.applyDocument] If `true`, apply check to document fragment.
333- * @param { (context: RuleContext, options: { coreHandlers: RuleListener }) => TemplateListener } [options.create] If define, extend core rule.
371+ * @param {WrapCoreRulePreprocess } [options.preprocess] Preprocess to calling create of core rule.
372+ * @param {WrapCoreRuleCreate } [options.create] If define, extend core rule.
334373 * @returns {RuleModule } The wrapped rule implementation.
335374 */
336375 wrapCoreRule ( coreRuleName , options ) {
@@ -366,6 +405,7 @@ module.exports = {
366405 skipDynamicArguments,
367406 skipDynamicArgumentsReport,
368407 applyDocument,
408+ preprocess,
369409 create
370410 } = options || { }
371411 return {
@@ -387,12 +427,25 @@ module.exports = {
387427 wrapContextToOverrideReportMethodToSkipDynamicArgument ( context )
388428 }
389429
390- // Move `Program` handlers to `VElement[parent.type!='VElement']`
430+ /** @type {TemplateListener } */
431+ const handlers = { }
432+
433+ if ( preprocess ) {
434+ preprocess ( context , {
435+ wrapContextToOverrideProperties ( override ) {
436+ context = newProxy ( context , override )
437+ return context
438+ } ,
439+ defineVisitor ( visitor ) {
440+ compositingVisitors ( handlers , visitor )
441+ }
442+ } )
443+ }
444+
391445 const coreHandlers = coreRule . create ( context )
446+ compositingVisitors ( handlers , coreHandlers )
392447
393- const handlers = /** @type {TemplateListener } */ (
394- Object . assign ( { } , coreHandlers )
395- )
448+ // Move `Program` handlers to `VElement[parent.type!='VElement']`
396449 if ( handlers . Program ) {
397450 handlers [
398451 applyDocument
@@ -462,6 +515,13 @@ module.exports = {
462515 * @returns {v is T }
463516 */
464517 isDef,
518+ /**
519+ * Flattens arrays, objects and iterable objects.
520+ * @template T
521+ * @param {T | Iterable<T> | null | undefined } v
522+ * @returns {T[] }
523+ */
524+ flatten,
465525 /**
466526 * Get the previous sibling element of the given element.
467527 * @param {VElement } node The element node to get the previous sibling element.
@@ -1837,6 +1897,33 @@ function isDef(v) {
18371897 return v != null
18381898}
18391899
1900+ /**
1901+ * Flattens arrays, objects and iterable objects.
1902+ * @template T
1903+ * @param {T | Iterable<T> | null | undefined } v
1904+ * @returns {T[] }
1905+ */
1906+ function flatten ( v ) {
1907+ /** @type {T[] } */
1908+ const result = [ ]
1909+ if ( v ) {
1910+ if ( isIterable ( v ) ) {
1911+ result . push ( ...v )
1912+ } else {
1913+ result . push ( v )
1914+ }
1915+ }
1916+ return result
1917+ }
1918+
1919+ /**
1920+ * @param {* } v
1921+ * @returns {v is Iterable<any> }
1922+ */
1923+ function isIterable ( v ) {
1924+ return v && Symbol . iterator in v
1925+ }
1926+
18401927// ------------------------------------------------------------------------------
18411928// Nodejs Helpers
18421929// ------------------------------------------------------------------------------
0 commit comments