@@ -2,72 +2,93 @@ import fs from 'node:fs/promises'
22import path from 'node:path'
33import { pathToFileURL } from 'node:url'
44
5- import * as parser from '@babel/parser '
6- import * as t from '@babel/types'
7- import recast from 'recast'
5+ import { parse , print , visit , types as t } from 'recast '
6+ // @ts -ignore
7+ import typescriptParser from 'recast/parsers/typescript '
88
99const reporterIdentifierName = 'VscodeJsonReporter'
1010
1111// This file is bundle as parser/ats.js at the package of vscode-webdriverio
1212// So, the correct reporter path is parent directory
1313const VSCODE_REPORTER_PATH = path . resolve ( __dirname , '../reporter.cjs' )
14+
15+ /**
16+ * Create AST nodes using ast-types builders
17+ */
18+ const b = t . builders
19+
1420/**
1521 * Since Windows cannot import by reporter file path due to issues with
1622 * the `initializePlugin` method of wdio-utils, the policy is to create a temporary configuration file.
1723 */
1824export async function createTempConfigFile ( filename : string , outDir : string ) {
1925 const source = await fs . readFile ( filename , { encoding : 'utf8' } )
20- const ast = recast . parse ( source , {
21- parser : {
22- parse ( source : string ) {
23- return parser . parse ( source , {
24- sourceType : 'unambiguous' ,
25- plugins : [ 'typescript' , 'jsx' , 'topLevelAwait' ] ,
26- } )
27- } ,
28- } ,
26+ const ast = parse ( source , {
27+ parser : typescriptParser ,
2928 } )
3029
31- const reporterIdentifier = t . identifier ( reporterIdentifierName )
32- const reporterConfigIdentifier = t . identifier ( `${ reporterIdentifierName } .default || ${ reporterIdentifierName } ` )
33- const reporterElement = t . arrayExpression ( [
30+ const reporterIdentifier = b . identifier ( reporterIdentifierName )
31+ const reporterConfigIdentifier = b . identifier ( `${ reporterIdentifierName } .default || ${ reporterIdentifierName } ` )
32+ const reporterElement = b . arrayExpression ( [
3433 reporterConfigIdentifier ,
35- t . objectExpression ( [
36- t . objectProperty ( t . identifier ( 'stdout' ) , t . booleanLiteral ( true ) ) ,
37- t . objectProperty ( t . identifier ( 'outputDir' ) , t . stringLiteral ( outDir ) ) ,
34+ b . objectExpression ( [
35+ b . property ( 'init' , b . identifier ( 'stdout' ) , b . literal ( true ) ) ,
36+ b . property ( 'init' , b . identifier ( 'outputDir' ) , b . literal ( outDir ) ) ,
3837 ] ) ,
3938 ] )
4039 let hasReporterImport = false
4140
42- function addOrUpdateReporters ( configObject : t . Node ) {
43- if ( ! t . isObjectExpression ( configObject ) ) {
41+ function addOrUpdateReporters ( configObject : any ) {
42+ if ( ! t . namedTypes . ObjectExpression . check ( configObject ) ) {
4443 return
4544 }
4645
47- const reportersProp = configObject . properties . find (
48- ( prop ) =>
49- t . isObjectProperty ( prop ) &&
50- ( ( t . isIdentifier ( prop . key ) && prop . key . name === 'reporters' ) ||
51- ( t . isStringLiteral ( prop . key ) && prop . key . value === 'reporters' ) )
52- )
46+ // Find existing reporters property
47+ let reportersProp = null
48+
49+ for ( let i = 0 ; i < configObject . properties . length ; i ++ ) {
50+ const prop = configObject . properties [ i ]
51+
52+ // Check for both Property and ObjectProperty nodes
53+ if ( t . namedTypes . Property . check ( prop ) || t . namedTypes . ObjectProperty ?. check ?.( prop ) ) {
54+ const isReportersKey =
55+ ( t . namedTypes . Identifier . check ( prop . key ) && prop . key . name === 'reporters' ) ||
56+ ( t . namedTypes . Literal . check ( prop . key ) && prop . key . value === 'reporters' )
57+
58+ if ( isReportersKey ) {
59+ reportersProp = prop
60+ break
61+ }
62+ }
63+ }
5364
54- if ( reportersProp && t . isObjectProperty ( reportersProp ) && t . isArrayExpression ( reportersProp . value ) ) {
65+ if ( reportersProp && t . namedTypes . ArrayExpression . check ( reportersProp . value ) ) {
66+ // Add to existing reporters array
5567 reportersProp . value . elements . push ( reporterElement )
68+ } else if ( reportersProp ) {
69+ // Replace existing non-array reporters with array including existing value
70+ const existingValue = reportersProp . value
71+ //@ts -ignore
72+ reportersProp . value = b . arrayExpression ( [ existingValue , reporterElement ] )
5673 } else {
74+ // Add new reporters property
5775 configObject . properties . push (
58- t . objectProperty ( t . identifier ( 'reporters' ) , t . arrayExpression ( [ reporterElement ] ) )
76+ b . property ( 'init' , b . identifier ( 'reporters' ) , b . arrayExpression ( [ reporterElement ] ) )
5977 )
6078 }
6179 }
6280
63- recast . types . visit ( ast , {
81+ visit ( ast , {
6482 visitImportDeclaration ( path ) {
6583 const { source, specifiers } = path . node
6684 if (
6785 source . value === pathToFileURL ( VSCODE_REPORTER_PATH ) . href &&
6886 specifiers &&
6987 //@ts -ignore
70- specifiers . some ( ( s ) => t . isImportDefaultSpecifier ( s ) && s . local . name === reporterIdentifierName )
88+ specifiers . some (
89+ //@ts -ignore
90+ ( s : any ) => t . namedTypes . ImportDefaultSpecifier . check ( s ) && s . local . name === reporterIdentifierName
91+ )
7192 ) {
7293 hasReporterImport = true
7394 }
@@ -77,21 +98,20 @@ export async function createTempConfigFile(filename: string, outDir: string) {
7798 visitExportNamedDeclaration ( path ) {
7899 const decl = path . node . declaration
79100
80- // @ts -ignore
81- if ( t . isVariableDeclaration ( decl ) ) {
101+ if ( t . namedTypes . VariableDeclaration . check ( decl ) ) {
82102 const first = decl . declarations [ 0 ]
83103
84- if ( t . isVariableDeclarator ( first ) ) {
104+ if ( t . namedTypes . VariableDeclarator . check ( first ) ) {
85105 const id = first . id
86106 const init = first . init
87107
88- if ( t . isIdentifier ( id ) && id . name === 'config' ) {
89- if ( t . isObjectExpression ( init ) ) {
108+ if ( t . namedTypes . Identifier . check ( id ) && id . name === 'config' ) {
109+ if ( t . namedTypes . ObjectExpression . check ( init ) ) {
90110 addOrUpdateReporters ( init )
91111 } else if (
92- t . isCallExpression ( init ) &&
112+ t . namedTypes . CallExpression . check ( init ) &&
93113 init . arguments . length > 0 &&
94- t . isObjectExpression ( init . arguments [ 0 ] )
114+ t . namedTypes . ObjectExpression . check ( init . arguments [ 0 ] )
95115 ) {
96116 const configObject = init . arguments [ 0 ]
97117 addOrUpdateReporters ( configObject )
@@ -111,12 +131,10 @@ export async function createTempConfigFile(filename: string, outDir: string) {
111131 }
112132
113133 if (
114- // @ts -ignore
115- t . isMemberExpression ( left ) &&
116- t . isIdentifier ( left . object ) &&
117- t . isIdentifier ( left . property ) &&
118- // @ts -ignore
119- t . isObjectExpression ( right )
134+ t . namedTypes . MemberExpression . check ( left ) &&
135+ t . namedTypes . Identifier . check ( left . object ) &&
136+ t . namedTypes . Identifier . check ( left . property ) &&
137+ t . namedTypes . ObjectExpression . check ( right )
120138 ) {
121139 const leftName = `${ left . object . name } .${ left . property . name } `
122140 if ( [ 'module.exports' , 'exports.config' ] . includes ( leftName ) ) {
@@ -128,13 +146,15 @@ export async function createTempConfigFile(filename: string, outDir: string) {
128146 } ,
129147
130148 visitCallExpression ( path ) {
131- const node = path . node as t . Node
149+ const node = path . node
132150
133151 if (
134- t . isCallExpression ( node ) &&
135- t . isIdentifier ( node . callee , { name : 'require' } ) &&
152+ t . namedTypes . CallExpression . check ( node ) &&
153+ t . namedTypes . Identifier . check ( node . callee ) &&
154+ node . callee . name === 'require' &&
136155 node . arguments . length === 1 &&
137- t . isStringLiteral ( node . arguments [ 0 ] ) &&
156+ t . namedTypes . Literal . check ( node . arguments [ 0 ] ) &&
157+ typeof node . arguments [ 0 ] . value === 'string' &&
138158 node . arguments [ 0 ] . value === pathToFileURL ( VSCODE_REPORTER_PATH ) . href
139159 ) {
140160 hasReporterImport = true
@@ -145,15 +165,15 @@ export async function createTempConfigFile(filename: string, outDir: string) {
145165 } )
146166
147167 if ( ! hasReporterImport ) {
148- const importedModule = t . importDeclaration (
149- [ t . importDefaultSpecifier ( reporterIdentifier ) ] ,
150- t . stringLiteral ( pathToFileURL ( VSCODE_REPORTER_PATH ) . href )
168+ const importedModule = b . importDeclaration (
169+ [ b . importDefaultSpecifier ( reporterIdentifier ) ] ,
170+ b . literal ( pathToFileURL ( VSCODE_REPORTER_PATH ) . href )
151171 )
152172
153173 ast . program . body . unshift ( importedModule )
154174 }
155175
156- const output = recast . print ( ast ) . code
176+ const output = print ( ast ) . code
157177 const ext = path . extname ( filename )
158178 const _filename = path . join ( path . dirname ( filename ) , `wdio-vscode-${ new Date ( ) . getTime ( ) } ${ ext } ` )
159179 await fs . writeFile ( _filename , output )
0 commit comments