Skip to content

Commit

Permalink
Merge pull request #18 from webdriverio/cb-typescript-support
Browse files Browse the repository at this point in the history
TypeScript support
  • Loading branch information
christian-bromann authored May 5, 2021
2 parents c553752 + 9677e32 commit 3993da6
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 120 deletions.
31 changes: 15 additions & 16 deletions common/compilers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ const {
COMPILER_OPTS_MAPPING
} = require('./constants')

exports.remove = function removeCompilers (j, root) {
const { isStringLiteral } = require('./utils')

exports.remove = function removeCompilers (j, root, opts) {
const autoCompileOpts = {}

/**
* remove compiler requires
*/
root.find(j.Property)
root.find(opts.parser === 'babel' ? j.Property : j.ObjectProperty)
.filter((path) => (
path.value.key && (
path.value.key.name === 'require' ||
Expand All @@ -23,7 +25,7 @@ exports.remove = function removeCompilers (j, root) {
j.arrayExpression(path.value.value.elements.filter((value) => {
let importName

if (value.type === 'Literal') {
if (isStringLiteral(value)) {
importName = value.value
} else if (value.type === 'ArrayExpression') {
importName = value.elements[0].value
Expand Down Expand Up @@ -57,8 +59,7 @@ exports.remove = function removeCompilers (j, root) {
})
root.find(j.ExpressionStatement, {
expression: { callee: {
callee: { name: 'require' },
arguments: [{ type: 'Literal' }]
callee: { name: 'require' }
} }
}).filter((path) => (
Object.keys(COMPILER_OPTS_MAPPING).includes(path.value.expression.callee.arguments[0].value)
Expand Down Expand Up @@ -87,26 +88,24 @@ exports.remove = function removeCompilers (j, root) {
return autoCompileOpts
}

exports.update = function (j, root, autoCompileOpts) {
exports.update = function (j, root, autoCompileOpts, opts) {
/**
* update config with compiler opts
*/
let wasReplaced = false
root.find(j.Property)
let wasInserted = false
root.find(opts.parser === 'babel' ? j.Property : j.ObjectProperty)
.filter((path) => (
path.value.key && (
path.value.key.name === 'capabilities' ||
path.value.key.name === 'framework'
)
))
.replaceWith((path) => {
if (wasReplaced) {
return path.value
.forEach((path) => {
if (wasInserted) {
return
}

wasReplaced = true
return [
path.value,
wasInserted = true
path.parentPath.value.push(
...(Object.keys(autoCompileOpts).length
? [j.objectProperty(
j.identifier('autoCompileOpts'),
Expand All @@ -125,6 +124,6 @@ exports.update = function (j, root, autoCompileOpts) {
)]
: []
)
]
)
})
}
12 changes: 12 additions & 0 deletions common/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function isStringLiteral (val) {
return ['Literal', 'StringLiteral'].includes(val.type)
}

function isNumericalLiteral (val) {
return ['Literal', 'NumericLiteral'].includes(val.type)
}

module.exports = {
isStringLiteral,
isNumericalLiteral
}
52 changes: 23 additions & 29 deletions protractor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ const {
replaceCommands,
parseConfigProperties,
sanitizeAsyncCalls,
failAsyncConstructor,
makeAsync
} = require('./utils')

module.exports = function transformer(file, api) {
module.exports = function transformer(file, api, opts) {
const j = api.jscodeshift;
const root = j(file.source);
j.file = file
Expand Down Expand Up @@ -91,7 +92,7 @@ module.exports = function transformer(file, api) {
)
})

const autoCompileOpts = compilers.remove(j, root)
const autoCompileOpts = compilers.remove(j, root, opts)

/**
* remove all protractor import declarations
Expand Down Expand Up @@ -253,7 +254,8 @@ module.exports = function transformer(file, api) {
})
.replaceWith((path) => j.memberExpression(
path.value.callee.object,
path.value.arguments[0]
path.value.arguments[0],
true
))

/**
Expand Down Expand Up @@ -570,7 +572,8 @@ module.exports = function transformer(file, api) {
j.memberExpression(
j.memberExpression(
path.value.callee.object.callee.object,
path.value.callee.object.arguments[0]
path.value.callee.object.arguments[0],
true
),
j.identifier(replaceCommands(command))
),
Expand Down Expand Up @@ -761,7 +764,7 @@ module.exports = function transformer(file, api) {
* transform element declarations in class constructors into getters
*/
const elementGetters = new Map()
root.find(j.MethodDefinition, { kind: 'constructor' }).replaceWith((path) => {
const setGetters = (path) => {
const isElementDeclaration = (e) => (
e.expression && e.expression.type === 'AssignmentExpression' &&
e.expression.left.object && e.expression.left.object.type === 'ThisExpression' &&
Expand All @@ -772,20 +775,16 @@ module.exports = function transformer(file, api) {
)
)

for (const e of path.value.value.body.body.filter(isElementDeclaration)) {
const body = path.value.body || path.value.value.body
const kind = path.value.kind || path.value.value.kind
const key = path.value.key || path.value.value.key
const params = path.value.params || path.value.value.params
for (const e of body.body.filter(isElementDeclaration)) {
elementGetters.set(e.expression.left.property, e.expression.right)
}

return [
j.methodDefinition(
path.value.kind,
path.value.key,
j.functionExpression(
path.value.value.id,
path.value.value.params,
j.blockStatement(path.value.value.body.body.filter((e) => !isElementDeclaration(e)))
)
),
j.classMethod(kind, key, params, j.blockStatement(body.body.filter((e) => !isElementDeclaration(e)))),
...[...elementGetters.entries()].map(([elemName, object]) => j.methodDefinition(
'get',
elemName,
Expand All @@ -798,7 +797,9 @@ module.exports = function transformer(file, api) {
)
))
]
})
}
root.find(j.ClassMethod, { key: { name: 'constructor' } }).replaceWith(setGetters)
root.find(j.MethodDefinition, { key: { name: 'constructor' } }).replaceWith(setGetters)

/**
* transform lazy loaded element calls in async context, e.g.
Expand Down Expand Up @@ -849,21 +850,14 @@ module.exports = function transformer(file, api) {
)).replaceWith((path) => {
j(path).closest(j.FunctionExpression).replaceWith(makeAsync)
j(path).closest(j.ArrowFunctionExpression).replaceWith(makeAsync)
j(path).closest(j.MethodDefinition, {
key: { name: 'constructor' }
}).forEach((p) => {
throw new TransformError('' +
`With "this.${path.value.property.name}" you are ` +
'trying to access an element within a constructor. Given that it ' +
'is not possible to run asynchronous code in this context, it ' +
'is advised to move this call into a method or getter function.',
path.value,
file
)
})
j(path).closest(j.ClassMethod).replaceWith(makeAsync)
const constructorFilter = { key: { name: 'constructor' } }
const throwConstructorError = () => failAsyncConstructor(path, file)
j(path).closest(j.MethodDefinition, constructorFilter).forEach(throwConstructorError)
j(path).closest(j.ClassMethod, constructorFilter).forEach(throwConstructorError)
return j.awaitExpression(path.value)
})

compilers.update(j, root, autoCompileOpts)
compilers.update(j, root, autoCompileOpts, opts)
return root.toSource()
}
Loading

0 comments on commit 3993da6

Please sign in to comment.