-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Expand file tree
/
Copy pathindex.ts
More file actions
130 lines (117 loc) · 5.64 KB
/
index.ts
File metadata and controls
130 lines (117 loc) · 5.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env node
import fs from 'node:fs'
import url from 'node:url'
import path from 'node:path'
import camelCase from 'camelcase'
import { type Plugin } from 'esbuild'
import { paramTypeMap, returnTypeMap } from './constants.js'
import { PROTOCOLS } from '../../../../scripts/protocols.js'
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
const TYPINGS_PATH = path.join(__dirname, '..', '..', '..', '..', 'packages', 'wdio-protocols', 'src', 'commands')
const INDENTATION = ' '.repeat(4)
const EXAMPLE_INDENTATION = `${INDENTATION} * `
const DEPRECATED_INDENTATION = `\n${INDENTATION} * @deprecated`
const jsDocTemplate = `
${INDENTATION}/**
${INDENTATION} * {PROTOCOL} Protocol Command
${INDENTATION} *
${INDENTATION} * {DESCRIPTION}
${INDENTATION} * @ref {REF}
${INDENTATION} *{EXAMPLE}
${INDENTATION} */`
export function generateTypes (): Plugin {
return {
name: 'generate-types',
setup (build) {
build.onStart(() => generate())
}
}
}
function generate () {
/**
* create directory if not existing
*/
if (!fs.existsSync(TYPINGS_PATH)) {
fs.mkdirSync(TYPINGS_PATH)
}
for (const [protocolName, definition] of Object.entries(PROTOCOLS)) {
if (protocolName === 'webdriverBidi') {
continue
}
const interfaceName = protocolName.slice(0, 1).toUpperCase() + protocolName.slice(1)
const customTypes = new Set()
const lines = ['']
lines.push(`// ${protocolName} types`)
lines.push(`export default interface ${interfaceName}Commands {`)
for (const methods of Object.values(definition)) {
for (const description of Object.values(methods)) {
const { command, parameters = [], variables = [], returns, ref, deprecated, examples } = description
if (!ref) {
throw new Error(`missing ref for command ${command} in ${protocolName}`)
}
const vars = variables
// sessionId is handled by WebdriverIO for all protocol requests
.filter((v) => v.name !== 'sessionId')
// url params are always type of string
.map((v) => `${v.name}: string`)
const params = parameters.map((p, idx) => {
const hasCustomType = (
paramTypeMap[command as keyof typeof paramTypeMap] &&
paramTypeMap[command as keyof typeof paramTypeMap][idx] &&
paramTypeMap[command as keyof typeof paramTypeMap][idx].name === p.name
)
const paramType = hasCustomType
? paramTypeMap[command as keyof typeof paramTypeMap][idx].type
: p.type.toLowerCase()
if (hasCustomType && Boolean(paramTypeMap[command as keyof typeof paramTypeMap][idx].requiresImport)) {
customTypes.add(paramType)
}
return `${camelCase(p.name)}${p.required !== true ? '?' : ''}: ${paramType}`
})
const varsAndParams = vars.concat(params)
let returnValue = returns ? returns.type.toLowerCase() : 'void'
returnValue = returnValue === '*' ? 'any' : returnValue
if (returnTypeMap[command as keyof typeof returnTypeMap]) {
returnValue = returnTypeMap[command as keyof typeof returnTypeMap]
customTypes.add(returnTypeMap[command as keyof typeof returnTypeMap].replace('[]', ''))
}
if (returnValue === 'object') {
returnValue = 'ProtocolCommandResponse'
customTypes.add(returnValue)
}
const jsDoc = jsDocTemplate
.replace('{PROTOCOL}', interfaceName)
.replace('{DESCRIPTION}', description.description || 'No description available, please see reference link.')
.replace('{EXAMPLE}', (
(examples || [])
.map((example) => (
`\n${EXAMPLE_INDENTATION}@example\n` +
`${EXAMPLE_INDENTATION}\`\`\`js\n` +
EXAMPLE_INDENTATION +
`${example.map((l, i) => (i === 0
? `${l}`
: `${EXAMPLE_INDENTATION}${l.replace(/(\/\*\*|\s\*\s|\s\*\/)/, '// ')}`.trimEnd())
).join('\n')}\n` +
`${EXAMPLE_INDENTATION}` + '```'
))
.join(`\n${EXAMPLE_INDENTATION}`.trim())
)
)
// Conditionally adds the '@deprecated' tag next to '{REF}' if 'deprecated' value is passed, otherwise leaves it unchanged.
.replace('{REF}', deprecated ? `{REF}${DEPRECATED_INDENTATION} ${deprecated}` : '{REF}')
.replace('{REF}', ref)
lines.push(jsDoc)
lines.push(`${INDENTATION}${command}(${varsAndParams.join(', ')}): Promise<${returnValue}>;`)
}
}
/**
* import missing protocol types
*/
if (customTypes.size) {
lines.unshift(`import type { ${[...customTypes].join(', ')} } from '../types.js'`)
}
lines.push('}')
fs.writeFileSync(path.join(TYPINGS_PATH, `${protocolName}.ts`), lines.join('\n'), 'utf8')
console.log(` 🧩 Generated typings file for ${protocolName}`)
}
}