Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Webdriverio Webdriverio InitializePlugin Function

From Leeroopedia
Revision as of 11:57, 16 February 2026 by Admin (talk | contribs) (Auto-imported from implementations/Webdriverio_Webdriverio_InitializePlugin_Function.md)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Template:Metadata

Overview

Concrete tool for dynamically resolving and importing WebdriverIO plugins provided by the @wdio/utils package.

Description

The initializePlugin function resolves plugin module names using a convention-based approach. It tries scoped names (@wdio/{name}-{type}), then community names (wdio-{name}-{type}), and supports direct imports for scoped packages and absolute paths.

The function is the core of WDIO's plugin ecosystem, used every time a plugin is specified by string name in the configuration. It works in conjunction with:

  • initializeServices -- Resolves service entries from the config into initialized service instances
  • initializeLauncherService -- Creates launcher-process service instances with proper separation from worker services
  • initializeWorkerService -- Creates worker-process service instances, filtering out launcher-only services

The resolution relies on safeImport, a utility that wraps dynamic import() in a try/catch, returning undefined on failure instead of throwing. This enables the chain-of-responsibility pattern where each naming convention is tried in sequence.

Source Files

File Purpose Lines
packages/wdio-utils/src/initializePlugin.ts Plugin resolution function L13-51
packages/wdio-utils/src/initializeServices.ts Service initialization pipeline L23-196

Code Reference

initializePlugin Function

// packages/wdio-utils/src/initializePlugin.ts:L13-51
import type { Services } from '@wdio/types'
import { safeImport, isAbsolute, REG_EXP_WINDOWS_ABS_PATH, SLASH } from './utils.js'

const FILE_PROTOCOL = 'file://'

/**
 * Initialize WebdriverIO compliant plugins like reporter or services in the following way:
 * 1. if package name is scoped (starts with "@"), require scoped package name
 * 2. otherwise try to require "@wdio/<name>-<type>"
 * 3. otherwise try to require "wdio-<name>-<type>"
 */
export default async function initializePlugin(
    name: string,
    type?: string
): Promise<Services.ServicePlugin | Services.RunnerPlugin> {
    /**
     * directly import packages that are scoped or start with an absolute path
     */
    if (name[0] === '@' || isAbsolute(name)) {
        const fileUrl = name[0] === '@' ? name : ensureFileURL(name)
        const service = await safeImport(fileUrl)
        if (service) {
            return service
        }
    }

    if (typeof type !== 'string') {
        throw new Error('No plugin type provided')
    }

    /**
     * check for scoped version of plugin first (e.g. @wdio/sauce-service)
     */
    const scopedPlugin = await safeImport(`@wdio/${name.toLowerCase()}-${type}`)
    if (scopedPlugin) {
        return scopedPlugin
    }

    /**
     * check for community naming convention
     */
    const plugin = await safeImport(`wdio-${name.toLowerCase()}-${type}`)
    if (plugin) {
        return plugin
    }

    throw new Error(
        `Couldn't find plugin "${name}" ${type}, neither as wdio scoped package ` +
        `"@wdio/${name.toLowerCase()}-${type}" nor as community package ` +
        `"wdio-${name.toLowerCase()}-${type}". Please make sure you have it installed!`
    )
}

ensureFileURL Helper

// packages/wdio-utils/src/initializePlugin.ts:L53-69
function ensureFileURL(path: string) {
    if (path.startsWith(FILE_PROTOCOL)) {
        return path
    }
    // Windows drive path
    if (REG_EXP_WINDOWS_ABS_PATH.test(path)) {
        return `${FILE_PROTOCOL}/${path.replace(/\\/g, '/')}`
    }
    // Unix absolute path
    if (path.startsWith(SLASH)) {
        return `${FILE_PROTOCOL}${path}`
    }
    return path
}

initializeLauncherService Function

// packages/wdio-utils/src/initializeServices.ts:L94-151
export async function initializeLauncherService(
    config: Omit<WebdriverIO.Config, 'capabilities' | keyof Services.HookFunctions>,
    caps: Capabilities.TestrunnerCapabilities
): Promise<{
    ignoredWorkerServices: string[];
    launcherServices: Services.ServiceInstance[];
}> {
    const ignoredWorkerServices = []
    const launcherServices: Services.ServiceInstance[] = []

    const services = await initializeServices(config.services!.map(sanitizeServiceArray))
    for (const [service, serviceConfig, serviceName] of services) {
        // Object services: use directly
        if (typeof service === 'object' && !serviceName) {
            launcherServices.push(service as object)
            continue
        }
        // Imported package with launcher export
        const Launcher = (service as Services.ServicePlugin).launcher
        if (typeof Launcher === 'function' && serviceName) {
            launcherServices.push(new Launcher(serviceConfig, caps, config))
        }
        // Class reference passed directly
        if (typeof service === 'function' && !serviceName) {
            launcherServices.push(new service(serviceConfig, caps, config))
        }
        // Mark launcher-only services to skip in workers
        if (serviceName && typeof (service as { default: Function }).default !== 'function'
            && typeof service !== 'function') {
            ignoredWorkerServices.push(serviceName)
        }
    }
    return { ignoredWorkerServices, launcherServices }
}

initializeWorkerService Function

// packages/wdio-utils/src/initializeServices.ts:L161-196
export async function initializeWorkerService(
    config: WebdriverIO.Config,
    caps: WebdriverIO.Capabilities,
    ignoredWorkerServices: string[] = []
): Promise<Services.ServiceInstance[]> {
    const initializedServices: Services.ServiceInstance[] = []
    const workerServices = config.services!
        .map(sanitizeServiceArray)
        .filter(([serviceName]) => !ignoredWorkerServices.includes(serviceName as string))

    const services = await initializeServices(workerServices)
    for (const [service, serviceConfig, serviceName] of services) {
        if (typeof service === 'object' && !serviceName) {
            initializedServices.push(service as Services.ServiceInstance)
            continue
        }
        const Service = (service as Services.ServicePlugin).default || service as Services.ServiceClass
        if (typeof Service === 'function') {
            initializedServices.push(new Service(serviceConfig, caps, config))
            continue
        }
    }
    return initializedServices
}

I/O Contract

initializePlugin

Import:

import { initializePlugin } from '@wdio/utils'

Inputs:

Parameter Type Required Description
name string Yes Plugin name: short name ('browserstack'), scoped name ('@wdio/browserstack-service'), or absolute path
type string No Plugin type: 'service', 'reporter', or 'runner'. Required if name is not scoped or absolute.

Output: Promise<Services.ServicePlugin | Services.RunnerPlugin> -- The imported plugin module

Errors:

  • 'No plugin type provided' -- When type is not a string and the name is not scoped or absolute
  • 'Couldn't find plugin "{name}" {type}...' -- When no naming convention yields a valid import

Resolution Order

Step Condition Attempted Import
1 Name starts with @ Direct import of scoped package name
2 Name is an absolute path Direct import via file:// URL
3 Scoped convention @wdio/{name.toLowerCase()}-{type}
4 Community convention wdio-{name.toLowerCase()}-{type}
5 All failed Throw descriptive error

Related Functions

Function Signature Purpose
initializeLauncherService (config, caps) => { ignoredWorkerServices, launcherServices } Initialize services for the launcher process, track launcher-only services
initializeWorkerService (config, caps, ignored) => ServiceInstance[] Initialize services for worker processes, filtering out launcher-only services
executeHooksWithArgs Error)[]> Execute all registered hook implementations, collecting results and errors

Usage Examples

How WDIO uses initializePlugin internally:

// packages/wdio-utils/src/initializeServices.ts:L67-69
// When a service is specified as a string name:
log.debug(`initialize service "${serviceName}" as NPM package`)
const service = await initializePlugin(serviceName, 'service')
initializedServices.push([service as Services.ServiceClass, serviceConfig, serviceName])

Resolution example walkthrough:

Input: name='browserstack', type='service'

Step 1: name[0] !== '@' and not absolute path → skip direct import
Step 2: type is 'string' → continue
Step 3: safeImport('@wdio/browserstack-service') → module found → return module
Input: name='my-custom', type='service'

Step 1: name[0] !== '@' and not absolute path → skip direct import
Step 2: type is 'string' → continue
Step 3: safeImport('@wdio/my-custom-service') → undefined (not found)
Step 4: safeImport('wdio-my-custom-service') → undefined (not found)
Step 5: throw Error("Couldn't find plugin \"my-custom\" service...")
Input: name='@my-org/special-service', type='service'

Step 1: name[0] === '@' → safeImport('@my-org/special-service') → module found → return module

Configuration patterns that trigger resolution:

// wdio.conf.ts
export const config: WebdriverIO.Config = {
    // All of these trigger initializePlugin:
    services: [
        'browserstack',                        // → @wdio/browserstack-service
        'sauce',                               // → @wdio/sauce-service
        '@my-org/custom-service',              // → @my-org/custom-service (direct)
        ['testingbot', { tbTunnel: true }],    // → @wdio/testingbot-service
    ],
    reporters: [
        'spec',                                // → @wdio/spec-reporter
        'allure',                              // → @wdio/allure-reporter
    ]
}

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment