From f059e0ccb88e273d24fd25fcaa380f1b2ea26a9b Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 25 Jul 2026 14:36:35 +0200 Subject: [PATCH 1/5] feat: Integrate shared oxc and release configs --- .editorconfig | 14 ++++++ .github/workflows/unit-test.yml | 2 +- .releaserc | 37 --------------- eslint.config.mjs | 10 ---- lib/helpers.ts | 10 ++-- lib/simctl.ts | 35 ++++++-------- lib/subcommands/addmedia.ts | 8 ++-- lib/subcommands/appinfo.ts | 16 ++----- lib/subcommands/bootstatus.ts | 19 ++------ lib/subcommands/create.ts | 7 ++- lib/subcommands/erase.ts | 1 + lib/subcommands/io.ts | 8 ++-- lib/subcommands/keychain.ts | 18 +++----- lib/subcommands/launch.ts | 7 +-- lib/subcommands/list.ts | 11 +---- lib/subcommands/location.ts | 6 +-- lib/subcommands/openurl.ts | 3 +- lib/subcommands/pbcopy.ts | 9 ++-- lib/subcommands/pbpaste.ts | 5 +- lib/subcommands/privacy.ts | 6 +-- lib/subcommands/push.ts | 8 ++-- lib/subcommands/spawn.ts | 3 +- lib/types.ts | 4 +- oxfmt.config.mjs | 6 +++ oxlint.config.mjs | 6 +++ package.json | 81 +++++++++++++++------------------ release.config.mjs | 3 ++ test/e2e/simctl-e2e.spec.ts | 27 +++++------ test/unit/simctl.spec.ts | 6 ++- tsconfig.json | 5 +- 30 files changed, 148 insertions(+), 233 deletions(-) create mode 100644 .editorconfig delete mode 100644 .releaserc delete mode 100644 eslint.config.mjs create mode 100644 oxfmt.config.mjs create mode 100644 oxlint.config.mjs create mode 100644 release.config.mjs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b8fa91e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 7c4acb0..6d4194d 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -39,6 +39,6 @@ jobs: - run: npm run lint name: Run linter - run: npm run format:check - name: Run Prettier check + name: Run format check - run: npm run test name: Run unit tests diff --git a/.releaserc b/.releaserc deleted file mode 100644 index 93cf006..0000000 --- a/.releaserc +++ /dev/null @@ -1,37 +0,0 @@ -{ - "plugins": [ - ["@semantic-release/commit-analyzer", { - "preset": "angular", - "releaseRules": [ - {"type": "chore", "release": "patch"} - ] - }], - ["@semantic-release/release-notes-generator", { - "preset": "conventionalcommits", - "presetConfig": { - "types": [ - {"type": "feat", "section": "Features"}, - {"type": "fix", "section": "Bug Fixes"}, - {"type": "perf", "section": "Performance Improvements"}, - {"type": "revert", "section": "Reverts"}, - {"type": "chore", "section": "Miscellaneous Chores"}, - {"type": "refactor", "section": "Code Refactoring"}, - {"type": "docs", "section": "Documentation", "hidden": true}, - {"type": "style", "section": "Styles", "hidden": true}, - {"type": "test", "section": "Tests", "hidden": true}, - {"type": "build", "section": "Build System", "hidden": true}, - {"type": "ci", "section": "Continuous Integration", "hidden": true} - ] - } - }], - ["@semantic-release/changelog", { - "changelogFile": "CHANGELOG.md" - }], - "@semantic-release/npm", - ["@semantic-release/git", { - "assets": ["docs", "package.json", "CHANGELOG.md"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" - }], - "@semantic-release/github" - ] -} diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 08011bc..0000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import appiumConfig from '@appium/eslint-config-appium-ts'; - -export default [ - ...appiumConfig, - { - ignores: [ - 'docs/**', - ], - }, -]; diff --git a/lib/helpers.ts b/lib/helpers.ts index 642971a..997ea0d 100644 --- a/lib/helpers.ts +++ b/lib/helpers.ts @@ -1,7 +1,8 @@ -import * as semver from 'semver'; import {spawn} from 'node:child_process'; import {Readable} from 'node:stream'; +import * as semver from 'semver'; + export const DEFAULT_EXEC_TIMEOUT = 10 * 60 * 1000; // ms export const SIM_RUNTIME_NAME = 'com.apple.CoreSimulator.SimRuntime.'; @@ -61,10 +62,9 @@ export async function convertPlistToJson(plistInput: string): Promise { }); } catch (err) { plutilProcess.kill(9); - throw new Error( - `Failed to convert plist to JSON: ${err instanceof Error ? err.message : String(err)}`, - {cause: err}, - ); + throw new Error(`Failed to convert plist to JSON: ${err instanceof Error ? err.message : String(err)}`, { + cause: err, + }); } finally { plutilProcess.removeAllListeners(); inputStream.removeAllListeners(); diff --git a/lib/simctl.ts b/lib/simctl.ts index 41f91a2..9cb1d95 100644 --- a/lib/simctl.ts +++ b/lib/simctl.ts @@ -1,7 +1,8 @@ +import {exec as tpExec, SubProcess} from 'teen_process'; import which from 'which'; -import {log, LOG_PREFIX} from './logger.js'; + import {DEFAULT_EXEC_TIMEOUT, getXcrunBinary} from './helpers.js'; -import {exec as tpExec, SubProcess} from 'teen_process'; +import {log, LOG_PREFIX} from './logger.js'; import * as addmediaCommands from './subcommands/addmedia.js'; import * as appinfoCommands from './subcommands/appinfo.js'; import * as bootCommands from './subcommands/boot.js'; @@ -10,23 +11,23 @@ import * as createCommands from './subcommands/create.js'; import * as deleteCommands from './subcommands/delete.js'; import * as eraseCommands from './subcommands/erase.js'; import * as getappcontainerCommands from './subcommands/get_app_container.js'; +import * as envCommands from './subcommands/getenv.js'; import * as installCommands from './subcommands/install.js'; import * as ioCommands from './subcommands/io.js'; import * as keychainCommands from './subcommands/keychain.js'; import * as launchCommands from './subcommands/launch.js'; import * as listCommands from './subcommands/list.js'; +import * as locationCommands from './subcommands/location.js'; import * as openurlCommands from './subcommands/openurl.js'; import * as pbcopyCommands from './subcommands/pbcopy.js'; import * as pbpasteCommands from './subcommands/pbpaste.js'; import * as privacyCommands from './subcommands/privacy.js'; import * as pushCommands from './subcommands/push.js'; -import * as envCommands from './subcommands/getenv.js'; import * as shutdownCommands from './subcommands/shutdown.js'; import * as spawnCommands from './subcommands/spawn.js'; import * as terminateCommands from './subcommands/terminate.js'; import * as uiCommands from './subcommands/ui.js'; import * as uninstallCommands from './subcommands/uninstall.js'; -import * as locationCommands from './subcommands/location.js'; import type {XCRun, ExecOpts, SimctlOpts, ExecResult} from './types.js'; const SIMCTL_ENV_PREFIX = 'SIMCTL_CHILD_'; @@ -113,8 +114,7 @@ export class Simctl { requireUdid(commandName: string | null = null): string { if (!this.udid) { throw new Error( - `udid is required to be set for ` + - (commandName ? `the '${commandName}' command` : 'this simctl command'), + `udid is required to be set for ` + (commandName ? `the '${commandName}' command` : 'this simctl command'), ); } return this.udid; @@ -130,10 +130,7 @@ export class Simctl { try { this.xcrun.path = await which(xcrunBinary); } catch { - throw new Error( - `${xcrunBinary} tool has not been found in PATH. ` + - `Are Xcode developers tools installed?`, - ); + throw new Error(`${xcrunBinary} tool has not been found in PATH. ` + `Are Xcode developers tools installed?`); } } if (!this.xcrun.path) { @@ -163,12 +160,7 @@ export class Simctl { timeout, } = opts ?? ({} as T); // run a particular simctl command - const args = [ - 'simctl', - ...(this.devicesSetPath ? ['--set', this.devicesSetPath] : []), - subcommand, - ...initialArgs, - ]; + const args = ['simctl', ...(this.devicesSetPath ? ['--set', this.devicesSetPath] : []), subcommand, ...initialArgs]; // Prefix all passed in environment variables with 'SIMCTL_CHILD_', simctl // will then pass these to the child (spawned) process. const envWithPrefixedKeys = Object.fromEntries( @@ -190,17 +182,16 @@ export class Simctl { try { let execArgs: [string, string[], any]; if (architectures?.length) { - const archArgs = (Array.isArray(architectures) ? architectures : [architectures]).flatMap( - (arch) => ['-arch', arch], - ); + const archArgs = (Array.isArray(architectures) ? architectures : [architectures]).flatMap((arch) => [ + '-arch', + arch, + ]); execArgs = ['arch', [...archArgs, xcrun, ...args], execOpts]; } else { execArgs = [xcrun, args, execOpts]; } // We know what we are doing here - the type system can't handle the dynamic nature - return ( - asynchronous ? new SubProcess(...execArgs) : await tpExec(...execArgs) - ) as ExecResult; + return (asynchronous ? new SubProcess(...execArgs) : await tpExec(...execArgs)) as ExecResult; } catch (e: any) { if (!this.logErrors || !logErrors) { // if we don't want to see the errors, just throw and allow the calling diff --git a/lib/subcommands/addmedia.ts b/lib/subcommands/addmedia.ts index a7da796..6d55ba1 100644 --- a/lib/subcommands/addmedia.ts +++ b/lib/subcommands/addmedia.ts @@ -1,6 +1,7 @@ -import type {Simctl} from '../simctl.js'; import type {TeenProcessExecResult} from 'teen_process'; +import type {Simctl} from '../simctl.js'; + /** * Add the particular media file to Simulator's library. * It is required that Simulator is in _booted_ state. @@ -12,10 +13,7 @@ import type {TeenProcessExecResult} from 'teen_process'; * returns non-zero return code. * @throws {Error} If the `udid` instance property is unset */ -export async function addMedia( - this: Simctl, - filePath: string, -): Promise> { +export async function addMedia(this: Simctl, filePath: string): Promise> { return await this.exec('addmedia', { args: [this.requireUdid('addmedia'), filePath], }); diff --git a/lib/subcommands/appinfo.ts b/lib/subcommands/appinfo.ts index 1cb89da..0cd1543 100644 --- a/lib/subcommands/appinfo.ts +++ b/lib/subcommands/appinfo.ts @@ -1,6 +1,6 @@ +import {convertPlistToJson} from '../helpers.js'; import type {Simctl} from '../simctl.js'; import type {AppInfo} from '../types.js'; -import {convertPlistToJson} from '../helpers.js'; /** * Get information about an app installed on the simulator @@ -24,19 +24,13 @@ export async function appInfo(this: Simctl, bundleId: string): Promise try { result = await convertPlistToJson(stdout); } catch (err) { - throw new Error( - `Cannot retrieve app info for ${bundleId}: ${err instanceof Error ? err.message : String(err)}`, - {cause: err}, - ); + throw new Error(`Cannot retrieve app info for ${bundleId}: ${err instanceof Error ? err.message : String(err)}`, { + cause: err, + }); } } - if ( - !result || - typeof result !== 'object' || - Array.isArray(result) || - !('ApplicationType' in result) - ) { + if (!result || typeof result !== 'object' || Array.isArray(result) || !('ApplicationType' in result)) { throw new Error(`App with bundle identifier "${bundleId}" not found. Is it installed?`); } diff --git a/lib/subcommands/bootstatus.ts b/lib/subcommands/bootstatus.ts index a4a3957..b1bcd47 100644 --- a/lib/subcommands/bootstatus.ts +++ b/lib/subcommands/bootstatus.ts @@ -1,8 +1,9 @@ -import {log, LOG_PREFIX} from '../logger.js'; import {waitForCondition} from 'asyncbox'; +import type {SubProcess} from 'teen_process'; + +import {log, LOG_PREFIX} from '../logger.js'; import type {Simctl} from '../simctl.js'; import type {BootMonitorOptions} from '../types.js'; -import type {SubProcess} from 'teen_process'; /** * Start monitoring for boot status of the particular Simulator. @@ -16,18 +17,8 @@ import type {SubProcess} from 'teen_process'; * property is not set. * @throws {Error} If the `udid` instance property is unset */ -export async function startBootMonitor( - this: Simctl, - opts: BootMonitorOptions = {}, -): Promise { - const { - timeout = 240000, - onWaitingDataMigration, - onWaitingSystemApp, - onFinished, - onError, - shouldPreboot, - } = opts; +export async function startBootMonitor(this: Simctl, opts: BootMonitorOptions = {}): Promise { + const {timeout = 240000, onWaitingDataMigration, onWaitingSystemApp, onFinished, onError, shouldPreboot} = opts; const udid = this.requireUdid('bootstatus'); const status: string[] = []; diff --git a/lib/subcommands/create.ts b/lib/subcommands/create.ts index b54a7de..e0ae467 100644 --- a/lib/subcommands/create.ts +++ b/lib/subcommands/create.ts @@ -1,6 +1,7 @@ -import {log, LOG_PREFIX} from '../logger.js'; import {retryInterval} from 'asyncbox'; + import {SIM_RUNTIME_NAME, normalizeVersion} from '../helpers.js'; +import {log, LOG_PREFIX} from '../logger.js'; import type {Simctl} from '../simctl.js'; import type {SimCreationOpts} from '../types.js'; @@ -58,9 +59,7 @@ export async function createDevice( // add modified versions, since modern Xcodes use this, then the bare // versions, to accomodate older Xcodes runtimeIds.push( - ...potentialRuntimeIds.map( - (id) => `${SIM_RUNTIME_NAME}${platform}-${id.replace(/\./g, '-')}`, - ), + ...potentialRuntimeIds.map((id) => `${SIM_RUNTIME_NAME}${platform}-${id.replace(/\./g, '-')}`), ...potentialRuntimeIds, ); } diff --git a/lib/subcommands/erase.ts b/lib/subcommands/erase.ts index 2a62e51..0067a17 100644 --- a/lib/subcommands/erase.ts +++ b/lib/subcommands/erase.ts @@ -1,4 +1,5 @@ import {retryInterval} from 'asyncbox'; + import type {Simctl} from '../simctl.js'; /** diff --git a/lib/subcommands/io.ts b/lib/subcommands/io.ts index 7afeed9..5ca2e29 100644 --- a/lib/subcommands/io.ts +++ b/lib/subcommands/io.ts @@ -1,8 +1,10 @@ -import {rimraf} from 'rimraf'; import {randomUUID} from 'node:crypto'; -import path from 'node:path'; -import os from 'node:os'; import fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; + +import {rimraf} from 'rimraf'; + import type {Simctl} from '../simctl.js'; /** diff --git a/lib/subcommands/keychain.ts b/lib/subcommands/keychain.ts index ed7ba85..a3fcb44 100644 --- a/lib/subcommands/keychain.ts +++ b/lib/subcommands/keychain.ts @@ -1,8 +1,10 @@ -import os from 'node:os'; -import fs from 'node:fs/promises'; import {randomUUID} from 'node:crypto'; +import fs from 'node:fs/promises'; +import os from 'node:os'; import path from 'node:path'; + import {rimraf} from 'rimraf'; + import type {Simctl} from '../simctl.js'; import type {CertOptions} from '../types.js'; @@ -18,11 +20,7 @@ import type {CertOptions} from '../types.js'; * or there was an error while adding the certificate * @throws {Error} If the `udid` instance property is unset */ -export async function addRootCertificate( - this: Simctl, - cert: string | Buffer, - opts: CertOptions = {}, -): Promise { +export async function addRootCertificate(this: Simctl, cert: string | Buffer, opts: CertOptions = {}): Promise { const {raw = false} = opts; const execMethod = async (certPath: string) => await this.exec('keychain', { @@ -47,11 +45,7 @@ export async function addRootCertificate( * or there was an error while adding the certificate * @throws {Error} If the `udid` instance property is unset */ -export async function addCertificate( - this: Simctl, - cert: string | Buffer, - opts: CertOptions = {}, -): Promise { +export async function addCertificate(this: Simctl, cert: string | Buffer, opts: CertOptions = {}): Promise { const {raw = false} = opts; const execMethod = async (certPath: string) => await this.exec('keychain', { diff --git a/lib/subcommands/launch.ts b/lib/subcommands/launch.ts index e9d516a..ec44913 100644 --- a/lib/subcommands/launch.ts +++ b/lib/subcommands/launch.ts @@ -1,4 +1,5 @@ import {retryInterval} from 'asyncbox'; + import type {Simctl} from '../simctl.js'; /** @@ -15,11 +16,7 @@ import type {Simctl} from '../simctl.js'; * returns non-zero return code. * @throws {Error} If the `udid` instance property is unset */ -export async function launchApp( - this: Simctl, - bundleId: string, - tries: number = 5, -): Promise { +export async function launchApp(this: Simctl, bundleId: string, tries: number = 5): Promise { const result = await retryInterval(tries, 1000, async () => { const {stdout} = await this.exec('launch', { args: [this.requireUdid('launch'), bundleId], diff --git a/lib/subcommands/list.ts b/lib/subcommands/list.ts index de07888..3295eec 100644 --- a/lib/subcommands/list.ts +++ b/lib/subcommands/list.ts @@ -94,11 +94,7 @@ export async function getDevicesByParsing( * returns non-zero return code or if no matching * platform version is found in the system. */ -export async function getDevices( - this: Simctl, - forSdk: string, - platform?: string | null, -): Promise; +export async function getDevices(this: Simctl, forSdk: string, platform?: string | null): Promise; export async function getDevices( this: Simctl, forSdk?: undefined | null, @@ -227,10 +223,7 @@ export async function getRuntimeForPlatformVersion( args: ['runtimes'], }); // https://regex101.com/r/UykjQZ/1 - const runtimeRe = new RegExp( - `${escapeRegExp(platform)}\\s+(\\d+\\.\\d+)\\s+\\((\\d+\\.\\d+\\.*\\d*)`, - 'i', - ); + const runtimeRe = new RegExp(`${escapeRegExp(platform)}\\s+(\\d+\\.\\d+)\\s+\\((\\d+\\.\\d+\\.*\\d*)`, 'i'); for (const line of stdout.split('\n')) { const match = runtimeRe.exec(line); if (match && match[1] === platformVersion) { diff --git a/lib/subcommands/location.ts b/lib/subcommands/location.ts index a42600f..02967e2 100644 --- a/lib/subcommands/location.ts +++ b/lib/subcommands/location.ts @@ -10,11 +10,7 @@ import type {Simctl} from '../simctl.js'; * returns non-zero return code. * @throws {TypeError} If any of the arguments is not a valid value. */ -export async function setLocation( - this: Simctl, - latitude: string | number, - longitude: string | number, -): Promise { +export async function setLocation(this: Simctl, latitude: string | number, longitude: string | number): Promise { const lat = formatArg('latitude', latitude); const lon = formatArg('longitude', longitude); await this.exec('location', { diff --git a/lib/subcommands/openurl.ts b/lib/subcommands/openurl.ts index a3c99b2..8ceb532 100644 --- a/lib/subcommands/openurl.ts +++ b/lib/subcommands/openurl.ts @@ -1,6 +1,7 @@ -import type {Simctl} from '../simctl.js'; import type {TeenProcessExecResult} from 'teen_process'; +import type {Simctl} from '../simctl.js'; + /** * Open URL scheme on Simulator. iOS will automatically try * to find a matching application, which supports the given scheme. diff --git a/lib/subcommands/pbcopy.ts b/lib/subcommands/pbcopy.ts index da4a6c9..4f2a46f 100644 --- a/lib/subcommands/pbcopy.ts +++ b/lib/subcommands/pbcopy.ts @@ -1,6 +1,7 @@ -import type {Simctl} from '../simctl.js'; import type {SubProcess} from 'teen_process'; +import type {Simctl} from '../simctl.js'; + /** * Set the content of Simulator pasteboard. * It is required that Simulator is in _booted_ state. @@ -13,11 +14,7 @@ import type {SubProcess} from 'teen_process'; * returns non-zero return code. * @throws {Error} If the `udid` instance property is unset */ -export async function setPasteboard( - this: Simctl, - content: string, - encoding: BufferEncoding = 'utf8', -): Promise { +export async function setPasteboard(this: Simctl, content: string, encoding: BufferEncoding = 'utf8'): Promise { const pbCopySubprocess = (await this.exec('pbcopy', { args: [this.requireUdid('pbcopy')], asynchronous: true, diff --git a/lib/subcommands/pbpaste.ts b/lib/subcommands/pbpaste.ts index 8e35232..43a9972 100644 --- a/lib/subcommands/pbpaste.ts +++ b/lib/subcommands/pbpaste.ts @@ -12,10 +12,7 @@ import type {Simctl} from '../simctl.js'; * returns non-zero return code. * @throws {Error} If the `udid` instance property is unset */ -export async function getPasteboard( - this: Simctl, - encoding: BufferEncoding = 'utf8', -): Promise { +export async function getPasteboard(this: Simctl, encoding: BufferEncoding = 'utf8'): Promise { const {stdout} = await this.exec('pbpaste', { args: [this.requireUdid('pbpaste')], encoding, diff --git a/lib/subcommands/privacy.ts b/lib/subcommands/privacy.ts index 6628c5c..cf2ca12 100644 --- a/lib/subcommands/privacy.ts +++ b/lib/subcommands/privacy.ts @@ -42,11 +42,7 @@ export async function grantPermission(this: Simctl, bundleId: string, perm: stri * or there was an error while revoking the permission * @throws {Error} If the `udid` instance property is unset */ -export async function revokePermission( - this: Simctl, - bundleId: string, - perm: string, -): Promise { +export async function revokePermission(this: Simctl, bundleId: string, perm: string): Promise { await this.exec('privacy', { args: [this.requireUdid('privacy revoke'), 'revoke', perm, bundleId], }); diff --git a/lib/subcommands/push.ts b/lib/subcommands/push.ts index ed98efe..fc36727 100644 --- a/lib/subcommands/push.ts +++ b/lib/subcommands/push.ts @@ -1,8 +1,10 @@ -import {rimraf} from 'rimraf'; import {randomUUID} from 'node:crypto'; -import path from 'node:path'; -import os from 'node:os'; import fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; + +import {rimraf} from 'rimraf'; + import type {Simctl} from '../simctl.js'; /** diff --git a/lib/subcommands/spawn.ts b/lib/subcommands/spawn.ts index 0457d5f..80061d0 100644 --- a/lib/subcommands/spawn.ts +++ b/lib/subcommands/spawn.ts @@ -1,6 +1,7 @@ -import type {Simctl} from '../simctl.js'; import type {TeenProcessExecResult, SubProcess} from 'teen_process'; +import type {Simctl} from '../simctl.js'; + /** * Spawn the particular process on Simulator. * It is required that Simulator is in _booted_ state. diff --git a/lib/types.ts b/lib/types.ts index c8d3914..8d4640a 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -139,9 +139,7 @@ export interface SimCreationOpts { /** * Result type for exec method - either SubProcess for async or TeenProcessExecResult for sync */ -export type ExecResult = T extends AsyncExecOpts - ? SubProcess - : TeenProcessExecResult; +export type ExecResult = T extends AsyncExecOpts ? SubProcess : TeenProcessExecResult; /** * Boot monitor options diff --git a/oxfmt.config.mjs b/oxfmt.config.mjs new file mode 100644 index 0000000..4752f81 --- /dev/null +++ b/oxfmt.config.mjs @@ -0,0 +1,6 @@ +import appiumConfig, {defineConfig, ignorePatterns} from '@appium/oxc-config/oxfmt'; + +export default defineConfig({ + ...appiumConfig, + ignorePatterns: [...ignorePatterns], +}); diff --git a/oxlint.config.mjs b/oxlint.config.mjs new file mode 100644 index 0000000..34ea238 --- /dev/null +++ b/oxlint.config.mjs @@ -0,0 +1,6 @@ +import appiumConfig, {defineConfig, ignorePatterns} from '@appium/oxc-config/oxlint'; + +export default defineConfig({ + extends: [appiumConfig], + ignorePatterns: [...ignorePatterns], +}); diff --git a/package.json b/package.json index 0ba2195..1715e69 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,16 @@ { "name": "node-simctl", - "description": "Wrapper around Apple's simctl binary", - "tags": [ - "apple", - "ios", - "simctl" - ], "version": "9.0.0", - "author": "Appium Contributors", + "description": "Wrapper around Apple's simctl binary", + "bugs": { + "url": "https://github.com/appium/node-simctl/issues" + }, "license": "Apache-2.0", + "author": "Appium Contributors", "repository": { "type": "git", "url": "https://github.com/appium/node-simctl.git" }, - "bugs": { - "url": "https://github.com/appium/node-simctl/issues" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", - "npm": ">=10" - }, - "main": "./build/lib/index.js", "bin": {}, "directories": { "lib": "./lib" @@ -30,38 +20,42 @@ "build/lib", "CHANGELOG.md" ], - "dependencies": { - "@appium/logger": "^2.0.0-rc.1", - "asyncbox": "^6.0.1", - "rimraf": "^6.0.1", - "semver": "^7.0.0", - "teen_process": "^4.0.4", - "which": "^6.0.0" + "type": "module", + "main": "./build/lib/index.js", + "types": "./build/lib/index.d.ts", + "exports": { + ".": { + "types": "./build/lib/index.d.ts", + "import": "./build/lib/index.js" + }, + "./package.json": "./package.json" }, "scripts": { "build": "tsc -b", "clean": "npm run build -- --clean", "rebuild": "npm run clean; npm run build", "dev": "npm run build -- --watch", - "lint": "eslint .", - "lint:fix": "npm run lint -- --fix", - "format": "prettier -w ./lib ./test", - "format:check": "prettier --check ./lib ./test", + "lint": "oxlint -c oxlint.config.mjs .", + "lint:fix": "oxlint -c oxlint.config.mjs --fix .", + "format": "oxfmt -c oxfmt.config.mjs .", + "format:check": "oxfmt -c oxfmt.config.mjs --check .", "prepare": "npm run build", "test": "node --enable-source-maps --test --test-timeout=60000 \"./build/test/unit/**/*.spec.js\"", "e2e-test": "node --enable-source-maps --test --test-force-exit --test-concurrency=1 --test-timeout=300000 \"./build/test/e2e/**/*.spec.js\"" }, - "prettier": { - "bracketSpacing": false, - "printWidth": 100, - "singleQuote": true + "dependencies": { + "@appium/logger": "^2.0.0-rc.1", + "asyncbox": "^6.0.1", + "rimraf": "^6.0.1", + "semver": "^7.0.0", + "teen_process": "^4.0.4", + "which": "^6.0.0" }, "devDependencies": { - "@appium/eslint-config-appium-ts": "^3.0.0", + "@appium/oxc-config": "^1.1.0", + "@appium/semantic-release-config": "^1.1.0", "@appium/tsconfig": "^1.0.0-rc.1", "@appium/types": "^1.0.0-rc.1", - "@semantic-release/changelog": "^6.0.1", - "@semantic-release/git": "^10.0.1", "@types/chai": "^5.2.3", "@types/chai-as-promised": "^8.0.2", "@types/node": "^26.0.0", @@ -71,19 +65,16 @@ "appium-xcode": "^6.0.0", "chai": "^6.0.0", "chai-as-promised": "^8.0.0", - "conventional-changelog-conventionalcommits": "^9.3.1", - "prettier": "^3.0.0", - "semantic-release": "^25.0.2", "sinon": "^22.0.0", "typescript": "^6.0.2" }, - "types": "./build/lib/index.d.ts", - "type": "module", - "exports": { - ".": { - "types": "./build/lib/index.d.ts", - "import": "./build/lib/index.js" - }, - "./package.json": "./package.json" - } + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "tags": [ + "apple", + "ios", + "simctl" + ] } diff --git a/release.config.mjs b/release.config.mjs new file mode 100644 index 0000000..83f3400 --- /dev/null +++ b/release.config.mjs @@ -0,0 +1,3 @@ +import releaseConfig from '@appium/semantic-release-config'; + +export default releaseConfig(); diff --git a/test/e2e/simctl-e2e.spec.ts b/test/e2e/simctl-e2e.spec.ts index e773f9f..c45c384 100644 --- a/test/e2e/simctl-e2e.spec.ts +++ b/test/e2e/simctl-e2e.spec.ts @@ -1,13 +1,15 @@ -import {Simctl} from '../../lib/simctl.js'; -import {rimraf} from 'rimraf'; import {randomUUID} from 'node:crypto'; -import path from 'node:path'; -import os from 'node:os'; import fs from 'node:fs/promises'; -import {expect, use} from 'chai'; -import chaiAsPromised from 'chai-as-promised'; +import os from 'node:os'; +import path from 'node:path'; import {describe, it, beforeEach, afterEach, after, before} from 'node:test'; + import {retryInterval} from 'asyncbox'; +import {expect, use} from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import {rimraf} from 'rimraf'; + +import {Simctl} from '../../lib/simctl.js'; use(chaiAsPromised); const BOOT_TIMEOUT_MS = 200000; @@ -180,11 +182,7 @@ describe('simctl', function () { let picturePath: string | undefined; before(async function () { picturePath = path.join(os.tmpdir(), `${randomUUID()}.png`); - await fs.writeFile( - picturePath, - Buffer.from(BASE64_PNG, 'base64').toString('binary'), - 'binary', - ); + await fs.writeFile(picturePath, Buffer.from(BASE64_PNG, 'base64').toString('binary'), 'binary'); }); after(async function () { if (picturePath) { @@ -237,14 +235,11 @@ describe('simctl', function () { expect(fullList.devicetypes.length).to.be.above(1); // at least one type, no matter the version of Xcode, should be an iPhone expect( - fullList.devicetypes.filter((el: {identifier: string}) => - el.identifier.includes('iPhone'), - ).length, + fullList.devicetypes.filter((el: {identifier: string}) => el.identifier.includes('iPhone')).length, ).to.be.above(0); // at least one runtime should be iOS expect( - fullList.runtimes.filter((el: {identifier: string}) => el.identifier.includes('iOS')) - .length, + fullList.runtimes.filter((el: {identifier: string}) => el.identifier.includes('iOS')).length, ).to.be.above(0); }); }); diff --git a/test/unit/simctl.spec.ts b/test/unit/simctl.spec.ts index 245abb5..8b3dc82 100644 --- a/test/unit/simctl.spec.ts +++ b/test/unit/simctl.spec.ts @@ -1,11 +1,13 @@ -import sinon from 'sinon'; import fs from 'node:fs'; import path from 'node:path'; +import {describe, it, beforeEach, afterEach, after} from 'node:test'; import {fileURLToPath} from 'node:url'; + import {expect, use} from 'chai'; import chaiAsPromised from 'chai-as-promised'; +import sinon from 'sinon'; + import {Simctl} from '../../lib/simctl.js'; -import {describe, it, beforeEach, afterEach, after} from 'node:test'; use(chaiAsPromised); const MODULE_NAME = 'node-simctl'; diff --git a/tsconfig.json b/tsconfig.json index 36b96b7..10acb16 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,8 +9,5 @@ "module": "NodeNext", "moduleResolution": "NodeNext" }, - "include": [ - "lib", - "test" - ] + "include": ["lib", "test"] } From c130514f55a95d8dfd36d99147603d16ab862b97 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 25 Jul 2026 15:30:34 +0200 Subject: [PATCH 2/5] bump tsconfig --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1715e69..014a24e 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "devDependencies": { "@appium/oxc-config": "^1.1.0", "@appium/semantic-release-config": "^1.1.0", - "@appium/tsconfig": "^1.0.0-rc.1", + "@appium/tsconfig": "^1.2.0", "@appium/types": "^1.0.0-rc.1", "@types/chai": "^5.2.3", "@types/chai-as-promised": "^8.0.2", @@ -65,8 +65,7 @@ "appium-xcode": "^6.0.0", "chai": "^6.0.0", "chai-as-promised": "^8.0.0", - "sinon": "^22.0.0", - "typescript": "^6.0.2" + "sinon": "^22.0.0" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0", From 4f24a31f7efa51616b91035013799014eb2b9859 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 25 Jul 2026 15:31:18 +0200 Subject: [PATCH 3/5] bump xcode --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 014a24e..3499595 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "@types/semver": "^7.7.1", "@types/sinon": "^22.0.0", "@types/which": "^3.0.4", - "appium-xcode": "^6.0.0", + "appium-xcode": "^7.0.0", "chai": "^6.0.0", "chai-as-promised": "^8.0.0", "sinon": "^22.0.0" From 13631e34f76d09cff7a98d2d6e1ec8b231abf711 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 25 Jul 2026 19:40:45 +0200 Subject: [PATCH 4/5] address comments --- lib/simctl.ts | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/simctl.ts b/lib/simctl.ts index 9cb1d95..9157c1f 100644 --- a/lib/simctl.ts +++ b/lib/simctl.ts @@ -130,7 +130,7 @@ export class Simctl { try { this.xcrun.path = await which(xcrunBinary); } catch { - throw new Error(`${xcrunBinary} tool has not been found in PATH. ` + `Are Xcode developers tools installed?`); + throw new Error(`${xcrunBinary} tool has not been found in PATH. Are Xcode developers tools installed?`); } } if (!this.xcrun.path) { diff --git a/package.json b/package.json index 3499595..746fa2f 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": ">=10" }, - "tags": [ + "keywords": [ "apple", "ios", "simctl" From 9399c7aa364462b9b7c0af1f104b443a4d62cf4f Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 25 Jul 2026 19:43:30 +0200 Subject: [PATCH 5/5] format --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 746fa2f..8888cbd 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,11 @@ "name": "node-simctl", "version": "9.0.0", "description": "Wrapper around Apple's simctl binary", + "keywords": [ + "apple", + "ios", + "simctl" + ], "bugs": { "url": "https://github.com/appium/node-simctl/issues" }, @@ -70,10 +75,5 @@ "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": ">=10" - }, - "keywords": [ - "apple", - "ios", - "simctl" - ] + } }